@danielsimonjr/memoryjs 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20844,6 +20844,27 @@ CREATE INDEX IF NOT EXISTS idx_entities_project_id ON entities(project_id);
20844
20844
  CREATE INDEX IF NOT EXISTS idx_entities_content_hash ON entities(content_hash);
20845
20845
  CREATE INDEX IF NOT EXISTS idx_entities_tags_gin ON entities USING GIN(tags);
20846
20846
 
20847
+ -- v2.8.0 \u2014 generated tsvector column for full-text search.
20848
+ -- name is weighted A (highest), observations B, tags C. PostgreSQL 12+
20849
+ -- supports GENERATED ALWAYS AS ... STORED; the DO block makes the column
20850
+ -- add idempotent on older + newer servers without requiring CREATE OR
20851
+ -- REPLACE semantics on ALTER.
20852
+ DO $$ BEGIN
20853
+ IF NOT EXISTS (
20854
+ SELECT 1 FROM information_schema.columns
20855
+ WHERE table_name = 'entities' AND column_name = 'fts_vector'
20856
+ ) THEN
20857
+ ALTER TABLE entities ADD COLUMN fts_vector tsvector
20858
+ GENERATED ALWAYS AS (
20859
+ setweight(to_tsvector('english', name), 'A') ||
20860
+ setweight(to_tsvector('english', coalesce(array_to_string(observations, ' '), '')), 'B') ||
20861
+ setweight(to_tsvector('english', coalesce(array_to_string(tags, ' '), '')), 'C')
20862
+ ) STORED;
20863
+ END IF;
20864
+ END $$;
20865
+
20866
+ CREATE INDEX IF NOT EXISTS idx_entities_fts_gin ON entities USING GIN(fts_vector);
20867
+
20847
20868
  CREATE TABLE IF NOT EXISTS relations (
20848
20869
  from_name TEXT NOT NULL,
20849
20870
  to_name TEXT NOT NULL,
@@ -21163,6 +21184,38 @@ var PostgreSQLStorage = class {
21163
21184
  hasRelations(entityName) {
21164
21185
  return (this.outgoingRelations.get(entityName)?.length ?? 0) + (this.incomingRelations.get(entityName)?.length ?? 0) > 0;
21165
21186
  }
21187
+ // ==================== Full-text search ====================
21188
+ /**
21189
+ * tsvector-backed full-text search. Uses `plainto_tsquery` so the input is
21190
+ * free-form (no boolean operator syntax required); ranks via `ts_rank` on
21191
+ * the weighted `fts_vector` column (name × A, observations × B, tags × C).
21192
+ *
21193
+ * Results are ordered by descending score and capped at `options.limit`
21194
+ * (default 50). Empty / whitespace-only queries return `[]` without
21195
+ * issuing a SQL call.
21196
+ *
21197
+ * @example
21198
+ * ```typescript
21199
+ * const matches = await storage.fullTextSearch('authentication flow', { limit: 10 });
21200
+ * // → [{ name: 'AuthService', score: 0.62 }, ...]
21201
+ * ```
21202
+ */
21203
+ async fullTextSearch(query, options = {}) {
21204
+ const trimmed = query.trim();
21205
+ if (trimmed.length === 0) return [];
21206
+ await this.initSchema();
21207
+ const pool = await this.getPool();
21208
+ const limit = options.limit ?? 50;
21209
+ const res = await pool.query(
21210
+ `SELECT name, ts_rank(fts_vector, plainto_tsquery('english', $1))::float AS score
21211
+ FROM entities
21212
+ WHERE fts_vector @@ plainto_tsquery('english', $1)
21213
+ ORDER BY score DESC
21214
+ LIMIT $2`,
21215
+ [trimmed, limit]
21216
+ );
21217
+ return res.rows.map((r) => ({ name: r.name, score: Number(r.score) }));
21218
+ }
21166
21219
  // ==================== Utility ====================
21167
21220
  getFilePath() {
21168
21221
  return this.connectionString;