@memberjunction/core 5.37.0 → 5.39.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.
Files changed (53) hide show
  1. package/dist/generic/RegisterForStartup.d.ts +7 -0
  2. package/dist/generic/RegisterForStartup.d.ts.map +1 -1
  3. package/dist/generic/RegisterForStartup.js +26 -17
  4. package/dist/generic/RegisterForStartup.js.map +1 -1
  5. package/dist/generic/baseEngine.d.ts.map +1 -1
  6. package/dist/generic/baseEngine.js +34 -4
  7. package/dist/generic/baseEngine.js.map +1 -1
  8. package/dist/generic/baseEngineRegistry.d.ts +90 -0
  9. package/dist/generic/baseEngineRegistry.d.ts.map +1 -1
  10. package/dist/generic/baseEngineRegistry.js +100 -0
  11. package/dist/generic/baseEngineRegistry.js.map +1 -1
  12. package/dist/generic/baseEntity.d.ts.map +1 -1
  13. package/dist/generic/baseEntity.js +33 -1
  14. package/dist/generic/baseEntity.js.map +1 -1
  15. package/dist/generic/entityInfo.d.ts +36 -1
  16. package/dist/generic/entityInfo.d.ts.map +1 -1
  17. package/dist/generic/entityInfo.js +96 -4
  18. package/dist/generic/entityInfo.js.map +1 -1
  19. package/dist/generic/interfaces.d.ts +141 -0
  20. package/dist/generic/interfaces.d.ts.map +1 -1
  21. package/dist/generic/interfaces.js +9 -0
  22. package/dist/generic/interfaces.js.map +1 -1
  23. package/dist/generic/localCacheManager.d.ts.map +1 -1
  24. package/dist/generic/localCacheManager.js +3 -7
  25. package/dist/generic/localCacheManager.js.map +1 -1
  26. package/dist/generic/metadata.d.ts +48 -1
  27. package/dist/generic/metadata.d.ts.map +1 -1
  28. package/dist/generic/metadata.js +53 -0
  29. package/dist/generic/metadata.js.map +1 -1
  30. package/dist/generic/providerBase.d.ts +142 -38
  31. package/dist/generic/providerBase.d.ts.map +1 -1
  32. package/dist/generic/providerBase.js +309 -51
  33. package/dist/generic/providerBase.js.map +1 -1
  34. package/dist/generic/queryInfo.d.ts +11 -0
  35. package/dist/generic/queryInfo.d.ts.map +1 -1
  36. package/dist/generic/queryInfo.js +11 -0
  37. package/dist/generic/queryInfo.js.map +1 -1
  38. package/dist/generic/queryInfoInterfaces.d.ts +14 -0
  39. package/dist/generic/queryInfoInterfaces.d.ts.map +1 -1
  40. package/dist/generic/scoring/ReciprocalRankFusion.d.ts +11 -4
  41. package/dist/generic/scoring/ReciprocalRankFusion.d.ts.map +1 -1
  42. package/dist/generic/scoring/ReciprocalRankFusion.js +18 -6
  43. package/dist/generic/scoring/ReciprocalRankFusion.js.map +1 -1
  44. package/dist/index.d.ts +0 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +0 -1
  47. package/dist/index.js.map +1 -1
  48. package/package.json +3 -3
  49. package/readme.md +164 -5
  50. package/dist/generic/QueryCacheManager.d.ts +0 -115
  51. package/dist/generic/QueryCacheManager.d.ts.map +0 -1
  52. package/dist/generic/QueryCacheManager.js +0 -329
  53. package/dist/generic/QueryCacheManager.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import { BaseEntity } from "./baseEntity.js";
2
- import { EntityDocumentTypeInfo, EntityInfo } from "./entityInfo.js";
2
+ import { EntityDocumentTypeInfo, EntityFieldTSType, EntityInfo } from "./entityInfo.js";
3
3
  import { AllMetadata } from "./interfaces.js";
4
+ import { ComputeRRF } from "./scoring/ReciprocalRankFusion.js";
4
5
  import { LocalCacheManager } from "./localCacheManager.js";
5
6
  import { ApplicationInfo } from "../generic/applicationInfo.js";
6
7
  import { AuditLogTypeInfo, AuthorizationInfo, AuthorizationRoleInfo, RoleInfo, RowLevelSecurityFilterInfo, UserInfo } from "./securityInfo.js";
@@ -768,6 +769,255 @@ export class ProviderBase {
768
769
  });
769
770
  return parts.join('||');
770
771
  }
772
+ /**
773
+ * Ranked search over **one** entity's records. See {@link IMetadataProvider.SearchEntity}
774
+ * for the contract and how this differs from {@link EntityByName} /
775
+ * {@link FullTextSearch}.
776
+ *
777
+ * Implementation overview (concrete on `ProviderBase`, used as-is by every
778
+ * server-side provider; `GraphQLDataProvider` overrides to proxy via GQL):
779
+ * 1. Resolve the EntityDocument (by `params.options.entityDocumentId`
780
+ * override or by looking up the active Search-category doc for the entity).
781
+ * 2. In parallel: run the lexical pass (RunView with LIKE filters on the
782
+ * name field + any `IncludeInUserSearchAPI` fields) and the semantic
783
+ * pass (`searchEntitiesSemanticPass`, the protected template method
784
+ * each concrete server provider implements).
785
+ * 3. Fuse via canonical `ComputeRRF()` with optional per-list weights.
786
+ * 4. Permission-filter via a second RunView constrained to the matched
787
+ * record IDs — that pipeline already enforces row-level read perms
788
+ * on this entity, so any rows the user can't read drop out.
789
+ * 5. Slice to topK, apply minScore cutoff, return.
790
+ */
791
+ async SearchEntity(params) {
792
+ const { entityName, searchText } = params;
793
+ const options = params.options ?? {};
794
+ const entity = this.EntityByName(entityName);
795
+ if (!entity) {
796
+ LogError(`SearchEntity: unknown entity "${entityName}"`);
797
+ return [];
798
+ }
799
+ if (!searchText || !searchText.trim()) {
800
+ return [];
801
+ }
802
+ const mode = options.mode ?? 'hybrid';
803
+ const topK = options.topK ?? 10;
804
+ const minScore = options.minScore ?? 0;
805
+ const overFetch = topK * 2;
806
+ const contextUser = options.contextUser;
807
+ // Resolve EntityDocument when semantic ranking is in play
808
+ let entityDocumentId = null;
809
+ let embeddingAIModelId = null;
810
+ if (mode === 'semantic' || mode === 'hybrid') {
811
+ const resolved = await this.resolveSearchEntityDocument(entity.ID, options.entityDocumentId, contextUser);
812
+ if (resolved) {
813
+ entityDocumentId = resolved.id;
814
+ embeddingAIModelId = resolved.aiModelId;
815
+ }
816
+ if (!entityDocumentId && mode === 'semantic') {
817
+ LogError(`SearchEntity: no active 'Search' EntityDocument for entity "${entityName}"; cannot run semantic-only mode`);
818
+ return [];
819
+ }
820
+ }
821
+ // Dispatch lexical and semantic in parallel. Wrapping the lexical pass
822
+ // in Promise.resolve adds no real cost today and keeps the shape ready
823
+ // if the lexical implementation ever goes async (FTS, external index).
824
+ const [lexicalRanked, semanticRanked] = await Promise.all([
825
+ mode === 'semantic'
826
+ ? Promise.resolve([])
827
+ : this.searchEntitiesLexicalPass(entity, searchText, overFetch, contextUser),
828
+ mode === 'lexical' || !entityDocumentId
829
+ ? Promise.resolve([])
830
+ : this.searchEntitiesSemanticPass(entityDocumentId, searchText, overFetch, embeddingAIModelId, contextUser),
831
+ ]);
832
+ // Blend
833
+ const blended = this.searchEntitiesBlend(mode, lexicalRanked, semanticRanked, options);
834
+ // Build component-score lookup for the result-shape step
835
+ const lexicalScoreById = new Map();
836
+ for (const c of lexicalRanked)
837
+ lexicalScoreById.set(c.ID, c.Score);
838
+ const semanticScoreById = new Map();
839
+ const semanticDocByRecord = new Map();
840
+ for (const c of semanticRanked) {
841
+ semanticScoreById.set(c.ID, c.Score);
842
+ const erdId = c.Metadata?.['entityRecordDocumentId'];
843
+ if (erdId)
844
+ semanticDocByRecord.set(c.ID, erdId);
845
+ }
846
+ // Permission-filter post hoc via RunView constrained to the matched
847
+ // record IDs — the existing pipeline already enforces row-level read
848
+ // permissions on this entity, so unauthorized rows simply drop out.
849
+ const ids = blended.map(c => c.ID);
850
+ const allowedIds = ids.length > 0
851
+ ? await this.searchEntitiesFilterByPermission(entity, ids, contextUser)
852
+ : new Set();
853
+ const results = [];
854
+ for (const cand of blended) {
855
+ if (!allowedIds.has(cand.ID))
856
+ continue;
857
+ if (cand.Score < minScore)
858
+ continue;
859
+ const lex = lexicalScoreById.get(cand.ID);
860
+ const sem = semanticScoreById.get(cand.ID);
861
+ const matchType = (lex != null && sem != null)
862
+ ? 'hybrid'
863
+ : (lex != null ? 'lexical' : 'semantic');
864
+ results.push({
865
+ entityRecordDocumentId: semanticDocByRecord.get(cand.ID) ?? null,
866
+ recordId: cand.ID,
867
+ score: cand.Score,
868
+ matchType,
869
+ components: { lexical: lex, semantic: sem },
870
+ });
871
+ if (results.length >= topK)
872
+ break;
873
+ }
874
+ return results;
875
+ }
876
+ /**
877
+ * Batch form of {@link SearchEntity}. Fans the input list out to N
878
+ * independent `SearchEntity` calls via `Promise.all`; result arrays come
879
+ * back aligned by input order (`result[i]` holds the matches for `params[i]`).
880
+ *
881
+ * On the server side, the per-entity passes are independent — running them
882
+ * concurrently is a real wall-clock win when the caller wants results from
883
+ * multiple entities. On the client side, `GraphQLDataProvider` overrides
884
+ * this method to pack the whole batch into a single GraphQL round-trip
885
+ * instead of issuing N parallel HTTP requests.
886
+ *
887
+ * See {@link IMetadataProvider.SearchEntities} for the contract.
888
+ */
889
+ async SearchEntities(params) {
890
+ if (!params || params.length === 0)
891
+ return [];
892
+ return Promise.all(params.map(p => this.SearchEntity(p)));
893
+ }
894
+ /**
895
+ * Look up the EntityDocument that backs entity search for `entityID`.
896
+ * Caller supplies an explicit ID override; otherwise we filter for an
897
+ * Active EntityDocument joined to the 'Search' type via the denormalized
898
+ * `Type` column on `vwEntityDocuments` (avoids a SQL subquery and keeps
899
+ * this metadata-layer code provider-agnostic).
900
+ *
901
+ * Returns just the EntityDocument PK and its AIModelID; concrete semantic-pass
902
+ * implementations look up the model from `AIEngine` to recover the driver
903
+ * class and API name (the view does not project them). Threading AIModelID
904
+ * through ensures the query embedding is generated with the *same* model
905
+ * used to build the index — anything else produces garbage cosine scores.
906
+ */
907
+ async resolveSearchEntityDocument(entityID, explicitId, contextUser) {
908
+ const filter = explicitId
909
+ ? `ID='${explicitId.replace(/'/g, "''")}'`
910
+ : `EntityID='${entityID.replace(/'/g, "''")}' AND Status='Active' AND Type='Search'`;
911
+ const r = await this.RunView({
912
+ EntityName: 'MJ: Entity Documents',
913
+ ExtraFilter: filter,
914
+ ResultType: 'simple',
915
+ MaxRows: 1,
916
+ }, contextUser);
917
+ if (!r.Success || (r.Results?.length ?? 0) === 0)
918
+ return null;
919
+ const row = r.Results[0];
920
+ return {
921
+ id: row.ID,
922
+ aiModelId: row.AIModelID ?? null,
923
+ };
924
+ }
925
+ /**
926
+ * Substring/prefix LIKE search against the entity's name field and any
927
+ * string-typed fields flagged `IncludeInUserSearchAPI`. Returns lexical
928
+ * scores in [0,1] blended by best match per row.
929
+ *
930
+ * **Wildcard handling.** SQL Server's `LIKE` treats `%`, `_`, and `[`
931
+ * specially; a user searching for `50%_off` would otherwise match far more
932
+ * than intended. We escape those three characters and declare an explicit
933
+ * `ESCAPE '\\'` so user-supplied text is matched literally.
934
+ *
935
+ * **Field filtering.** Only string-typed fields are searched. Bit/numeric/
936
+ * date fields can be flagged `IncludeInUserSearchAPI` via metadata edit;
937
+ * applying `LIKE` to those would error or implicit-convert in subtle ways.
938
+ */
939
+ async searchEntitiesLexicalPass(entity, searchText, overFetch, contextUser) {
940
+ const trimmed = searchText.trim();
941
+ if (!trimmed)
942
+ return [];
943
+ // Escape quotes for SQL string literal, then escape LIKE wildcards
944
+ // (%, _, [) with the explicit ESCAPE character we declare below.
945
+ const sanitized = trimmed
946
+ .replace(/'/g, "''")
947
+ .replace(/\\/g, '\\\\')
948
+ .replace(/%/g, '\\%')
949
+ .replace(/_/g, '\\_')
950
+ .replace(/\[/g, '\\[');
951
+ const searchableFields = entity.Fields
952
+ .filter(f => (f.IncludeInUserSearchAPI || f.IsNameField) && f.TSType === EntityFieldTSType.String);
953
+ if (searchableFields.length === 0)
954
+ return [];
955
+ const likeClauses = searchableFields
956
+ .map(f => `${f.Name} LIKE '%${sanitized}%' ESCAPE '\\'`)
957
+ .join(' OR ');
958
+ const r = await this.RunView({
959
+ EntityName: entity.Name,
960
+ ExtraFilter: likeClauses,
961
+ ResultType: 'simple',
962
+ MaxRows: overFetch,
963
+ }, contextUser);
964
+ if (!r.Success)
965
+ return [];
966
+ const lower = trimmed.toLowerCase();
967
+ const nameField = entity.NameField?.Name ?? entity.Fields.find(f => f.IsNameField)?.Name ?? null;
968
+ const out = [];
969
+ for (const row of (r.Results ?? [])) {
970
+ const id = String(row['ID'] ?? '');
971
+ if (!id)
972
+ continue;
973
+ const nameVal = nameField ? String(row[nameField] ?? '').toLowerCase() : '';
974
+ let score = 0.5; // any match (in some searchable field)
975
+ if (nameVal) {
976
+ if (nameVal === lower)
977
+ score = 1.0;
978
+ else if (nameVal.startsWith(lower))
979
+ score = 0.85;
980
+ else if (nameVal.includes(lower))
981
+ score = 0.7;
982
+ }
983
+ out.push({ ID: id, Score: score });
984
+ }
985
+ // ComputeRRF reads order, not magnitude — sort by score so rank 1 = best lexical match
986
+ out.sort((a, b) => b.Score - a.Score);
987
+ return out;
988
+ }
989
+ /** Blend lexical + semantic via canonical weighted ComputeRRF. */
990
+ searchEntitiesBlend(mode, lexicalRanked, semanticRanked, options) {
991
+ if (mode === 'lexical')
992
+ return lexicalRanked;
993
+ if (mode === 'semantic')
994
+ return semanticRanked;
995
+ const weights = [
996
+ options.weights?.lexical ?? 1.0,
997
+ options.weights?.semantic ?? 1.0,
998
+ ];
999
+ return ComputeRRF([lexicalRanked, semanticRanked], options.rrfK ?? 60, weights);
1000
+ }
1001
+ /**
1002
+ * Run a permission-aware RunView restricted to the matched IDs. Whatever
1003
+ * comes back is what the user is allowed to see. Used as a post-filter
1004
+ * over the fused result set.
1005
+ */
1006
+ async searchEntitiesFilterByPermission(entity, ids, contextUser) {
1007
+ if (ids.length === 0)
1008
+ return new Set();
1009
+ const escaped = ids.map(id => `'${id.replace(/'/g, "''")}'`).join(',');
1010
+ const r = await this.RunView({
1011
+ EntityName: entity.Name,
1012
+ ExtraFilter: `ID IN (${escaped})`,
1013
+ Fields: ['ID'],
1014
+ ResultType: 'simple',
1015
+ MaxRows: ids.length,
1016
+ }, contextUser);
1017
+ if (!r.Success)
1018
+ return new Set();
1019
+ return new Set((r.Results ?? []).map(row => row.ID));
1020
+ }
771
1021
  /**
772
1022
  * Returns true if any param in the batch has SaveViewResults set,
773
1023
  * which means the call has a side effect (creating UserViewRun records)
@@ -1273,9 +1523,16 @@ export class ProviderBase {
1273
1523
  const preResolvedCache = currentFingerprints.length > 0
1274
1524
  ? await LocalCacheManager.Instance.GetRunViewResults(currentFingerprints)
1275
1525
  : new Map();
1526
+ // Pre-build index Map for server results lookup — avoids O(N^2) .find() scans below.
1527
+ const serverResultsByViewIndex = new Map();
1528
+ if (response.results) {
1529
+ for (const r of response.results) {
1530
+ serverResultsByViewIndex.set(r.viewIndex, r);
1531
+ }
1532
+ }
1276
1533
  // Process all results in parallel — 'current' entries now read from the
1277
1534
  // pre-resolved map instead of issuing their own GetRunViewResult calls.
1278
- const processingPromises = params.map((param, i) => this.processSingleSmartCacheResult(param, i, response.results, preResolvedCache, contextUser));
1535
+ const processingPromises = params.map((param, i) => this.processSingleSmartCacheResult(param, i, serverResultsByViewIndex.get(i), preResolvedCache, contextUser));
1279
1536
  const processedResults = await Promise.all(processingPromises);
1280
1537
  // Aggregate telemetry stats
1281
1538
  let cacheHits = 0;
@@ -1305,8 +1562,7 @@ export class ProviderBase {
1305
1562
  * 'current' items hit this map instead of issuing per-param IDB reads,
1306
1563
  * amortizing IndexedDB transaction overhead across the whole batch.
1307
1564
  */
1308
- async processSingleSmartCacheResult(param, index, serverResults, preResolvedCache, contextUser) {
1309
- const checkResult = serverResults.find(r => r.viewIndex === index);
1565
+ async processSingleSmartCacheResult(param, index, checkResult, preResolvedCache, contextUser) {
1310
1566
  if (!checkResult) {
1311
1567
  return {
1312
1568
  result: {
@@ -2204,30 +2460,59 @@ export class ProviderBase {
2204
2460
  * @param settings - Array of entity settings metadata
2205
2461
  * @returns Processed array of EntityInfo instances with all relationships established
2206
2462
  */
2463
+ /**
2464
+ * Groups items into a Map keyed by a NormalizeUUID-transformed value.
2465
+ * Single-pass O(N) helper used to build pre-indexed lookup maps for
2466
+ * metadata post-processing, replacing repeated O(N*M) filter scans.
2467
+ */
2468
+ groupByNormalizedUUID(items, keyFn) {
2469
+ const map = new Map();
2470
+ for (const item of items) {
2471
+ const key = NormalizeUUID(keyFn(item));
2472
+ let list = map.get(key);
2473
+ if (!list) {
2474
+ list = [];
2475
+ map.set(key, list);
2476
+ }
2477
+ list.push(item);
2478
+ }
2479
+ return map;
2480
+ }
2207
2481
  PostProcessEntityMetadata(entities, fields, fieldValues, permissions, relationships, settings, organicKeys, organicKeyRelatedEntities) {
2208
2482
  const result = [];
2209
2483
  // Sort entities alphabetically by name to ensure deterministic ordering
2210
2484
  // This prevents non-deterministic output in CodeGen and other metadata consumers
2211
2485
  const sortedEntities = entities.sort((a, b) => a.Name.localeCompare(b.Name));
2212
- if (fieldValues && fieldValues.length > 0)
2213
- for (let f of fields) {
2214
- // populate the field values for each field, if we have them
2215
- f.EntityFieldValues = fieldValues.filter(fv => UUIDsEqual(fv.EntityFieldID, f.ID));
2486
+ if (fieldValues && fieldValues.length > 0) {
2487
+ const fieldValuesByFieldId = this.groupByNormalizedUUID(fieldValues, fv => fv.EntityFieldID);
2488
+ for (const f of fields) {
2489
+ f.EntityFieldValues = fieldValuesByFieldId.get(NormalizeUUID(f.ID)) || [];
2216
2490
  }
2491
+ }
2217
2492
  // Link organic key related entities to their parent organic keys
2218
2493
  if (organicKeys && organicKeyRelatedEntities && organicKeyRelatedEntities.length > 0) {
2494
+ const okreByOrganicKeyId = this.groupByNormalizedUUID(organicKeyRelatedEntities, okre => okre.EntityOrganicKeyID);
2219
2495
  for (const ok of organicKeys) {
2220
- ok.EntityOrganicKeyRelatedEntities = organicKeyRelatedEntities.filter(okre => UUIDsEqual(okre.EntityOrganicKeyID, ok.ID));
2496
+ ok.EntityOrganicKeyRelatedEntities = okreByOrganicKeyId.get(NormalizeUUID(ok.ID)) || [];
2221
2497
  }
2222
2498
  }
2223
- for (let e of sortedEntities) {
2224
- e.EntityFields = fields.filter(f => UUIDsEqual(f.EntityID, e.ID)).sort((a, b) => a.Sequence - b.Sequence);
2225
- e.EntityPermissions = permissions.filter(p => UUIDsEqual(p.EntityID, e.ID));
2226
- e.EntityRelationships = relationships.filter(r => UUIDsEqual(r.EntityID, e.ID));
2227
- e.EntitySettings = settings.filter(s => UUIDsEqual(s.EntityID, e.ID));
2499
+ const fieldsByEntityId = this.groupByNormalizedUUID(fields, f => f.EntityID);
2500
+ const permissionsByEntityId = this.groupByNormalizedUUID(permissions, p => p.EntityID);
2501
+ const relationshipsByEntityId = this.groupByNormalizedUUID(relationships, r => r.EntityID);
2502
+ const settingsByEntityId = this.groupByNormalizedUUID(settings, s => s.EntityID);
2503
+ const activeOrganicKeysByEntityId = organicKeys
2504
+ ? this.groupByNormalizedUUID(organicKeys.filter(ok => ok.Status === 'Active'), ok => ok.EntityID)
2505
+ : new Map();
2506
+ for (const e of sortedEntities) {
2507
+ const entityIdKey = NormalizeUUID(e.ID);
2508
+ const entityFields = fieldsByEntityId.get(entityIdKey) || [];
2509
+ e.EntityFields = entityFields.sort((a, b) => a.Sequence - b.Sequence);
2510
+ e.EntityPermissions = permissionsByEntityId.get(entityIdKey) || [];
2511
+ e.EntityRelationships = relationshipsByEntityId.get(entityIdKey) || [];
2512
+ e.EntitySettings = settingsByEntityId.get(entityIdKey) || [];
2228
2513
  // Link active organic keys to the entity
2229
2514
  if (organicKeys) {
2230
- e.EntityOrganicKeys = organicKeys.filter(ok => UUIDsEqual(ok.EntityID, e.ID) && ok.Status === 'Active');
2515
+ e.EntityOrganicKeys = activeOrganicKeysByEntityId.get(entityIdKey) || [];
2231
2516
  }
2232
2517
  result.push(new EntityInfo(e));
2233
2518
  }
@@ -2330,66 +2615,39 @@ export class ProviderBase {
2330
2615
  get AuthorizationRoles() {
2331
2616
  return this._localMetadata.AllAuthorizationRoles;
2332
2617
  }
2333
- /**
2334
- * Gets all saved queries in the system.
2335
- * @returns Array of QueryInfo objects representing stored queries
2336
- */
2618
+ /** @deprecated Use `QueryEngine.Instance.Queries` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2337
2619
  get Queries() {
2338
2620
  return this._localMetadata.AllQueries;
2339
2621
  }
2340
- /**
2341
- * Gets all query category definitions.
2342
- * @returns Array of QueryCategoryInfo objects for query organization
2343
- */
2622
+ /** @deprecated Use `QueryEngine.Instance.Categories` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2344
2623
  get QueryCategories() {
2345
2624
  return this._localMetadata.AllQueryCategories;
2346
2625
  }
2347
- /**
2348
- * Gets all query field definitions.
2349
- * @returns Array of QueryFieldInfo objects defining query result columns
2350
- */
2626
+ /** @deprecated Use `QueryEngine.Instance.Fields` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2351
2627
  get QueryFields() {
2352
2628
  return this._localMetadata.AllQueryFields;
2353
2629
  }
2354
- /**
2355
- * Gets all query permission assignments.
2356
- * @returns Array of QueryPermissionInfo objects defining query access
2357
- */
2630
+ /** @deprecated Use `QueryEngine.Instance.Permissions` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2358
2631
  get QueryPermissions() {
2359
2632
  return this._localMetadata.AllQueryPermissions;
2360
2633
  }
2361
- /**
2362
- * Gets all query entity associations.
2363
- * @returns Array of QueryEntityInfo objects linking queries to entities
2364
- */
2634
+ /** @deprecated Use `QueryEngine.Instance.QueryEntities` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2365
2635
  get QueryEntities() {
2366
2636
  return this._localMetadata.AllQueryEntities;
2367
2637
  }
2368
- /**
2369
- * Gets all query parameter definitions.
2370
- * @returns Array of QueryParameterInfo objects for parameterized queries
2371
- */
2638
+ /** @deprecated Use `QueryEngine.Instance.Parameters` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2372
2639
  get QueryParameters() {
2373
2640
  return this._localMetadata.AllQueryParameters;
2374
2641
  }
2375
- /**
2376
- * Gets all query dependency records tracking composition references between queries.
2377
- * @returns Array of QueryDependencyInfo objects representing query-to-query dependencies
2378
- */
2642
+ /** @deprecated Use `QueryEngine.Instance.Dependencies` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2379
2643
  get QueryDependencies() {
2380
2644
  return this._localMetadata.AllQueryDependencies;
2381
2645
  }
2382
- /**
2383
- * Gets all SQL dialect definitions.
2384
- * @returns Array of SQLDialectInfo objects representing supported SQL dialects
2385
- */
2646
+ /** @deprecated Use `QueryEngine.Instance.SQLDialects` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2386
2647
  get SQLDialects() {
2387
2648
  return this._localMetadata.AllSQLDialects;
2388
2649
  }
2389
- /**
2390
- * Gets all query SQL dialect variants.
2391
- * @returns Array of QuerySQLInfo objects containing dialect-specific SQL for queries
2392
- */
2650
+ /** @deprecated Use `QueryEngine.Instance.QuerySQLs` from `@memberjunction/core-entities`. Will be removed in v6.x. */
2393
2651
  get QuerySQLs() {
2394
2652
  return this._localMetadata.AllQuerySQLs;
2395
2653
  }