@open-mercato/core 0.4.6-develop-05b3bcb6f5 → 0.4.6-develop-db1058d334
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.
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@open-mercato/shared/lib/query/join-utils";
|
|
13
13
|
import { resolveSearchConfig } from "@open-mercato/shared/lib/search/config";
|
|
14
14
|
import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
|
|
15
|
+
import { runBeforeQueryPipeline, runAfterQueryPipeline } from "@open-mercato/shared/lib/query/query-extension-runner";
|
|
15
16
|
function resolveBooleanEnv(names, defaultValue) {
|
|
16
17
|
for (const name of names) {
|
|
17
18
|
const raw = process.env[name];
|
|
@@ -67,6 +68,31 @@ class HybridQueryEngine {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
async query(entity, opts = {}) {
|
|
71
|
+
const ext = opts.extensions;
|
|
72
|
+
let hybridExtCtx = null;
|
|
73
|
+
const noopDi = { resolve: (_name) => {
|
|
74
|
+
throw new Error("No DI context");
|
|
75
|
+
} };
|
|
76
|
+
if (ext) {
|
|
77
|
+
hybridExtCtx = {
|
|
78
|
+
entity: String(entity),
|
|
79
|
+
engine: "hybrid",
|
|
80
|
+
tenantId: opts.tenantId ?? "",
|
|
81
|
+
organizationId: opts.organizationId,
|
|
82
|
+
userId: ext.userId,
|
|
83
|
+
em: this.em,
|
|
84
|
+
container: ext.container,
|
|
85
|
+
userFeatures: ext.userFeatures
|
|
86
|
+
};
|
|
87
|
+
const diCtx = ext.resolve ? { resolve: ext.resolve } : noopDi;
|
|
88
|
+
const beforeResult = await runBeforeQueryPipeline(opts, hybridExtCtx, diCtx);
|
|
89
|
+
if (beforeResult.blocked) {
|
|
90
|
+
throw new Error(beforeResult.errorMessage ?? "Query blocked by extension subscriber");
|
|
91
|
+
}
|
|
92
|
+
opts = beforeResult.query;
|
|
93
|
+
}
|
|
94
|
+
const { extensions: _stripExt, ...coreOpts } = opts;
|
|
95
|
+
opts = coreOpts;
|
|
70
96
|
const providedProfiler = opts.profiler;
|
|
71
97
|
const profiler = providedProfiler && providedProfiler.enabled ? providedProfiler : createQueryProfiler(String(entity));
|
|
72
98
|
profiler.mark("query:init");
|
|
@@ -76,6 +102,16 @@ class HybridQueryEngine {
|
|
|
76
102
|
profileClosed = true;
|
|
77
103
|
profiler.end(meta);
|
|
78
104
|
};
|
|
105
|
+
const applyAfterExtensions = async (queryResult) => {
|
|
106
|
+
if (!ext || !hybridExtCtx) return queryResult;
|
|
107
|
+
const diCtx = ext.resolve ? { resolve: ext.resolve } : noopDi;
|
|
108
|
+
return await runAfterQueryPipeline(
|
|
109
|
+
queryResult,
|
|
110
|
+
opts,
|
|
111
|
+
hybridExtCtx,
|
|
112
|
+
diCtx
|
|
113
|
+
);
|
|
114
|
+
};
|
|
79
115
|
try {
|
|
80
116
|
const debugEnabled = this.isDebugVerbosity();
|
|
81
117
|
if (debugEnabled) this.debug("query:start", { entity });
|
|
@@ -91,7 +127,7 @@ class HybridQueryEngine {
|
|
|
91
127
|
result: "custom_entity",
|
|
92
128
|
total: Array.isArray(result2.items) ? result2.items.length : void 0
|
|
93
129
|
});
|
|
94
|
-
return result2;
|
|
130
|
+
return await applyAfterExtensions(result2);
|
|
95
131
|
} catch (err) {
|
|
96
132
|
section.end({ error: err instanceof Error ? err.message : String(err) });
|
|
97
133
|
throw err;
|
|
@@ -109,7 +145,7 @@ class HybridQueryEngine {
|
|
|
109
145
|
if (debugEnabled) this.debug("query:fallback:missing-base", { entity, baseTable });
|
|
110
146
|
const fallbackResult = await this.fallback.query(entity, opts);
|
|
111
147
|
finishProfile({ result: "fallback", reason: "missing_base" });
|
|
112
|
-
return fallbackResult;
|
|
148
|
+
return await applyAfterExtensions(fallbackResult);
|
|
113
149
|
}
|
|
114
150
|
const normalizedFilters = normalizeFilters(opts.filters);
|
|
115
151
|
const cfFilters = normalizedFilters.filter((filter) => filter.field.startsWith("cf:") || filter.field.startsWith("l10n:"));
|
|
@@ -136,7 +172,7 @@ class HybridQueryEngine {
|
|
|
136
172
|
if (debugEnabled) this.debug("query:fallback:no-index", { entity });
|
|
137
173
|
const fallbackResult = await this.fallback.query(entity, opts);
|
|
138
174
|
finishProfile({ result: "fallback", reason: "no_index_rows" });
|
|
139
|
-
return fallbackResult;
|
|
175
|
+
return await applyAfterExtensions(fallbackResult);
|
|
140
176
|
}
|
|
141
177
|
if (entityHasActiveCustomFields) {
|
|
142
178
|
const gap = await profiler.measure(
|
|
@@ -182,7 +218,7 @@ class HybridQueryEngine {
|
|
|
182
218
|
baseCount: gap.stats?.baseCount ?? null,
|
|
183
219
|
indexedCount: gap.stats?.indexedCount ?? null
|
|
184
220
|
});
|
|
185
|
-
return resultWithWarning;
|
|
221
|
+
return await applyAfterExtensions(resultWithWarning);
|
|
186
222
|
}
|
|
187
223
|
if (gap.stats) {
|
|
188
224
|
console.warn("[HybridQueryEngine] Partial index coverage detected; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:", { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope });
|
|
@@ -647,10 +683,11 @@ class HybridQueryEngine {
|
|
|
647
683
|
);
|
|
648
684
|
}
|
|
649
685
|
const typedItems = items;
|
|
650
|
-
|
|
686
|
+
let result = { items: typedItems, page, pageSize, total };
|
|
651
687
|
if (partialIndexWarning) {
|
|
652
688
|
result.meta = { partialIndexWarning };
|
|
653
689
|
}
|
|
690
|
+
result = await applyAfterExtensions(result);
|
|
654
691
|
finishProfile({
|
|
655
692
|
result: "ok",
|
|
656
693
|
total,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/query_index/lib/engine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { QueryEngine, QueryOptions, QueryResult, FilterOp, Filter, QueryCustomFieldSource, PartialIndexWarning } from '@open-mercato/shared/lib/query/types'\nimport { SortDir } from '@open-mercato/shared/lib/query/types'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine, resolveEntityTableName } from '@open-mercato/shared/lib/query/engine'\nimport type { Knex } from 'knex'\nimport type { EventBus } from '@open-mercato/events'\nimport { readCoverageSnapshot, refreshCoverageSnapshot } from './coverage'\nimport { createProfiler, shouldEnableProfiler, type Profiler } from '@open-mercato/shared/lib/profiler'\nimport type { VectorIndexService } from '@open-mercato/search/vector'\nimport { decryptIndexDocCustomFields } from '@open-mercato/shared/lib/encryption/indexDoc'\nimport { parseBooleanToken, parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport {\n applyJoinFilters,\n normalizeFilters,\n partitionFilters,\n resolveJoins,\n type BaseFilter,\n type ResolvedJoin,\n} from '@open-mercato/shared/lib/query/join-utils'\nimport { resolveSearchConfig, type SearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\n\nfunction resolveBooleanEnv(names: readonly string[], defaultValue: boolean): boolean {\n for (const name of names) {\n const raw = process.env[name]\n if (raw !== undefined) return parseBooleanWithDefault(raw, defaultValue)\n }\n return defaultValue\n}\n\nfunction resolveDebugVerbosity(): boolean {\n // Check explicit OM_QUERY_INDEX_DEBUG flag first\n const queryIndexDebug = process.env.OM_QUERY_INDEX_DEBUG\n if (queryIndexDebug !== undefined) {\n return parseBooleanToken(queryIndexDebug) ?? false\n }\n // Fall back to log level or NODE_ENV\n const level = (process.env.LOG_VERBOSITY ?? process.env.LOG_LEVEL ?? '').toLowerCase()\n if (['debug', 'trace', 'silly'].includes(level)) return true\n // Default to false (don't spam logs in development)\n return false\n}\n\ntype ResultRow = Record<string, unknown>\ntype ResultBuilder<TResult = ResultRow[]> = Knex.QueryBuilder<ResultRow, TResult>\ntype NormalizedFilter = { field: string; op: FilterOp; value?: unknown }\ntype IndexDocSource = { alias: string; entityId: EntityId; recordIdColumn: string }\ntype PreparedCustomFieldSource = {\n alias: string\n indexAlias: string\n entityId: EntityId\n recordIdColumn: string\n organizationField?: string\n tenantField?: string\n table: string\n}\ntype SearchRuntime = {\n enabled: boolean\n config: SearchConfig\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n tenantId?: string | null\n searchSources?: SearchTokenSource[]\n}\n\ntype EncryptionResolver = () => {\n decryptEntityPayload?: (entityId: EntityId, payload: Record<string, unknown>, tenantId?: string | null, organizationId?: string | null) => Promise<Record<string, unknown>>\n isEnabled?: () => boolean\n} | null\n\ntype SearchTokenSource = { entity: string; recordIdColumn: string }\n\nfunction createQueryProfiler(entity: string): Profiler {\n const enabled = shouldEnableProfiler(entity)\n return createProfiler({\n scope: 'query_engine',\n target: entity,\n label: `query_engine:${entity}`,\n loggerLabel: '[qe:profile]',\n enabled,\n })\n}\n\nexport class HybridQueryEngine implements QueryEngine {\n private coverageStatsTtlMs: number\n private customFieldKeysCache = new Map<string, { expiresAt: number; value: string[] }>()\n private customFieldKeysTtlMs: number\n private columnCache = new Map<string, boolean>()\n private debugVerbosity: boolean | null = null\n private sqlDebugEnabled: boolean | null = null\n private forcePartialIndexEnabled: boolean | null = null\n private autoReindexEnabled: boolean | null = null\n private coverageOptimizationEnabled: boolean | null = null\n private pendingCoverageRefreshKeys = new Set<string>()\n private searchAliasSeq = 0\n\n constructor(\n private em: EntityManager,\n private fallback: BasicQueryEngine,\n private eventBusResolver?: () => Pick<EventBus, 'emitEvent'> | null | undefined,\n private vectorServiceResolver?: () => VectorIndexService | null | undefined,\n private encryptionResolver?: EncryptionResolver,\n ) {\n const coverageTtl = Number.parseInt(process.env.QUERY_INDEX_COVERAGE_CACHE_MS ?? '', 10)\n this.coverageStatsTtlMs = Number.isFinite(coverageTtl) && coverageTtl >= 0 ? coverageTtl : 5 * 60 * 1000\n const cfTtl = Number.parseInt(process.env.QUERY_INDEX_CF_KEYS_CACHE_MS ?? '', 10)\n this.customFieldKeysTtlMs = Number.isFinite(cfTtl) && cfTtl >= 0 ? cfTtl : 5 * 60 * 1000\n }\n\n private getEncryptionService() {\n try {\n return this.encryptionResolver?.() ?? null\n } catch {\n return null\n }\n }\n\n async query<T = unknown>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n const providedProfiler = opts.profiler\n const profiler = providedProfiler && providedProfiler.enabled\n ? providedProfiler\n : createQueryProfiler(String(entity))\n profiler.mark('query:init')\n let profileClosed = false\n const finishProfile = (meta?: Record<string, unknown>) => {\n if (!profiler.enabled || profileClosed) return\n profileClosed = true\n profiler.end(meta)\n }\n\n try {\n const debugEnabled = this.isDebugVerbosity()\n if (debugEnabled) this.debug('query:start', { entity })\n this.searchAliasSeq = 0\n\n const isCustom = await this.isCustomEntity(entity)\n if (isCustom) {\n if (debugEnabled) this.debug('query:custom-entity', { entity })\n const section = profiler.section('custom_entity')\n try {\n const result = await this.queryCustomEntity<T>(entity, opts)\n section.end({ mode: 'custom_entity' })\n finishProfile({\n result: 'custom_entity',\n total: Array.isArray(result.items) ? result.items.length : undefined,\n })\n return result\n } catch (err) {\n section.end({ error: err instanceof Error ? err.message : String(err) })\n throw err\n }\n }\n\n const knex = this.getKnex()\n profiler.mark('query:knex_ready')\n const baseTable = resolveEntityTableName(this.em, entity)\n profiler.mark('query:base_table_resolved')\n const searchConfig = resolveSearchConfig()\n const orgScope = this.resolveOrganizationScope(opts)\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n\n const baseExists = await profiler.measure('base_table_exists', () => this.tableExists(baseTable))\n if (!baseExists) {\n if (debugEnabled) this.debug('query:fallback:missing-base', { entity, baseTable })\n const fallbackResult = await this.fallback.query(entity, opts)\n finishProfile({ result: 'fallback', reason: 'missing_base' })\n return fallbackResult\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n const cfFilters = normalizedFilters.filter((filter) => filter.field.startsWith('cf:') || filter.field.startsWith('l10n:'))\n const coverageScope = this.resolveCoverageSnapshotScope(opts)\n const wantsCf = (\n (opts.fields || []).some((field) => typeof field === 'string' && (field.startsWith('cf:') || field.startsWith('l10n:'))) ||\n cfFilters.length > 0 ||\n opts.includeCustomFields === true ||\n (Array.isArray(opts.includeCustomFields) && opts.includeCustomFields.length > 0)\n )\n\n if (debugEnabled) {\n this.debug('query:config', {\n entity,\n wantsCustomFields: wantsCf,\n customFieldSources: Array.isArray(opts.customFieldSources) ? opts.customFieldSources.map((src) => src?.entityId) : undefined,\n fields: opts.fields,\n })\n }\n\n let partialIndexWarning: PartialIndexWarning | null = null\n let entityHasActiveCustomFields = true\n\n if (wantsCf) {\n entityHasActiveCustomFields = await this.entityHasActiveCustomFields(entity, opts.tenantId ?? null)\n const hasIndexRows = await profiler.measure(\n 'index_any_rows',\n () => this.indexAnyRows(entity),\n (value) => ({ hasIndexRows: value })\n )\n if (!hasIndexRows) {\n if (debugEnabled) this.debug('query:fallback:no-index', { entity })\n const fallbackResult = await this.fallback.query(entity, opts)\n finishProfile({ result: 'fallback', reason: 'no_index_rows' })\n return fallbackResult\n }\n if (entityHasActiveCustomFields) {\n const gap = await profiler.measure(\n 'resolve_coverage_gap',\n () => this.resolveCoverageGap(entity, opts, coverageScope),\n (value) => (value\n ? {\n scope: value.scope,\n baseCount: value.stats?.baseCount ?? null,\n indexedCount: value.stats?.indexedCount ?? null,\n }\n : { scope: null })\n )\n if (gap) {\n if (!opts.skipAutoReindex) {\n this.scheduleAutoReindex(entity, opts, gap.stats, coverageScope?.organizationId ?? null)\n }\n const force = this.isForcePartialIndexEnabled()\n if (!force) {\n if (gap.stats) {\n console.warn('[HybridQueryEngine] Partial index coverage detected; falling back to basic engine:', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n if (debugEnabled) this.debug('query:fallback:partial-coverage', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n } else {\n console.warn('[HybridQueryEngine] Partial index coverage detected; falling back to basic engine:', { entity })\n if (debugEnabled) this.debug('query:fallback:partial-coverage', { entity })\n }\n const fallbackResult = await this.fallback.query(entity, opts)\n const resultWithWarning: QueryResult<T> = {\n ...fallbackResult,\n meta: {\n ...(fallbackResult.meta ?? {}),\n partialIndexWarning: {\n entity,\n entityLabel: this.resolveEntityLabel(entity),\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n scope: gap.stats ? gap.scope : undefined,\n },\n },\n }\n finishProfile({\n result: 'fallback',\n reason: 'partial_index',\n scope: gap.scope,\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n })\n return resultWithWarning\n }\n if (gap.stats) {\n console.warn('[HybridQueryEngine] Partial index coverage detected; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n if (debugEnabled) this.debug('query:partial-coverage:forced', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n } else {\n console.warn('[HybridQueryEngine] Partial index coverage detected; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity })\n if (debugEnabled) this.debug('query:partial-coverage:forced', { entity })\n }\n partialIndexWarning = {\n entity,\n entityLabel: this.resolveEntityLabel(entity),\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n scope: gap.stats ? gap.scope : undefined,\n }\n }\n } else if (debugEnabled) {\n this.debug('query:coverage:skip-no-custom-fields', { entity })\n }\n }\n\n const qualify = (col: string) => `b.${col}`\n let builder: ResultBuilder = knex({ b: baseTable })\n const hasCustomFieldFilters = cfFilters.length > 0\n const canOptimizeCount = !hasCustomFieldFilters\n let optimizedCountBuilder: ResultBuilder | null = canOptimizeCount ? knex({ b: baseTable }) : null\n\n const resolvedJoinsConfig = resolveJoins(baseTable, opts.joins, (entityId) => resolveEntityTableName(this.em, entityId as any))\n const joinMap = new Map<string, ResolvedJoin>()\n const aliasTables = new Map<string, string>()\n aliasTables.set('b', baseTable)\n aliasTables.set('base', baseTable)\n aliasTables.set(baseTable, baseTable)\n for (const join of resolvedJoinsConfig) {\n joinMap.set(join.alias, join)\n aliasTables.set(join.alias, join.table)\n }\n const { baseFilters, joinFilters } = partitionFilters(baseTable, normalizedFilters, joinMap)\n\n if (!opts.tenantId) throw new Error('QueryEngine: tenantId is required')\n\n const hasOrganizationColumn = await this.columnExists(baseTable, 'organization_id')\n const hasTenantColumn = await this.columnExists(baseTable, 'tenant_id')\n const hasDeletedColumn = await this.columnExists(baseTable, 'deleted_at')\n const searchRuntimeBase = {\n enabled: false,\n config: searchConfig,\n organizationScope: orgScope,\n tenantId: opts.tenantId ?? null,\n }\n\n if (orgScope && hasOrganizationColumn) {\n builder = this.applyOrganizationScope(builder, qualify('organization_id'), orgScope)\n if (optimizedCountBuilder) optimizedCountBuilder = this.applyOrganizationScope(optimizedCountBuilder, qualify('organization_id'), orgScope)\n }\n if (hasTenantColumn) {\n builder = builder.where(qualify('tenant_id'), opts.tenantId)\n if (optimizedCountBuilder) optimizedCountBuilder = optimizedCountBuilder.where(qualify('tenant_id'), opts.tenantId)\n }\n if (!opts.withDeleted && hasDeletedColumn) {\n builder = builder.whereNull(qualify('deleted_at'))\n if (optimizedCountBuilder) optimizedCountBuilder = optimizedCountBuilder.whereNull(qualify('deleted_at'))\n }\n\n const baseJoinParts: string[] = []\n baseJoinParts.push(`ei.entity_type = ${knex.raw('?', [entity]).toString()}`)\n baseJoinParts.push(`ei.entity_id = (${qualify('id')}::text)`)\n if (hasOrganizationColumn) {\n baseJoinParts.push(`ei.organization_id = ${qualify('organization_id')}`)\n baseJoinParts.push('ei.organization_id is not null')\n }\n if (hasTenantColumn) {\n baseJoinParts.push(`ei.tenant_id = ${qualify('tenant_id')}`)\n baseJoinParts.push('ei.tenant_id is not null')\n }\n if (!opts.withDeleted) baseJoinParts.push(`ei.deleted_at is null`)\n builder = builder.leftJoin({ ei: 'entity_indexes' }, knex.raw(baseJoinParts.join(' AND ')))\n\n const columns = await this.getBaseColumnsForEntity(entity)\n const indexSources: IndexDocSource[] = [{ alias: 'ei', entityId: entity, recordIdColumn: 'b.id' }]\n\n const shouldAttachCustomSources = Array.isArray(opts.customFieldSources) && opts.customFieldSources.length > 0 && (wantsCf || searchEnabled)\n if (shouldAttachCustomSources) {\n const prepared = this.prepareCustomFieldSources(knex, builder, opts.customFieldSources ?? [], qualify)\n builder = prepared.builder\n for (const source of prepared.sources) {\n const fragments: string[] = []\n fragments.push(`${source.indexAlias}.entity_type = ${knex.raw('?', [source.entityId]).toString()}`)\n fragments.push(`${source.indexAlias}.entity_id = (${knex.raw('??::text', [`${source.alias}.${source.recordIdColumn}`]).toString()})`)\n const orgExpr = source.organizationField\n ? knex.raw('??', [`${source.alias}.${source.organizationField}`]).toString()\n : (columns.has('organization_id') ? qualify('organization_id') : null)\n if (orgExpr) {\n fragments.push(`${source.indexAlias}.organization_id = ${orgExpr}`)\n fragments.push(`${source.indexAlias}.organization_id is not null`)\n }\n const tenantExpr = source.tenantField\n ? knex.raw('??', [`${source.alias}.${source.tenantField}`]).toString()\n : (columns.has('tenant_id') ? qualify('tenant_id') : null)\n if (tenantExpr) {\n fragments.push(`${source.indexAlias}.tenant_id = ${tenantExpr}`)\n fragments.push(`${source.indexAlias}.tenant_id is not null`)\n }\n if (!opts.withDeleted) fragments.push(`${source.indexAlias}.deleted_at is null`)\n builder = builder.leftJoin({ [source.indexAlias]: 'entity_indexes' }, knex.raw(fragments.join(' AND ')))\n indexSources.push({ alias: source.indexAlias, entityId: source.entityId, recordIdColumn: `${source.alias}.${source.recordIdColumn}` })\n }\n }\n\n if (debugEnabled) {\n this.debug('query:index-sources', {\n entity,\n sources: indexSources.map((src) => ({ alias: src.alias, entity: src.entityId })),\n })\n }\n\n const searchSources: SearchTokenSource[] = indexSources\n .map((src) => ({\n entity: String(src.entityId),\n recordIdColumn: src.recordIdColumn,\n }))\n .filter((src) => src.recordIdColumn && src.entity)\n const hasSearchTokens = searchEnabled && searchSources.length\n ? await this.searchSourcesHaveTokens(searchSources, opts.tenantId ?? null, orgScope)\n : false\n const searchRuntime: SearchRuntime = { ...searchRuntimeBase, searchSources, enabled: searchEnabled && hasSearchTokens }\n const searchFilters = normalizeFilters(opts.filters).filter((filter) => filter.op === 'like' || filter.op === 'ilike')\n if (searchFilters.length) {\n this.logSearchDebug('search:init', {\n entity,\n baseTable,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n fields: searchFilters.map((filter) => String(filter.field)),\n searchEnabled,\n hasSearchTokens,\n searchSources,\n searchConfig: {\n enabled: searchConfig.enabled,\n minTokenLength: searchConfig.minTokenLength,\n enablePartials: searchConfig.enablePartials,\n hashAlgorithm: searchConfig.hashAlgorithm,\n blocklistedFields: searchConfig.blocklistedFields,\n },\n })\n if (!searchEnabled) {\n this.logSearchDebug('search:disabled', { entity, baseTable })\n } else if (!hasSearchTokens) {\n this.logSearchDebug('search:no-search-tokens', {\n entity,\n baseTable,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n searchSources,\n })\n }\n }\n const hasNonBaseSearchSource = searchSources.some(\n (src) => src.entity !== String(entity) || src.recordIdColumn !== 'b.id'\n )\n if (hasNonBaseSearchSource) {\n optimizedCountBuilder = null\n }\n\n if (!partialIndexWarning && Array.isArray(opts.customFieldSources) && opts.customFieldSources.length > 0 && this.isForcePartialIndexEnabled()) {\n const seen = new Set<string>([entity])\n for (const source of opts.customFieldSources) {\n const targetEntity = source?.entityId ? String(source.entityId) : null\n if (!targetEntity || seen.has(targetEntity)) continue\n seen.add(targetEntity)\n const sourceHasCustomFields = await this.entityHasActiveCustomFields(targetEntity, opts.tenantId ?? null)\n if (!sourceHasCustomFields) {\n if (debugEnabled) this.debug('query:coverage:skip-no-custom-fields', { entity: targetEntity })\n continue\n }\n const sourceTable = source.table ?? resolveEntityTableName(this.em, targetEntity)\n try {\n const gap = await profiler.measure(\n 'resolve_coverage_gap',\n () => this.resolveCoverageGap(targetEntity, opts, coverageScope, sourceTable),\n (value) => (value\n ? {\n entity: targetEntity,\n scope: value.scope,\n baseCount: value.stats?.baseCount ?? null,\n indexedCount: value.stats?.indexedCount ?? null,\n }\n : { entity: targetEntity, scope: null })\n )\n if (!gap) continue\n if (!opts.skipAutoReindex) {\n this.scheduleAutoReindex(targetEntity, opts, gap.stats, coverageScope?.organizationId ?? null)\n }\n partialIndexWarning = {\n entity: targetEntity,\n entityLabel: this.resolveEntityLabel(targetEntity),\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n scope: gap.stats ? gap.scope : undefined,\n }\n if (debugEnabled) {\n if (gap.stats) this.debug('query:partial-coverage:forced', { entity: targetEntity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n else this.debug('query:partial-coverage:forced', { entity: targetEntity })\n }\n break\n } catch (err) {\n if (debugEnabled) this.debug('query:partial-coverage:check-failed', { entity: targetEntity, error: err instanceof Error ? err.message : err })\n }\n }\n }\n\n if (\n !partialIndexWarning &&\n wantsCf &&\n entityHasActiveCustomFields &&\n this.isForcePartialIndexEnabled() &&\n opts.tenantId\n ) {\n try {\n await this.indexCoverageStats(entity, opts, coverageScope)\n const globalStats = await this.indexCoverageStats(entity, opts, coverageScope)\n if (globalStats) {\n const globalBase = globalStats.baseCount\n const globalIndexed = globalStats.indexedCount\n const globalGap = (globalBase > 0 && globalIndexed < globalBase) || globalIndexed > globalBase\n if (globalGap) {\n console.warn('[HybridQueryEngine] Partial index coverage detected at global scope; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity, baseCount: globalBase, indexedCount: globalIndexed, scope: 'global' })\n if (debugEnabled) {\n this.debug('query:partial-coverage:forced', {\n entity,\n baseCount: globalBase,\n indexedCount: globalIndexed,\n scope: 'global',\n })\n }\n partialIndexWarning = {\n entity,\n entityLabel: this.resolveEntityLabel(entity),\n baseCount: globalBase,\n indexedCount: globalIndexed,\n scope: 'global',\n }\n }\n }\n } catch (err) {\n if (debugEnabled) {\n this.debug('query:partial-coverage:global-check-failed', {\n entity,\n error: err instanceof Error ? err.message : err,\n })\n }\n }\n }\n\n const resolveBaseColumn = (field: string): string | null => {\n if (columns.has(field)) return field\n if (field === 'organization_id' && columns.has('id')) return 'id'\n return null\n }\n\n for (const filter of cfFilters) {\n builder = this.applyCfFilterAcrossSources(\n knex,\n builder,\n filter.field,\n filter.op,\n filter.value,\n indexSources,\n searchRuntime\n )\n }\n\n for (const filter of baseFilters) {\n const baseField = resolveBaseColumn(String(filter.field))\n if (!baseField) continue\n const column = qualify(baseField)\n builder = this.applyColumnFilter(builder, column, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: 'b.id',\n })\n if (optimizedCountBuilder) {\n optimizedCountBuilder = this.applyColumnFilter(optimizedCountBuilder, column, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: 'b.id',\n })\n }\n }\n\n const applyAliasScopes = async (target: ResultBuilder, aliasName: string) => {\n const tableName = aliasTables.get(aliasName)\n if (!tableName) return\n if (orgScope && await this.columnExists(tableName, 'organization_id')) {\n this.applyOrganizationScope(target, `${aliasName}.organization_id`, orgScope)\n }\n if (opts.tenantId && await this.columnExists(tableName, 'tenant_id')) {\n target.where(`${aliasName}.tenant_id`, opts.tenantId)\n }\n if (!opts.withDeleted && await this.columnExists(tableName, 'deleted_at')) {\n target.whereNull(`${aliasName}.deleted_at`)\n }\n }\n\n const applyJoinFilterOp = (target: ResultBuilder, column: string, op: FilterOp, value?: unknown) => {\n switch (op) {\n case 'eq':\n target.where(column, value as Knex.Value)\n break\n case 'ne':\n target.whereNot(column, value as Knex.Value)\n break\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='\n target.where(column, operator, value as Knex.Value)\n break\n }\n case 'in':\n target.whereIn(column, this.toArray(value) as readonly Knex.Value[])\n break\n case 'nin':\n target.whereNotIn(column, this.toArray(value) as readonly Knex.Value[])\n break\n case 'like':\n target.where(column, 'like', value as Knex.Value)\n break\n case 'ilike':\n target.where(column, 'ilike', value as Knex.Value)\n break\n case 'exists':\n value ? target.whereNotNull(column) : target.whereNull(column)\n break\n }\n }\n\n await applyJoinFilters({\n knex,\n baseTable,\n builder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (target, alias) => applyAliasScopes(target, alias),\n applyFilterOp: (target, column, op, value) => applyJoinFilterOp(target as ResultBuilder, column, op, value),\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n }) as ResultBuilder\n\n if (optimizedCountBuilder) {\n await applyJoinFilters({\n knex,\n baseTable,\n builder: optimizedCountBuilder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (target, alias) => applyAliasScopes(target, alias),\n applyFilterOp: (target, column, op, value) => applyJoinFilterOp(target as ResultBuilder, column, op, value),\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n })\n }\n\n // When no fields specified, select all base table columns (like BasicQueryEngine does)\n const selectFieldSet = new Set<string>((opts.fields && opts.fields.length) ? opts.fields.map(String) : Array.from(columns.keys()))\n if (opts.includeCustomFields === true) {\n const entityIds = Array.from(new Set(indexSources.map((src) => String(src.entityId))))\n try {\n const resolvedKeys = await this.resolveAvailableCustomFieldKeys(entityIds, opts.tenantId ?? null)\n resolvedKeys.forEach((key) => selectFieldSet.add(`cf:${key}`))\n if (this.isDebugVerbosity()) {\n this.debug('query:cf:resolved-keys', { entity, keys: resolvedKeys })\n }\n } catch (err) {\n console.warn('[HybridQueryEngine] Failed to resolve custom field keys for', entity, err)\n }\n } else if (Array.isArray(opts.includeCustomFields)) {\n opts.includeCustomFields\n .map((key) => String(key))\n .forEach((key) => selectFieldSet.add(`cf:${key}`))\n }\n const selectFields = Array.from(selectFieldSet)\n for (const field of selectFields) {\n const fieldName = String(field)\n if (fieldName.startsWith('cf:')) {\n const alias = this.sanitize(fieldName)\n const { jsonSql } = this.buildCfExpressions(knex, fieldName, indexSources)\n const exprSql = jsonSql === 'NULL' ? 'NULL::jsonb' : jsonSql\n builder = builder.select(knex.raw(`${exprSql} as ??`, [alias]))\n } else if (columns.has(fieldName)) {\n builder = builder.select(knex.raw('?? as ??', [qualify(fieldName), fieldName]))\n }\n }\n\n for (const sort of opts.sort || []) {\n const fieldName = String(sort.field)\n if (fieldName.startsWith('cf:')) {\n const { textSql } = this.buildCfExpressions(knex, fieldName, indexSources)\n if (textSql !== 'NULL') {\n const direction = sort.dir ?? SortDir.Asc\n builder = builder.orderByRaw(`${textSql} ${direction}`)\n }\n } else {\n const baseField = resolveBaseColumn(fieldName)\n if (!baseField) continue\n builder = builder.orderBy(qualify(baseField), sort.dir ?? SortDir.Asc)\n }\n }\n\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n\n const sqlDebugEnabled = this.isSqlDebugEnabled()\n let total: number\n\n if (optimizedCountBuilder) {\n const countSource = optimizedCountBuilder.clone().clearSelect().clearOrder().select(knex.raw(`${qualify('id')} as id`)).groupBy(qualify('id'))\n const countQuery = knex.from(countSource.as('sq')).count({ count: knex.raw('*') })\n if (debugEnabled && sqlDebugEnabled) {\n const { sql, bindings } = countQuery.clone().toSQL()\n this.debug('query:sql:count', { entity, sql, bindings })\n }\n const countRow = await this.captureSqlTiming(\n 'query:sql:count',\n entity,\n () => countQuery.first(),\n { optimized: true },\n profiler\n )\n total = this.parseCount(countRow)\n } else {\n const countBuilder = builder.clone().clearSelect().clearOrder().countDistinct(`${qualify('id')} as count`)\n if (debugEnabled && sqlDebugEnabled) {\n const { sql, bindings } = countBuilder.clone().toSQL()\n this.debug('query:sql:count', { entity, sql, bindings })\n }\n const countRow = await this.captureSqlTiming(\n 'query:sql:count',\n entity,\n () => countBuilder.first(),\n { optimized: false },\n profiler\n )\n total = this.parseCount(countRow)\n }\n\n const dataBuilder = builder.clone().limit(pageSize).offset((page - 1) * pageSize)\n\n if (debugEnabled && sqlDebugEnabled) {\n const { sql, bindings } = dataBuilder.clone().toSQL()\n this.debug('query:sql:data', { entity, sql, bindings, page, pageSize })\n }\n const itemsRaw = await this.captureSqlTiming(\n 'query:sql:data',\n entity,\n () => dataBuilder,\n { page, pageSize },\n profiler\n )\n if (debugEnabled) this.debug('query:complete', { entity, total, items: Array.isArray(itemsRaw) ? itemsRaw.length : 0 })\n\n let items = itemsRaw as any[]\n const encSvc = this.getEncryptionService()\n const dekKeyCache = new Map<string | null, string | null>()\n if (encSvc?.decryptEntityPayload) {\n const decrypt = encSvc.decryptEntityPayload.bind(encSvc) as (\n entityId: EntityId,\n payload: Record<string, unknown>,\n tenantId: string | null,\n organizationId: string | null,\n ) => Promise<Record<string, unknown>>\n items = await Promise.all(\n items.map(async (item) => {\n try {\n const decrypted = await decrypt(\n entity,\n item,\n item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n item?.organization_id ?? item?.organizationId ?? null,\n )\n return { ...item, ...decrypted }\n } catch (err) {\n console.error('Error decrypting entity payload', err);\n return item\n }\n })\n )\n }\n if (encSvc) {\n items = await Promise.all(\n items.map(async (item) => {\n try {\n return await decryptIndexDocCustomFields(\n item,\n {\n tenantId: item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n organizationId: item?.organization_id ?? item?.organizationId ?? null,\n },\n encSvc as any,\n dekKeyCache,\n )\n } catch {\n return item\n }\n }),\n )\n }\n\n const typedItems = items as unknown as T[]\n const result: QueryResult<T> = { items: typedItems, page, pageSize, total }\n if (partialIndexWarning) {\n result.meta = { partialIndexWarning }\n }\n finishProfile({\n result: 'ok',\n total,\n page,\n pageSize,\n itemCount: Array.isArray(items) ? items.length : undefined,\n partialIndexWarning: partialIndexWarning ? true : false,\n })\n return result\n } catch (err) {\n finishProfile({ result: 'error', error: err instanceof Error ? err.message : String(err) })\n throw err\n }\n }\n\n private getKnex(): Knex {\n const connection = this.em.getConnection()\n const withKnex = connection as { getKnex?: () => Knex }\n if (typeof withKnex.getKnex === 'function') {\n return withKnex.getKnex()\n }\n throw new Error('HybridQueryEngine requires a SQL connection that exposes getKnex()')\n }\n\n private prepareCustomFieldSources(\n knex: Knex,\n builder: ResultBuilder,\n sources: QueryCustomFieldSource[],\n qualify: (column: string) => string\n ): { builder: ResultBuilder; sources: PreparedCustomFieldSource[] } {\n let current = builder\n const prepared: PreparedCustomFieldSource[] = []\n sources.forEach((source, index) => {\n if (!source) return\n const joinTable = source.table ?? resolveEntityTableName(this.em, source.entityId)\n const alias = source.alias ?? `cfs_${index}`\n const join = source.join\n if (!join) {\n throw new Error(`QueryEngine: customFieldSources entry for ${String(source.entityId)} requires a join configuration`)\n }\n const joinArgs = { [alias]: joinTable }\n const joinCallback = function (this: Knex.JoinClause) {\n this.on(`${alias}.${join.toField}`, '=', qualify(join.fromField))\n }\n current = (join.type ?? 'left') === 'inner'\n ? current.join(joinArgs, joinCallback)\n : current.leftJoin(joinArgs, joinCallback)\n prepared.push({\n alias,\n indexAlias: `ei_${alias}`,\n entityId: source.entityId,\n recordIdColumn: source.recordIdColumn ?? 'id',\n organizationField: source.organizationField,\n tenantField: source.tenantField,\n table: joinTable,\n })\n })\n return { builder: current, sources: prepared }\n }\n\n private async isCustomEntity(entity: string): Promise<boolean> {\n try {\n const knex = this.getKnex()\n const row = await knex('custom_entities').where({ entity_id: entity, is_active: true }).first()\n return !!row\n } catch {\n return false\n }\n }\n\n private applySearchTokens<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n opts: {\n knex: Knex\n entity: string\n field: string\n hashes: string[]\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n combineWith?: 'and' | 'or'\n }\n ): boolean {\n if (!opts.hashes.length) {\n this.logSearchDebug('search:skip-no-hashes', {\n entity: opts.entity,\n field: opts.field,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n return false\n }\n const alias = `st_${this.searchAliasSeq++}`\n const combineWith = opts.combineWith === 'or' ? 'orWhereExists' : 'whereExists'\n const engine = this\n this.logSearchDebug('search:apply-search-tokens', {\n entity: opts.entity,\n field: opts.field,\n alias,\n tokenCount: opts.hashes.length,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n combineWith: opts.combineWith ?? 'and',\n })\n ;(q as any)[combineWith](function (this: Knex.QueryBuilder) {\n this.select(1)\n .from({ [alias]: 'search_tokens' })\n .where(`${alias}.entity_type`, opts.entity)\n .andWhere(`${alias}.field`, opts.field)\n .andWhereRaw('?? = ??::text', [`${alias}.entity_id`, opts.recordIdColumn])\n .whereIn(`${alias}.token_hash`, opts.hashes)\n .groupBy(`${alias}.entity_id`, `${alias}.field`)\n .havingRaw(`count(distinct ${alias}.token_hash) >= ?`, [opts.hashes.length])\n if (opts.tenantId !== undefined) {\n this.andWhereRaw(`${alias}.tenant_id is not distinct from ?`, [opts.tenantId ?? null])\n }\n if (opts.organizationScope) {\n engine.applyOrganizationScope(this as any, `${alias}.organization_id`, opts.organizationScope)\n }\n })\n return true\n }\n\n private jsonbRawAlias(knex: Knex, alias: string, key: string): Knex.Raw {\n // Prefer cf:<key> but fall back to bare <key> for legacy docs\n if (key.startsWith('cf:')) {\n const bare = key.slice(3)\n return knex.raw(`coalesce(${alias}.doc -> ?, ${alias}.doc -> ?)`, [key, bare])\n }\n return knex.raw(`${alias}.doc -> ?`, [key])\n }\n private cfTextExprAlias(knex: Knex, alias: string, key: string): Knex.Raw {\n if (key.startsWith('cf:')) {\n const bare = key.slice(3)\n return knex.raw(`coalesce((${alias}.doc ->> ?), (${alias}.doc ->> ?))`, [key, bare])\n }\n return knex.raw(`(${alias}.doc ->> ?)`, [key])\n }\n private buildCfExpressions(knex: Knex, key: string, sources: IndexDocSource[]): { jsonSql: string; textSql: string } {\n if (!sources.length) return { jsonSql: 'NULL', textSql: 'NULL' }\n const jsonFragments = sources.map((source) => this.jsonbRawAlias(knex, source.alias, key).toString())\n const textFragments = sources.map((source) => this.cfTextExprAlias(knex, source.alias, key).toString())\n const jsonSql = jsonFragments.length === 1 ? jsonFragments[0] : `coalesce(${jsonFragments.join(', ')})`\n const textSql = textFragments.length === 1 ? textFragments[0] : `coalesce(${textFragments.join(', ')})`\n return { jsonSql, textSql }\n }\n\n private applyCfFilterAcrossSources(\n knex: Knex,\n builder: ResultBuilder,\n key: string,\n op: FilterOp,\n value: unknown,\n sources: IndexDocSource[],\n search?: SearchRuntime\n ): ResultBuilder {\n if (!sources.length) return builder\n if ((op === 'like' || op === 'ilike') && search?.enabled && typeof value === 'string') {\n const tokens = tokenizeText(String(value), search.config)\n const hashes = tokens.hashes\n if (hashes.length) {\n let applied = false\n if (sources.length) {\n builder = builder.where((qb) => {\n sources.forEach((source, idx) => {\n const ok = this.applySearchTokens(qb as any, {\n knex,\n entity: source.entityId,\n field: key,\n hashes,\n recordIdColumn: `${source.alias}.entity_id`,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope ?? null,\n combineWith: idx === 0 ? 'and' : 'or',\n })\n if (ok) applied = true\n })\n })\n }\n this.logSearchDebug('search:cf-filter-across', {\n entity: sources.map((src) => src.entityId),\n field: key,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope,\n })\n if (applied) return builder\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: sources.map((src) => src.entityId),\n field: key,\n value,\n })\n }\n return builder\n }\n const { jsonSql, textSql } = this.buildCfExpressions(knex, key, sources)\n if (jsonSql === 'NULL' || textSql === 'NULL') return builder\n const textExpr = knex.raw(textSql)\n const arrContains = (val: unknown) => knex.raw(`${jsonSql} @> ?::jsonb`, [JSON.stringify([val])])\n switch (op) {\n case 'eq':\n return builder.where((qb) => {\n qb.orWhere(textExpr, '=', value as Knex.Value)\n qb.orWhere(arrContains(value))\n })\n case 'ne':\n return builder.whereNot(textExpr, '=', value as Knex.Value)\n case 'in': {\n const values = this.toArray(value)\n return builder.where((qb) => {\n values.forEach((val) => {\n qb.orWhere(textExpr, '=', val as Knex.Value)\n qb.orWhere(arrContains(val))\n })\n })\n }\n case 'nin': {\n const values = this.toArray(value) as readonly Knex.Value[]\n return builder.whereNotIn(textExpr as any, values as any)\n }\n case 'like':\n return builder.where(textExpr, 'like', value as Knex.Value)\n case 'ilike':\n return builder.where(textExpr, 'ilike', value as Knex.Value)\n case 'exists':\n return value\n ? builder.whereRaw(`${textExpr.toString()} is not null`)\n : builder.whereRaw(`${textExpr.toString()} is null`)\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='\n return builder.where(textExpr, operator, value as Knex.Value)\n }\n default:\n return builder\n }\n }\n\n private applyCfFilterFromAlias(\n knex: Knex,\n q: ResultBuilder,\n alias: string,\n entityType: string,\n key: string,\n op: FilterOp,\n value: unknown,\n search?: SearchRuntime\n ): ResultBuilder {\n const text = this.cfTextExprAlias(knex, alias, key)\n const arrExpr = knex.raw(`(${alias}.doc -> ?)`, [key])\n const arrContains = (val: unknown) => knex.raw(`${arrExpr.toString()} @> ?::jsonb`, [JSON.stringify([val])])\n if ((op === 'like' || op === 'ilike') && search?.enabled && typeof value === 'string') {\n const tokens = tokenizeText(String(value), search.config)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(q, {\n knex,\n entity: entityType,\n field: key,\n hashes,\n recordIdColumn: `${alias}.entity_id`,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope ?? null,\n })\n this.logSearchDebug('search:cf-filter', {\n entity: entityType,\n field: key,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope,\n })\n if (applied) return q\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: entityType,\n field: key,\n value,\n })\n }\n return q\n }\n switch (op) {\n case 'eq':\n return q.where((builder) => {\n builder.orWhere(text, '=', value as Knex.Value)\n builder.orWhere(arrContains(value))\n })\n case 'ne':\n return q.whereNot(text, '=', value as Knex.Value)\n case 'in': {\n const vals = this.toArray(value)\n return q.where((builder) => {\n vals.forEach((val) => {\n builder.orWhere(text, '=', val as Knex.Value)\n builder.orWhere(arrContains(val))\n })\n })\n }\n case 'nin': {\n const vals = this.toArray(value) as readonly Knex.Value[]\n return q.whereNotIn(text as any, vals as any)\n }\n case 'like':\n return q.where(text, 'like', value as Knex.Value)\n case 'ilike':\n return q.where(text, 'ilike', value as Knex.Value)\n case 'exists':\n return value\n ? q.whereRaw(`${text.toString()} is not null`)\n : q.whereRaw(`${text.toString()} is null`)\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='\n return q.where(text, operator, value as Knex.Value)\n }\n default:\n return q\n }\n }\n\n private async queryCustomEntity<T = unknown>(entity: string, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n const knex = this.getKnex()\n const alias = 'ce'\n let q = knex({ [alias]: 'custom_entities_storage' }).where(`${alias}.entity_type`, entity)\n\n const orgScope = this.resolveOrganizationScope(opts)\n\n // Require tenant scope; custom entities are tenant-scoped only\n if (!opts.tenantId) throw new Error('QueryEngine: tenantId is required')\n q = q.andWhere(`${alias}.tenant_id`, opts.tenantId)\n if (orgScope) {\n q = this.applyOrganizationScope(q, `${alias}.organization_id`, orgScope)\n }\n if (!opts.withDeleted) q = q.whereNull(`${alias}.deleted_at`)\n const searchConfig = resolveSearchConfig()\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n const hasSearchTokens = searchEnabled\n ? await this.hasSearchTokens(entity, opts.tenantId ?? null, orgScope)\n : false\n const searchRuntime: SearchRuntime = {\n enabled: searchEnabled && hasSearchTokens,\n config: searchConfig,\n organizationScope: orgScope,\n tenantId: opts.tenantId ?? null,\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n\n // Apply filters: cf:* via JSONB; other keys: special-case id/created_at/updated_at/deleted_at, otherwise from doc\n for (const filter of normalizedFilters) {\n if (filter.field.startsWith('cf:')) {\n q = this.applyCfFilterFromAlias(knex, q, alias, entity, filter.field, filter.op, filter.value, searchRuntime)\n continue\n }\n const column = this.resolveCustomEntityColumn(alias, String(filter.field))\n if (column) {\n q = this.applyColumnFilter(q, column, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: `${alias}.entity_id`,\n })\n continue\n }\n const docExpr = knex.raw(`(${alias}.doc ->> ?)`, [String(filter.field)])\n q = this.applyColumnFilter(q, docExpr, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: `${alias}.entity_id`,\n })\n }\n\n // Determine CFs and l10n keys to include\n const cfKeys = new Set<string>()\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n else if (typeof f === 'string' && f.startsWith('l10n:')) cfKeys.add(f)\n }\n for (const filter of normalizedFilters) {\n if (typeof filter.field === 'string' && filter.field.startsWith('cf:')) cfKeys.add(filter.field.slice(3))\n else if (typeof filter.field === 'string' && filter.field.startsWith('l10n:')) cfKeys.add(filter.field)\n }\n if (opts.includeCustomFields === true) {\n try {\n const rows = await knex('custom_field_defs')\n .select('key')\n .where({ entity_id: entity, is_active: true })\n .modify((qb) => {\n qb.andWhere({ tenant_id: opts.tenantId })\n // NOTE: organization-level scoping intentionally disabled for custom fields\n // if (opts.organizationId != null) qb.andWhere((b: any) => b.where({ organization_id: opts.organizationId }).orWhereNull('organization_id'))\n // else qb.whereNull('organization_id')\n })\n for (const row of rows) {\n const key = (row as Record<string, unknown>).key\n if (typeof key === 'string') {\n cfKeys.add(key)\n } else if (key != null) {\n cfKeys.add(String(key))\n }\n }\n } catch {\n // ignore and fall back to whatever keys we already have\n }\n } else if (Array.isArray(opts.includeCustomFields)) {\n for (const k of opts.includeCustomFields) cfKeys.add(k)\n }\n\n // Selection\n const requested = (opts.fields && opts.fields.length) ? opts.fields : ['id']\n for (const field of requested) {\n const f = String(field)\n if (f.startsWith('cf:')) {\n const aliasName = this.sanitize(f)\n const expr = this.jsonbRawAlias(knex, alias, f)\n q = q.select({ [aliasName]: expr })\n } else if (f === 'id') {\n q = q.select(knex.raw(`${alias}.entity_id as ??`, ['id']))\n } else if (f === 'created_at' || f === 'updated_at' || f === 'deleted_at') {\n q = q.select(knex.raw(`${alias}.?? as ??`, [f, f]))\n } else {\n // Non-cf from doc\n const expr = knex.raw(`(${alias}.doc ->> ?)`, [f])\n q = q.select({ [f]: expr })\n }\n }\n // Ensure CFs necessary for sort are selected\n const cfSelectedAliases: string[] = []\n for (const key of cfKeys) {\n const aliasName = this.sanitize(`cf:${key}`)\n const expr = this.jsonbRawAlias(knex, alias, `cf:${key}`)\n q = q.select({ [aliasName]: expr })\n cfSelectedAliases.push(aliasName)\n }\n\n // Sorting\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n const key = s.field.slice(3)\n const aliasName = this.sanitize(`cf:${key}`)\n if (!cfSelectedAliases.includes(aliasName)) {\n const expr = this.jsonbRawAlias(knex, alias, `cf:${key}`)\n q = q.select({ [aliasName]: expr })\n cfSelectedAliases.push(aliasName)\n }\n q = q.orderBy(aliasName, s.dir ?? SortDir.Asc)\n } else if (s.field === 'id') {\n q = q.orderBy(`${alias}.entity_id`, s.dir ?? SortDir.Asc)\n } else if (s.field === 'created_at' || s.field === 'updated_at' || s.field === 'deleted_at') {\n q = q.orderBy(`${alias}.${s.field}`, s.dir ?? SortDir.Asc)\n } else {\n const direction = s.dir ?? SortDir.Asc\n q = q.orderByRaw(`(${alias}.doc ->> ?) ${direction}`, [s.field])\n }\n }\n\n // Pagination + totals\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n const countClone = q.clone()\n if (typeof countClone.clearSelect === 'function') countClone.clearSelect()\n if (typeof countClone.clearOrder === 'function') countClone.clearOrder()\n const countRow = await countClone.countDistinct(`${alias}.entity_id as count`).first()\n const total = this.parseCount(countRow)\n const items = await q.limit(pageSize).offset((page - 1) * pageSize)\n return { items, page, pageSize, total }\n }\n\n private async tableExists(table: string): Promise<boolean> {\n const knex = this.getKnex()\n const exists = await knex('information_schema.tables').where({ table_name: table }).first()\n return !!exists\n }\n\n private async hasSearchTokens(\n entity: string,\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n try {\n const knex = this.getKnex()\n const query = knex('search_tokens').select(1).where('entity_type', entity).limit(1)\n if (tenantId !== undefined) {\n query.andWhereRaw('tenant_id is not distinct from ?', [tenantId])\n }\n if (orgScope) {\n this.applyOrganizationScope(query as any, 'search_tokens.organization_id', orgScope)\n }\n const row = await query.first()\n return !!row\n } catch (err) {\n this.logSearchDebug('search:has-tokens-error', {\n entity,\n tenantId,\n organizationScope: orgScope,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n }\n\n private async searchSourcesHaveTokens(\n sources: SearchTokenSource[],\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n for (const source of sources) {\n const ok = await this.hasSearchTokens(source.entity, tenantId, orgScope)\n this.logSearchDebug('search:source-has-tokens', {\n entity: source.entity,\n recordIdColumn: source.recordIdColumn,\n tenantId,\n organizationScope: orgScope,\n hasTokens: ok,\n })\n if (ok) return true\n }\n return false\n }\n\n private async resolveAvailableCustomFieldKeys(entityIds: string[], tenantId: string | null): Promise<string[]> {\n if (!entityIds.length) return []\n const cacheKey = this.customFieldKeysCacheKey(entityIds, tenantId)\n const now = Date.now()\n const cached = this.customFieldKeysCache.get(cacheKey)\n if (cached && cached.expiresAt > now) {\n return cached.value.slice()\n }\n\n const knex = this.getKnex()\n const rows = await knex('custom_field_defs')\n .select('key')\n .whereIn('entity_id', entityIds)\n .andWhere('is_active', true)\n .modify((qb: any) => {\n qb.andWhere((inner: any) => {\n inner.where({ tenant_id: tenantId }).orWhereNull('tenant_id')\n })\n })\n const keys = new Set<string>()\n for (const row of rows || []) {\n const key = (row as Record<string, unknown>).key\n if (typeof key === 'string' && key.trim().length) keys.add(key.trim())\n else if (key != null) keys.add(String(key))\n }\n const result = Array.from(keys)\n if (this.customFieldKeysTtlMs > 0) {\n this.customFieldKeysCache.set(cacheKey, { expiresAt: now + this.customFieldKeysTtlMs, value: result })\n }\n return result.slice()\n }\n\n private async entityHasActiveCustomFields(entityId: string, tenantId: string | null): Promise<boolean> {\n try {\n const keys = await this.resolveAvailableCustomFieldKeys([entityId], tenantId)\n return keys.length > 0\n } catch (err) {\n if (this.isDebugVerbosity()) {\n this.debug('query:cf:check-error', {\n entity: entityId,\n tenantId: tenantId ?? null,\n error: err instanceof Error ? err.message : err,\n })\n }\n return true\n }\n }\n\n private customFieldKeysCacheKey(entityIds: string[], tenantId: string | null): string {\n const sorted = entityIds.slice().sort((a, b) => a.localeCompare(b)).join(',')\n return `${tenantId ?? '__none__'}|${sorted}`\n }\n\n private resolveVectorService(): VectorIndexService | null {\n if (!this.vectorServiceResolver) return null\n try {\n return this.vectorServiceResolver() ?? null\n } catch {\n return null\n }\n }\n\n private resolveEntityLabel(entity: string): string {\n return entity\n }\n\n private async indexAnyRows(entity: string): Promise<boolean> {\n const knex = this.getKnex()\n // Prefer coverage snapshots \u2013 cheap and already scoped by maintenance jobs.\n const coverage = await knex('entity_index_coverage')\n .select(1)\n .where('entity_type', entity)\n .where('indexed_count', '>', 0)\n .first()\n if (coverage) return true\n const exists = await knex('entity_indexes').select('entity_id').where({ entity_type: entity }).first()\n return !!exists\n }\n private async getStoredCoverageSnapshot(\n entity: string,\n tenantId: string | null,\n organizationId: string | null,\n withDeleted: boolean\n ): Promise<{ baseCount: number; indexedCount: number } | null> {\n try {\n if (!this.isCoverageOptimizationEnabled()) {\n await refreshCoverageSnapshot(\n this.em,\n {\n entityType: entity,\n tenantId,\n organizationId,\n withDeleted,\n },\n )\n }\n const knex = this.getKnex()\n const row = await readCoverageSnapshot(knex, {\n entityType: entity,\n tenantId,\n organizationId,\n withDeleted,\n })\n if (!row) return null\n return { baseCount: row.baseCount, indexedCount: row.indexedCount }\n } catch (err) {\n if (this.isDebugVerbosity()) {\n this.debug('coverage:snapshot:read-error', {\n entity,\n tenantId,\n organizationId,\n withDeleted,\n error: err instanceof Error ? err.message : err,\n })\n }\n return null\n }\n }\n\n private scheduleAutoReindex(\n entity: string,\n opts: QueryOptions,\n stats?: { baseCount: number; indexedCount: number },\n organizationIdOverride?: string | null\n ) {\n if (!this.isAutoReindexEnabled()) return\n\n const bus = this.resolveEventBus()\n if (!bus) return\n const payload = {\n entityType: entity,\n tenantId: opts.tenantId ?? null,\n organizationId: organizationIdOverride ?? opts.organizationId ?? null,\n force: false,\n }\n const context = stats\n ? {\n entity,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n baseCount: stats.baseCount,\n indexedCount: stats.indexedCount,\n }\n : { entity, tenantId: payload.tenantId, organizationId: payload.organizationId }\n\n void Promise.resolve()\n .then(async () => {\n try {\n await bus.emitEvent('query_index.reindex', payload, { persistent: true })\n if (this.isDebugVerbosity()) this.debug('query:auto-reindex:scheduled', context)\n } catch (err) {\n console.warn('[HybridQueryEngine] Failed to schedule auto reindex:', {\n ...context,\n error: err instanceof Error ? err.message : err,\n })\n }\n })\n }\n\n private scheduleCoverageRefresh(\n entity: string,\n tenantId: string | null | undefined,\n organizationId: string | null | undefined,\n withDeleted: boolean\n ): void {\n const bus = this.resolveEventBus()\n if (!bus) return\n const key = [\n entity,\n tenantId ?? '__tenant__',\n organizationId ?? '__org__',\n withDeleted ? '1' : '0',\n ].join('|')\n if (this.pendingCoverageRefreshKeys.has(key)) return\n this.pendingCoverageRefreshKeys.add(key)\n void Promise.resolve()\n .then(async () => {\n try {\n await bus.emitEvent('query_index.coverage.refresh', {\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n withDeleted,\n delayMs: 0,\n })\n if (this.isDebugVerbosity()) {\n this.debug('coverage:refresh:scheduled', {\n entity,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n withDeleted,\n })\n }\n } catch (err) {\n if (this.isDebugVerbosity()) {\n this.debug('coverage:refresh:failed', {\n entity,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n withDeleted,\n error: err instanceof Error ? err.message : err,\n })\n }\n }\n })\n .finally(() => {\n this.pendingCoverageRefreshKeys.delete(key)\n })\n }\n\n private resolveEventBus(): Pick<EventBus, 'emitEvent'> | null {\n if (!this.eventBusResolver) return null\n try {\n const bus = this.eventBusResolver()\n return bus ?? null\n } catch {\n return null\n }\n }\n\n private isAutoReindexEnabled(): boolean {\n if (this.autoReindexEnabled != null) return this.autoReindexEnabled\n const raw = (\n process.env.SCHEDULE_AUTO_REINDEX ??\n process.env.QUERY_INDEX_AUTO_REINDEX ??\n ''\n )\n .trim()\n .toLowerCase()\n if (!raw) {\n this.autoReindexEnabled = true\n return true\n }\n const parsed = parseBooleanToken(raw)\n this.autoReindexEnabled = parsed === null ? true : parsed\n return this.autoReindexEnabled\n }\n\n private isCoverageOptimizationEnabled(): boolean {\n if (this.coverageOptimizationEnabled != null) return this.coverageOptimizationEnabled\n const raw = (process.env.OPTIMIZE_INDEX_COVERAGE_STATS ?? '').trim().toLowerCase()\n if (!raw) {\n this.coverageOptimizationEnabled = false\n return false\n }\n this.coverageOptimizationEnabled = parseBooleanToken(raw) === true\n return this.coverageOptimizationEnabled\n }\n\n private async columnExists(table: string, column: string): Promise<boolean> {\n const key = `${table}.${column}`\n if (this.columnCache.has(key)) {\n const cached = this.columnCache.get(key)\n if (cached === true) return true\n this.columnCache.delete(key)\n }\n const knex = this.getKnex()\n const exists = await knex('information_schema.columns')\n .where({ table_name: table, column_name: column })\n .first()\n const present = !!exists\n if (present) this.columnCache.set(key, true)\n else this.columnCache.delete(key)\n return present\n }\n\n private async getBaseColumnsForEntity(entity: string): Promise<Map<string, string>> {\n const knex = this.getKnex()\n const table = resolveEntityTableName(this.em, entity)\n const rows = await knex('information_schema.columns')\n .select('column_name', 'data_type')\n .where({ table_name: table })\n const map = new Map<string, string>()\n for (const r of rows) map.set(r.column_name, r.data_type)\n return map\n }\n\n private resolveOrganizationScope(opts: QueryOptions): { ids: string[]; includeNull: boolean } | null {\n if (opts.organizationIds !== undefined) {\n const raw = (opts.organizationIds ?? []).map((id) => (typeof id === 'string' ? id.trim() : id))\n const includeNull = raw.some((id) => id == null || id === '')\n const ids = raw.filter((id): id is string => typeof id === 'string' && id.length > 0)\n const unique = Array.from(new Set(ids))\n return { ids: unique, includeNull }\n }\n if (typeof opts.organizationId === 'string' && opts.organizationId.trim().length > 0) {\n return { ids: [opts.organizationId], includeNull: false }\n }\n return null\n }\n\n private resolveCoverageSnapshotScope(\n opts: QueryOptions\n ): { tenantId: string | null; organizationId: string | null } | null {\n const tenantId = opts.tenantId ?? null\n const orgScope = this.resolveOrganizationScope(opts)\n if (!orgScope) return { tenantId, organizationId: null }\n if (orgScope.includeNull) {\n if (orgScope.ids.length === 0) return { tenantId, organizationId: null }\n return null\n }\n if (orgScope.ids.length === 1) return { tenantId, organizationId: orgScope.ids[0] }\n if (orgScope.ids.length === 0) return { tenantId, organizationId: null }\n return null\n }\n\n private applyOrganizationScope<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n column: string,\n scope: { ids: string[]; includeNull: boolean }\n ): Knex.QueryBuilder<TRecord, TResult> {\n if (scope.ids.length === 0 && !scope.includeNull) {\n return q.whereRaw('1 = 0')\n }\n return q.where((builder) => {\n let applied = false\n if (scope.ids.length > 0) {\n builder.whereIn(column, scope.ids as readonly string[])\n applied = true\n }\n if (scope.includeNull) {\n if (applied) builder.orWhereNull(column)\n else builder.whereNull(column)\n } else if (!applied) {\n builder.whereRaw('1 = 0')\n }\n })\n }\n\n private normalizeFilters(filters?: QueryOptions['filters']): NormalizedFilter[] {\n if (!filters) return []\n const normalizeField = (k: string) => k.startsWith('cf_') ? `cf:${k.slice(3)}` : k\n if (Array.isArray(filters)) {\n return (filters as Filter[]).map((filter) => ({\n field: normalizeField(String(filter.field)),\n op: filter.op,\n value: filter.value,\n }))\n }\n const out: NormalizedFilter[] = []\n const obj = filters as Record<string, unknown>\n const add = (field: string, op: FilterOp, value?: unknown) => out.push({ field, op, value })\n for (const [rawKey, rawVal] of Object.entries(obj)) {\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n switch (opKey) {\n case '$eq': add(field, 'eq', opVal); break\n case '$ne': add(field, 'ne', opVal); break\n case '$gt': add(field, 'gt', opVal); break\n case '$gte': add(field, 'gte', opVal); break\n case '$lt': add(field, 'lt', opVal); break\n case '$lte': add(field, 'lte', opVal); break\n case '$in': add(field, 'in', opVal); break\n case '$nin': add(field, 'nin', opVal); break\n case '$like': add(field, 'like', opVal); break\n case '$ilike': add(field, 'ilike', opVal); break\n case '$exists': add(field, 'exists', opVal); break\n }\n }\n } else {\n add(field, 'eq', rawVal)\n }\n }\n return out\n }\n\n private sanitize(s: string): string {\n return s.replace(/[^a-zA-Z0-9_]/g, '_')\n }\n\n private toArray(value: unknown): readonly unknown[] {\n if (Array.isArray(value)) {\n return value\n }\n if (value === undefined) {\n return []\n }\n return [value]\n }\n\n private parseCount(row: unknown): number {\n if (row && typeof row === 'object' && 'count' in row) {\n const value = (row as { count: unknown }).count\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isNaN(parsed) ? 0 : parsed\n }\n }\n return 0\n }\n\n private logSearchDebug(event: string, payload: Record<string, unknown>) {\n if (!this.isDebugVerbosity()) return\n try {\n console.info('[query-index:search]', event, JSON.stringify(payload))\n } catch {\n console.info('[query-index:search]', event, payload)\n }\n }\n\n private applyColumnFilter<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n column: string | Knex.Raw,\n filter: NormalizedFilter,\n search?: SearchRuntime & { knex: Knex; entity: string; field: string; recordIdColumn?: string }\n ): Knex.QueryBuilder<TRecord, TResult> {\n if (\n (filter.op === 'like' || filter.op === 'ilike') &&\n search?.enabled &&\n typeof filter.value === 'string'\n ) {\n const tokens = tokenizeText(String(filter.value), search.config)\n const hashes = tokens.hashes\n if (hashes.length) {\n const sources: SearchTokenSource[] = (search.searchSources && search.searchSources.length\n ? search.searchSources\n : [{ entity: search.entity, recordIdColumn: search.recordIdColumn ?? '' }]\n ).filter((src) => src.recordIdColumn && src.entity)\n let applied = false\n if (sources.length) {\n q = q.where((qb) => {\n sources.forEach((src, idx) => {\n const ok = this.applySearchTokens(qb as any, {\n knex: search.knex,\n entity: src.entity,\n field: search.field,\n hashes,\n recordIdColumn: src.recordIdColumn,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope ?? null,\n combineWith: idx === 0 ? 'and' : 'or',\n })\n if (ok) applied = true\n })\n })\n }\n this.logSearchDebug('search:filter', {\n entity: search.entity,\n field: search.field,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope,\n sources: sources.map((src) => ({ entity: src.entity, recordIdColumn: src.recordIdColumn })),\n })\n if (applied) return q\n } else {\n this.logSearchDebug('search:skip-empty-hashes', {\n entity: search.entity,\n field: search.field,\n value: filter.value,\n })\n }\n return q\n }\n const col = column as any\n switch (filter.op) {\n case 'eq':\n return q.where(col, filter.value as Knex.Value)\n case 'ne':\n return q.whereNot(col, filter.value as Knex.Value)\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = filter.op === 'gt' ? '>' : filter.op === 'gte' ? '>=' : filter.op === 'lt' ? '<' : '<='\n return q.where(col, operator, filter.value as Knex.Value)\n }\n case 'in': {\n const values = this.toArray(filter.value) as readonly Knex.Value[]\n return q.whereIn(col, values)\n }\n case 'nin': {\n const values = this.toArray(filter.value) as readonly Knex.Value[]\n return q.whereNotIn(col, values)\n }\n case 'like':\n return q.where(col, 'like', filter.value as Knex.Value)\n case 'ilike':\n return q.where(col, 'ilike', filter.value as Knex.Value)\n case 'exists':\n return filter.value ? q.whereNotNull(col) : q.whereNull(col)\n default:\n return q\n }\n }\n\n private resolveCustomEntityColumn(alias: string, field: string): string | null {\n if (field === 'id') return `${alias}.entity_id`\n if (field === 'organization_id' || field === 'organizationId') return `${alias}.organization_id`\n if (field === 'tenant_id' || field === 'tenantId') return `${alias}.tenant_id`\n if (field === 'created_at' || field === 'updated_at' || field === 'deleted_at') return `${alias}.${field}`\n return null\n }\n\n private isDebugVerbosity(): boolean {\n if (this.debugVerbosity != null) return this.debugVerbosity\n this.debugVerbosity = resolveDebugVerbosity()\n return this.debugVerbosity\n }\n\n private isSqlDebugEnabled(): boolean {\n if (this.sqlDebugEnabled != null) return this.sqlDebugEnabled\n this.sqlDebugEnabled = resolveBooleanEnv(['QUERY_ENGINE_DEBUG_SQL'], false)\n return this.sqlDebugEnabled\n }\n\n private isForcePartialIndexEnabled(): boolean {\n if (this.forcePartialIndexEnabled != null) return this.forcePartialIndexEnabled\n this.forcePartialIndexEnabled = resolveBooleanEnv(['FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES'], true)\n return this.forcePartialIndexEnabled\n }\n\n private async resolveCoverageGap(\n entity: string,\n opts: QueryOptions,\n coverageScope?: { tenantId: string | null; organizationId: string | null } | null,\n _sourceTable?: string\n ): Promise<{ stats?: { baseCount: number; indexedCount: number }; scope: 'scoped' | 'global' } | null> {\n const scope = coverageScope ?? this.resolveCoverageSnapshotScope(opts)\n if (!scope) return null\n const tenantId = scope.tenantId\n const organizationId = scope.organizationId\n const withDeleted = !!opts.withDeleted\n\n const snapshot = await this.getStoredCoverageSnapshot(entity, tenantId, organizationId, withDeleted)\n if (!snapshot) {\n this.scheduleCoverageRefresh(entity, tenantId, organizationId, withDeleted)\n return { stats: undefined, scope: 'scoped' }\n }\n\n const baseCount = snapshot.baseCount\n const indexCount = snapshot.indexedCount\n const hasGap = baseCount > 0 && indexCount < baseCount\n if (hasGap || indexCount > baseCount) {\n return { stats: snapshot, scope: 'scoped' }\n }\n\n return null\n }\n\n // Backward-compatible hook for tests that mock coverage stats\n private async indexCoverageStats(\n entity: string,\n opts: QueryOptions,\n coverageScope?: { tenantId: string | null; organizationId: string | null } | null,\n ): Promise<{ baseCount: number; indexedCount: number } | null> {\n const gap = await this.resolveCoverageGap(entity, opts, coverageScope)\n return gap?.stats ?? null\n }\n\n private async captureSqlTiming<TResult>(\n label: string,\n entity: EntityId,\n execute: () => Promise<TResult> | TResult,\n extra?: Record<string, unknown>,\n profiler?: Profiler\n ): Promise<TResult> {\n const shouldDebug = this.isSqlDebugEnabled() && this.isDebugVerbosity()\n const shouldProfile = profiler?.enabled === true\n if (!shouldDebug && !shouldProfile) {\n return Promise.resolve(execute())\n }\n const startedAt = process.hrtime.bigint()\n try {\n return await Promise.resolve(execute())\n } finally {\n const elapsedMs = Number(process.hrtime.bigint() - startedAt) / 1_000_000\n const context: Record<string, unknown> = {\n entity,\n durationMs: Math.round(elapsedMs * 1000) / 1000,\n }\n if (extra) Object.assign(context, extra)\n if (shouldProfile) profiler!.record(label, context.durationMs as number, extra)\n if (shouldDebug) this.debug(`${label}:timing`, context)\n }\n }\n\n private debug(message: string, context?: Record<string, unknown>): void {\n if (!this.isDebugVerbosity()) return\n if (!this.isSqlDebugEnabled()) return\n if (context) console.debug('[HybridQueryEngine]', message, context)\n else console.debug('[HybridQueryEngine]', message)\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { QueryEngine, QueryOptions, QueryResult, FilterOp, Filter, QueryCustomFieldSource, PartialIndexWarning, QueryExtensionsConfig } from '@open-mercato/shared/lib/query/types'\nimport { SortDir } from '@open-mercato/shared/lib/query/types'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { BasicQueryEngine, resolveEntityTableName } from '@open-mercato/shared/lib/query/engine'\nimport type { Knex } from 'knex'\nimport type { EventBus } from '@open-mercato/events'\nimport { readCoverageSnapshot, refreshCoverageSnapshot } from './coverage'\nimport { createProfiler, shouldEnableProfiler, type Profiler } from '@open-mercato/shared/lib/profiler'\nimport type { VectorIndexService } from '@open-mercato/search/vector'\nimport { decryptIndexDocCustomFields } from '@open-mercato/shared/lib/encryption/indexDoc'\nimport { parseBooleanToken, parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport {\n applyJoinFilters,\n normalizeFilters,\n partitionFilters,\n resolveJoins,\n type BaseFilter,\n type ResolvedJoin,\n} from '@open-mercato/shared/lib/query/join-utils'\nimport { resolveSearchConfig, type SearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { runBeforeQueryPipeline, runAfterQueryPipeline, type QueryExtensionContext } from '@open-mercato/shared/lib/query/query-extension-runner'\n\nfunction resolveBooleanEnv(names: readonly string[], defaultValue: boolean): boolean {\n for (const name of names) {\n const raw = process.env[name]\n if (raw !== undefined) return parseBooleanWithDefault(raw, defaultValue)\n }\n return defaultValue\n}\n\nfunction resolveDebugVerbosity(): boolean {\n // Check explicit OM_QUERY_INDEX_DEBUG flag first\n const queryIndexDebug = process.env.OM_QUERY_INDEX_DEBUG\n if (queryIndexDebug !== undefined) {\n return parseBooleanToken(queryIndexDebug) ?? false\n }\n // Fall back to log level or NODE_ENV\n const level = (process.env.LOG_VERBOSITY ?? process.env.LOG_LEVEL ?? '').toLowerCase()\n if (['debug', 'trace', 'silly'].includes(level)) return true\n // Default to false (don't spam logs in development)\n return false\n}\n\ntype ResultRow = Record<string, unknown>\ntype ResultBuilder<TResult = ResultRow[]> = Knex.QueryBuilder<ResultRow, TResult>\ntype NormalizedFilter = { field: string; op: FilterOp; value?: unknown }\ntype IndexDocSource = { alias: string; entityId: EntityId; recordIdColumn: string }\ntype PreparedCustomFieldSource = {\n alias: string\n indexAlias: string\n entityId: EntityId\n recordIdColumn: string\n organizationField?: string\n tenantField?: string\n table: string\n}\ntype SearchRuntime = {\n enabled: boolean\n config: SearchConfig\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n tenantId?: string | null\n searchSources?: SearchTokenSource[]\n}\n\ntype EncryptionResolver = () => {\n decryptEntityPayload?: (entityId: EntityId, payload: Record<string, unknown>, tenantId?: string | null, organizationId?: string | null) => Promise<Record<string, unknown>>\n isEnabled?: () => boolean\n} | null\n\ntype SearchTokenSource = { entity: string; recordIdColumn: string }\n\nfunction createQueryProfiler(entity: string): Profiler {\n const enabled = shouldEnableProfiler(entity)\n return createProfiler({\n scope: 'query_engine',\n target: entity,\n label: `query_engine:${entity}`,\n loggerLabel: '[qe:profile]',\n enabled,\n })\n}\n\nexport class HybridQueryEngine implements QueryEngine {\n private coverageStatsTtlMs: number\n private customFieldKeysCache = new Map<string, { expiresAt: number; value: string[] }>()\n private customFieldKeysTtlMs: number\n private columnCache = new Map<string, boolean>()\n private debugVerbosity: boolean | null = null\n private sqlDebugEnabled: boolean | null = null\n private forcePartialIndexEnabled: boolean | null = null\n private autoReindexEnabled: boolean | null = null\n private coverageOptimizationEnabled: boolean | null = null\n private pendingCoverageRefreshKeys = new Set<string>()\n private searchAliasSeq = 0\n\n constructor(\n private em: EntityManager,\n private fallback: BasicQueryEngine,\n private eventBusResolver?: () => Pick<EventBus, 'emitEvent'> | null | undefined,\n private vectorServiceResolver?: () => VectorIndexService | null | undefined,\n private encryptionResolver?: EncryptionResolver,\n ) {\n const coverageTtl = Number.parseInt(process.env.QUERY_INDEX_COVERAGE_CACHE_MS ?? '', 10)\n this.coverageStatsTtlMs = Number.isFinite(coverageTtl) && coverageTtl >= 0 ? coverageTtl : 5 * 60 * 1000\n const cfTtl = Number.parseInt(process.env.QUERY_INDEX_CF_KEYS_CACHE_MS ?? '', 10)\n this.customFieldKeysTtlMs = Number.isFinite(cfTtl) && cfTtl >= 0 ? cfTtl : 5 * 60 * 1000\n }\n\n private getEncryptionService() {\n try {\n return this.encryptionResolver?.() ?? null\n } catch {\n return null\n }\n }\n\n async query<T = unknown>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n // --- UMES query extension: before-query pipeline ---\n const ext: QueryExtensionsConfig | undefined = opts.extensions\n let hybridExtCtx: QueryExtensionContext | null = null\n const noopDi = { resolve: <R = unknown>(_name: string): R => { throw new Error('No DI context') } }\n\n if (ext) {\n hybridExtCtx = {\n entity: String(entity),\n engine: 'hybrid',\n tenantId: opts.tenantId ?? '',\n organizationId: opts.organizationId,\n userId: ext.userId,\n em: this.em,\n container: ext.container,\n userFeatures: ext.userFeatures,\n }\n const diCtx = ext.resolve ? { resolve: ext.resolve } : noopDi\n const beforeResult = await runBeforeQueryPipeline(opts, hybridExtCtx, diCtx)\n if (beforeResult.blocked) {\n throw new Error(beforeResult.errorMessage ?? 'Query blocked by extension subscriber')\n }\n opts = beforeResult.query\n }\n // Strip extensions so fallback to BasicQueryEngine doesn't double-execute\n const { extensions: _stripExt, ...coreOpts } = opts\n opts = coreOpts\n\n const providedProfiler = opts.profiler\n const profiler = providedProfiler && providedProfiler.enabled\n ? providedProfiler\n : createQueryProfiler(String(entity))\n profiler.mark('query:init')\n let profileClosed = false\n const finishProfile = (meta?: Record<string, unknown>) => {\n if (!profiler.enabled || profileClosed) return\n profileClosed = true\n profiler.end(meta)\n }\n\n const applyAfterExtensions = async <R>(queryResult: QueryResult<R>): Promise<QueryResult<R>> => {\n if (!ext || !hybridExtCtx) return queryResult\n const diCtx = ext.resolve ? { resolve: ext.resolve } : noopDi\n return await runAfterQueryPipeline(\n queryResult as QueryResult<Record<string, unknown>>,\n opts,\n hybridExtCtx,\n diCtx,\n ) as QueryResult<R>\n }\n\n try {\n const debugEnabled = this.isDebugVerbosity()\n if (debugEnabled) this.debug('query:start', { entity })\n this.searchAliasSeq = 0\n\n const isCustom = await this.isCustomEntity(entity)\n if (isCustom) {\n if (debugEnabled) this.debug('query:custom-entity', { entity })\n const section = profiler.section('custom_entity')\n try {\n const result = await this.queryCustomEntity<T>(entity, opts)\n section.end({ mode: 'custom_entity' })\n finishProfile({\n result: 'custom_entity',\n total: Array.isArray(result.items) ? result.items.length : undefined,\n })\n return await applyAfterExtensions(result)\n } catch (err) {\n section.end({ error: err instanceof Error ? err.message : String(err) })\n throw err\n }\n }\n\n const knex = this.getKnex()\n profiler.mark('query:knex_ready')\n const baseTable = resolveEntityTableName(this.em, entity)\n profiler.mark('query:base_table_resolved')\n const searchConfig = resolveSearchConfig()\n const orgScope = this.resolveOrganizationScope(opts)\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n\n const baseExists = await profiler.measure('base_table_exists', () => this.tableExists(baseTable))\n if (!baseExists) {\n if (debugEnabled) this.debug('query:fallback:missing-base', { entity, baseTable })\n const fallbackResult = await this.fallback.query(entity, opts)\n finishProfile({ result: 'fallback', reason: 'missing_base' })\n return await applyAfterExtensions(fallbackResult)\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n const cfFilters = normalizedFilters.filter((filter) => filter.field.startsWith('cf:') || filter.field.startsWith('l10n:'))\n const coverageScope = this.resolveCoverageSnapshotScope(opts)\n const wantsCf = (\n (opts.fields || []).some((field) => typeof field === 'string' && (field.startsWith('cf:') || field.startsWith('l10n:'))) ||\n cfFilters.length > 0 ||\n opts.includeCustomFields === true ||\n (Array.isArray(opts.includeCustomFields) && opts.includeCustomFields.length > 0)\n )\n\n if (debugEnabled) {\n this.debug('query:config', {\n entity,\n wantsCustomFields: wantsCf,\n customFieldSources: Array.isArray(opts.customFieldSources) ? opts.customFieldSources.map((src) => src?.entityId) : undefined,\n fields: opts.fields,\n })\n }\n\n let partialIndexWarning: PartialIndexWarning | null = null\n let entityHasActiveCustomFields = true\n\n if (wantsCf) {\n entityHasActiveCustomFields = await this.entityHasActiveCustomFields(entity, opts.tenantId ?? null)\n const hasIndexRows = await profiler.measure(\n 'index_any_rows',\n () => this.indexAnyRows(entity),\n (value) => ({ hasIndexRows: value })\n )\n if (!hasIndexRows) {\n if (debugEnabled) this.debug('query:fallback:no-index', { entity })\n const fallbackResult = await this.fallback.query(entity, opts)\n finishProfile({ result: 'fallback', reason: 'no_index_rows' })\n return await applyAfterExtensions(fallbackResult)\n }\n if (entityHasActiveCustomFields) {\n const gap = await profiler.measure(\n 'resolve_coverage_gap',\n () => this.resolveCoverageGap(entity, opts, coverageScope),\n (value) => (value\n ? {\n scope: value.scope,\n baseCount: value.stats?.baseCount ?? null,\n indexedCount: value.stats?.indexedCount ?? null,\n }\n : { scope: null })\n )\n if (gap) {\n if (!opts.skipAutoReindex) {\n this.scheduleAutoReindex(entity, opts, gap.stats, coverageScope?.organizationId ?? null)\n }\n const force = this.isForcePartialIndexEnabled()\n if (!force) {\n if (gap.stats) {\n console.warn('[HybridQueryEngine] Partial index coverage detected; falling back to basic engine:', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n if (debugEnabled) this.debug('query:fallback:partial-coverage', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n } else {\n console.warn('[HybridQueryEngine] Partial index coverage detected; falling back to basic engine:', { entity })\n if (debugEnabled) this.debug('query:fallback:partial-coverage', { entity })\n }\n const fallbackResult = await this.fallback.query(entity, opts)\n const resultWithWarning: QueryResult<T> = {\n ...fallbackResult,\n meta: {\n ...(fallbackResult.meta ?? {}),\n partialIndexWarning: {\n entity,\n entityLabel: this.resolveEntityLabel(entity),\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n scope: gap.stats ? gap.scope : undefined,\n },\n },\n }\n finishProfile({\n result: 'fallback',\n reason: 'partial_index',\n scope: gap.scope,\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n })\n return await applyAfterExtensions(resultWithWarning)\n }\n if (gap.stats) {\n console.warn('[HybridQueryEngine] Partial index coverage detected; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n if (debugEnabled) this.debug('query:partial-coverage:forced', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n } else {\n console.warn('[HybridQueryEngine] Partial index coverage detected; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity })\n if (debugEnabled) this.debug('query:partial-coverage:forced', { entity })\n }\n partialIndexWarning = {\n entity,\n entityLabel: this.resolveEntityLabel(entity),\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n scope: gap.stats ? gap.scope : undefined,\n }\n }\n } else if (debugEnabled) {\n this.debug('query:coverage:skip-no-custom-fields', { entity })\n }\n }\n\n const qualify = (col: string) => `b.${col}`\n let builder: ResultBuilder = knex({ b: baseTable })\n const hasCustomFieldFilters = cfFilters.length > 0\n const canOptimizeCount = !hasCustomFieldFilters\n let optimizedCountBuilder: ResultBuilder | null = canOptimizeCount ? knex({ b: baseTable }) : null\n\n const resolvedJoinsConfig = resolveJoins(baseTable, opts.joins, (entityId) => resolveEntityTableName(this.em, entityId as any))\n const joinMap = new Map<string, ResolvedJoin>()\n const aliasTables = new Map<string, string>()\n aliasTables.set('b', baseTable)\n aliasTables.set('base', baseTable)\n aliasTables.set(baseTable, baseTable)\n for (const join of resolvedJoinsConfig) {\n joinMap.set(join.alias, join)\n aliasTables.set(join.alias, join.table)\n }\n const { baseFilters, joinFilters } = partitionFilters(baseTable, normalizedFilters, joinMap)\n\n if (!opts.tenantId) throw new Error('QueryEngine: tenantId is required')\n\n const hasOrganizationColumn = await this.columnExists(baseTable, 'organization_id')\n const hasTenantColumn = await this.columnExists(baseTable, 'tenant_id')\n const hasDeletedColumn = await this.columnExists(baseTable, 'deleted_at')\n const searchRuntimeBase = {\n enabled: false,\n config: searchConfig,\n organizationScope: orgScope,\n tenantId: opts.tenantId ?? null,\n }\n\n if (orgScope && hasOrganizationColumn) {\n builder = this.applyOrganizationScope(builder, qualify('organization_id'), orgScope)\n if (optimizedCountBuilder) optimizedCountBuilder = this.applyOrganizationScope(optimizedCountBuilder, qualify('organization_id'), orgScope)\n }\n if (hasTenantColumn) {\n builder = builder.where(qualify('tenant_id'), opts.tenantId)\n if (optimizedCountBuilder) optimizedCountBuilder = optimizedCountBuilder.where(qualify('tenant_id'), opts.tenantId)\n }\n if (!opts.withDeleted && hasDeletedColumn) {\n builder = builder.whereNull(qualify('deleted_at'))\n if (optimizedCountBuilder) optimizedCountBuilder = optimizedCountBuilder.whereNull(qualify('deleted_at'))\n }\n\n const baseJoinParts: string[] = []\n baseJoinParts.push(`ei.entity_type = ${knex.raw('?', [entity]).toString()}`)\n baseJoinParts.push(`ei.entity_id = (${qualify('id')}::text)`)\n if (hasOrganizationColumn) {\n baseJoinParts.push(`ei.organization_id = ${qualify('organization_id')}`)\n baseJoinParts.push('ei.organization_id is not null')\n }\n if (hasTenantColumn) {\n baseJoinParts.push(`ei.tenant_id = ${qualify('tenant_id')}`)\n baseJoinParts.push('ei.tenant_id is not null')\n }\n if (!opts.withDeleted) baseJoinParts.push(`ei.deleted_at is null`)\n builder = builder.leftJoin({ ei: 'entity_indexes' }, knex.raw(baseJoinParts.join(' AND ')))\n\n const columns = await this.getBaseColumnsForEntity(entity)\n const indexSources: IndexDocSource[] = [{ alias: 'ei', entityId: entity, recordIdColumn: 'b.id' }]\n\n const shouldAttachCustomSources = Array.isArray(opts.customFieldSources) && opts.customFieldSources.length > 0 && (wantsCf || searchEnabled)\n if (shouldAttachCustomSources) {\n const prepared = this.prepareCustomFieldSources(knex, builder, opts.customFieldSources ?? [], qualify)\n builder = prepared.builder\n for (const source of prepared.sources) {\n const fragments: string[] = []\n fragments.push(`${source.indexAlias}.entity_type = ${knex.raw('?', [source.entityId]).toString()}`)\n fragments.push(`${source.indexAlias}.entity_id = (${knex.raw('??::text', [`${source.alias}.${source.recordIdColumn}`]).toString()})`)\n const orgExpr = source.organizationField\n ? knex.raw('??', [`${source.alias}.${source.organizationField}`]).toString()\n : (columns.has('organization_id') ? qualify('organization_id') : null)\n if (orgExpr) {\n fragments.push(`${source.indexAlias}.organization_id = ${orgExpr}`)\n fragments.push(`${source.indexAlias}.organization_id is not null`)\n }\n const tenantExpr = source.tenantField\n ? knex.raw('??', [`${source.alias}.${source.tenantField}`]).toString()\n : (columns.has('tenant_id') ? qualify('tenant_id') : null)\n if (tenantExpr) {\n fragments.push(`${source.indexAlias}.tenant_id = ${tenantExpr}`)\n fragments.push(`${source.indexAlias}.tenant_id is not null`)\n }\n if (!opts.withDeleted) fragments.push(`${source.indexAlias}.deleted_at is null`)\n builder = builder.leftJoin({ [source.indexAlias]: 'entity_indexes' }, knex.raw(fragments.join(' AND ')))\n indexSources.push({ alias: source.indexAlias, entityId: source.entityId, recordIdColumn: `${source.alias}.${source.recordIdColumn}` })\n }\n }\n\n if (debugEnabled) {\n this.debug('query:index-sources', {\n entity,\n sources: indexSources.map((src) => ({ alias: src.alias, entity: src.entityId })),\n })\n }\n\n const searchSources: SearchTokenSource[] = indexSources\n .map((src) => ({\n entity: String(src.entityId),\n recordIdColumn: src.recordIdColumn,\n }))\n .filter((src) => src.recordIdColumn && src.entity)\n const hasSearchTokens = searchEnabled && searchSources.length\n ? await this.searchSourcesHaveTokens(searchSources, opts.tenantId ?? null, orgScope)\n : false\n const searchRuntime: SearchRuntime = { ...searchRuntimeBase, searchSources, enabled: searchEnabled && hasSearchTokens }\n const searchFilters = normalizeFilters(opts.filters).filter((filter) => filter.op === 'like' || filter.op === 'ilike')\n if (searchFilters.length) {\n this.logSearchDebug('search:init', {\n entity,\n baseTable,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n fields: searchFilters.map((filter) => String(filter.field)),\n searchEnabled,\n hasSearchTokens,\n searchSources,\n searchConfig: {\n enabled: searchConfig.enabled,\n minTokenLength: searchConfig.minTokenLength,\n enablePartials: searchConfig.enablePartials,\n hashAlgorithm: searchConfig.hashAlgorithm,\n blocklistedFields: searchConfig.blocklistedFields,\n },\n })\n if (!searchEnabled) {\n this.logSearchDebug('search:disabled', { entity, baseTable })\n } else if (!hasSearchTokens) {\n this.logSearchDebug('search:no-search-tokens', {\n entity,\n baseTable,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n searchSources,\n })\n }\n }\n const hasNonBaseSearchSource = searchSources.some(\n (src) => src.entity !== String(entity) || src.recordIdColumn !== 'b.id'\n )\n if (hasNonBaseSearchSource) {\n optimizedCountBuilder = null\n }\n\n if (!partialIndexWarning && Array.isArray(opts.customFieldSources) && opts.customFieldSources.length > 0 && this.isForcePartialIndexEnabled()) {\n const seen = new Set<string>([entity])\n for (const source of opts.customFieldSources) {\n const targetEntity = source?.entityId ? String(source.entityId) : null\n if (!targetEntity || seen.has(targetEntity)) continue\n seen.add(targetEntity)\n const sourceHasCustomFields = await this.entityHasActiveCustomFields(targetEntity, opts.tenantId ?? null)\n if (!sourceHasCustomFields) {\n if (debugEnabled) this.debug('query:coverage:skip-no-custom-fields', { entity: targetEntity })\n continue\n }\n const sourceTable = source.table ?? resolveEntityTableName(this.em, targetEntity)\n try {\n const gap = await profiler.measure(\n 'resolve_coverage_gap',\n () => this.resolveCoverageGap(targetEntity, opts, coverageScope, sourceTable),\n (value) => (value\n ? {\n entity: targetEntity,\n scope: value.scope,\n baseCount: value.stats?.baseCount ?? null,\n indexedCount: value.stats?.indexedCount ?? null,\n }\n : { entity: targetEntity, scope: null })\n )\n if (!gap) continue\n if (!opts.skipAutoReindex) {\n this.scheduleAutoReindex(targetEntity, opts, gap.stats, coverageScope?.organizationId ?? null)\n }\n partialIndexWarning = {\n entity: targetEntity,\n entityLabel: this.resolveEntityLabel(targetEntity),\n baseCount: gap.stats?.baseCount ?? null,\n indexedCount: gap.stats?.indexedCount ?? null,\n scope: gap.stats ? gap.scope : undefined,\n }\n if (debugEnabled) {\n if (gap.stats) this.debug('query:partial-coverage:forced', { entity: targetEntity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })\n else this.debug('query:partial-coverage:forced', { entity: targetEntity })\n }\n break\n } catch (err) {\n if (debugEnabled) this.debug('query:partial-coverage:check-failed', { entity: targetEntity, error: err instanceof Error ? err.message : err })\n }\n }\n }\n\n if (\n !partialIndexWarning &&\n wantsCf &&\n entityHasActiveCustomFields &&\n this.isForcePartialIndexEnabled() &&\n opts.tenantId\n ) {\n try {\n await this.indexCoverageStats(entity, opts, coverageScope)\n const globalStats = await this.indexCoverageStats(entity, opts, coverageScope)\n if (globalStats) {\n const globalBase = globalStats.baseCount\n const globalIndexed = globalStats.indexedCount\n const globalGap = (globalBase > 0 && globalIndexed < globalBase) || globalIndexed > globalBase\n if (globalGap) {\n console.warn('[HybridQueryEngine] Partial index coverage detected at global scope; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity, baseCount: globalBase, indexedCount: globalIndexed, scope: 'global' })\n if (debugEnabled) {\n this.debug('query:partial-coverage:forced', {\n entity,\n baseCount: globalBase,\n indexedCount: globalIndexed,\n scope: 'global',\n })\n }\n partialIndexWarning = {\n entity,\n entityLabel: this.resolveEntityLabel(entity),\n baseCount: globalBase,\n indexedCount: globalIndexed,\n scope: 'global',\n }\n }\n }\n } catch (err) {\n if (debugEnabled) {\n this.debug('query:partial-coverage:global-check-failed', {\n entity,\n error: err instanceof Error ? err.message : err,\n })\n }\n }\n }\n\n const resolveBaseColumn = (field: string): string | null => {\n if (columns.has(field)) return field\n if (field === 'organization_id' && columns.has('id')) return 'id'\n return null\n }\n\n for (const filter of cfFilters) {\n builder = this.applyCfFilterAcrossSources(\n knex,\n builder,\n filter.field,\n filter.op,\n filter.value,\n indexSources,\n searchRuntime\n )\n }\n\n for (const filter of baseFilters) {\n const baseField = resolveBaseColumn(String(filter.field))\n if (!baseField) continue\n const column = qualify(baseField)\n builder = this.applyColumnFilter(builder, column, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: 'b.id',\n })\n if (optimizedCountBuilder) {\n optimizedCountBuilder = this.applyColumnFilter(optimizedCountBuilder, column, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: 'b.id',\n })\n }\n }\n\n const applyAliasScopes = async (target: ResultBuilder, aliasName: string) => {\n const tableName = aliasTables.get(aliasName)\n if (!tableName) return\n if (orgScope && await this.columnExists(tableName, 'organization_id')) {\n this.applyOrganizationScope(target, `${aliasName}.organization_id`, orgScope)\n }\n if (opts.tenantId && await this.columnExists(tableName, 'tenant_id')) {\n target.where(`${aliasName}.tenant_id`, opts.tenantId)\n }\n if (!opts.withDeleted && await this.columnExists(tableName, 'deleted_at')) {\n target.whereNull(`${aliasName}.deleted_at`)\n }\n }\n\n const applyJoinFilterOp = (target: ResultBuilder, column: string, op: FilterOp, value?: unknown) => {\n switch (op) {\n case 'eq':\n target.where(column, value as Knex.Value)\n break\n case 'ne':\n target.whereNot(column, value as Knex.Value)\n break\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='\n target.where(column, operator, value as Knex.Value)\n break\n }\n case 'in':\n target.whereIn(column, this.toArray(value) as readonly Knex.Value[])\n break\n case 'nin':\n target.whereNotIn(column, this.toArray(value) as readonly Knex.Value[])\n break\n case 'like':\n target.where(column, 'like', value as Knex.Value)\n break\n case 'ilike':\n target.where(column, 'ilike', value as Knex.Value)\n break\n case 'exists':\n value ? target.whereNotNull(column) : target.whereNull(column)\n break\n }\n }\n\n await applyJoinFilters({\n knex,\n baseTable,\n builder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (target, alias) => applyAliasScopes(target, alias),\n applyFilterOp: (target, column, op, value) => applyJoinFilterOp(target as ResultBuilder, column, op, value),\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n }) as ResultBuilder\n\n if (optimizedCountBuilder) {\n await applyJoinFilters({\n knex,\n baseTable,\n builder: optimizedCountBuilder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (target, alias) => applyAliasScopes(target, alias),\n applyFilterOp: (target, column, op, value) => applyJoinFilterOp(target as ResultBuilder, column, op, value),\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n })\n }\n\n // When no fields specified, select all base table columns (like BasicQueryEngine does)\n const selectFieldSet = new Set<string>((opts.fields && opts.fields.length) ? opts.fields.map(String) : Array.from(columns.keys()))\n if (opts.includeCustomFields === true) {\n const entityIds = Array.from(new Set(indexSources.map((src) => String(src.entityId))))\n try {\n const resolvedKeys = await this.resolveAvailableCustomFieldKeys(entityIds, opts.tenantId ?? null)\n resolvedKeys.forEach((key) => selectFieldSet.add(`cf:${key}`))\n if (this.isDebugVerbosity()) {\n this.debug('query:cf:resolved-keys', { entity, keys: resolvedKeys })\n }\n } catch (err) {\n console.warn('[HybridQueryEngine] Failed to resolve custom field keys for', entity, err)\n }\n } else if (Array.isArray(opts.includeCustomFields)) {\n opts.includeCustomFields\n .map((key) => String(key))\n .forEach((key) => selectFieldSet.add(`cf:${key}`))\n }\n const selectFields = Array.from(selectFieldSet)\n for (const field of selectFields) {\n const fieldName = String(field)\n if (fieldName.startsWith('cf:')) {\n const alias = this.sanitize(fieldName)\n const { jsonSql } = this.buildCfExpressions(knex, fieldName, indexSources)\n const exprSql = jsonSql === 'NULL' ? 'NULL::jsonb' : jsonSql\n builder = builder.select(knex.raw(`${exprSql} as ??`, [alias]))\n } else if (columns.has(fieldName)) {\n builder = builder.select(knex.raw('?? as ??', [qualify(fieldName), fieldName]))\n }\n }\n\n for (const sort of opts.sort || []) {\n const fieldName = String(sort.field)\n if (fieldName.startsWith('cf:')) {\n const { textSql } = this.buildCfExpressions(knex, fieldName, indexSources)\n if (textSql !== 'NULL') {\n const direction = sort.dir ?? SortDir.Asc\n builder = builder.orderByRaw(`${textSql} ${direction}`)\n }\n } else {\n const baseField = resolveBaseColumn(fieldName)\n if (!baseField) continue\n builder = builder.orderBy(qualify(baseField), sort.dir ?? SortDir.Asc)\n }\n }\n\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n\n const sqlDebugEnabled = this.isSqlDebugEnabled()\n let total: number\n\n if (optimizedCountBuilder) {\n const countSource = optimizedCountBuilder.clone().clearSelect().clearOrder().select(knex.raw(`${qualify('id')} as id`)).groupBy(qualify('id'))\n const countQuery = knex.from(countSource.as('sq')).count({ count: knex.raw('*') })\n if (debugEnabled && sqlDebugEnabled) {\n const { sql, bindings } = countQuery.clone().toSQL()\n this.debug('query:sql:count', { entity, sql, bindings })\n }\n const countRow = await this.captureSqlTiming(\n 'query:sql:count',\n entity,\n () => countQuery.first(),\n { optimized: true },\n profiler\n )\n total = this.parseCount(countRow)\n } else {\n const countBuilder = builder.clone().clearSelect().clearOrder().countDistinct(`${qualify('id')} as count`)\n if (debugEnabled && sqlDebugEnabled) {\n const { sql, bindings } = countBuilder.clone().toSQL()\n this.debug('query:sql:count', { entity, sql, bindings })\n }\n const countRow = await this.captureSqlTiming(\n 'query:sql:count',\n entity,\n () => countBuilder.first(),\n { optimized: false },\n profiler\n )\n total = this.parseCount(countRow)\n }\n\n const dataBuilder = builder.clone().limit(pageSize).offset((page - 1) * pageSize)\n\n if (debugEnabled && sqlDebugEnabled) {\n const { sql, bindings } = dataBuilder.clone().toSQL()\n this.debug('query:sql:data', { entity, sql, bindings, page, pageSize })\n }\n const itemsRaw = await this.captureSqlTiming(\n 'query:sql:data',\n entity,\n () => dataBuilder,\n { page, pageSize },\n profiler\n )\n if (debugEnabled) this.debug('query:complete', { entity, total, items: Array.isArray(itemsRaw) ? itemsRaw.length : 0 })\n\n let items = itemsRaw as any[]\n const encSvc = this.getEncryptionService()\n const dekKeyCache = new Map<string | null, string | null>()\n if (encSvc?.decryptEntityPayload) {\n const decrypt = encSvc.decryptEntityPayload.bind(encSvc) as (\n entityId: EntityId,\n payload: Record<string, unknown>,\n tenantId: string | null,\n organizationId: string | null,\n ) => Promise<Record<string, unknown>>\n items = await Promise.all(\n items.map(async (item) => {\n try {\n const decrypted = await decrypt(\n entity,\n item,\n item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n item?.organization_id ?? item?.organizationId ?? null,\n )\n return { ...item, ...decrypted }\n } catch (err) {\n console.error('Error decrypting entity payload', err);\n return item\n }\n })\n )\n }\n if (encSvc) {\n items = await Promise.all(\n items.map(async (item) => {\n try {\n return await decryptIndexDocCustomFields(\n item,\n {\n tenantId: item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n organizationId: item?.organization_id ?? item?.organizationId ?? null,\n },\n encSvc as any,\n dekKeyCache,\n )\n } catch {\n return item\n }\n }),\n )\n }\n\n const typedItems = items as unknown as T[]\n let result: QueryResult<T> = { items: typedItems, page, pageSize, total }\n if (partialIndexWarning) {\n result.meta = { partialIndexWarning }\n }\n\n // --- UMES query extension: after-query pipeline ---\n result = await applyAfterExtensions(result)\n\n finishProfile({\n result: 'ok',\n total,\n page,\n pageSize,\n itemCount: Array.isArray(items) ? items.length : undefined,\n partialIndexWarning: partialIndexWarning ? true : false,\n })\n return result\n } catch (err) {\n finishProfile({ result: 'error', error: err instanceof Error ? err.message : String(err) })\n throw err\n }\n }\n\n private getKnex(): Knex {\n const connection = this.em.getConnection()\n const withKnex = connection as { getKnex?: () => Knex }\n if (typeof withKnex.getKnex === 'function') {\n return withKnex.getKnex()\n }\n throw new Error('HybridQueryEngine requires a SQL connection that exposes getKnex()')\n }\n\n private prepareCustomFieldSources(\n knex: Knex,\n builder: ResultBuilder,\n sources: QueryCustomFieldSource[],\n qualify: (column: string) => string\n ): { builder: ResultBuilder; sources: PreparedCustomFieldSource[] } {\n let current = builder\n const prepared: PreparedCustomFieldSource[] = []\n sources.forEach((source, index) => {\n if (!source) return\n const joinTable = source.table ?? resolveEntityTableName(this.em, source.entityId)\n const alias = source.alias ?? `cfs_${index}`\n const join = source.join\n if (!join) {\n throw new Error(`QueryEngine: customFieldSources entry for ${String(source.entityId)} requires a join configuration`)\n }\n const joinArgs = { [alias]: joinTable }\n const joinCallback = function (this: Knex.JoinClause) {\n this.on(`${alias}.${join.toField}`, '=', qualify(join.fromField))\n }\n current = (join.type ?? 'left') === 'inner'\n ? current.join(joinArgs, joinCallback)\n : current.leftJoin(joinArgs, joinCallback)\n prepared.push({\n alias,\n indexAlias: `ei_${alias}`,\n entityId: source.entityId,\n recordIdColumn: source.recordIdColumn ?? 'id',\n organizationField: source.organizationField,\n tenantField: source.tenantField,\n table: joinTable,\n })\n })\n return { builder: current, sources: prepared }\n }\n\n private async isCustomEntity(entity: string): Promise<boolean> {\n try {\n const knex = this.getKnex()\n const row = await knex('custom_entities').where({ entity_id: entity, is_active: true }).first()\n return !!row\n } catch {\n return false\n }\n }\n\n private applySearchTokens<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n opts: {\n knex: Knex\n entity: string\n field: string\n hashes: string[]\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n combineWith?: 'and' | 'or'\n }\n ): boolean {\n if (!opts.hashes.length) {\n this.logSearchDebug('search:skip-no-hashes', {\n entity: opts.entity,\n field: opts.field,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n return false\n }\n const alias = `st_${this.searchAliasSeq++}`\n const combineWith = opts.combineWith === 'or' ? 'orWhereExists' : 'whereExists'\n const engine = this\n this.logSearchDebug('search:apply-search-tokens', {\n entity: opts.entity,\n field: opts.field,\n alias,\n tokenCount: opts.hashes.length,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n combineWith: opts.combineWith ?? 'and',\n })\n ;(q as any)[combineWith](function (this: Knex.QueryBuilder) {\n this.select(1)\n .from({ [alias]: 'search_tokens' })\n .where(`${alias}.entity_type`, opts.entity)\n .andWhere(`${alias}.field`, opts.field)\n .andWhereRaw('?? = ??::text', [`${alias}.entity_id`, opts.recordIdColumn])\n .whereIn(`${alias}.token_hash`, opts.hashes)\n .groupBy(`${alias}.entity_id`, `${alias}.field`)\n .havingRaw(`count(distinct ${alias}.token_hash) >= ?`, [opts.hashes.length])\n if (opts.tenantId !== undefined) {\n this.andWhereRaw(`${alias}.tenant_id is not distinct from ?`, [opts.tenantId ?? null])\n }\n if (opts.organizationScope) {\n engine.applyOrganizationScope(this as any, `${alias}.organization_id`, opts.organizationScope)\n }\n })\n return true\n }\n\n private jsonbRawAlias(knex: Knex, alias: string, key: string): Knex.Raw {\n // Prefer cf:<key> but fall back to bare <key> for legacy docs\n if (key.startsWith('cf:')) {\n const bare = key.slice(3)\n return knex.raw(`coalesce(${alias}.doc -> ?, ${alias}.doc -> ?)`, [key, bare])\n }\n return knex.raw(`${alias}.doc -> ?`, [key])\n }\n private cfTextExprAlias(knex: Knex, alias: string, key: string): Knex.Raw {\n if (key.startsWith('cf:')) {\n const bare = key.slice(3)\n return knex.raw(`coalesce((${alias}.doc ->> ?), (${alias}.doc ->> ?))`, [key, bare])\n }\n return knex.raw(`(${alias}.doc ->> ?)`, [key])\n }\n private buildCfExpressions(knex: Knex, key: string, sources: IndexDocSource[]): { jsonSql: string; textSql: string } {\n if (!sources.length) return { jsonSql: 'NULL', textSql: 'NULL' }\n const jsonFragments = sources.map((source) => this.jsonbRawAlias(knex, source.alias, key).toString())\n const textFragments = sources.map((source) => this.cfTextExprAlias(knex, source.alias, key).toString())\n const jsonSql = jsonFragments.length === 1 ? jsonFragments[0] : `coalesce(${jsonFragments.join(', ')})`\n const textSql = textFragments.length === 1 ? textFragments[0] : `coalesce(${textFragments.join(', ')})`\n return { jsonSql, textSql }\n }\n\n private applyCfFilterAcrossSources(\n knex: Knex,\n builder: ResultBuilder,\n key: string,\n op: FilterOp,\n value: unknown,\n sources: IndexDocSource[],\n search?: SearchRuntime\n ): ResultBuilder {\n if (!sources.length) return builder\n if ((op === 'like' || op === 'ilike') && search?.enabled && typeof value === 'string') {\n const tokens = tokenizeText(String(value), search.config)\n const hashes = tokens.hashes\n if (hashes.length) {\n let applied = false\n if (sources.length) {\n builder = builder.where((qb) => {\n sources.forEach((source, idx) => {\n const ok = this.applySearchTokens(qb as any, {\n knex,\n entity: source.entityId,\n field: key,\n hashes,\n recordIdColumn: `${source.alias}.entity_id`,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope ?? null,\n combineWith: idx === 0 ? 'and' : 'or',\n })\n if (ok) applied = true\n })\n })\n }\n this.logSearchDebug('search:cf-filter-across', {\n entity: sources.map((src) => src.entityId),\n field: key,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope,\n })\n if (applied) return builder\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: sources.map((src) => src.entityId),\n field: key,\n value,\n })\n }\n return builder\n }\n const { jsonSql, textSql } = this.buildCfExpressions(knex, key, sources)\n if (jsonSql === 'NULL' || textSql === 'NULL') return builder\n const textExpr = knex.raw(textSql)\n const arrContains = (val: unknown) => knex.raw(`${jsonSql} @> ?::jsonb`, [JSON.stringify([val])])\n switch (op) {\n case 'eq':\n return builder.where((qb) => {\n qb.orWhere(textExpr, '=', value as Knex.Value)\n qb.orWhere(arrContains(value))\n })\n case 'ne':\n return builder.whereNot(textExpr, '=', value as Knex.Value)\n case 'in': {\n const values = this.toArray(value)\n return builder.where((qb) => {\n values.forEach((val) => {\n qb.orWhere(textExpr, '=', val as Knex.Value)\n qb.orWhere(arrContains(val))\n })\n })\n }\n case 'nin': {\n const values = this.toArray(value) as readonly Knex.Value[]\n return builder.whereNotIn(textExpr as any, values as any)\n }\n case 'like':\n return builder.where(textExpr, 'like', value as Knex.Value)\n case 'ilike':\n return builder.where(textExpr, 'ilike', value as Knex.Value)\n case 'exists':\n return value\n ? builder.whereRaw(`${textExpr.toString()} is not null`)\n : builder.whereRaw(`${textExpr.toString()} is null`)\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='\n return builder.where(textExpr, operator, value as Knex.Value)\n }\n default:\n return builder\n }\n }\n\n private applyCfFilterFromAlias(\n knex: Knex,\n q: ResultBuilder,\n alias: string,\n entityType: string,\n key: string,\n op: FilterOp,\n value: unknown,\n search?: SearchRuntime\n ): ResultBuilder {\n const text = this.cfTextExprAlias(knex, alias, key)\n const arrExpr = knex.raw(`(${alias}.doc -> ?)`, [key])\n const arrContains = (val: unknown) => knex.raw(`${arrExpr.toString()} @> ?::jsonb`, [JSON.stringify([val])])\n if ((op === 'like' || op === 'ilike') && search?.enabled && typeof value === 'string') {\n const tokens = tokenizeText(String(value), search.config)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(q, {\n knex,\n entity: entityType,\n field: key,\n hashes,\n recordIdColumn: `${alias}.entity_id`,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope ?? null,\n })\n this.logSearchDebug('search:cf-filter', {\n entity: entityType,\n field: key,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope,\n })\n if (applied) return q\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: entityType,\n field: key,\n value,\n })\n }\n return q\n }\n switch (op) {\n case 'eq':\n return q.where((builder) => {\n builder.orWhere(text, '=', value as Knex.Value)\n builder.orWhere(arrContains(value))\n })\n case 'ne':\n return q.whereNot(text, '=', value as Knex.Value)\n case 'in': {\n const vals = this.toArray(value)\n return q.where((builder) => {\n vals.forEach((val) => {\n builder.orWhere(text, '=', val as Knex.Value)\n builder.orWhere(arrContains(val))\n })\n })\n }\n case 'nin': {\n const vals = this.toArray(value) as readonly Knex.Value[]\n return q.whereNotIn(text as any, vals as any)\n }\n case 'like':\n return q.where(text, 'like', value as Knex.Value)\n case 'ilike':\n return q.where(text, 'ilike', value as Knex.Value)\n case 'exists':\n return value\n ? q.whereRaw(`${text.toString()} is not null`)\n : q.whereRaw(`${text.toString()} is null`)\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='\n return q.where(text, operator, value as Knex.Value)\n }\n default:\n return q\n }\n }\n\n private async queryCustomEntity<T = unknown>(entity: string, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n const knex = this.getKnex()\n const alias = 'ce'\n let q = knex({ [alias]: 'custom_entities_storage' }).where(`${alias}.entity_type`, entity)\n\n const orgScope = this.resolveOrganizationScope(opts)\n\n // Require tenant scope; custom entities are tenant-scoped only\n if (!opts.tenantId) throw new Error('QueryEngine: tenantId is required')\n q = q.andWhere(`${alias}.tenant_id`, opts.tenantId)\n if (orgScope) {\n q = this.applyOrganizationScope(q, `${alias}.organization_id`, orgScope)\n }\n if (!opts.withDeleted) q = q.whereNull(`${alias}.deleted_at`)\n const searchConfig = resolveSearchConfig()\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n const hasSearchTokens = searchEnabled\n ? await this.hasSearchTokens(entity, opts.tenantId ?? null, orgScope)\n : false\n const searchRuntime: SearchRuntime = {\n enabled: searchEnabled && hasSearchTokens,\n config: searchConfig,\n organizationScope: orgScope,\n tenantId: opts.tenantId ?? null,\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n\n // Apply filters: cf:* via JSONB; other keys: special-case id/created_at/updated_at/deleted_at, otherwise from doc\n for (const filter of normalizedFilters) {\n if (filter.field.startsWith('cf:')) {\n q = this.applyCfFilterFromAlias(knex, q, alias, entity, filter.field, filter.op, filter.value, searchRuntime)\n continue\n }\n const column = this.resolveCustomEntityColumn(alias, String(filter.field))\n if (column) {\n q = this.applyColumnFilter(q, column, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: `${alias}.entity_id`,\n })\n continue\n }\n const docExpr = knex.raw(`(${alias}.doc ->> ?)`, [String(filter.field)])\n q = this.applyColumnFilter(q, docExpr, filter, {\n ...searchRuntime,\n knex,\n entity,\n field: String(filter.field),\n recordIdColumn: `${alias}.entity_id`,\n })\n }\n\n // Determine CFs and l10n keys to include\n const cfKeys = new Set<string>()\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n else if (typeof f === 'string' && f.startsWith('l10n:')) cfKeys.add(f)\n }\n for (const filter of normalizedFilters) {\n if (typeof filter.field === 'string' && filter.field.startsWith('cf:')) cfKeys.add(filter.field.slice(3))\n else if (typeof filter.field === 'string' && filter.field.startsWith('l10n:')) cfKeys.add(filter.field)\n }\n if (opts.includeCustomFields === true) {\n try {\n const rows = await knex('custom_field_defs')\n .select('key')\n .where({ entity_id: entity, is_active: true })\n .modify((qb) => {\n qb.andWhere({ tenant_id: opts.tenantId })\n // NOTE: organization-level scoping intentionally disabled for custom fields\n // if (opts.organizationId != null) qb.andWhere((b: any) => b.where({ organization_id: opts.organizationId }).orWhereNull('organization_id'))\n // else qb.whereNull('organization_id')\n })\n for (const row of rows) {\n const key = (row as Record<string, unknown>).key\n if (typeof key === 'string') {\n cfKeys.add(key)\n } else if (key != null) {\n cfKeys.add(String(key))\n }\n }\n } catch {\n // ignore and fall back to whatever keys we already have\n }\n } else if (Array.isArray(opts.includeCustomFields)) {\n for (const k of opts.includeCustomFields) cfKeys.add(k)\n }\n\n // Selection\n const requested = (opts.fields && opts.fields.length) ? opts.fields : ['id']\n for (const field of requested) {\n const f = String(field)\n if (f.startsWith('cf:')) {\n const aliasName = this.sanitize(f)\n const expr = this.jsonbRawAlias(knex, alias, f)\n q = q.select({ [aliasName]: expr })\n } else if (f === 'id') {\n q = q.select(knex.raw(`${alias}.entity_id as ??`, ['id']))\n } else if (f === 'created_at' || f === 'updated_at' || f === 'deleted_at') {\n q = q.select(knex.raw(`${alias}.?? as ??`, [f, f]))\n } else {\n // Non-cf from doc\n const expr = knex.raw(`(${alias}.doc ->> ?)`, [f])\n q = q.select({ [f]: expr })\n }\n }\n // Ensure CFs necessary for sort are selected\n const cfSelectedAliases: string[] = []\n for (const key of cfKeys) {\n const aliasName = this.sanitize(`cf:${key}`)\n const expr = this.jsonbRawAlias(knex, alias, `cf:${key}`)\n q = q.select({ [aliasName]: expr })\n cfSelectedAliases.push(aliasName)\n }\n\n // Sorting\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n const key = s.field.slice(3)\n const aliasName = this.sanitize(`cf:${key}`)\n if (!cfSelectedAliases.includes(aliasName)) {\n const expr = this.jsonbRawAlias(knex, alias, `cf:${key}`)\n q = q.select({ [aliasName]: expr })\n cfSelectedAliases.push(aliasName)\n }\n q = q.orderBy(aliasName, s.dir ?? SortDir.Asc)\n } else if (s.field === 'id') {\n q = q.orderBy(`${alias}.entity_id`, s.dir ?? SortDir.Asc)\n } else if (s.field === 'created_at' || s.field === 'updated_at' || s.field === 'deleted_at') {\n q = q.orderBy(`${alias}.${s.field}`, s.dir ?? SortDir.Asc)\n } else {\n const direction = s.dir ?? SortDir.Asc\n q = q.orderByRaw(`(${alias}.doc ->> ?) ${direction}`, [s.field])\n }\n }\n\n // Pagination + totals\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n const countClone = q.clone()\n if (typeof countClone.clearSelect === 'function') countClone.clearSelect()\n if (typeof countClone.clearOrder === 'function') countClone.clearOrder()\n const countRow = await countClone.countDistinct(`${alias}.entity_id as count`).first()\n const total = this.parseCount(countRow)\n const items = await q.limit(pageSize).offset((page - 1) * pageSize)\n return { items, page, pageSize, total }\n }\n\n private async tableExists(table: string): Promise<boolean> {\n const knex = this.getKnex()\n const exists = await knex('information_schema.tables').where({ table_name: table }).first()\n return !!exists\n }\n\n private async hasSearchTokens(\n entity: string,\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n try {\n const knex = this.getKnex()\n const query = knex('search_tokens').select(1).where('entity_type', entity).limit(1)\n if (tenantId !== undefined) {\n query.andWhereRaw('tenant_id is not distinct from ?', [tenantId])\n }\n if (orgScope) {\n this.applyOrganizationScope(query as any, 'search_tokens.organization_id', orgScope)\n }\n const row = await query.first()\n return !!row\n } catch (err) {\n this.logSearchDebug('search:has-tokens-error', {\n entity,\n tenantId,\n organizationScope: orgScope,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n }\n\n private async searchSourcesHaveTokens(\n sources: SearchTokenSource[],\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n for (const source of sources) {\n const ok = await this.hasSearchTokens(source.entity, tenantId, orgScope)\n this.logSearchDebug('search:source-has-tokens', {\n entity: source.entity,\n recordIdColumn: source.recordIdColumn,\n tenantId,\n organizationScope: orgScope,\n hasTokens: ok,\n })\n if (ok) return true\n }\n return false\n }\n\n private async resolveAvailableCustomFieldKeys(entityIds: string[], tenantId: string | null): Promise<string[]> {\n if (!entityIds.length) return []\n const cacheKey = this.customFieldKeysCacheKey(entityIds, tenantId)\n const now = Date.now()\n const cached = this.customFieldKeysCache.get(cacheKey)\n if (cached && cached.expiresAt > now) {\n return cached.value.slice()\n }\n\n const knex = this.getKnex()\n const rows = await knex('custom_field_defs')\n .select('key')\n .whereIn('entity_id', entityIds)\n .andWhere('is_active', true)\n .modify((qb: any) => {\n qb.andWhere((inner: any) => {\n inner.where({ tenant_id: tenantId }).orWhereNull('tenant_id')\n })\n })\n const keys = new Set<string>()\n for (const row of rows || []) {\n const key = (row as Record<string, unknown>).key\n if (typeof key === 'string' && key.trim().length) keys.add(key.trim())\n else if (key != null) keys.add(String(key))\n }\n const result = Array.from(keys)\n if (this.customFieldKeysTtlMs > 0) {\n this.customFieldKeysCache.set(cacheKey, { expiresAt: now + this.customFieldKeysTtlMs, value: result })\n }\n return result.slice()\n }\n\n private async entityHasActiveCustomFields(entityId: string, tenantId: string | null): Promise<boolean> {\n try {\n const keys = await this.resolveAvailableCustomFieldKeys([entityId], tenantId)\n return keys.length > 0\n } catch (err) {\n if (this.isDebugVerbosity()) {\n this.debug('query:cf:check-error', {\n entity: entityId,\n tenantId: tenantId ?? null,\n error: err instanceof Error ? err.message : err,\n })\n }\n return true\n }\n }\n\n private customFieldKeysCacheKey(entityIds: string[], tenantId: string | null): string {\n const sorted = entityIds.slice().sort((a, b) => a.localeCompare(b)).join(',')\n return `${tenantId ?? '__none__'}|${sorted}`\n }\n\n private resolveVectorService(): VectorIndexService | null {\n if (!this.vectorServiceResolver) return null\n try {\n return this.vectorServiceResolver() ?? null\n } catch {\n return null\n }\n }\n\n private resolveEntityLabel(entity: string): string {\n return entity\n }\n\n private async indexAnyRows(entity: string): Promise<boolean> {\n const knex = this.getKnex()\n // Prefer coverage snapshots \u2013 cheap and already scoped by maintenance jobs.\n const coverage = await knex('entity_index_coverage')\n .select(1)\n .where('entity_type', entity)\n .where('indexed_count', '>', 0)\n .first()\n if (coverage) return true\n const exists = await knex('entity_indexes').select('entity_id').where({ entity_type: entity }).first()\n return !!exists\n }\n private async getStoredCoverageSnapshot(\n entity: string,\n tenantId: string | null,\n organizationId: string | null,\n withDeleted: boolean\n ): Promise<{ baseCount: number; indexedCount: number } | null> {\n try {\n if (!this.isCoverageOptimizationEnabled()) {\n await refreshCoverageSnapshot(\n this.em,\n {\n entityType: entity,\n tenantId,\n organizationId,\n withDeleted,\n },\n )\n }\n const knex = this.getKnex()\n const row = await readCoverageSnapshot(knex, {\n entityType: entity,\n tenantId,\n organizationId,\n withDeleted,\n })\n if (!row) return null\n return { baseCount: row.baseCount, indexedCount: row.indexedCount }\n } catch (err) {\n if (this.isDebugVerbosity()) {\n this.debug('coverage:snapshot:read-error', {\n entity,\n tenantId,\n organizationId,\n withDeleted,\n error: err instanceof Error ? err.message : err,\n })\n }\n return null\n }\n }\n\n private scheduleAutoReindex(\n entity: string,\n opts: QueryOptions,\n stats?: { baseCount: number; indexedCount: number },\n organizationIdOverride?: string | null\n ) {\n if (!this.isAutoReindexEnabled()) return\n\n const bus = this.resolveEventBus()\n if (!bus) return\n const payload = {\n entityType: entity,\n tenantId: opts.tenantId ?? null,\n organizationId: organizationIdOverride ?? opts.organizationId ?? null,\n force: false,\n }\n const context = stats\n ? {\n entity,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n baseCount: stats.baseCount,\n indexedCount: stats.indexedCount,\n }\n : { entity, tenantId: payload.tenantId, organizationId: payload.organizationId }\n\n void Promise.resolve()\n .then(async () => {\n try {\n await bus.emitEvent('query_index.reindex', payload, { persistent: true })\n if (this.isDebugVerbosity()) this.debug('query:auto-reindex:scheduled', context)\n } catch (err) {\n console.warn('[HybridQueryEngine] Failed to schedule auto reindex:', {\n ...context,\n error: err instanceof Error ? err.message : err,\n })\n }\n })\n }\n\n private scheduleCoverageRefresh(\n entity: string,\n tenantId: string | null | undefined,\n organizationId: string | null | undefined,\n withDeleted: boolean\n ): void {\n const bus = this.resolveEventBus()\n if (!bus) return\n const key = [\n entity,\n tenantId ?? '__tenant__',\n organizationId ?? '__org__',\n withDeleted ? '1' : '0',\n ].join('|')\n if (this.pendingCoverageRefreshKeys.has(key)) return\n this.pendingCoverageRefreshKeys.add(key)\n void Promise.resolve()\n .then(async () => {\n try {\n await bus.emitEvent('query_index.coverage.refresh', {\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n withDeleted,\n delayMs: 0,\n })\n if (this.isDebugVerbosity()) {\n this.debug('coverage:refresh:scheduled', {\n entity,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n withDeleted,\n })\n }\n } catch (err) {\n if (this.isDebugVerbosity()) {\n this.debug('coverage:refresh:failed', {\n entity,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n withDeleted,\n error: err instanceof Error ? err.message : err,\n })\n }\n }\n })\n .finally(() => {\n this.pendingCoverageRefreshKeys.delete(key)\n })\n }\n\n private resolveEventBus(): Pick<EventBus, 'emitEvent'> | null {\n if (!this.eventBusResolver) return null\n try {\n const bus = this.eventBusResolver()\n return bus ?? null\n } catch {\n return null\n }\n }\n\n private isAutoReindexEnabled(): boolean {\n if (this.autoReindexEnabled != null) return this.autoReindexEnabled\n const raw = (\n process.env.SCHEDULE_AUTO_REINDEX ??\n process.env.QUERY_INDEX_AUTO_REINDEX ??\n ''\n )\n .trim()\n .toLowerCase()\n if (!raw) {\n this.autoReindexEnabled = true\n return true\n }\n const parsed = parseBooleanToken(raw)\n this.autoReindexEnabled = parsed === null ? true : parsed\n return this.autoReindexEnabled\n }\n\n private isCoverageOptimizationEnabled(): boolean {\n if (this.coverageOptimizationEnabled != null) return this.coverageOptimizationEnabled\n const raw = (process.env.OPTIMIZE_INDEX_COVERAGE_STATS ?? '').trim().toLowerCase()\n if (!raw) {\n this.coverageOptimizationEnabled = false\n return false\n }\n this.coverageOptimizationEnabled = parseBooleanToken(raw) === true\n return this.coverageOptimizationEnabled\n }\n\n private async columnExists(table: string, column: string): Promise<boolean> {\n const key = `${table}.${column}`\n if (this.columnCache.has(key)) {\n const cached = this.columnCache.get(key)\n if (cached === true) return true\n this.columnCache.delete(key)\n }\n const knex = this.getKnex()\n const exists = await knex('information_schema.columns')\n .where({ table_name: table, column_name: column })\n .first()\n const present = !!exists\n if (present) this.columnCache.set(key, true)\n else this.columnCache.delete(key)\n return present\n }\n\n private async getBaseColumnsForEntity(entity: string): Promise<Map<string, string>> {\n const knex = this.getKnex()\n const table = resolveEntityTableName(this.em, entity)\n const rows = await knex('information_schema.columns')\n .select('column_name', 'data_type')\n .where({ table_name: table })\n const map = new Map<string, string>()\n for (const r of rows) map.set(r.column_name, r.data_type)\n return map\n }\n\n private resolveOrganizationScope(opts: QueryOptions): { ids: string[]; includeNull: boolean } | null {\n if (opts.organizationIds !== undefined) {\n const raw = (opts.organizationIds ?? []).map((id) => (typeof id === 'string' ? id.trim() : id))\n const includeNull = raw.some((id) => id == null || id === '')\n const ids = raw.filter((id): id is string => typeof id === 'string' && id.length > 0)\n const unique = Array.from(new Set(ids))\n return { ids: unique, includeNull }\n }\n if (typeof opts.organizationId === 'string' && opts.organizationId.trim().length > 0) {\n return { ids: [opts.organizationId], includeNull: false }\n }\n return null\n }\n\n private resolveCoverageSnapshotScope(\n opts: QueryOptions\n ): { tenantId: string | null; organizationId: string | null } | null {\n const tenantId = opts.tenantId ?? null\n const orgScope = this.resolveOrganizationScope(opts)\n if (!orgScope) return { tenantId, organizationId: null }\n if (orgScope.includeNull) {\n if (orgScope.ids.length === 0) return { tenantId, organizationId: null }\n return null\n }\n if (orgScope.ids.length === 1) return { tenantId, organizationId: orgScope.ids[0] }\n if (orgScope.ids.length === 0) return { tenantId, organizationId: null }\n return null\n }\n\n private applyOrganizationScope<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n column: string,\n scope: { ids: string[]; includeNull: boolean }\n ): Knex.QueryBuilder<TRecord, TResult> {\n if (scope.ids.length === 0 && !scope.includeNull) {\n return q.whereRaw('1 = 0')\n }\n return q.where((builder) => {\n let applied = false\n if (scope.ids.length > 0) {\n builder.whereIn(column, scope.ids as readonly string[])\n applied = true\n }\n if (scope.includeNull) {\n if (applied) builder.orWhereNull(column)\n else builder.whereNull(column)\n } else if (!applied) {\n builder.whereRaw('1 = 0')\n }\n })\n }\n\n private normalizeFilters(filters?: QueryOptions['filters']): NormalizedFilter[] {\n if (!filters) return []\n const normalizeField = (k: string) => k.startsWith('cf_') ? `cf:${k.slice(3)}` : k\n if (Array.isArray(filters)) {\n return (filters as Filter[]).map((filter) => ({\n field: normalizeField(String(filter.field)),\n op: filter.op,\n value: filter.value,\n }))\n }\n const out: NormalizedFilter[] = []\n const obj = filters as Record<string, unknown>\n const add = (field: string, op: FilterOp, value?: unknown) => out.push({ field, op, value })\n for (const [rawKey, rawVal] of Object.entries(obj)) {\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n switch (opKey) {\n case '$eq': add(field, 'eq', opVal); break\n case '$ne': add(field, 'ne', opVal); break\n case '$gt': add(field, 'gt', opVal); break\n case '$gte': add(field, 'gte', opVal); break\n case '$lt': add(field, 'lt', opVal); break\n case '$lte': add(field, 'lte', opVal); break\n case '$in': add(field, 'in', opVal); break\n case '$nin': add(field, 'nin', opVal); break\n case '$like': add(field, 'like', opVal); break\n case '$ilike': add(field, 'ilike', opVal); break\n case '$exists': add(field, 'exists', opVal); break\n }\n }\n } else {\n add(field, 'eq', rawVal)\n }\n }\n return out\n }\n\n private sanitize(s: string): string {\n return s.replace(/[^a-zA-Z0-9_]/g, '_')\n }\n\n private toArray(value: unknown): readonly unknown[] {\n if (Array.isArray(value)) {\n return value\n }\n if (value === undefined) {\n return []\n }\n return [value]\n }\n\n private parseCount(row: unknown): number {\n if (row && typeof row === 'object' && 'count' in row) {\n const value = (row as { count: unknown }).count\n if (typeof value === 'number') return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n return Number.isNaN(parsed) ? 0 : parsed\n }\n }\n return 0\n }\n\n private logSearchDebug(event: string, payload: Record<string, unknown>) {\n if (!this.isDebugVerbosity()) return\n try {\n console.info('[query-index:search]', event, JSON.stringify(payload))\n } catch {\n console.info('[query-index:search]', event, payload)\n }\n }\n\n private applyColumnFilter<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n column: string | Knex.Raw,\n filter: NormalizedFilter,\n search?: SearchRuntime & { knex: Knex; entity: string; field: string; recordIdColumn?: string }\n ): Knex.QueryBuilder<TRecord, TResult> {\n if (\n (filter.op === 'like' || filter.op === 'ilike') &&\n search?.enabled &&\n typeof filter.value === 'string'\n ) {\n const tokens = tokenizeText(String(filter.value), search.config)\n const hashes = tokens.hashes\n if (hashes.length) {\n const sources: SearchTokenSource[] = (search.searchSources && search.searchSources.length\n ? search.searchSources\n : [{ entity: search.entity, recordIdColumn: search.recordIdColumn ?? '' }]\n ).filter((src) => src.recordIdColumn && src.entity)\n let applied = false\n if (sources.length) {\n q = q.where((qb) => {\n sources.forEach((src, idx) => {\n const ok = this.applySearchTokens(qb as any, {\n knex: search.knex,\n entity: src.entity,\n field: search.field,\n hashes,\n recordIdColumn: src.recordIdColumn,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope ?? null,\n combineWith: idx === 0 ? 'and' : 'or',\n })\n if (ok) applied = true\n })\n })\n }\n this.logSearchDebug('search:filter', {\n entity: search.entity,\n field: search.field,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: search.tenantId ?? null,\n organizationScope: search.organizationScope,\n sources: sources.map((src) => ({ entity: src.entity, recordIdColumn: src.recordIdColumn })),\n })\n if (applied) return q\n } else {\n this.logSearchDebug('search:skip-empty-hashes', {\n entity: search.entity,\n field: search.field,\n value: filter.value,\n })\n }\n return q\n }\n const col = column as any\n switch (filter.op) {\n case 'eq':\n return q.where(col, filter.value as Knex.Value)\n case 'ne':\n return q.whereNot(col, filter.value as Knex.Value)\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const operator = filter.op === 'gt' ? '>' : filter.op === 'gte' ? '>=' : filter.op === 'lt' ? '<' : '<='\n return q.where(col, operator, filter.value as Knex.Value)\n }\n case 'in': {\n const values = this.toArray(filter.value) as readonly Knex.Value[]\n return q.whereIn(col, values)\n }\n case 'nin': {\n const values = this.toArray(filter.value) as readonly Knex.Value[]\n return q.whereNotIn(col, values)\n }\n case 'like':\n return q.where(col, 'like', filter.value as Knex.Value)\n case 'ilike':\n return q.where(col, 'ilike', filter.value as Knex.Value)\n case 'exists':\n return filter.value ? q.whereNotNull(col) : q.whereNull(col)\n default:\n return q\n }\n }\n\n private resolveCustomEntityColumn(alias: string, field: string): string | null {\n if (field === 'id') return `${alias}.entity_id`\n if (field === 'organization_id' || field === 'organizationId') return `${alias}.organization_id`\n if (field === 'tenant_id' || field === 'tenantId') return `${alias}.tenant_id`\n if (field === 'created_at' || field === 'updated_at' || field === 'deleted_at') return `${alias}.${field}`\n return null\n }\n\n private isDebugVerbosity(): boolean {\n if (this.debugVerbosity != null) return this.debugVerbosity\n this.debugVerbosity = resolveDebugVerbosity()\n return this.debugVerbosity\n }\n\n private isSqlDebugEnabled(): boolean {\n if (this.sqlDebugEnabled != null) return this.sqlDebugEnabled\n this.sqlDebugEnabled = resolveBooleanEnv(['QUERY_ENGINE_DEBUG_SQL'], false)\n return this.sqlDebugEnabled\n }\n\n private isForcePartialIndexEnabled(): boolean {\n if (this.forcePartialIndexEnabled != null) return this.forcePartialIndexEnabled\n this.forcePartialIndexEnabled = resolveBooleanEnv(['FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES'], true)\n return this.forcePartialIndexEnabled\n }\n\n private async resolveCoverageGap(\n entity: string,\n opts: QueryOptions,\n coverageScope?: { tenantId: string | null; organizationId: string | null } | null,\n _sourceTable?: string\n ): Promise<{ stats?: { baseCount: number; indexedCount: number }; scope: 'scoped' | 'global' } | null> {\n const scope = coverageScope ?? this.resolveCoverageSnapshotScope(opts)\n if (!scope) return null\n const tenantId = scope.tenantId\n const organizationId = scope.organizationId\n const withDeleted = !!opts.withDeleted\n\n const snapshot = await this.getStoredCoverageSnapshot(entity, tenantId, organizationId, withDeleted)\n if (!snapshot) {\n this.scheduleCoverageRefresh(entity, tenantId, organizationId, withDeleted)\n return { stats: undefined, scope: 'scoped' }\n }\n\n const baseCount = snapshot.baseCount\n const indexCount = snapshot.indexedCount\n const hasGap = baseCount > 0 && indexCount < baseCount\n if (hasGap || indexCount > baseCount) {\n return { stats: snapshot, scope: 'scoped' }\n }\n\n return null\n }\n\n // Backward-compatible hook for tests that mock coverage stats\n private async indexCoverageStats(\n entity: string,\n opts: QueryOptions,\n coverageScope?: { tenantId: string | null; organizationId: string | null } | null,\n ): Promise<{ baseCount: number; indexedCount: number } | null> {\n const gap = await this.resolveCoverageGap(entity, opts, coverageScope)\n return gap?.stats ?? null\n }\n\n private async captureSqlTiming<TResult>(\n label: string,\n entity: EntityId,\n execute: () => Promise<TResult> | TResult,\n extra?: Record<string, unknown>,\n profiler?: Profiler\n ): Promise<TResult> {\n const shouldDebug = this.isSqlDebugEnabled() && this.isDebugVerbosity()\n const shouldProfile = profiler?.enabled === true\n if (!shouldDebug && !shouldProfile) {\n return Promise.resolve(execute())\n }\n const startedAt = process.hrtime.bigint()\n try {\n return await Promise.resolve(execute())\n } finally {\n const elapsedMs = Number(process.hrtime.bigint() - startedAt) / 1_000_000\n const context: Record<string, unknown> = {\n entity,\n durationMs: Math.round(elapsedMs * 1000) / 1000,\n }\n if (extra) Object.assign(context, extra)\n if (shouldProfile) profiler!.record(label, context.durationMs as number, extra)\n if (shouldDebug) this.debug(`${label}:timing`, context)\n }\n }\n\n private debug(message: string, context?: Record<string, unknown>): void {\n if (!this.isDebugVerbosity()) return\n if (!this.isSqlDebugEnabled()) return\n if (context) console.debug('[HybridQueryEngine]', message, context)\n else console.debug('[HybridQueryEngine]', message)\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["result"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.4.6-develop-
|
|
3
|
+
"version": "0.4.6-develop-db1058d334",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
}
|
|
208
208
|
},
|
|
209
209
|
"dependencies": {
|
|
210
|
-
"@open-mercato/shared": "0.4.6-develop-
|
|
210
|
+
"@open-mercato/shared": "0.4.6-develop-db1058d334",
|
|
211
211
|
"@types/html-to-text": "^9.0.4",
|
|
212
212
|
"@types/semver": "^7.5.8",
|
|
213
213
|
"@xyflow/react": "^12.6.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QueryEngine, QueryOptions, QueryResult, FilterOp, Filter, QueryCustomFieldSource, PartialIndexWarning } from '@open-mercato/shared/lib/query/types'
|
|
1
|
+
import type { QueryEngine, QueryOptions, QueryResult, FilterOp, Filter, QueryCustomFieldSource, PartialIndexWarning, QueryExtensionsConfig } from '@open-mercato/shared/lib/query/types'
|
|
2
2
|
import { SortDir } from '@open-mercato/shared/lib/query/types'
|
|
3
3
|
import type { EntityId } from '@open-mercato/shared/modules/entities'
|
|
4
4
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from '@open-mercato/shared/lib/query/join-utils'
|
|
21
21
|
import { resolveSearchConfig, type SearchConfig } from '@open-mercato/shared/lib/search/config'
|
|
22
22
|
import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
|
|
23
|
+
import { runBeforeQueryPipeline, runAfterQueryPipeline, type QueryExtensionContext } from '@open-mercato/shared/lib/query/query-extension-runner'
|
|
23
24
|
|
|
24
25
|
function resolveBooleanEnv(names: readonly string[], defaultValue: boolean): boolean {
|
|
25
26
|
for (const name of names) {
|
|
@@ -116,6 +117,33 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
async query<T = unknown>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {
|
|
120
|
+
// --- UMES query extension: before-query pipeline ---
|
|
121
|
+
const ext: QueryExtensionsConfig | undefined = opts.extensions
|
|
122
|
+
let hybridExtCtx: QueryExtensionContext | null = null
|
|
123
|
+
const noopDi = { resolve: <R = unknown>(_name: string): R => { throw new Error('No DI context') } }
|
|
124
|
+
|
|
125
|
+
if (ext) {
|
|
126
|
+
hybridExtCtx = {
|
|
127
|
+
entity: String(entity),
|
|
128
|
+
engine: 'hybrid',
|
|
129
|
+
tenantId: opts.tenantId ?? '',
|
|
130
|
+
organizationId: opts.organizationId,
|
|
131
|
+
userId: ext.userId,
|
|
132
|
+
em: this.em,
|
|
133
|
+
container: ext.container,
|
|
134
|
+
userFeatures: ext.userFeatures,
|
|
135
|
+
}
|
|
136
|
+
const diCtx = ext.resolve ? { resolve: ext.resolve } : noopDi
|
|
137
|
+
const beforeResult = await runBeforeQueryPipeline(opts, hybridExtCtx, diCtx)
|
|
138
|
+
if (beforeResult.blocked) {
|
|
139
|
+
throw new Error(beforeResult.errorMessage ?? 'Query blocked by extension subscriber')
|
|
140
|
+
}
|
|
141
|
+
opts = beforeResult.query
|
|
142
|
+
}
|
|
143
|
+
// Strip extensions so fallback to BasicQueryEngine doesn't double-execute
|
|
144
|
+
const { extensions: _stripExt, ...coreOpts } = opts
|
|
145
|
+
opts = coreOpts
|
|
146
|
+
|
|
119
147
|
const providedProfiler = opts.profiler
|
|
120
148
|
const profiler = providedProfiler && providedProfiler.enabled
|
|
121
149
|
? providedProfiler
|
|
@@ -128,6 +156,17 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
128
156
|
profiler.end(meta)
|
|
129
157
|
}
|
|
130
158
|
|
|
159
|
+
const applyAfterExtensions = async <R>(queryResult: QueryResult<R>): Promise<QueryResult<R>> => {
|
|
160
|
+
if (!ext || !hybridExtCtx) return queryResult
|
|
161
|
+
const diCtx = ext.resolve ? { resolve: ext.resolve } : noopDi
|
|
162
|
+
return await runAfterQueryPipeline(
|
|
163
|
+
queryResult as QueryResult<Record<string, unknown>>,
|
|
164
|
+
opts,
|
|
165
|
+
hybridExtCtx,
|
|
166
|
+
diCtx,
|
|
167
|
+
) as QueryResult<R>
|
|
168
|
+
}
|
|
169
|
+
|
|
131
170
|
try {
|
|
132
171
|
const debugEnabled = this.isDebugVerbosity()
|
|
133
172
|
if (debugEnabled) this.debug('query:start', { entity })
|
|
@@ -144,7 +183,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
144
183
|
result: 'custom_entity',
|
|
145
184
|
total: Array.isArray(result.items) ? result.items.length : undefined,
|
|
146
185
|
})
|
|
147
|
-
return result
|
|
186
|
+
return await applyAfterExtensions(result)
|
|
148
187
|
} catch (err) {
|
|
149
188
|
section.end({ error: err instanceof Error ? err.message : String(err) })
|
|
150
189
|
throw err
|
|
@@ -164,7 +203,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
164
203
|
if (debugEnabled) this.debug('query:fallback:missing-base', { entity, baseTable })
|
|
165
204
|
const fallbackResult = await this.fallback.query(entity, opts)
|
|
166
205
|
finishProfile({ result: 'fallback', reason: 'missing_base' })
|
|
167
|
-
return fallbackResult
|
|
206
|
+
return await applyAfterExtensions(fallbackResult)
|
|
168
207
|
}
|
|
169
208
|
|
|
170
209
|
const normalizedFilters = normalizeFilters(opts.filters)
|
|
@@ -200,7 +239,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
200
239
|
if (debugEnabled) this.debug('query:fallback:no-index', { entity })
|
|
201
240
|
const fallbackResult = await this.fallback.query(entity, opts)
|
|
202
241
|
finishProfile({ result: 'fallback', reason: 'no_index_rows' })
|
|
203
|
-
return fallbackResult
|
|
242
|
+
return await applyAfterExtensions(fallbackResult)
|
|
204
243
|
}
|
|
205
244
|
if (entityHasActiveCustomFields) {
|
|
206
245
|
const gap = await profiler.measure(
|
|
@@ -248,7 +287,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
248
287
|
baseCount: gap.stats?.baseCount ?? null,
|
|
249
288
|
indexedCount: gap.stats?.indexedCount ?? null,
|
|
250
289
|
})
|
|
251
|
-
return resultWithWarning
|
|
290
|
+
return await applyAfterExtensions(resultWithWarning)
|
|
252
291
|
}
|
|
253
292
|
if (gap.stats) {
|
|
254
293
|
console.warn('[HybridQueryEngine] Partial index coverage detected; forcing query index usage due to FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES:', { entity, baseCount: gap.stats.baseCount, indexedCount: gap.stats.indexedCount, scope: gap.scope })
|
|
@@ -765,10 +804,14 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
765
804
|
}
|
|
766
805
|
|
|
767
806
|
const typedItems = items as unknown as T[]
|
|
768
|
-
|
|
807
|
+
let result: QueryResult<T> = { items: typedItems, page, pageSize, total }
|
|
769
808
|
if (partialIndexWarning) {
|
|
770
809
|
result.meta = { partialIndexWarning }
|
|
771
810
|
}
|
|
811
|
+
|
|
812
|
+
// --- UMES query extension: after-query pipeline ---
|
|
813
|
+
result = await applyAfterExtensions(result)
|
|
814
|
+
|
|
772
815
|
finishProfile({
|
|
773
816
|
result: 'ok',
|
|
774
817
|
total,
|