@open-mercato/core 0.6.7-develop.6696.1.4236709f7e → 0.6.7-develop.6725.1.07a748cfc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/auth/api/admin/nav.js +14 -4
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +8 -19
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/frontend/login.js +4 -1
- package/dist/modules/auth/frontend/login.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +31 -14
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +3 -4
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +3 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/utils.js +12 -18
- package/dist/modules/customers/api/utils.js.map +2 -2
- package/dist/modules/customers/cli.js +11 -4
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/components/detail/ActiveDealCard.js +2 -1
- package/dist/modules/customers/components/detail/ActiveDealCard.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +6 -5
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +4 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/dashboard/helpers.js +4 -3
- package/dist/modules/customers/components/detail/dashboard/helpers.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +14 -10
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/lib/dealStatus.js +38 -0
- package/dist/modules/customers/lib/dealStatus.js.map +7 -0
- package/dist/modules/dashboards/api/widgets/data/batch/route.js +4 -2
- package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +20 -2
- package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
- package/dist/modules/dashboards/lib/aggregations.js +76 -40
- package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
- package/dist/modules/dashboards/lib/exactDecimal.js +69 -0
- package/dist/modules/dashboards/lib/exactDecimal.js.map +7 -0
- package/dist/modules/dashboards/services/widgetDataService.js +228 -5
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureGuard.js +3 -2
- package/dist/modules/feature_toggles/components/FeatureGuard.js.map +2 -2
- package/dist/modules/feature_toggles/components/hooks/staleTime.js +5 -0
- package/dist/modules/feature_toggles/components/hooks/staleTime.js.map +7 -0
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js +5 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js.map +2 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js +5 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js.map +2 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js +5 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js.map +2 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js +5 -2
- package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/route.js +16 -1
- package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
- package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js +32 -0
- package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js.map +7 -0
- package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -5
- package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +2 -2
- package/dist/modules/integrations/widgets/injection/external-ids/widget.js +14 -0
- package/dist/modules/integrations/widgets/injection/external-ids/widget.js.map +7 -0
- package/dist/modules/messages/lib/searchLookup.js +8 -16
- package/dist/modules/messages/lib/searchLookup.js.map +2 -2
- package/dist/modules/payment_gateways/api/sessions/route.js +5 -0
- package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
- package/dist/modules/payment_gateways/di.js +17 -1
- package/dist/modules/payment_gateways/di.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +8 -0
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/payment_gateways/lib/order-amount-reconciliation.js +58 -0
- package/dist/modules/payment_gateways/lib/order-amount-reconciliation.js.map +7 -0
- package/dist/modules/query_index/api/openapi.js +13 -1
- package/dist/modules/query_index/api/openapi.js.map +2 -2
- package/dist/modules/query_index/api/status.js +165 -105
- package/dist/modules/query_index/api/status.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +128 -41
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/query_index/lib/batch.js +11 -4
- package/dist/modules/query_index/lib/batch.js.map +2 -2
- package/dist/modules/query_index/lib/document.js +12 -5
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +36 -4
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +1 -1
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +7 -4
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +14 -3
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +22 -1
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/useSalesChannelsEnabled.js +2 -35
- package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +2 -2
- package/dist/modules/sales/data/validators.js +25 -4
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/sales/di.js +7 -0
- package/dist/modules/sales/di.js.map +2 -2
- package/dist/modules/sales/services/paymentOrderTotalResolver.js +52 -0
- package/dist/modules/sales/services/paymentOrderTotalResolver.js.map +7 -0
- package/package.json +7 -7
- package/src/modules/auth/api/admin/nav.ts +24 -4
- package/src/modules/auth/api/users/route.ts +8 -28
- package/src/modules/auth/frontend/login.tsx +4 -1
- package/src/modules/auth/lib/backendChrome.tsx +51 -13
- package/src/modules/customers/api/companies/[id]/route.ts +3 -4
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +3 -2
- package/src/modules/customers/api/utils.ts +12 -31
- package/src/modules/customers/cli.ts +11 -4
- package/src/modules/customers/components/detail/ActiveDealCard.tsx +2 -1
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +6 -5
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +4 -0
- package/src/modules/customers/components/detail/dashboard/helpers.ts +4 -3
- package/src/modules/customers/components/formConfig.tsx +12 -8
- package/src/modules/customers/lib/dealStatus.ts +48 -0
- package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -0
- package/src/modules/dashboards/api/widgets/data/route.ts +21 -0
- package/src/modules/dashboards/lib/aggregations.ts +122 -43
- package/src/modules/dashboards/lib/exactDecimal.ts +96 -0
- package/src/modules/dashboards/services/widgetDataService.ts +342 -4
- package/src/modules/feature_toggles/components/FeatureGuard.tsx +9 -2
- package/src/modules/feature_toggles/components/hooks/staleTime.ts +5 -0
- package/src/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.ts +13 -1
- package/src/modules/feature_toggles/components/hooks/useFeatureFlagJson.ts +12 -3
- package/src/modules/feature_toggles/components/hooks/useFeatureFlagNumber.ts +10 -1
- package/src/modules/feature_toggles/components/hooks/useFeatureFlagString.ts +10 -1
- package/src/modules/inbox_ops/api/proposals/route.ts +20 -1
- package/src/modules/inbox_ops/subscribers/search-reindex-proposal-created.ts +39 -0
- package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -5
- package/src/modules/integrations/widgets/injection/external-ids/widget.ts +13 -0
- package/src/modules/messages/lib/searchLookup.ts +11 -34
- package/src/modules/payment_gateways/api/sessions/route.ts +5 -0
- package/src/modules/payment_gateways/di.ts +28 -2
- package/src/modules/payment_gateways/i18n/de.json +3 -0
- package/src/modules/payment_gateways/i18n/en.json +3 -0
- package/src/modules/payment_gateways/i18n/es.json +3 -0
- package/src/modules/payment_gateways/i18n/pl.json +3 -0
- package/src/modules/payment_gateways/lib/gateway-service.ts +14 -0
- package/src/modules/payment_gateways/lib/order-amount-reconciliation.ts +93 -0
- package/src/modules/query_index/api/openapi.ts +12 -0
- package/src/modules/query_index/api/status.ts +238 -136
- package/src/modules/query_index/components/QueryIndexesTable.tsx +192 -46
- package/src/modules/query_index/i18n/de.json +5 -3
- package/src/modules/query_index/i18n/en.json +5 -3
- package/src/modules/query_index/i18n/es.json +5 -3
- package/src/modules/query_index/i18n/pl.json +5 -3
- package/src/modules/query_index/lib/batch.ts +11 -4
- package/src/modules/query_index/lib/document.ts +35 -4
- package/src/modules/query_index/lib/engine.ts +53 -5
- package/src/modules/query_index/lib/indexer.ts +1 -1
- package/src/modules/query_index/lib/search-tokens.ts +13 -4
- package/src/modules/sales/api/documents/factory.ts +28 -3
- package/src/modules/sales/commands/documents.ts +26 -2
- package/src/modules/sales/components/useSalesChannelsEnabled.ts +2 -48
- package/src/modules/sales/data/validators.ts +50 -5
- package/src/modules/sales/di.ts +9 -0
- package/src/modules/sales/services/paymentOrderTotalResolver.ts +67 -0
|
@@ -3,6 +3,7 @@ import * as React from 'react'
|
|
|
3
3
|
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
4
4
|
import type { ColumnDef, SortingState } from '@tanstack/react-table'
|
|
5
5
|
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
6
|
+
import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
6
7
|
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
7
8
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
9
|
import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
@@ -52,15 +53,54 @@ type Row = {
|
|
|
52
53
|
baseCount: number | null
|
|
53
54
|
indexCount: number | null
|
|
54
55
|
vectorCount: number | null
|
|
56
|
+
/** The entity is configured for vector search (declares `buildSource`). */
|
|
55
57
|
vectorEnabled: boolean
|
|
58
|
+
/** Vector indexing can actually run — provider reachable and auto-indexing on. */
|
|
59
|
+
vectorIndexingActive?: boolean
|
|
56
60
|
fulltextCount: number | null
|
|
57
61
|
fulltextEnabled: boolean
|
|
62
|
+
hasCustomFields?: boolean
|
|
63
|
+
/** Legacy aggregate: query index AND vector coverage. Preserved for API consumers. */
|
|
58
64
|
ok: boolean
|
|
65
|
+
/** Query index alone is in sync with the base table — the signal this page reports. */
|
|
66
|
+
queryIndexOk?: boolean
|
|
59
67
|
job?: JobStatus
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
type Resp = { items: Row[] }
|
|
63
71
|
|
|
72
|
+
const BACKEND_FILTER_ID = 'backend'
|
|
73
|
+
// Kept at or below the repository-wide 100-row cap; the entity list grows with every
|
|
74
|
+
// installed module, so it cannot be allowed to scale with the row count.
|
|
75
|
+
const PAGE_SIZE = 50
|
|
76
|
+
|
|
77
|
+
/** Sort key for a column id. Mirrors the column `accessorFn`s so paging and sorting agree. */
|
|
78
|
+
function sortValue(row: Row, columnId: string): string | number {
|
|
79
|
+
if (columnId === 'entityId') return row.entityId
|
|
80
|
+
if (columnId === 'baseCount') return row.baseCount ?? 0
|
|
81
|
+
if (columnId === 'indexCount') return row.indexCount ?? 0
|
|
82
|
+
if (columnId === 'vectorCount') return isVectorReportable(row) ? row.vectorCount ?? 0 : -1
|
|
83
|
+
if (columnId === 'fulltextCount') return row.fulltextEnabled ? row.fulltextCount ?? 0 : -1
|
|
84
|
+
return row.entityId
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type BackendFilter = 'vector' | 'fulltext' | 'custom_fields'
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Vector coverage is only worth showing when embeddings can actually be written; otherwise
|
|
91
|
+
* the count is a permanent 0 that reads as a gap. Older payloads without the flag are
|
|
92
|
+
* treated as active so the column keeps working against an un-upgraded server.
|
|
93
|
+
*/
|
|
94
|
+
function isVectorReportable(row: Row): boolean {
|
|
95
|
+
return row.vectorEnabled && (row.vectorIndexingActive ?? true)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function matchesBackendFilter(row: Row, backend: BackendFilter): boolean {
|
|
99
|
+
if (backend === 'vector') return row.vectorEnabled
|
|
100
|
+
if (backend === 'fulltext') return row.fulltextEnabled
|
|
101
|
+
return Boolean(row.hasCustomFields)
|
|
102
|
+
}
|
|
103
|
+
|
|
64
104
|
function formatCount(value: number | null): string {
|
|
65
105
|
if (value == null) return '—'
|
|
66
106
|
return value.toLocaleString()
|
|
@@ -83,15 +123,24 @@ function formatProgressLabel(
|
|
|
83
123
|
return t('query_index.table.status.progressSingle', { processed: processedText })
|
|
84
124
|
}
|
|
85
125
|
|
|
86
|
-
function translateJobStatus(
|
|
87
|
-
|
|
126
|
+
function translateJobStatus(
|
|
127
|
+
t: Translator,
|
|
128
|
+
status: JobStatus['status'] | undefined,
|
|
129
|
+
ok: boolean,
|
|
130
|
+
measured: boolean,
|
|
131
|
+
): string {
|
|
132
|
+
const idleLabel = () => {
|
|
133
|
+
// No coverage snapshot yet — an async refresh is pending, so the index is not known to
|
|
134
|
+
// be out of sync. Saying so would be the same false alarm this page used to raise.
|
|
135
|
+
if (!measured) return t('query_index.table.status.not_measured')
|
|
88
136
|
return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')
|
|
89
137
|
}
|
|
138
|
+
if (!status || status === 'idle') return idleLabel()
|
|
90
139
|
if (status === 'reindexing') return t('query_index.table.status.reindexing')
|
|
91
140
|
if (status === 'purging') return t('query_index.table.status.purging')
|
|
92
141
|
if (status === 'stalled') return t('query_index.table.status.stalled')
|
|
93
142
|
if (status === 'failed') return t('query_index.table.status.failed')
|
|
94
|
-
return
|
|
143
|
+
return idleLabel()
|
|
95
144
|
}
|
|
96
145
|
|
|
97
146
|
function translateScopeStatus(
|
|
@@ -108,8 +157,15 @@ function translateScopeStatus(
|
|
|
108
157
|
|
|
109
158
|
function createColumns(t: Translator): ColumnDef<Row>[] {
|
|
110
159
|
return [
|
|
111
|
-
{
|
|
112
|
-
|
|
160
|
+
{
|
|
161
|
+
id: 'entityId',
|
|
162
|
+
header: () => t('query_index.table.columns.entity'),
|
|
163
|
+
accessorKey: 'entityId',
|
|
164
|
+
// The entity id is the only identifier on this page and ids run to ~45 characters
|
|
165
|
+
// (`catalog:catalog_product_category_assignment`). DataTable's default 150px clamp
|
|
166
|
+
// ellipsised them into indistinguishable `customers:customer…` rows.
|
|
167
|
+
meta: { priority: 1, maxWidth: '340px' },
|
|
168
|
+
},
|
|
113
169
|
{
|
|
114
170
|
id: 'baseCount',
|
|
115
171
|
header: () => t('query_index.table.columns.records'),
|
|
@@ -127,10 +183,10 @@ function createColumns(t: Translator): ColumnDef<Row>[] {
|
|
|
127
183
|
{
|
|
128
184
|
id: 'vectorCount',
|
|
129
185
|
header: () => t('query_index.table.columns.vector'),
|
|
130
|
-
accessorFn: (row) => (row
|
|
186
|
+
accessorFn: (row) => (isVectorReportable(row) ? row.vectorCount ?? 0 : -1),
|
|
131
187
|
cell: ({ row }) => {
|
|
132
188
|
const record = row.original
|
|
133
|
-
if (!record
|
|
189
|
+
if (!isVectorReportable(record)) return <span>—</span>
|
|
134
190
|
const ok = record.vectorCount != null && record.baseCount != null && record.vectorCount === record.baseCount
|
|
135
191
|
const display = formatCount(record.vectorCount)
|
|
136
192
|
const className = ok ? 'text-status-success-text' : 'text-status-warning-text'
|
|
@@ -159,17 +215,45 @@ function createColumns(t: Translator): ColumnDef<Row>[] {
|
|
|
159
215
|
const record = row.original
|
|
160
216
|
const job = record.job
|
|
161
217
|
const partitions = job?.partitions ?? []
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
218
|
+
const measured = record.baseCount != null || record.indexCount != null
|
|
219
|
+
|
|
220
|
+
// A non-partitioned reindex — and every purge, which never partitions
|
|
221
|
+
// (`lib/purge.ts` always passes `partitionIndex: null`) — is tracked as a scope row.
|
|
222
|
+
// The server derives the top-level status from partition rows only, so it reports
|
|
223
|
+
// `idle` for those runs; without folding the scope row in, the default reindex and
|
|
224
|
+
// every purge would render as though nothing were happening.
|
|
225
|
+
const scopeStatus = job?.scope?.status ?? null
|
|
226
|
+
const scopeInFlight = scopeStatus != null && scopeStatus !== 'completed'
|
|
227
|
+
const effectiveStatus: JobStatus['status'] | undefined = job
|
|
228
|
+
? (job.status !== 'idle'
|
|
229
|
+
? job.status
|
|
230
|
+
: (scopeInFlight ? (scopeStatus as JobStatus['status']) : 'idle'))
|
|
231
|
+
: undefined
|
|
232
|
+
|
|
233
|
+
const indexOk = record.queryIndexOk ?? record.ok
|
|
234
|
+
const ok = indexOk && (!job || effectiveStatus === 'idle')
|
|
235
|
+
const jobFailed = effectiveStatus === 'failed'
|
|
236
|
+
const statusText = translateJobStatus(t, effectiveStatus, ok, measured)
|
|
237
|
+
|
|
238
|
+
const jobInFlight = Boolean(effectiveStatus && effectiveStatus !== 'idle')
|
|
239
|
+
|| partitions.some((part) => part.status !== 'completed')
|
|
240
|
+
|
|
241
|
+
// Job counters and index coverage are different numbers: the counters describe the
|
|
242
|
+
// last reindex run, the Records/Indexed columns describe the index right now. While
|
|
243
|
+
// a job runs, "Reindexing (5/14)" is the only place progress is visible. Once it is
|
|
244
|
+
// idle the counters are stale and duplicate the columns — worse, they can contradict
|
|
245
|
+
// them: an entity whose last run processed 14 rows but landed 2 in the index rendered
|
|
246
|
+
// "Out of sync (14/14)" next to Records 14 / Indexed 2.
|
|
247
|
+
const jobProgress = jobInFlight && job
|
|
248
|
+
? formatProgressLabel(job.processedCount ?? null, job.totalCount ?? null, t)
|
|
249
|
+
: null
|
|
166
250
|
const label = jobProgress
|
|
167
251
|
? t('query_index.table.status.withProgress', { status: statusText, progress: jobProgress })
|
|
168
252
|
: statusText
|
|
169
253
|
let variant: StatusBadgeVariant = 'neutral'
|
|
170
254
|
if (job) {
|
|
171
|
-
if (
|
|
172
|
-
else if (
|
|
255
|
+
if (effectiveStatus === 'stalled' || jobFailed) variant = 'error'
|
|
256
|
+
else if (effectiveStatus === 'reindexing' || effectiveStatus === 'purging') variant = 'warning'
|
|
173
257
|
else variant = ok ? 'success' : 'neutral'
|
|
174
258
|
} else {
|
|
175
259
|
variant = ok ? 'success' : 'neutral'
|
|
@@ -177,33 +261,28 @@ function createColumns(t: Translator): ColumnDef<Row>[] {
|
|
|
177
261
|
|
|
178
262
|
const lines: string[] = []
|
|
179
263
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const partitionLabel =
|
|
190
|
-
part.partitionIndex != null
|
|
191
|
-
? t('query_index.table.status.partitionLabel', { index: Number(part.partitionIndex) + 1 })
|
|
192
|
-
: t('query_index.table.status.scopeLabel')
|
|
193
|
-
const partitionStatus = translateScopeStatus(t, part.status)
|
|
194
|
-
const partitionProgress = formatProgressLabel(part.processedCount ?? null, part.totalCount ?? null, t)
|
|
195
|
-
lines.push(`${partitionLabel}: ${partitionStatus}${partitionProgress ? ` (${partitionProgress})` : ''}`)
|
|
264
|
+
// Per-partition detail earns its space only while work is in flight — that is when
|
|
265
|
+
// it tells you which partition is running or stuck. Once the job is idle and every
|
|
266
|
+
// partition is finished, each line just restates the badge, on every row.
|
|
267
|
+
if (jobInFlight) {
|
|
268
|
+
if (job?.scope && partitions.length <= 1) {
|
|
269
|
+
const scopeStatusText = translateScopeStatus(t, scopeStatus)
|
|
270
|
+
const scopeProgress = formatProgressLabel(job.scope.processedCount ?? null, job.scope.totalCount ?? null, t)
|
|
271
|
+
const scopeLabel = t('query_index.table.status.scopeLabel')
|
|
272
|
+
lines.push(`${scopeLabel}: ${scopeStatusText}${scopeProgress ? ` (${scopeProgress})` : ''}`)
|
|
196
273
|
}
|
|
197
|
-
}
|
|
198
274
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
275
|
+
if (partitions.length > 1) {
|
|
276
|
+
for (const part of partitions) {
|
|
277
|
+
const partitionLabel =
|
|
278
|
+
part.partitionIndex != null
|
|
279
|
+
? t('query_index.table.status.partitionLabel', { index: Number(part.partitionIndex) + 1 })
|
|
280
|
+
: t('query_index.table.status.scopeLabel')
|
|
281
|
+
const partitionStatus = translateScopeStatus(t, part.status)
|
|
282
|
+
const partitionProgress = formatProgressLabel(part.processedCount ?? null, part.totalCount ?? null, t)
|
|
283
|
+
lines.push(`${partitionLabel}: ${partitionStatus}${partitionProgress ? ` (${partitionProgress})` : ''}`)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
207
286
|
}
|
|
208
287
|
|
|
209
288
|
return (
|
|
@@ -230,14 +309,27 @@ function createColumns(t: Translator): ColumnDef<Row>[] {
|
|
|
230
309
|
|
|
231
310
|
export default function QueryIndexesTable() {
|
|
232
311
|
const [sorting, setSorting] = React.useState<SortingState>([{ id: 'entityId', desc: false }])
|
|
233
|
-
const [page, setPage] = React.useState(1)
|
|
234
312
|
const [search, setSearch] = React.useState('')
|
|
313
|
+
const [page, setPage] = React.useState(1)
|
|
314
|
+
const [filterValues, setFilterValues] = React.useState<FilterValues>({})
|
|
235
315
|
const qc = useQueryClient()
|
|
236
316
|
const scopeVersion = useOrganizationScopeVersion()
|
|
237
317
|
const [refreshSeq, setRefreshSeq] = React.useState(0)
|
|
238
318
|
const t = useT()
|
|
239
319
|
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
240
320
|
const columns = React.useMemo(() => createColumns(t), [t])
|
|
321
|
+
const filters = React.useMemo<FilterDef[]>(() => [
|
|
322
|
+
{
|
|
323
|
+
id: BACKEND_FILTER_ID,
|
|
324
|
+
label: t('query_index.table.filters.backend'),
|
|
325
|
+
type: 'select',
|
|
326
|
+
options: [
|
|
327
|
+
{ label: t('query_index.table.filters.backendOptions.vector'), value: 'vector' },
|
|
328
|
+
{ label: t('query_index.table.filters.backendOptions.fulltext'), value: 'fulltext' },
|
|
329
|
+
{ label: t('query_index.table.filters.backendOptions.customFields'), value: 'custom_fields' },
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
], [t])
|
|
241
333
|
const { runMutation, retryLastMutation } = useGuardedMutation<{
|
|
242
334
|
formId: string
|
|
243
335
|
resourceKind: string
|
|
@@ -263,11 +355,49 @@ export default function QueryIndexesTable() {
|
|
|
263
355
|
})
|
|
264
356
|
|
|
265
357
|
const rowsAll = data?.items || []
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
const q = search.toLowerCase()
|
|
269
|
-
|
|
270
|
-
|
|
358
|
+
const matchedRows = React.useMemo(() => {
|
|
359
|
+
const backend = filterValues[BACKEND_FILTER_ID] as BackendFilter | undefined
|
|
360
|
+
const q = search.trim().toLowerCase()
|
|
361
|
+
if (!q && !backend) return rowsAll
|
|
362
|
+
return rowsAll.filter((row) => {
|
|
363
|
+
if (q && !row.entityId.toLowerCase().includes(q)) return false
|
|
364
|
+
if (backend && !matchesBackendFilter(row, backend)) return false
|
|
365
|
+
return true
|
|
366
|
+
})
|
|
367
|
+
}, [rowsAll, search, filterValues])
|
|
368
|
+
|
|
369
|
+
// The endpoint returns every entity in one payload, so paging is ours to do. Sorting has
|
|
370
|
+
// to happen here rather than in the table: slicing first would let DataTable sort only the
|
|
371
|
+
// visible page, so page 2 would not hold the rows that sort into it.
|
|
372
|
+
const sortedRows = React.useMemo(() => {
|
|
373
|
+
const sort = sorting[0]
|
|
374
|
+
if (!sort) return matchedRows
|
|
375
|
+
const direction = sort.desc ? -1 : 1
|
|
376
|
+
return [...matchedRows].sort((a, b) => {
|
|
377
|
+
const left = sortValue(a, sort.id)
|
|
378
|
+
const right = sortValue(b, sort.id)
|
|
379
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
380
|
+
return String(left).localeCompare(String(right)) * direction
|
|
381
|
+
}
|
|
382
|
+
return (Number(left) - Number(right)) * direction
|
|
383
|
+
})
|
|
384
|
+
}, [matchedRows, sorting])
|
|
385
|
+
|
|
386
|
+
const total = sortedRows.length
|
|
387
|
+
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE))
|
|
388
|
+
const currentPage = Math.min(page, totalPages)
|
|
389
|
+
const rows = React.useMemo(
|
|
390
|
+
() => sortedRows.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE),
|
|
391
|
+
[sortedRows, currentPage],
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
const pagination = React.useMemo(() => ({
|
|
395
|
+
page: currentPage,
|
|
396
|
+
pageSize: PAGE_SIZE,
|
|
397
|
+
total,
|
|
398
|
+
totalPages,
|
|
399
|
+
onPageChange: setPage,
|
|
400
|
+
}), [currentPage, total, totalPages])
|
|
271
401
|
|
|
272
402
|
const trigger = React.useCallback(
|
|
273
403
|
async (action: 'reindex' | 'purge', entityId: string, opts?: { force?: boolean }) => {
|
|
@@ -416,9 +546,23 @@ export default function QueryIndexesTable() {
|
|
|
416
546
|
setSearch(value)
|
|
417
547
|
setPage(1)
|
|
418
548
|
}}
|
|
549
|
+
filters={filters}
|
|
550
|
+
filterValues={filterValues}
|
|
551
|
+
onFiltersApply={(values) => {
|
|
552
|
+
setFilterValues(values)
|
|
553
|
+
setPage(1)
|
|
554
|
+
}}
|
|
555
|
+
onFiltersClear={() => {
|
|
556
|
+
setFilterValues({})
|
|
557
|
+
setPage(1)
|
|
558
|
+
}}
|
|
419
559
|
sortable
|
|
560
|
+
manualSorting
|
|
420
561
|
sorting={sorting}
|
|
421
|
-
onSortingChange={
|
|
562
|
+
onSortingChange={(next) => {
|
|
563
|
+
setSorting(next)
|
|
564
|
+
setPage(1)
|
|
565
|
+
}}
|
|
422
566
|
perspective={{ tableId: 'query_index.status.list' }}
|
|
423
567
|
rowActions={(row) => {
|
|
424
568
|
const items: Array<{ id: string; label: string; onSelect: () => void; destructive?: boolean }> = [
|
|
@@ -436,6 +580,8 @@ export default function QueryIndexesTable() {
|
|
|
436
580
|
},
|
|
437
581
|
]
|
|
438
582
|
|
|
583
|
+
// Manual vector actions stay available whenever the entity declares `buildSource`,
|
|
584
|
+
// even when auto-indexing is off — a manual reindex is still a valid operation.
|
|
439
585
|
if (row.vectorEnabled) {
|
|
440
586
|
items.push(
|
|
441
587
|
{
|
|
@@ -470,7 +616,7 @@ export default function QueryIndexesTable() {
|
|
|
470
616
|
|
|
471
617
|
return <RowActions items={items} />
|
|
472
618
|
}}
|
|
473
|
-
pagination={
|
|
619
|
+
pagination={pagination}
|
|
474
620
|
isLoading={isLoading}
|
|
475
621
|
/>
|
|
476
622
|
{ConfirmDialogElement}
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"query_index.table.columns.entity": "Entität",
|
|
19
19
|
"query_index.table.columns.fulltext": "Volltext",
|
|
20
20
|
"query_index.table.columns.indexed": "Indexiert",
|
|
21
|
-
"query_index.table.columns.label": "Beschriftung",
|
|
22
21
|
"query_index.table.columns.records": "Datensätze",
|
|
23
22
|
"query_index.table.columns.status": "Status",
|
|
24
23
|
"query_index.table.columns.vector": "Vektor",
|
|
@@ -35,6 +34,10 @@
|
|
|
35
34
|
"query_index.table.errors.subtitle": "Die letzten 100 Fehler für Query- und Vektor-Indexer in diesem Bereich.",
|
|
36
35
|
"query_index.table.errors.title": "Aktuelle Indexer-Fehler",
|
|
37
36
|
"query_index.table.errors.viewDetails": "Technische Details anzeigen",
|
|
37
|
+
"query_index.table.filters.backend": "Backend",
|
|
38
|
+
"query_index.table.filters.backendOptions.customFields": "Benutzerdefinierte Felder",
|
|
39
|
+
"query_index.table.filters.backendOptions.fulltext": "Volltextsuche",
|
|
40
|
+
"query_index.table.filters.backendOptions.vector": "Vektorsuche",
|
|
38
41
|
"query_index.table.logs.columns.message": "Nachricht",
|
|
39
42
|
"query_index.table.logs.columns.source": "Quelle",
|
|
40
43
|
"query_index.table.logs.columns.timestamp": "Zeitstempel",
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
"query_index.table.searchPlaceholder": "Nach Entität filtern",
|
|
49
52
|
"query_index.table.status.failed": "Fehlgeschlagen",
|
|
50
53
|
"query_index.table.status.in_sync": "Synchron",
|
|
54
|
+
"query_index.table.status.not_measured": "Noch nicht gemessen",
|
|
51
55
|
"query_index.table.status.out_of_sync": "Nicht synchron",
|
|
52
56
|
"query_index.table.status.partitionLabel": "Partition {{index}}",
|
|
53
57
|
"query_index.table.status.progress": "{{processed}}/{{total}}",
|
|
@@ -61,7 +65,5 @@
|
|
|
61
65
|
"query_index.table.status.scope.stalled": "Angehalten",
|
|
62
66
|
"query_index.table.status.scopeLabel": "Bereich",
|
|
63
67
|
"query_index.table.status.stalled": "Angehalten",
|
|
64
|
-
"query_index.table.status.vectorLabel": "Vektor",
|
|
65
|
-
"query_index.table.status.vectorValue": "{{count}} / {{total}}",
|
|
66
68
|
"query_index.table.status.withProgress": "{{status}} ({{progress}})"
|
|
67
69
|
}
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"query_index.table.columns.entity": "Entity",
|
|
19
19
|
"query_index.table.columns.fulltext": "Full-text",
|
|
20
20
|
"query_index.table.columns.indexed": "Indexed",
|
|
21
|
-
"query_index.table.columns.label": "Label",
|
|
22
21
|
"query_index.table.columns.records": "Records",
|
|
23
22
|
"query_index.table.columns.status": "Status",
|
|
24
23
|
"query_index.table.columns.vector": "Vector",
|
|
@@ -35,6 +34,10 @@
|
|
|
35
34
|
"query_index.table.errors.subtitle": "Last 100 errors recorded for query index and vector index jobs in this scope.",
|
|
36
35
|
"query_index.table.errors.title": "Recent indexer errors",
|
|
37
36
|
"query_index.table.errors.viewDetails": "View technical details",
|
|
37
|
+
"query_index.table.filters.backend": "Backend",
|
|
38
|
+
"query_index.table.filters.backendOptions.customFields": "Custom fields",
|
|
39
|
+
"query_index.table.filters.backendOptions.fulltext": "Full-text search",
|
|
40
|
+
"query_index.table.filters.backendOptions.vector": "Vector search",
|
|
38
41
|
"query_index.table.logs.columns.message": "Message",
|
|
39
42
|
"query_index.table.logs.columns.source": "Source",
|
|
40
43
|
"query_index.table.logs.columns.timestamp": "Timestamp",
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
"query_index.table.searchPlaceholder": "Filter by entity",
|
|
49
52
|
"query_index.table.status.failed": "Failed",
|
|
50
53
|
"query_index.table.status.in_sync": "In sync",
|
|
54
|
+
"query_index.table.status.not_measured": "Not measured yet",
|
|
51
55
|
"query_index.table.status.out_of_sync": "Out of sync",
|
|
52
56
|
"query_index.table.status.partitionLabel": "Partition {{index}}",
|
|
53
57
|
"query_index.table.status.progress": "{{processed}}/{{total}}",
|
|
@@ -61,7 +65,5 @@
|
|
|
61
65
|
"query_index.table.status.scope.stalled": "Stalled",
|
|
62
66
|
"query_index.table.status.scopeLabel": "Scope",
|
|
63
67
|
"query_index.table.status.stalled": "Stalled",
|
|
64
|
-
"query_index.table.status.vectorLabel": "Vector",
|
|
65
|
-
"query_index.table.status.vectorValue": "{{count}} / {{total}}",
|
|
66
68
|
"query_index.table.status.withProgress": "{{status}} ({{progress}})"
|
|
67
69
|
}
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"query_index.table.columns.entity": "Entidad",
|
|
19
19
|
"query_index.table.columns.fulltext": "Texto completo",
|
|
20
20
|
"query_index.table.columns.indexed": "Indexados",
|
|
21
|
-
"query_index.table.columns.label": "Etiqueta",
|
|
22
21
|
"query_index.table.columns.records": "Registros",
|
|
23
22
|
"query_index.table.columns.status": "Estado",
|
|
24
23
|
"query_index.table.columns.vector": "Vectorial",
|
|
@@ -35,6 +34,10 @@
|
|
|
35
34
|
"query_index.table.errors.subtitle": "Últimos 100 errores registrados para los trabajos del índice de consultas y vector en este ámbito.",
|
|
36
35
|
"query_index.table.errors.title": "Errores recientes del indexador",
|
|
37
36
|
"query_index.table.errors.viewDetails": "Ver detalles técnicos",
|
|
37
|
+
"query_index.table.filters.backend": "Backend",
|
|
38
|
+
"query_index.table.filters.backendOptions.customFields": "Campos personalizados",
|
|
39
|
+
"query_index.table.filters.backendOptions.fulltext": "Búsqueda de texto completo",
|
|
40
|
+
"query_index.table.filters.backendOptions.vector": "Búsqueda vectorial",
|
|
38
41
|
"query_index.table.logs.columns.message": "Mensaje",
|
|
39
42
|
"query_index.table.logs.columns.source": "Origen",
|
|
40
43
|
"query_index.table.logs.columns.timestamp": "Marca de tiempo",
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
"query_index.table.searchPlaceholder": "Filtrar por entidad",
|
|
49
52
|
"query_index.table.status.failed": "Con errores",
|
|
50
53
|
"query_index.table.status.in_sync": "Sincronizado",
|
|
54
|
+
"query_index.table.status.not_measured": "Aún no medido",
|
|
51
55
|
"query_index.table.status.out_of_sync": "Fuera de sincronización",
|
|
52
56
|
"query_index.table.status.partitionLabel": "Partición {{index}}",
|
|
53
57
|
"query_index.table.status.progress": "{{processed}}/{{total}}",
|
|
@@ -61,7 +65,5 @@
|
|
|
61
65
|
"query_index.table.status.scope.stalled": "Detenido",
|
|
62
66
|
"query_index.table.status.scopeLabel": "Ámbito",
|
|
63
67
|
"query_index.table.status.stalled": "Detenido",
|
|
64
|
-
"query_index.table.status.vectorLabel": "Vectorial",
|
|
65
|
-
"query_index.table.status.vectorValue": "{{count}} / {{total}}",
|
|
66
68
|
"query_index.table.status.withProgress": "{{status}} ({{progress}})"
|
|
67
69
|
}
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"query_index.table.columns.entity": "Encja",
|
|
19
19
|
"query_index.table.columns.fulltext": "Pełnotekstowy",
|
|
20
20
|
"query_index.table.columns.indexed": "Zindeksowane",
|
|
21
|
-
"query_index.table.columns.label": "Etykieta",
|
|
22
21
|
"query_index.table.columns.records": "Rekordy",
|
|
23
22
|
"query_index.table.columns.status": "Status",
|
|
24
23
|
"query_index.table.columns.vector": "Wektor",
|
|
@@ -35,6 +34,10 @@
|
|
|
35
34
|
"query_index.table.errors.subtitle": "Ostatnie 100 błędów zapisanych dla zadań indeksu zapytań i wektorowego w tym zakresie.",
|
|
36
35
|
"query_index.table.errors.title": "Ostatnie błędy indeksowania",
|
|
37
36
|
"query_index.table.errors.viewDetails": "Pokaż szczegóły techniczne",
|
|
37
|
+
"query_index.table.filters.backend": "Backend",
|
|
38
|
+
"query_index.table.filters.backendOptions.customFields": "Pola niestandardowe",
|
|
39
|
+
"query_index.table.filters.backendOptions.fulltext": "Wyszukiwanie pełnotekstowe",
|
|
40
|
+
"query_index.table.filters.backendOptions.vector": "Wyszukiwanie wektorowe",
|
|
38
41
|
"query_index.table.logs.columns.message": "Komunikat",
|
|
39
42
|
"query_index.table.logs.columns.source": "Źródło",
|
|
40
43
|
"query_index.table.logs.columns.timestamp": "Znacznik czasu",
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
"query_index.table.searchPlaceholder": "Filtruj według encji",
|
|
49
52
|
"query_index.table.status.failed": "Niepowodzenie",
|
|
50
53
|
"query_index.table.status.in_sync": "Zsynchronizowany",
|
|
54
|
+
"query_index.table.status.not_measured": "Jeszcze nie zmierzono",
|
|
51
55
|
"query_index.table.status.out_of_sync": "Poza synchronizacją",
|
|
52
56
|
"query_index.table.status.partitionLabel": "Partycja {{index}}",
|
|
53
57
|
"query_index.table.status.progress": "{{processed}}/{{total}}",
|
|
@@ -61,7 +65,5 @@
|
|
|
61
65
|
"query_index.table.status.scope.stalled": "Zatrzymane",
|
|
62
66
|
"query_index.table.status.scopeLabel": "Zakres",
|
|
63
67
|
"query_index.table.status.stalled": "Zatrzymany",
|
|
64
|
-
"query_index.table.status.vectorLabel": "Wektor",
|
|
65
|
-
"query_index.table.status.vectorValue": "{{count}} / {{total}}",
|
|
66
68
|
"query_index.table.status.withProgress": "{{status}} ({{progress}})"
|
|
67
69
|
}
|
|
@@ -4,6 +4,7 @@ import { isUniqueViolation } from '@open-mercato/shared/lib/db/pg-errors'
|
|
|
4
4
|
import { buildIndexDocument, type IndexCustomFieldValue } from './document'
|
|
5
5
|
import { replaceSearchTokensForBatch, isSearchDebugEnabled } from './search-tokens'
|
|
6
6
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
7
|
+
import { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'
|
|
7
8
|
|
|
8
9
|
const logger = createLogger('query_index').child({ component: 'reindex-batch' })
|
|
9
10
|
|
|
@@ -228,6 +229,7 @@ export async function upsertIndexBatch(
|
|
|
228
229
|
const basePayloads: IndexRowPayload[] = []
|
|
229
230
|
|
|
230
231
|
const debugEnabled = isSearchDebugEnabled()
|
|
232
|
+
const searchConfig = resolveSearchConfig()
|
|
231
233
|
|
|
232
234
|
for (const row of rows) {
|
|
233
235
|
const recordId = normalizeId(row.id)
|
|
@@ -264,10 +266,15 @@ export async function upsertIndexBatch(
|
|
|
264
266
|
if (!entityRow) return row
|
|
265
267
|
return { ...entityRow, ...row }
|
|
266
268
|
})()
|
|
267
|
-
let doc = buildIndexDocument(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
let doc = buildIndexDocument(
|
|
270
|
+
mergedRow,
|
|
271
|
+
values,
|
|
272
|
+
{
|
|
273
|
+
organizationId: scopeOrg ?? null,
|
|
274
|
+
tenantId: scopeTenant ?? null,
|
|
275
|
+
},
|
|
276
|
+
{ entityType, config: searchConfig },
|
|
277
|
+
)
|
|
271
278
|
let tokenDoc: Record<string, unknown> = doc
|
|
272
279
|
if (typeof options.encryptDoc === 'function') {
|
|
273
280
|
try {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isSearchFieldBlocklisted,
|
|
3
|
+
resolveSearchConfig,
|
|
4
|
+
type SearchConfig,
|
|
5
|
+
} from '@open-mercato/shared/lib/search/config'
|
|
6
|
+
|
|
1
7
|
export type IndexDocumentScope = {
|
|
2
8
|
organizationId?: string | null
|
|
3
9
|
tenantId?: string | null
|
|
@@ -12,6 +18,18 @@ export type IndexCustomFieldValue = {
|
|
|
12
18
|
|
|
13
19
|
export const AGGREGATE_SEARCH_FIELD = 'search_text'
|
|
14
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Controls which fields may contribute text to the `search_text` aggregate.
|
|
23
|
+
*
|
|
24
|
+
* Both properties are optional so existing callers keep working unchanged: an
|
|
25
|
+
* omitted `config` is resolved from the environment, and an omitted `entityType`
|
|
26
|
+
* means only the global blocklist entries apply.
|
|
27
|
+
*/
|
|
28
|
+
export type AggregateSearchOptions = {
|
|
29
|
+
entityType?: string | null
|
|
30
|
+
config?: SearchConfig
|
|
31
|
+
}
|
|
32
|
+
|
|
15
33
|
function normalizeScopeValue(value: string | null | undefined): string | null {
|
|
16
34
|
if (value === undefined || value === null || value === '') return null
|
|
17
35
|
return value
|
|
@@ -30,7 +48,12 @@ function normalizeValue(value: unknown): unknown {
|
|
|
30
48
|
return value
|
|
31
49
|
}
|
|
32
50
|
|
|
33
|
-
function collectAggregateSearchValues(
|
|
51
|
+
function collectAggregateSearchValues(
|
|
52
|
+
field: string,
|
|
53
|
+
value: unknown,
|
|
54
|
+
entityType: string | null,
|
|
55
|
+
config: SearchConfig,
|
|
56
|
+
): string[] {
|
|
34
57
|
const lower = field.toLowerCase()
|
|
35
58
|
if (
|
|
36
59
|
lower === AGGREGATE_SEARCH_FIELD
|
|
@@ -43,6 +66,8 @@ function collectAggregateSearchValues(field: string, value: unknown): string[] {
|
|
|
43
66
|
return []
|
|
44
67
|
}
|
|
45
68
|
|
|
69
|
+
if (isSearchFieldBlocklisted(field, entityType, config)) return []
|
|
70
|
+
|
|
46
71
|
if (typeof value === 'string') {
|
|
47
72
|
const trimmed = value.trim()
|
|
48
73
|
return trimmed.length > 0 ? [trimmed] : []
|
|
@@ -58,12 +83,17 @@ function collectAggregateSearchValues(field: string, value: unknown): string[] {
|
|
|
58
83
|
return []
|
|
59
84
|
}
|
|
60
85
|
|
|
61
|
-
export function attachAggregateSearchField(
|
|
86
|
+
export function attachAggregateSearchField(
|
|
87
|
+
doc: Record<string, unknown>,
|
|
88
|
+
options: AggregateSearchOptions = {},
|
|
89
|
+
): Record<string, unknown> {
|
|
90
|
+
const config = options.config ?? resolveSearchConfig()
|
|
91
|
+
const entityType = options.entityType ?? null
|
|
62
92
|
const parts: string[] = []
|
|
63
93
|
const seen = new Set<string>()
|
|
64
94
|
|
|
65
95
|
for (const [field, value] of Object.entries(doc)) {
|
|
66
|
-
const values = collectAggregateSearchValues(field, value)
|
|
96
|
+
const values = collectAggregateSearchValues(field, value, entityType, config)
|
|
67
97
|
for (const entry of values) {
|
|
68
98
|
const key = entry.toLowerCase()
|
|
69
99
|
if (seen.has(key)) continue
|
|
@@ -83,6 +113,7 @@ export function buildIndexDocument(
|
|
|
83
113
|
baseRow: Record<string, unknown>,
|
|
84
114
|
customFieldValues: Iterable<IndexCustomFieldValue> = [],
|
|
85
115
|
scope: IndexDocumentScope = {},
|
|
116
|
+
options: AggregateSearchOptions = {},
|
|
86
117
|
): Record<string, unknown> {
|
|
87
118
|
const doc: Record<string, unknown> = {}
|
|
88
119
|
for (const [key, value] of Object.entries(baseRow)) {
|
|
@@ -123,5 +154,5 @@ export function buildIndexDocument(
|
|
|
123
154
|
}
|
|
124
155
|
}
|
|
125
156
|
|
|
126
|
-
return attachAggregateSearchField(doc)
|
|
157
|
+
return attachAggregateSearchField(doc, options)
|
|
127
158
|
}
|