@memberjunction/search-engine 0.0.1 → 5.26.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/generic/EntitySearchProvider.d.ts +54 -0
- package/dist/generic/EntitySearchProvider.d.ts.map +1 -0
- package/dist/generic/EntitySearchProvider.js +207 -0
- package/dist/generic/EntitySearchProvider.js.map +1 -0
- package/dist/generic/FullTextSearchProvider.d.ts +35 -0
- package/dist/generic/FullTextSearchProvider.d.ts.map +1 -0
- package/dist/generic/FullTextSearchProvider.js +89 -0
- package/dist/generic/FullTextSearchProvider.js.map +1 -0
- package/dist/generic/ISearchProvider.d.ts +90 -0
- package/dist/generic/ISearchProvider.d.ts.map +1 -0
- package/dist/generic/ISearchProvider.js +62 -0
- package/dist/generic/ISearchProvider.js.map +1 -0
- package/dist/generic/SearchEngine.d.ts +140 -0
- package/dist/generic/SearchEngine.d.ts.map +1 -0
- package/dist/generic/SearchEngine.js +446 -0
- package/dist/generic/SearchEngine.js.map +1 -0
- package/dist/generic/SearchEnricher.d.ts +69 -0
- package/dist/generic/SearchEnricher.d.ts.map +1 -0
- package/dist/generic/SearchEnricher.js +241 -0
- package/dist/generic/SearchEnricher.js.map +1 -0
- package/dist/generic/SearchFusion.d.ts +62 -0
- package/dist/generic/SearchFusion.d.ts.map +1 -0
- package/dist/generic/SearchFusion.js +147 -0
- package/dist/generic/SearchFusion.js.map +1 -0
- package/dist/generic/StorageSearchProvider.d.ts +87 -0
- package/dist/generic/StorageSearchProvider.d.ts.map +1 -0
- package/dist/generic/StorageSearchProvider.js +322 -0
- package/dist/generic/StorageSearchProvider.js.map +1 -0
- package/dist/generic/VectorSearchProvider.d.ts +65 -0
- package/dist/generic/VectorSearchProvider.d.ts.map +1 -0
- package/dist/generic/VectorSearchProvider.js +347 -0
- package/dist/generic/VectorSearchProvider.js.map +1 -0
- package/dist/generic/search.types.d.ts +151 -0
- package/dist/generic/search.types.d.ts.map +1 -0
- package/dist/generic/search.types.js +11 -0
- package/dist/generic/search.types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -7
- package/README.md +0 -45
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Entity search provider using RunView with UserSearchString.
|
|
3
|
+
*
|
|
4
|
+
* Searches entities where AllowUserSearchAPI=true using the database-native
|
|
5
|
+
* LIKE-based search infrastructure. Returns scored candidates suitable for
|
|
6
|
+
* RRF fusion with vector and full-text results.
|
|
7
|
+
*
|
|
8
|
+
* @module @memberjunction/search-engine
|
|
9
|
+
*/
|
|
10
|
+
import { UserInfo } from '@memberjunction/core';
|
|
11
|
+
import { BaseSearchProvider } from './ISearchProvider.js';
|
|
12
|
+
import { SearchSource, SearchFilters, SearchResultItem } from './search.types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Provides entity-level LIKE-based search using RunView + UserSearchString.
|
|
15
|
+
* Searches all entities where AllowUserSearchAPI=true, returning results
|
|
16
|
+
* with rank-based scores.
|
|
17
|
+
*/
|
|
18
|
+
export declare class EntitySearchProvider extends BaseSearchProvider {
|
|
19
|
+
readonly SourceType: SearchSource;
|
|
20
|
+
/**
|
|
21
|
+
* Execute an entity search across all entities with AllowUserSearchAPI=true.
|
|
22
|
+
*
|
|
23
|
+
* @param query - The search query text
|
|
24
|
+
* @param topK - Maximum number of results per entity
|
|
25
|
+
* @param filters - Optional filters (EntityNames to restrict search scope)
|
|
26
|
+
* @param contextUser - The user performing the search
|
|
27
|
+
* @returns Scored result items from entity search
|
|
28
|
+
*/
|
|
29
|
+
Search(query: string, topK: number, filters: SearchFilters | undefined, contextUser: UserInfo): Promise<SearchResultItem[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Get the list of entities eligible for search, optionally filtered by name.
|
|
32
|
+
*/
|
|
33
|
+
private getSearchableEntities;
|
|
34
|
+
/**
|
|
35
|
+
* Search a single entity using RunView with UserSearchString.
|
|
36
|
+
*/
|
|
37
|
+
private searchOneEntity;
|
|
38
|
+
/**
|
|
39
|
+
* Convert RunView results to SearchResultItem format with field-match relevance scores.
|
|
40
|
+
* Score is based on how many searchable fields contain the query and whether
|
|
41
|
+
* the match is in a name/title field (higher weight) vs description/other fields.
|
|
42
|
+
*/
|
|
43
|
+
private convertResults;
|
|
44
|
+
/**
|
|
45
|
+
* Extract a display title from record data using entity metadata.
|
|
46
|
+
* Combines IsNameField fields in Sequence order.
|
|
47
|
+
*/
|
|
48
|
+
private extractTitle;
|
|
49
|
+
/**
|
|
50
|
+
* Extract a display snippet from record data using entity metadata.
|
|
51
|
+
*/
|
|
52
|
+
private extractSnippet;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=EntitySearchProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntitySearchProvider.d.ts","sourceRoot":"","sources":["../../src/generic/EntitySearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAA0C,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAExF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAoB,MAAM,gBAAgB,CAAC;AAEjG;;;;GAIG;AACH,qBACa,oBAAqB,SAAQ,kBAAkB;IACxD,SAAgB,UAAU,EAAE,YAAY,CAAY;IAEpD;;;;;;;;OAQG;IACU,MAAM,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyC9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;OAEG;YACW,eAAe;IA4B7B;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAwDtB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAmCpB;;OAEG;IACH,OAAO,CAAC,cAAc;CAczB"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Entity search provider using RunView with UserSearchString.
|
|
3
|
+
*
|
|
4
|
+
* Searches entities where AllowUserSearchAPI=true using the database-native
|
|
5
|
+
* LIKE-based search infrastructure. Returns scored candidates suitable for
|
|
6
|
+
* RRF fusion with vector and full-text results.
|
|
7
|
+
*
|
|
8
|
+
* @module @memberjunction/search-engine
|
|
9
|
+
*/
|
|
10
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
11
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
12
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
13
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
|
+
};
|
|
16
|
+
import { LogError, LogStatus, Metadata, RunView } from '@memberjunction/core';
|
|
17
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
18
|
+
import { BaseSearchProvider } from './ISearchProvider.js';
|
|
19
|
+
/**
|
|
20
|
+
* Provides entity-level LIKE-based search using RunView + UserSearchString.
|
|
21
|
+
* Searches all entities where AllowUserSearchAPI=true, returning results
|
|
22
|
+
* with rank-based scores.
|
|
23
|
+
*/
|
|
24
|
+
let EntitySearchProvider = class EntitySearchProvider extends BaseSearchProvider {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(...arguments);
|
|
27
|
+
this.SourceType = 'entity';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Execute an entity search across all entities with AllowUserSearchAPI=true.
|
|
31
|
+
*
|
|
32
|
+
* @param query - The search query text
|
|
33
|
+
* @param topK - Maximum number of results per entity
|
|
34
|
+
* @param filters - Optional filters (EntityNames to restrict search scope)
|
|
35
|
+
* @param contextUser - The user performing the search
|
|
36
|
+
* @returns Scored result items from entity search
|
|
37
|
+
*/
|
|
38
|
+
async Search(query, topK, filters, contextUser) {
|
|
39
|
+
try {
|
|
40
|
+
const md = new Metadata();
|
|
41
|
+
const searchableEntities = this.getSearchableEntities(md, filters);
|
|
42
|
+
if (searchableEntities.length === 0) {
|
|
43
|
+
LogStatus('EntitySearchProvider: No searchable entities found');
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
// Debug: log searchable entities and their search fields
|
|
47
|
+
LogStatus(`EntitySearchProvider: Searching ${searchableEntities.length} entities for "${query}"`);
|
|
48
|
+
for (const e of searchableEntities.slice(0, 3)) {
|
|
49
|
+
const entity = md.Entities.find(ent => ent.Name === e.Name);
|
|
50
|
+
if (entity) {
|
|
51
|
+
const searchFields = entity.Fields.filter(f => f.IncludeInUserSearchAPI);
|
|
52
|
+
LogStatus(` Entity "${e.Name}": ${searchFields.length} searchable fields [${searchFields.slice(0, 5).map(f => f.Name).join(', ')}${searchFields.length > 5 ? '...' : ''}]`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Calculate per-entity limit: distribute topK across entities
|
|
56
|
+
const perEntityLimit = Math.max(3, Math.ceil(topK / Math.max(1, searchableEntities.length)));
|
|
57
|
+
// Search all entities in parallel
|
|
58
|
+
const searchPromises = searchableEntities.map(entity => this.searchOneEntity(entity.Name, query, perEntityLimit, contextUser));
|
|
59
|
+
const results = await Promise.all(searchPromises);
|
|
60
|
+
const allResults = results.flat();
|
|
61
|
+
// Sort by score descending and limit to topK
|
|
62
|
+
allResults.sort((a, b) => b.Score - a.Score);
|
|
63
|
+
return allResults.slice(0, topK);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
67
|
+
LogError(`EntitySearchProvider: Search failed: ${msg}`);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the list of entities eligible for search, optionally filtered by name.
|
|
73
|
+
*/
|
|
74
|
+
getSearchableEntities(md, filters) {
|
|
75
|
+
let entities = md.Entities.filter(e => e.AllowUserSearchAPI);
|
|
76
|
+
if (filters?.EntityNames?.length) {
|
|
77
|
+
const allowedNames = new Set(filters.EntityNames.map(n => n.toLowerCase()));
|
|
78
|
+
entities = entities.filter(e => allowedNames.has(e.Name.toLowerCase()));
|
|
79
|
+
}
|
|
80
|
+
return entities;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Search a single entity using RunView with UserSearchString.
|
|
84
|
+
*/
|
|
85
|
+
async searchOneEntity(entityName, query, maxRows, contextUser) {
|
|
86
|
+
try {
|
|
87
|
+
const rv = new RunView();
|
|
88
|
+
const result = await rv.RunView({
|
|
89
|
+
EntityName: entityName,
|
|
90
|
+
UserSearchString: query,
|
|
91
|
+
MaxRows: maxRows,
|
|
92
|
+
ResultType: 'simple'
|
|
93
|
+
}, contextUser);
|
|
94
|
+
if (!result.Success) {
|
|
95
|
+
LogError(`EntitySearchProvider: Failed to search "${entityName}": ${result.ErrorMessage}`);
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
return this.convertResults(result.Results, entityName, query);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
102
|
+
LogError(`EntitySearchProvider: Error searching "${entityName}": ${msg}`);
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Convert RunView results to SearchResultItem format with field-match relevance scores.
|
|
108
|
+
* Score is based on how many searchable fields contain the query and whether
|
|
109
|
+
* the match is in a name/title field (higher weight) vs description/other fields.
|
|
110
|
+
*/
|
|
111
|
+
convertResults(records, entityName, query) {
|
|
112
|
+
const md = new Metadata();
|
|
113
|
+
const entityInfo = md.Entities.find(e => e.Name === entityName);
|
|
114
|
+
const queryLower = query.toLowerCase();
|
|
115
|
+
// Get searchable fields and classify them by importance
|
|
116
|
+
const searchFields = entityInfo?.Fields.filter(f => f.IncludeInUserSearchAPI) ?? [];
|
|
117
|
+
const nameFields = searchFields.filter(f => f.IsNameField);
|
|
118
|
+
const totalSearchableFields = Math.max(searchFields.length, 1);
|
|
119
|
+
return records.map((record) => {
|
|
120
|
+
const recordID = String(record.ID ?? '');
|
|
121
|
+
const title = this.extractTitle(record, entityInfo);
|
|
122
|
+
const snippet = this.extractSnippet(record, entityInfo);
|
|
123
|
+
// Calculate relevance: check how many fields match the query
|
|
124
|
+
let matchedFields = 0;
|
|
125
|
+
let nameFieldMatch = false;
|
|
126
|
+
for (const field of searchFields) {
|
|
127
|
+
const val = record[field.Name];
|
|
128
|
+
if (val != null && String(val).toLowerCase().includes(queryLower)) {
|
|
129
|
+
matchedFields++;
|
|
130
|
+
if (nameFields.some(nf => nf.Name === field.Name)) {
|
|
131
|
+
nameFieldMatch = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Score: base from field match ratio, boost for name field matches
|
|
136
|
+
// Range: ~0.15 (weak match in one field) to ~0.95 (name field + multiple fields)
|
|
137
|
+
const fieldRatio = matchedFields / totalSearchableFields;
|
|
138
|
+
const baseScore = 0.15 + (fieldRatio * 0.45); // 0.15 to 0.60
|
|
139
|
+
const nameBoost = nameFieldMatch ? 0.35 : 0; // +0.35 for name field match
|
|
140
|
+
const score = Math.min(baseScore + nameBoost, 0.95);
|
|
141
|
+
return {
|
|
142
|
+
ID: recordID,
|
|
143
|
+
EntityName: entityName,
|
|
144
|
+
RecordID: recordID,
|
|
145
|
+
SourceType: 'entity',
|
|
146
|
+
ResultType: 'entity-record',
|
|
147
|
+
Title: title,
|
|
148
|
+
Snippet: snippet,
|
|
149
|
+
Score: Math.round(score * 100) / 100, // Round to 2 decimal places
|
|
150
|
+
ScoreBreakdown: { Entity: Math.round(score * 100) / 100 },
|
|
151
|
+
Tags: [],
|
|
152
|
+
EntityIcon: entityInfo?.Icon ?? undefined,
|
|
153
|
+
MatchedAt: new Date()
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Extract a display title from record data using entity metadata.
|
|
159
|
+
* Combines IsNameField fields in Sequence order.
|
|
160
|
+
*/
|
|
161
|
+
extractTitle(record, entityInfo) {
|
|
162
|
+
if (entityInfo) {
|
|
163
|
+
// Try IsNameField fields first
|
|
164
|
+
const nameFields = entityInfo.Fields
|
|
165
|
+
.filter(f => f.IsNameField)
|
|
166
|
+
.sort((a, b) => (a.Sequence ?? 9999) - (b.Sequence ?? 9999));
|
|
167
|
+
if (nameFields.length > 0) {
|
|
168
|
+
const parts = nameFields
|
|
169
|
+
.map(f => record[f.Name])
|
|
170
|
+
.filter(v => v != null && String(v).trim() !== '')
|
|
171
|
+
.map(v => String(v));
|
|
172
|
+
if (parts.length > 0)
|
|
173
|
+
return parts.join(' ');
|
|
174
|
+
}
|
|
175
|
+
// Single NameField fallback
|
|
176
|
+
if (entityInfo.NameField && record[entityInfo.NameField.Name]) {
|
|
177
|
+
return String(record[entityInfo.NameField.Name]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Heuristic fallbacks
|
|
181
|
+
const heuristicFields = ['Name', 'Title', 'Subject', 'Label', 'DisplayName'];
|
|
182
|
+
for (const field of heuristicFields) {
|
|
183
|
+
if (record[field] && typeof record[field] === 'string') {
|
|
184
|
+
return record[field];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return `Record`;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Extract a display snippet from record data using entity metadata.
|
|
191
|
+
*/
|
|
192
|
+
extractSnippet(record, entityInfo) {
|
|
193
|
+
const descFields = ['Description', 'Summary', 'Body', 'Content', 'Text', 'Notes'];
|
|
194
|
+
for (const field of descFields) {
|
|
195
|
+
if (record[field] && typeof record[field] === 'string') {
|
|
196
|
+
const val = record[field];
|
|
197
|
+
return val.length > 200 ? val.substring(0, 200) + '...' : val;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return entityInfo ? `Matched in ${entityInfo.Name}` : 'Matched record';
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
EntitySearchProvider = __decorate([
|
|
204
|
+
RegisterClass(BaseSearchProvider, 'EntitySearchProvider')
|
|
205
|
+
], EntitySearchProvider);
|
|
206
|
+
export { EntitySearchProvider };
|
|
207
|
+
//# sourceMappingURL=EntitySearchProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntitySearchProvider.js","sourceRoot":"","sources":["../../src/generic/EntitySearchProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Full-text search provider for the SearchEngine.
|
|
3
|
+
*
|
|
4
|
+
* Uses Metadata.FullTextSearch() which delegates to the database-native FTS
|
|
5
|
+
* capabilities (SQL Server FREETEXT, PostgreSQL tsvector) via RunView +
|
|
6
|
+
* UserSearchString. Enriches results with tags from TaggedItems and
|
|
7
|
+
* ContentItemTags entities.
|
|
8
|
+
*
|
|
9
|
+
* @module @memberjunction/search-engine
|
|
10
|
+
*/
|
|
11
|
+
import { UserInfo } from '@memberjunction/core';
|
|
12
|
+
import { BaseSearchProvider } from './ISearchProvider.js';
|
|
13
|
+
import { SearchSource, SearchFilters, SearchResultItem } from './search.types.js';
|
|
14
|
+
import { SearchEnricher } from './SearchEnricher.js';
|
|
15
|
+
/**
|
|
16
|
+
* Provides full-text search using the MJ Metadata.FullTextSearch() method.
|
|
17
|
+
* Always available since it relies on the standard MJ provider infrastructure.
|
|
18
|
+
*/
|
|
19
|
+
export declare class FullTextSearchProvider extends BaseSearchProvider {
|
|
20
|
+
readonly SourceType: SearchSource;
|
|
21
|
+
private enricher;
|
|
22
|
+
/** Set the enricher instance. Called by SearchEngine after construction. */
|
|
23
|
+
SetEnricher(enricher: SearchEnricher): void;
|
|
24
|
+
/**
|
|
25
|
+
* Execute a full-text search across all FTS-enabled entities.
|
|
26
|
+
*
|
|
27
|
+
* @param query - The search query text
|
|
28
|
+
* @param topK - Maximum number of results
|
|
29
|
+
* @param filters - Optional filters (EntityNames, Tags)
|
|
30
|
+
* @param contextUser - The user performing the search
|
|
31
|
+
* @returns Scored result items from full-text search
|
|
32
|
+
*/
|
|
33
|
+
Search(query: string, topK: number, filters: SearchFilters | undefined, contextUser: UserInfo): Promise<SearchResultItem[]>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=FullTextSearchProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FullTextSearchProvider.d.ts","sourceRoot":"","sources":["../../src/generic/FullTextSearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAsB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EAAE,kBAAkB,EAAwB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAoB,MAAM,gBAAgB,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;GAGG;AACH,qBACa,sBAAuB,SAAQ,kBAAkB;IAC1D,SAAgB,UAAU,EAAE,YAAY,CAAc;IAEtD,OAAO,CAAC,QAAQ,CAA+B;IAE/C,4EAA4E;IACrE,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIlD;;;;;;;;OAQG;IACU,MAAM,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC;CA6CjC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Full-text search provider for the SearchEngine.
|
|
3
|
+
*
|
|
4
|
+
* Uses Metadata.FullTextSearch() which delegates to the database-native FTS
|
|
5
|
+
* capabilities (SQL Server FREETEXT, PostgreSQL tsvector) via RunView +
|
|
6
|
+
* UserSearchString. Enriches results with tags from TaggedItems and
|
|
7
|
+
* ContentItemTags entities.
|
|
8
|
+
*
|
|
9
|
+
* @module @memberjunction/search-engine
|
|
10
|
+
*/
|
|
11
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
12
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
13
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
14
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
15
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
16
|
+
};
|
|
17
|
+
import { LogError, Metadata } from '@memberjunction/core';
|
|
18
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
19
|
+
import { BaseSearchProvider } from './ISearchProvider.js';
|
|
20
|
+
/**
|
|
21
|
+
* Provides full-text search using the MJ Metadata.FullTextSearch() method.
|
|
22
|
+
* Always available since it relies on the standard MJ provider infrastructure.
|
|
23
|
+
*/
|
|
24
|
+
let FullTextSearchProvider = class FullTextSearchProvider extends BaseSearchProvider {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(...arguments);
|
|
27
|
+
this.SourceType = 'fulltext';
|
|
28
|
+
this.enricher = null;
|
|
29
|
+
}
|
|
30
|
+
/** Set the enricher instance. Called by SearchEngine after construction. */
|
|
31
|
+
SetEnricher(enricher) {
|
|
32
|
+
this.enricher = enricher;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Execute a full-text search across all FTS-enabled entities.
|
|
36
|
+
*
|
|
37
|
+
* @param query - The search query text
|
|
38
|
+
* @param topK - Maximum number of results
|
|
39
|
+
* @param filters - Optional filters (EntityNames, Tags)
|
|
40
|
+
* @param contextUser - The user performing the search
|
|
41
|
+
* @returns Scored result items from full-text search
|
|
42
|
+
*/
|
|
43
|
+
async Search(query, topK, filters, contextUser) {
|
|
44
|
+
try {
|
|
45
|
+
const md = new Metadata();
|
|
46
|
+
const ftsResult = await md.FullTextSearch({
|
|
47
|
+
SearchText: query,
|
|
48
|
+
EntityNames: filters?.EntityNames,
|
|
49
|
+
MaxRowsPerEntity: Math.max(3, Math.ceil(topK / 10))
|
|
50
|
+
}, contextUser);
|
|
51
|
+
if (!ftsResult.Success) {
|
|
52
|
+
LogError(`FullTextSearchProvider: FTS error: ${ftsResult.ErrorMessage}`);
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const results = ftsResult.Results.map(r => ({
|
|
56
|
+
ID: r.RecordID,
|
|
57
|
+
EntityName: r.EntityName,
|
|
58
|
+
RecordID: r.RecordID,
|
|
59
|
+
SourceType: 'fulltext',
|
|
60
|
+
ResultType: 'entity-record',
|
|
61
|
+
Title: r.Title,
|
|
62
|
+
Snippet: r.Snippet,
|
|
63
|
+
Score: r.Score,
|
|
64
|
+
ScoreBreakdown: { FullText: r.Score },
|
|
65
|
+
Tags: [],
|
|
66
|
+
MatchedAt: new Date()
|
|
67
|
+
}));
|
|
68
|
+
// Batch-load tags for FTS results
|
|
69
|
+
if (this.enricher) {
|
|
70
|
+
await this.enricher.EnrichWithTags(results, contextUser);
|
|
71
|
+
// Apply tag filter if specified
|
|
72
|
+
if (filters?.Tags?.length) {
|
|
73
|
+
return this.enricher.FilterByTags(results, filters.Tags);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
80
|
+
LogError(`FullTextSearchProvider: Full-text search error: ${msg}`);
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
FullTextSearchProvider = __decorate([
|
|
86
|
+
RegisterClass(BaseSearchProvider, 'FullTextSearchProvider')
|
|
87
|
+
], FullTextSearchProvider);
|
|
88
|
+
export { FullTextSearchProvider };
|
|
89
|
+
//# sourceMappingURL=FullTextSearchProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FullTextSearchProvider.js","sourceRoot":"","sources":["../../src/generic/FullTextSearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;;;;;;;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAY,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAwB,MAAM,mBAAmB,CAAC;AAI7E;;;GAGG;AAEI,IAAM,sBAAsB,GAA5B,MAAM,sBAAuB,SAAQ,kBAAkB;IAAvD;;QACa,eAAU,GAAiB,UAAU,CAAC;QAE9C,aAAQ,GAA0B,IAAI,CAAC;IAkEnD,CAAC;IAhEG,4EAA4E;IACrE,WAAW,CAAC,QAAwB;QACvC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,MAAM,CACf,KAAa,EACb,IAAY,EACZ,OAAkC,EAClC,WAAqB;QAErB,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,cAAc,CAAC;gBACtC,UAAU,EAAE,KAAK;gBACjB,WAAW,EAAE,OAAO,EAAE,WAAW;gBACjC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;aACtD,EAAE,WAAW,CAAC,CAAC;YAEhB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;gBACrB,QAAQ,CAAC,sCAAsC,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;gBACzE,OAAO,EAAE,CAAC;YACd,CAAC;YAED,MAAM,OAAO,GAAuB,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC5D,EAAE,EAAE,CAAC,CAAC,QAAQ;gBACd,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,UAAU,EAAE,UAAU;gBACtB,UAAU,EAAE,eAAmC;gBAC/C,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE;gBACrC,IAAI,EAAE,EAAc;gBACpB,SAAS,EAAE,IAAI,IAAI,EAAE;aACxB,CAAC,CAAC,CAAC;YAEJ,kCAAkC;YAClC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChB,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;gBAEzD,gCAAgC;gBAChC,IAAI,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oBACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC7D,CAAC;YACL,CAAC;YAED,OAAO,OAAO,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,QAAQ,CAAC,mDAAmD,GAAG,EAAE,CAAC,CAAC;YACnE,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;CACJ,CAAA;AArEY,sBAAsB;IADlC,aAAa,CAAC,kBAAkB,EAAE,wBAAwB,CAAC;GAC/C,sBAAsB,CAqElC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Base class and interface for search providers.
|
|
3
|
+
*
|
|
4
|
+
* Each search provider implements a single retrieval strategy
|
|
5
|
+
* (vector similarity, full-text, entity LIKE-search, storage, etc.)
|
|
6
|
+
* and returns scored candidates suitable for RRF fusion.
|
|
7
|
+
*
|
|
8
|
+
* Providers are discovered at runtime via @RegisterClass and the
|
|
9
|
+
* SearchProvider metadata entity. The DriverClass column in the
|
|
10
|
+
* SearchProvider table maps to the @RegisterClass key.
|
|
11
|
+
*
|
|
12
|
+
* @module @memberjunction/search-engine
|
|
13
|
+
*/
|
|
14
|
+
import { UserInfo } from '@memberjunction/core';
|
|
15
|
+
import { SearchSource, SearchFilters, SearchResultItem } from './search.types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Configuration passed to a search provider during initialization.
|
|
18
|
+
* Populated from the SearchProvider entity record.
|
|
19
|
+
*/
|
|
20
|
+
export interface SearchProviderConfig {
|
|
21
|
+
/** The provider's display name from the metadata record */
|
|
22
|
+
Name: string;
|
|
23
|
+
/** Optional JSON config blob from ProviderConfig column (already parsed) */
|
|
24
|
+
ProviderConfig: Record<string, unknown> | null;
|
|
25
|
+
/** Optional credential ID for providers that need authentication */
|
|
26
|
+
CredentialID: string | null;
|
|
27
|
+
/** Per-provider max results override (null = use engine default) */
|
|
28
|
+
MaxResultsOverride: number | null;
|
|
29
|
+
/** Whether this provider should participate in preview/autocomplete searches */
|
|
30
|
+
SupportsPreview: boolean;
|
|
31
|
+
/** Priority value from the metadata record (lower = higher priority) */
|
|
32
|
+
Priority: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Abstract base class for search providers. Subclasses must be registered
|
|
36
|
+
* with @RegisterClass(BaseSearchProvider, 'DriverClassName') to be
|
|
37
|
+
* discoverable by the SearchEngine.
|
|
38
|
+
*
|
|
39
|
+
* Lifecycle:
|
|
40
|
+
* 1. SearchEngine loads active SearchProvider records from the database
|
|
41
|
+
* 2. For each record, ClassFactory creates an instance using DriverClass
|
|
42
|
+
* 3. Initialize() is called with the provider's config from the DB record
|
|
43
|
+
* 4. CheckAvailability() is called to verify the provider is operational
|
|
44
|
+
* 5. If IsAvailable(), the provider participates in searches
|
|
45
|
+
*/
|
|
46
|
+
export declare abstract class BaseSearchProvider {
|
|
47
|
+
/** Which search source this provider represents */
|
|
48
|
+
abstract readonly SourceType: SearchSource;
|
|
49
|
+
/** Config from the SearchProvider metadata record, set during Initialize() */
|
|
50
|
+
protected config: SearchProviderConfig | null;
|
|
51
|
+
/**
|
|
52
|
+
* Initialize the provider with configuration from the SearchProvider entity.
|
|
53
|
+
* Called once during SearchEngine.Config(). Override to perform provider-specific
|
|
54
|
+
* setup (e.g., connecting to external APIs using credentials).
|
|
55
|
+
*
|
|
56
|
+
* @param config - Configuration from the SearchProvider metadata record
|
|
57
|
+
* @param contextUser - The user context for initialization
|
|
58
|
+
*/
|
|
59
|
+
Initialize(config: SearchProviderConfig, contextUser: UserInfo): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Check and cache whether this provider is operational.
|
|
62
|
+
* Called after Initialize(). Override for providers that need to verify
|
|
63
|
+
* external dependencies (e.g., vector indexes exist, API keys are valid).
|
|
64
|
+
*
|
|
65
|
+
* The default implementation returns true (always available).
|
|
66
|
+
*
|
|
67
|
+
* @param contextUser - The user context for availability checks
|
|
68
|
+
*/
|
|
69
|
+
CheckAvailability(contextUser: UserInfo): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Whether this provider is currently available (configured and operational).
|
|
72
|
+
* The SearchEngine will skip providers that return false.
|
|
73
|
+
*/
|
|
74
|
+
IsAvailable(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Execute a search and return result items with scores.
|
|
77
|
+
*
|
|
78
|
+
* @param query - The search query text
|
|
79
|
+
* @param topK - Maximum number of results to retrieve
|
|
80
|
+
* @param filters - Optional filters to narrow results
|
|
81
|
+
* @param contextUser - The user performing the search
|
|
82
|
+
* @returns Scored result items from this provider
|
|
83
|
+
*/
|
|
84
|
+
abstract Search(query: string, topK: number, filters: SearchFilters | undefined, contextUser: UserInfo): Promise<SearchResultItem[]>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated Use BaseSearchProvider instead. Kept for backwards compatibility.
|
|
88
|
+
*/
|
|
89
|
+
export type ISearchProvider = BaseSearchProvider;
|
|
90
|
+
//# sourceMappingURL=ISearchProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ISearchProvider.d.ts","sourceRoot":"","sources":["../../src/generic/ISearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE/E;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACjC,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,oEAAoE;IACpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gFAAgF;IAChF,eAAe,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,8BAAsB,kBAAkB;IACpC,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC;IAE3C,8EAA8E;IAC9E,SAAS,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAQ;IAErD;;;;;;;OAOG;IACU,UAAU,CAAC,MAAM,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3F;;;;;;;;OAQG;IACU,iBAAiB,CAAC,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;OAGG;IACI,WAAW,IAAI,OAAO;IAI7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CACX,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,WAAW,EAAE,QAAQ,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Base class and interface for search providers.
|
|
3
|
+
*
|
|
4
|
+
* Each search provider implements a single retrieval strategy
|
|
5
|
+
* (vector similarity, full-text, entity LIKE-search, storage, etc.)
|
|
6
|
+
* and returns scored candidates suitable for RRF fusion.
|
|
7
|
+
*
|
|
8
|
+
* Providers are discovered at runtime via @RegisterClass and the
|
|
9
|
+
* SearchProvider metadata entity. The DriverClass column in the
|
|
10
|
+
* SearchProvider table maps to the @RegisterClass key.
|
|
11
|
+
*
|
|
12
|
+
* @module @memberjunction/search-engine
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Abstract base class for search providers. Subclasses must be registered
|
|
16
|
+
* with @RegisterClass(BaseSearchProvider, 'DriverClassName') to be
|
|
17
|
+
* discoverable by the SearchEngine.
|
|
18
|
+
*
|
|
19
|
+
* Lifecycle:
|
|
20
|
+
* 1. SearchEngine loads active SearchProvider records from the database
|
|
21
|
+
* 2. For each record, ClassFactory creates an instance using DriverClass
|
|
22
|
+
* 3. Initialize() is called with the provider's config from the DB record
|
|
23
|
+
* 4. CheckAvailability() is called to verify the provider is operational
|
|
24
|
+
* 5. If IsAvailable(), the provider participates in searches
|
|
25
|
+
*/
|
|
26
|
+
export class BaseSearchProvider {
|
|
27
|
+
constructor() {
|
|
28
|
+
/** Config from the SearchProvider metadata record, set during Initialize() */
|
|
29
|
+
this.config = null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize the provider with configuration from the SearchProvider entity.
|
|
33
|
+
* Called once during SearchEngine.Config(). Override to perform provider-specific
|
|
34
|
+
* setup (e.g., connecting to external APIs using credentials).
|
|
35
|
+
*
|
|
36
|
+
* @param config - Configuration from the SearchProvider metadata record
|
|
37
|
+
* @param contextUser - The user context for initialization
|
|
38
|
+
*/
|
|
39
|
+
async Initialize(config, contextUser) {
|
|
40
|
+
this.config = config;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check and cache whether this provider is operational.
|
|
44
|
+
* Called after Initialize(). Override for providers that need to verify
|
|
45
|
+
* external dependencies (e.g., vector indexes exist, API keys are valid).
|
|
46
|
+
*
|
|
47
|
+
* The default implementation returns true (always available).
|
|
48
|
+
*
|
|
49
|
+
* @param contextUser - The user context for availability checks
|
|
50
|
+
*/
|
|
51
|
+
async CheckAvailability(contextUser) {
|
|
52
|
+
// Default: no-op, provider is always available
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether this provider is currently available (configured and operational).
|
|
56
|
+
* The SearchEngine will skip providers that return false.
|
|
57
|
+
*/
|
|
58
|
+
IsAvailable() {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=ISearchProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ISearchProvider.js","sourceRoot":"","sources":["../../src/generic/ISearchProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAwBH;;;;;;;;;;;GAWG;AACH,MAAM,OAAgB,kBAAkB;IAAxC;QAII,8EAA8E;QACpE,WAAM,GAAgC,IAAI,CAAC;IAkDzD,CAAC;IAhDG;;;;;;;OAOG;IACI,KAAK,CAAC,UAAU,CAAC,MAA4B,EAAE,WAAqB;QACvE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,iBAAiB,CAAC,WAAqB;QAChD,+CAA+C;IACnD,CAAC;IAED;;;OAGG;IACI,WAAW;QACd,OAAO,IAAI,CAAC;IAChB,CAAC;CAiBJ"}
|