@resistdesign/voltra 3.0.0-alpha.35 → 3.0.0-alpha.37
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/api/Indexing/ddb/Types.d.ts +40 -0
- package/api/Indexing/structured/StructuredDdb.d.ts +19 -1
- package/api/Indexing/structured/StructuredDdbBackend.d.ts +4 -1
- package/api/Indexing/structured/StructuredInMemoryBackend.d.ts +6 -0
- package/api/Indexing/structured/StructuredInMemoryIndex.d.ts +3 -0
- package/api/Indexing/structured/StructuredStringLike.d.ts +31 -2
- package/api/Indexing/structured/StructuredWriter.d.ts +19 -6
- package/api/ORM/TypeInfoORMService.d.ts +193 -8
- package/api/ORM/indexing/criteriaToStructuredWhere.d.ts +6 -1
- package/api/index.js +624 -161
- package/app/index.js +4 -4
- package/app/utils/Route.d.ts +16 -1
- package/app/utils/RouteHistory.d.ts +1 -0
- package/build/index.js +1 -1
- package/{chunk-BKRJZXWX.js → chunk-2MOLWZMQ.js} +1 -1
- package/{chunk-X3NHBZUT.js → chunk-DT6WWJUI.js} +64 -22
- package/{chunk-GYWRAW3Y.js → chunk-WNFRDIBW.js} +56 -13
- package/{chunk-WTD5BBJP.js → chunk-YCTVEW2I.js} +10 -2
- package/common/Routing.d.ts +14 -0
- package/common/SearchTypes.d.ts +0 -21
- package/common/index.js +2 -2
- package/native/index.js +6 -5
- package/package.json +1 -1
- package/web/index.js +4 -4
package/api/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ITEM_RELATIONSHIP_DAC_RESOURCE_NAME, ComparisonOperators } from '../chunk-7AMEFPPP.js';
|
|
2
|
-
import { getNoErrorDescriptor, getErrorDescriptor, ERROR_MESSAGE_CONSTANTS, validateTypeOperationAllowed, getValidityValue, validateTypeInfoValue, validateTypeInfoFieldValue } from '../chunk-
|
|
3
|
-
import { mergeStringPaths, getPathString, getPathArray } from '../chunk-
|
|
2
|
+
import { getNoErrorDescriptor, getErrorDescriptor, ERROR_MESSAGE_CONSTANTS, validateTypeOperationAllowed, getValidityValue, validateTypeInfoValue, validateTypeInfoFieldValue } from '../chunk-YCTVEW2I.js';
|
|
3
|
+
import { mergeStringPaths, getPathString, getPathArray } from '../chunk-WNFRDIBW.js';
|
|
4
4
|
import '../chunk-I2KLQ2HA.js';
|
|
5
|
-
import { QueryCommand, GetItemCommand, BatchGetItemCommand, BatchWriteItemCommand, DynamoDBClient,
|
|
5
|
+
import { QueryCommand, PutItemCommand, ConditionalCheckFailedException, GetItemCommand, BatchGetItemCommand, BatchWriteItemCommand, DynamoDBClient, UpdateItemCommand, DeleteItemCommand, ScanCommand } from '@aws-sdk/client-dynamodb';
|
|
6
6
|
import { marshall, unmarshall } from '@aws-sdk/util-dynamodb';
|
|
7
7
|
import { S3, PutObjectCommand, HeadObjectCommand, CopyObjectCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
|
|
8
8
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
@@ -1171,6 +1171,25 @@ var createAwsSdkV3DynamoClient = (client) => ({
|
|
|
1171
1171
|
Item: response.Item ? fromAwsKey(response.Item) : void 0
|
|
1172
1172
|
};
|
|
1173
1173
|
},
|
|
1174
|
+
putItem: async (input) => {
|
|
1175
|
+
try {
|
|
1176
|
+
await client.send(
|
|
1177
|
+
new PutItemCommand({
|
|
1178
|
+
TableName: input.TableName,
|
|
1179
|
+
Item: toAwsKey(input.Item),
|
|
1180
|
+
ConditionExpression: input.ConditionExpression,
|
|
1181
|
+
ExpressionAttributeNames: input.ExpressionAttributeNames,
|
|
1182
|
+
ExpressionAttributeValues: input.ExpressionAttributeValues ? toAwsKey(input.ExpressionAttributeValues) : void 0
|
|
1183
|
+
})
|
|
1184
|
+
);
|
|
1185
|
+
return {};
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
if (error instanceof ConditionalCheckFailedException) {
|
|
1188
|
+
return { conditionFailed: true };
|
|
1189
|
+
}
|
|
1190
|
+
throw error;
|
|
1191
|
+
}
|
|
1192
|
+
},
|
|
1174
1193
|
query: async (input) => {
|
|
1175
1194
|
const response = await client.send(
|
|
1176
1195
|
new QueryCommand({
|
|
@@ -3324,18 +3343,53 @@ async function structuredHandler(event) {
|
|
|
3324
3343
|
}
|
|
3325
3344
|
|
|
3326
3345
|
// src/api/Indexing/structured/StructuredStringLike.ts
|
|
3327
|
-
var
|
|
3328
|
-
var
|
|
3329
|
-
var
|
|
3346
|
+
var DEFAULT_MAX_INDEXED_STRING_LENGTH = 128;
|
|
3347
|
+
var DEFAULT_MAX_TOKENS_PER_VALUE = 256;
|
|
3348
|
+
var DEFAULT_MIN_NGRAM_SIZE = 1;
|
|
3349
|
+
var DEFAULT_MAX_NGRAM_SIZE = 3;
|
|
3330
3350
|
var LIKE_WILDCARD_REGEX = /[%_]/;
|
|
3331
3351
|
var NORMALIZED_WHITESPACE_REGEX = /\s+/g;
|
|
3332
3352
|
var STRUCTURED_STRING_CONTAINS_TOKEN_PREFIX = "__str__:";
|
|
3353
|
+
var DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG = {
|
|
3354
|
+
minNgramSize: DEFAULT_MIN_NGRAM_SIZE,
|
|
3355
|
+
maxNgramSize: DEFAULT_MAX_NGRAM_SIZE,
|
|
3356
|
+
maxIndexedStringLength: DEFAULT_MAX_INDEXED_STRING_LENGTH,
|
|
3357
|
+
maxTokensPerValue: DEFAULT_MAX_TOKENS_PER_VALUE
|
|
3358
|
+
};
|
|
3359
|
+
var clampInteger = (value, fallback, minimum = 1) => {
|
|
3360
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
3361
|
+
return fallback;
|
|
3362
|
+
}
|
|
3363
|
+
return Math.max(minimum, Math.floor(value));
|
|
3364
|
+
};
|
|
3365
|
+
var resolveStructuredStringTokenizerConfig = (config) => {
|
|
3366
|
+
const minNgramSize = clampInteger(
|
|
3367
|
+
config?.minNgramSize,
|
|
3368
|
+
DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.minNgramSize
|
|
3369
|
+
);
|
|
3370
|
+
const maxNgramSize = clampInteger(
|
|
3371
|
+
config?.maxNgramSize,
|
|
3372
|
+
DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.maxNgramSize
|
|
3373
|
+
);
|
|
3374
|
+
return {
|
|
3375
|
+
minNgramSize: Math.min(minNgramSize, maxNgramSize),
|
|
3376
|
+
maxNgramSize: Math.max(minNgramSize, maxNgramSize),
|
|
3377
|
+
maxIndexedStringLength: clampInteger(
|
|
3378
|
+
config?.maxIndexedStringLength,
|
|
3379
|
+
DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.maxIndexedStringLength
|
|
3380
|
+
),
|
|
3381
|
+
maxTokensPerValue: clampInteger(
|
|
3382
|
+
config?.maxTokensPerValue,
|
|
3383
|
+
DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.maxTokensPerValue
|
|
3384
|
+
)
|
|
3385
|
+
};
|
|
3386
|
+
};
|
|
3333
3387
|
var normalizeStructuredLikeString = (value) => value.toLowerCase().trim().replace(NORMALIZED_WHITESPACE_REGEX, " ");
|
|
3334
|
-
var toNgrams = (normalized) => {
|
|
3388
|
+
var toNgrams = (normalized, config) => {
|
|
3335
3389
|
const tokens = [];
|
|
3336
3390
|
const seen = /* @__PURE__ */ new Set();
|
|
3337
|
-
const limited = normalized.slice(0,
|
|
3338
|
-
for (let size =
|
|
3391
|
+
const limited = normalized.slice(0, config.maxIndexedStringLength);
|
|
3392
|
+
for (let size = config.minNgramSize; size <= config.maxNgramSize; size += 1) {
|
|
3339
3393
|
if (limited.length < size) {
|
|
3340
3394
|
break;
|
|
3341
3395
|
}
|
|
@@ -3344,7 +3398,7 @@ var toNgrams = (normalized) => {
|
|
|
3344
3398
|
if (!seen.has(token)) {
|
|
3345
3399
|
seen.add(token);
|
|
3346
3400
|
tokens.push(token);
|
|
3347
|
-
if (tokens.length >=
|
|
3401
|
+
if (tokens.length >= config.maxTokensPerValue) {
|
|
3348
3402
|
return tokens;
|
|
3349
3403
|
}
|
|
3350
3404
|
}
|
|
@@ -3353,26 +3407,28 @@ var toNgrams = (normalized) => {
|
|
|
3353
3407
|
return tokens;
|
|
3354
3408
|
};
|
|
3355
3409
|
var toContainsToken = (token) => `${STRUCTURED_STRING_CONTAINS_TOKEN_PREFIX}${token}`;
|
|
3356
|
-
var buildStructuredStringContainsTokens = (value) => {
|
|
3410
|
+
var buildStructuredStringContainsTokens = (value, tokenizerConfig) => {
|
|
3411
|
+
const config = resolveStructuredStringTokenizerConfig(tokenizerConfig);
|
|
3357
3412
|
const normalized = normalizeStructuredLikeString(value);
|
|
3358
3413
|
if (!normalized.length) {
|
|
3359
3414
|
return [];
|
|
3360
3415
|
}
|
|
3361
|
-
return toNgrams(normalized).map(toContainsToken);
|
|
3416
|
+
return toNgrams(normalized, config).map(toContainsToken);
|
|
3362
3417
|
};
|
|
3363
|
-
var buildStructuredLikePatternTokens = (value) => {
|
|
3418
|
+
var buildStructuredLikePatternTokens = (value, tokenizerConfig) => {
|
|
3419
|
+
const config = resolveStructuredStringTokenizerConfig(tokenizerConfig);
|
|
3364
3420
|
const normalized = normalizeStructuredLikeString(value);
|
|
3365
3421
|
const pattern = LIKE_WILDCARD_REGEX.test(normalized) ? normalized : `%${normalized}%`;
|
|
3366
3422
|
const literalSegments = pattern.split(LIKE_WILDCARD_REGEX).map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
3367
3423
|
const tokens = [];
|
|
3368
3424
|
const seen = /* @__PURE__ */ new Set();
|
|
3369
3425
|
for (const segment of literalSegments) {
|
|
3370
|
-
for (const token of toNgrams(segment)) {
|
|
3426
|
+
for (const token of toNgrams(segment, config)) {
|
|
3371
3427
|
const containsToken = toContainsToken(token);
|
|
3372
3428
|
if (!seen.has(containsToken)) {
|
|
3373
3429
|
seen.add(containsToken);
|
|
3374
3430
|
tokens.push(containsToken);
|
|
3375
|
-
if (tokens.length >=
|
|
3431
|
+
if (tokens.length >= config.maxTokensPerValue) {
|
|
3376
3432
|
return tokens;
|
|
3377
3433
|
}
|
|
3378
3434
|
}
|
|
@@ -3505,6 +3561,9 @@ function addPosting(index, field, value, docId) {
|
|
|
3505
3561
|
index.set(field, fieldIndex);
|
|
3506
3562
|
}
|
|
3507
3563
|
var StructuredInMemoryIndex = class {
|
|
3564
|
+
constructor(tokenizer) {
|
|
3565
|
+
this.tokenizer = tokenizer;
|
|
3566
|
+
}
|
|
3508
3567
|
eqIndex = /* @__PURE__ */ new Map();
|
|
3509
3568
|
containsIndex = /* @__PURE__ */ new Map();
|
|
3510
3569
|
rangeIndex = /* @__PURE__ */ new Map();
|
|
@@ -3525,7 +3584,10 @@ var StructuredInMemoryIndex = class {
|
|
|
3525
3584
|
}
|
|
3526
3585
|
addPosting(this.eqIndex, field, value, docId);
|
|
3527
3586
|
if (typeof value === "string") {
|
|
3528
|
-
for (const token of buildStructuredStringContainsTokens(
|
|
3587
|
+
for (const token of buildStructuredStringContainsTokens(
|
|
3588
|
+
value,
|
|
3589
|
+
this.tokenizer
|
|
3590
|
+
)) {
|
|
3529
3591
|
addPosting(this.containsIndex, field, token, docId);
|
|
3530
3592
|
}
|
|
3531
3593
|
}
|
|
@@ -3612,10 +3674,17 @@ var normalizeFields = (fields) => {
|
|
|
3612
3674
|
return normalized;
|
|
3613
3675
|
};
|
|
3614
3676
|
var StructuredInMemoryBackend = class {
|
|
3677
|
+
/**
|
|
3678
|
+
* @param tokenizer Optional tokenizer overrides for structured string contains behavior.
|
|
3679
|
+
*/
|
|
3680
|
+
constructor(tokenizer) {
|
|
3681
|
+
this.tokenizer = tokenizer;
|
|
3682
|
+
this.index = new StructuredInMemoryIndex(tokenizer);
|
|
3683
|
+
}
|
|
3615
3684
|
docFields = /* @__PURE__ */ new Map();
|
|
3616
|
-
index
|
|
3685
|
+
index;
|
|
3617
3686
|
rebuildIndex() {
|
|
3618
|
-
const nextIndex = new StructuredInMemoryIndex();
|
|
3687
|
+
const nextIndex = new StructuredInMemoryIndex(this.tokenizer);
|
|
3619
3688
|
for (const [docId, fields] of this.docFields.entries()) {
|
|
3620
3689
|
nextIndex.addDocument(docId, fields);
|
|
3621
3690
|
}
|
|
@@ -3705,7 +3774,8 @@ var structuredRangeIndexSchema = {
|
|
|
3705
3774
|
};
|
|
3706
3775
|
var structuredDocFieldsSchema = {
|
|
3707
3776
|
partitionKey: "docId",
|
|
3708
|
-
fieldsAttribute: "fields"
|
|
3777
|
+
fieldsAttribute: "fields",
|
|
3778
|
+
versionAttribute: "version"
|
|
3709
3779
|
};
|
|
3710
3780
|
function serializeStructuredValue(value) {
|
|
3711
3781
|
if (value === null) {
|
|
@@ -3745,8 +3815,8 @@ function buildStructuredRangeItem(field, value, docId) {
|
|
|
3745
3815
|
docId
|
|
3746
3816
|
};
|
|
3747
3817
|
}
|
|
3748
|
-
function buildStructuredDocFieldsItem(docId, fields) {
|
|
3749
|
-
return { docId, fields };
|
|
3818
|
+
function buildStructuredDocFieldsItem(docId, fields, version) {
|
|
3819
|
+
return { docId, fields, version };
|
|
3750
3820
|
}
|
|
3751
3821
|
|
|
3752
3822
|
// src/api/Indexing/structured/StructuredWriter.ts
|
|
@@ -3762,7 +3832,7 @@ function normalizeFields2(fields) {
|
|
|
3762
3832
|
}
|
|
3763
3833
|
return normalized;
|
|
3764
3834
|
}
|
|
3765
|
-
function buildTermEntries(docId, fields) {
|
|
3835
|
+
function buildTermEntries(docId, fields, options) {
|
|
3766
3836
|
const entries = [];
|
|
3767
3837
|
for (const [field, value] of Object.entries(fields)) {
|
|
3768
3838
|
if (Array.isArray(value)) {
|
|
@@ -3773,7 +3843,10 @@ function buildTermEntries(docId, fields) {
|
|
|
3773
3843
|
} else {
|
|
3774
3844
|
entries.push(buildStructuredTermItem(field, value, "eq", docId));
|
|
3775
3845
|
if (typeof value === "string") {
|
|
3776
|
-
for (const token of buildStructuredStringContainsTokens(
|
|
3846
|
+
for (const token of buildStructuredStringContainsTokens(
|
|
3847
|
+
value,
|
|
3848
|
+
options.tokenizer
|
|
3849
|
+
)) {
|
|
3777
3850
|
entries.push(buildStructuredTermItem(field, token, "contains", docId));
|
|
3778
3851
|
}
|
|
3779
3852
|
}
|
|
@@ -3830,9 +3903,11 @@ var StructuredDdbWriter = class {
|
|
|
3830
3903
|
/**
|
|
3831
3904
|
* @param dependencies Writer dependencies for persistence.
|
|
3832
3905
|
*/
|
|
3833
|
-
constructor(dependencies3) {
|
|
3906
|
+
constructor(dependencies3, options = {}) {
|
|
3834
3907
|
this.dependencies = dependencies3;
|
|
3908
|
+
this.options = options;
|
|
3835
3909
|
}
|
|
3910
|
+
options;
|
|
3836
3911
|
/**
|
|
3837
3912
|
* Write structured fields for a document, diffing term/range entries.
|
|
3838
3913
|
* @param docId Document id to write.
|
|
@@ -3841,29 +3916,52 @@ var StructuredDdbWriter = class {
|
|
|
3841
3916
|
*/
|
|
3842
3917
|
async write(docId, fields) {
|
|
3843
3918
|
const normalized = normalizeFields2(fields);
|
|
3844
|
-
const
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
}
|
|
3855
|
-
if (rangeDiff.toDelete.length > 0) {
|
|
3856
|
-
await this.dependencies.deleteRangeEntries(
|
|
3857
|
-
toRangeKeys(rangeDiff.toDelete)
|
|
3919
|
+
const maxRetries = this.options.maxConcurrentWriteRetries ?? 8;
|
|
3920
|
+
let attempts = 0;
|
|
3921
|
+
while (attempts <= maxRetries) {
|
|
3922
|
+
const previousState = await this.dependencies.loadDocFieldsState(docId);
|
|
3923
|
+
const previousNormalized = previousState ? normalizeFields2(previousState.fields) : {};
|
|
3924
|
+
const expectedVersion = previousState?.version;
|
|
3925
|
+
const previousTerms = buildTermEntries(
|
|
3926
|
+
docId,
|
|
3927
|
+
previousNormalized,
|
|
3928
|
+
this.options
|
|
3858
3929
|
);
|
|
3930
|
+
const nextTerms = buildTermEntries(docId, normalized, this.options);
|
|
3931
|
+
const previousRanges = buildRangeEntries(docId, previousNormalized);
|
|
3932
|
+
const nextRanges = buildRangeEntries(docId, normalized);
|
|
3933
|
+
const termDiff = diffEntries(previousTerms, nextTerms, termEntryKey);
|
|
3934
|
+
const rangeDiff = diffEntries(previousRanges, nextRanges, rangeEntryKey);
|
|
3935
|
+
const noDiff = termDiff.toAdd.length === 0 && termDiff.toDelete.length === 0 && rangeDiff.toAdd.length === 0 && rangeDiff.toDelete.length === 0;
|
|
3936
|
+
if (noDiff) {
|
|
3937
|
+
return;
|
|
3938
|
+
}
|
|
3939
|
+
const swapped = await this.dependencies.putDocFieldsIfVersion(
|
|
3940
|
+
docId,
|
|
3941
|
+
expectedVersion,
|
|
3942
|
+
normalized
|
|
3943
|
+
);
|
|
3944
|
+
if (!swapped) {
|
|
3945
|
+
attempts += 1;
|
|
3946
|
+
continue;
|
|
3947
|
+
}
|
|
3948
|
+
if (termDiff.toDelete.length > 0) {
|
|
3949
|
+
await this.dependencies.deleteTermEntries(toTermKeys(termDiff.toDelete));
|
|
3950
|
+
}
|
|
3951
|
+
if (rangeDiff.toDelete.length > 0) {
|
|
3952
|
+
await this.dependencies.deleteRangeEntries(
|
|
3953
|
+
toRangeKeys(rangeDiff.toDelete)
|
|
3954
|
+
);
|
|
3955
|
+
}
|
|
3956
|
+
if (termDiff.toAdd.length > 0) {
|
|
3957
|
+
await this.dependencies.putTermEntries(termDiff.toAdd);
|
|
3958
|
+
}
|
|
3959
|
+
if (rangeDiff.toAdd.length > 0) {
|
|
3960
|
+
await this.dependencies.putRangeEntries(rangeDiff.toAdd);
|
|
3961
|
+
}
|
|
3962
|
+
return;
|
|
3859
3963
|
}
|
|
3860
|
-
|
|
3861
|
-
await this.dependencies.putTermEntries(termDiff.toAdd);
|
|
3862
|
-
}
|
|
3863
|
-
if (rangeDiff.toAdd.length > 0) {
|
|
3864
|
-
await this.dependencies.putRangeEntries(rangeDiff.toAdd);
|
|
3865
|
-
}
|
|
3866
|
-
await this.dependencies.putDocFields(docId, normalized);
|
|
3964
|
+
throw new Error("Structured writer concurrent write retries exceeded.");
|
|
3867
3965
|
}
|
|
3868
3966
|
};
|
|
3869
3967
|
|
|
@@ -4052,7 +4150,7 @@ var StructuredDdbWriterDependencies = class {
|
|
|
4052
4150
|
this.rangeTableName = config.tables.rangeIndex;
|
|
4053
4151
|
this.docFieldsTableName = config.tables.docFields;
|
|
4054
4152
|
}
|
|
4055
|
-
async
|
|
4153
|
+
async loadDocFieldsState(docId) {
|
|
4056
4154
|
const response = await this.client.getItem({
|
|
4057
4155
|
TableName: this.docFieldsTableName,
|
|
4058
4156
|
Key: { [structuredDocFieldsSchema.partitionKey]: docId }
|
|
@@ -4061,22 +4159,40 @@ var StructuredDdbWriterDependencies = class {
|
|
|
4061
4159
|
return void 0;
|
|
4062
4160
|
}
|
|
4063
4161
|
const item = response.Item;
|
|
4064
|
-
|
|
4162
|
+
if (!item.fields) {
|
|
4163
|
+
return void 0;
|
|
4164
|
+
}
|
|
4165
|
+
return {
|
|
4166
|
+
fields: item.fields,
|
|
4167
|
+
version: typeof item.version === "number" && Number.isFinite(item.version) ? item.version : 0
|
|
4168
|
+
};
|
|
4065
4169
|
}
|
|
4066
|
-
async
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
}
|
|
4075
|
-
}
|
|
4170
|
+
async putDocFieldsIfVersion(docId, expectedVersion, fields) {
|
|
4171
|
+
if (typeof expectedVersion === "undefined") {
|
|
4172
|
+
const createResult = await this.client.putItem({
|
|
4173
|
+
TableName: this.docFieldsTableName,
|
|
4174
|
+
Item: buildStructuredDocFieldsItem(docId, fields, 1),
|
|
4175
|
+
ConditionExpression: "attribute_not_exists(#docId)",
|
|
4176
|
+
ExpressionAttributeNames: {
|
|
4177
|
+
"#docId": structuredDocFieldsSchema.partitionKey
|
|
4076
4178
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4179
|
+
});
|
|
4180
|
+
return !createResult.conditionFailed;
|
|
4181
|
+
}
|
|
4182
|
+
const nextVersion = expectedVersion + 1;
|
|
4183
|
+
const updateResult = await this.client.putItem({
|
|
4184
|
+
TableName: this.docFieldsTableName,
|
|
4185
|
+
Item: buildStructuredDocFieldsItem(docId, fields, nextVersion),
|
|
4186
|
+
ConditionExpression: "(#version = :expectedVersion) OR (attribute_not_exists(#version) AND :expectedVersion = :zero)",
|
|
4187
|
+
ExpressionAttributeNames: {
|
|
4188
|
+
"#version": structuredDocFieldsSchema.versionAttribute
|
|
4189
|
+
},
|
|
4190
|
+
ExpressionAttributeValues: {
|
|
4191
|
+
":expectedVersion": expectedVersion,
|
|
4192
|
+
":zero": 0
|
|
4193
|
+
}
|
|
4194
|
+
});
|
|
4195
|
+
return !updateResult.conditionFailed;
|
|
4080
4196
|
}
|
|
4081
4197
|
async putTermEntries(entries) {
|
|
4082
4198
|
await this.batchWrite(
|
|
@@ -4154,7 +4270,11 @@ var StructuredDdbBackend = class {
|
|
|
4154
4270
|
constructor(config) {
|
|
4155
4271
|
this.reader = new StructuredDdbReader(config);
|
|
4156
4272
|
this.writer = new StructuredDdbWriter(
|
|
4157
|
-
new StructuredDdbWriterDependencies(config)
|
|
4273
|
+
new StructuredDdbWriterDependencies(config),
|
|
4274
|
+
{
|
|
4275
|
+
...config.writerOptions,
|
|
4276
|
+
tokenizer: config.writerOptions?.tokenizer ?? config.tokenizer
|
|
4277
|
+
}
|
|
4158
4278
|
);
|
|
4159
4279
|
}
|
|
4160
4280
|
};
|
|
@@ -4354,7 +4474,7 @@ var compareArray = (fieldCriterion, fieldValue) => {
|
|
|
4354
4474
|
}
|
|
4355
4475
|
};
|
|
4356
4476
|
var getFilterTypeInfoDataItemsBySearchCriteria = (searchCriteria, items, typeInfoName, typeInfoMap) => {
|
|
4357
|
-
const { fields = {} } = {};
|
|
4477
|
+
const { fields = {} } = typeInfoMap?.[typeInfoName] || {};
|
|
4358
4478
|
const { logicalOperator = "AND" /* AND */, fieldCriteria = [] } = searchCriteria;
|
|
4359
4479
|
const filteredItems = [];
|
|
4360
4480
|
for (const currentItem of items) {
|
|
@@ -7006,7 +7126,7 @@ var buildTerm = (fieldName, mode, value) => ({
|
|
|
7006
7126
|
mode,
|
|
7007
7127
|
value
|
|
7008
7128
|
});
|
|
7009
|
-
var buildWhereForCriterion = (criterion) => {
|
|
7129
|
+
var buildWhereForCriterion = (criterion, tokenizer) => {
|
|
7010
7130
|
const { fieldName, operator = "EQUALS" /* EQUALS */, value } = criterion;
|
|
7011
7131
|
switch (operator) {
|
|
7012
7132
|
case "EQUALS" /* EQUALS */:
|
|
@@ -7017,7 +7137,7 @@ var buildWhereForCriterion = (criterion) => {
|
|
|
7017
7137
|
if (typeof value !== "string") {
|
|
7018
7138
|
return buildTerm(fieldName, "contains", value);
|
|
7019
7139
|
}
|
|
7020
|
-
const tokens = buildStructuredLikePatternTokens(value);
|
|
7140
|
+
const tokens = buildStructuredLikePatternTokens(value, tokenizer);
|
|
7021
7141
|
const tokenClauses = tokens.map(
|
|
7022
7142
|
(token) => buildTerm(fieldName, "contains", token)
|
|
7023
7143
|
);
|
|
@@ -7063,12 +7183,12 @@ var buildWhereForCriterion = (criterion) => {
|
|
|
7063
7183
|
};
|
|
7064
7184
|
}
|
|
7065
7185
|
};
|
|
7066
|
-
var criteriaToStructuredWhere = (criteria) => {
|
|
7186
|
+
var criteriaToStructuredWhere = (criteria, tokenizer) => {
|
|
7067
7187
|
if (!criteria || !Array.isArray(criteria.fieldCriteria)) {
|
|
7068
7188
|
return void 0;
|
|
7069
7189
|
}
|
|
7070
7190
|
const clauses = criteria.fieldCriteria.map(
|
|
7071
|
-
(criterion) => buildWhereForCriterion(criterion)
|
|
7191
|
+
(criterion) => buildWhereForCriterion(criterion, tokenizer)
|
|
7072
7192
|
);
|
|
7073
7193
|
if (clauses.length === 0) {
|
|
7074
7194
|
return void 0;
|
|
@@ -7115,6 +7235,37 @@ var TypeInfoORMService = class {
|
|
|
7115
7235
|
}
|
|
7116
7236
|
dacRoleCache = {};
|
|
7117
7237
|
indexingRelationshipDriver;
|
|
7238
|
+
/**
|
|
7239
|
+
* Emit list routing decision observability events without impacting runtime behavior.
|
|
7240
|
+
*/
|
|
7241
|
+
emitListRoutingDecision = (typeName, path, reason, criteriaCount) => {
|
|
7242
|
+
const hook = this.config.indexing?.observability?.onListRoutingDecision;
|
|
7243
|
+
if (!hook) {
|
|
7244
|
+
return;
|
|
7245
|
+
}
|
|
7246
|
+
try {
|
|
7247
|
+
hook({
|
|
7248
|
+
typeName,
|
|
7249
|
+
path,
|
|
7250
|
+
reason,
|
|
7251
|
+
criteriaCount
|
|
7252
|
+
});
|
|
7253
|
+
} catch (_error) {
|
|
7254
|
+
}
|
|
7255
|
+
};
|
|
7256
|
+
/**
|
|
7257
|
+
* Emit structured index write observability events without impacting behavior.
|
|
7258
|
+
*/
|
|
7259
|
+
emitStructuredIndexWrite = (typeName, docId, action, indexedFieldCount) => {
|
|
7260
|
+
const hook = this.config.indexing?.observability?.onStructuredIndexWrite;
|
|
7261
|
+
if (!hook) {
|
|
7262
|
+
return;
|
|
7263
|
+
}
|
|
7264
|
+
try {
|
|
7265
|
+
hook({ typeName, docId, action, indexedFieldCount });
|
|
7266
|
+
} catch (_error) {
|
|
7267
|
+
}
|
|
7268
|
+
};
|
|
7118
7269
|
resolveAccessingRole = async (context) => {
|
|
7119
7270
|
const { useDAC } = this.config;
|
|
7120
7271
|
if (!useDAC) {
|
|
@@ -7374,14 +7525,278 @@ var TypeInfoORMService = class {
|
|
|
7374
7525
|
return typeInfo;
|
|
7375
7526
|
};
|
|
7376
7527
|
/**
|
|
7377
|
-
* @returns Resolved index field
|
|
7528
|
+
* @returns Resolved full-text index field names.
|
|
7378
7529
|
*/
|
|
7379
|
-
|
|
7530
|
+
resolveFullTextIndexFields = (typeName, override) => {
|
|
7380
7531
|
if (override) {
|
|
7381
|
-
return override;
|
|
7532
|
+
return [override];
|
|
7533
|
+
}
|
|
7534
|
+
const defaults = this.config.indexing?.fullText?.defaultIndexFieldByType?.[typeName];
|
|
7535
|
+
if (typeof defaults === "string") {
|
|
7536
|
+
return [defaults];
|
|
7537
|
+
}
|
|
7538
|
+
if (!Array.isArray(defaults)) {
|
|
7539
|
+
return [];
|
|
7540
|
+
}
|
|
7541
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7542
|
+
const fields = [];
|
|
7543
|
+
for (const field of defaults) {
|
|
7544
|
+
if (typeof field !== "string") {
|
|
7545
|
+
continue;
|
|
7546
|
+
}
|
|
7547
|
+
const trimmed = field.trim();
|
|
7548
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
7549
|
+
continue;
|
|
7550
|
+
}
|
|
7551
|
+
seen.add(trimmed);
|
|
7552
|
+
fields.push(trimmed);
|
|
7553
|
+
}
|
|
7554
|
+
return fields;
|
|
7555
|
+
};
|
|
7556
|
+
/**
|
|
7557
|
+
* @returns True when the operator maps to full-text search.
|
|
7558
|
+
*/
|
|
7559
|
+
isFullTextSearchOperator = (operator) => operator === "LIKE" /* LIKE */ || operator === "CONTAINS" /* CONTAINS */ || operator === "STARTS_WITH" /* STARTS_WITH */;
|
|
7560
|
+
/**
|
|
7561
|
+
* @returns Explicitly indexed structured field names for a type.
|
|
7562
|
+
*/
|
|
7563
|
+
resolveStructuredIndexedFields = (typeName) => {
|
|
7564
|
+
const configured = this.config.indexing?.structured?.indexedFieldsByType?.[typeName];
|
|
7565
|
+
if (!Array.isArray(configured)) {
|
|
7566
|
+
return /* @__PURE__ */ new Set();
|
|
7567
|
+
}
|
|
7568
|
+
const fields = /* @__PURE__ */ new Set();
|
|
7569
|
+
for (const field of configured) {
|
|
7570
|
+
if (typeof field !== "string") {
|
|
7571
|
+
continue;
|
|
7572
|
+
}
|
|
7573
|
+
const trimmed = field.trim();
|
|
7574
|
+
if (trimmed) {
|
|
7575
|
+
fields.add(trimmed);
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
return fields;
|
|
7579
|
+
};
|
|
7580
|
+
/**
|
|
7581
|
+
* @returns True when the field type and operator can be served by structured indexing.
|
|
7582
|
+
*/
|
|
7583
|
+
isStructuredOperatorSupportedForField = (field, operator) => {
|
|
7584
|
+
const { array: isArray = false, type } = field;
|
|
7585
|
+
if (isArray) {
|
|
7586
|
+
return operator === "CONTAINS" /* CONTAINS */;
|
|
7587
|
+
}
|
|
7588
|
+
if (type === "string") {
|
|
7589
|
+
return operator === "EQUALS" /* EQUALS */ || operator === "IN" /* IN */ || operator === "LIKE" /* LIKE */ || operator === "GREATER_THAN_OR_EQUAL" /* GREATER_THAN_OR_EQUAL */ || operator === "LESS_THAN_OR_EQUAL" /* LESS_THAN_OR_EQUAL */ || operator === "BETWEEN" /* BETWEEN */;
|
|
7590
|
+
}
|
|
7591
|
+
if (type === "number") {
|
|
7592
|
+
return operator === "EQUALS" /* EQUALS */ || operator === "IN" /* IN */ || operator === "GREATER_THAN_OR_EQUAL" /* GREATER_THAN_OR_EQUAL */ || operator === "LESS_THAN_OR_EQUAL" /* LESS_THAN_OR_EQUAL */ || operator === "BETWEEN" /* BETWEEN */;
|
|
7593
|
+
}
|
|
7594
|
+
if (type === "boolean") {
|
|
7595
|
+
return operator === "EQUALS" /* EQUALS */ || operator === "IN" /* IN */;
|
|
7596
|
+
}
|
|
7597
|
+
return false;
|
|
7598
|
+
};
|
|
7599
|
+
/**
|
|
7600
|
+
* @returns True when criteria can be evaluated using structured indexing.
|
|
7601
|
+
*/
|
|
7602
|
+
canUseStructuredIndexForCriteria = (typeName, criteria) => {
|
|
7603
|
+
if (!criteria?.fieldCriteria?.length) {
|
|
7604
|
+
return false;
|
|
7605
|
+
}
|
|
7606
|
+
const typeInfo = this.getTypeInfo(typeName);
|
|
7607
|
+
const { fields = {} } = typeInfo;
|
|
7608
|
+
const indexedFields = this.resolveStructuredIndexedFields(typeName);
|
|
7609
|
+
if (indexedFields.size === 0) {
|
|
7610
|
+
return false;
|
|
7611
|
+
}
|
|
7612
|
+
for (const criterion of criteria.fieldCriteria) {
|
|
7613
|
+
const { fieldName } = criterion;
|
|
7614
|
+
const field = fields[fieldName];
|
|
7615
|
+
if (!field || field.typeReference || !indexedFields.has(fieldName)) {
|
|
7616
|
+
return false;
|
|
7617
|
+
}
|
|
7618
|
+
const operator = criterion.operator ?? "EQUALS" /* EQUALS */;
|
|
7619
|
+
if (!this.isStructuredOperatorSupportedForField(field, operator)) {
|
|
7620
|
+
return false;
|
|
7621
|
+
}
|
|
7622
|
+
}
|
|
7623
|
+
return true;
|
|
7624
|
+
};
|
|
7625
|
+
/**
|
|
7626
|
+
* @returns Full-text query plan derived from a field criterion.
|
|
7627
|
+
*/
|
|
7628
|
+
toFullTextSearchPlan = (criterion) => {
|
|
7629
|
+
const operator = criterion.operator ?? "EQUALS" /* EQUALS */;
|
|
7630
|
+
if (!this.isFullTextSearchOperator(operator)) {
|
|
7631
|
+
return void 0;
|
|
7632
|
+
}
|
|
7633
|
+
if (typeof criterion.value !== "string") {
|
|
7634
|
+
throw {
|
|
7635
|
+
message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
|
|
7636
|
+
operator,
|
|
7637
|
+
fieldName: criterion.fieldName
|
|
7638
|
+
};
|
|
7639
|
+
}
|
|
7640
|
+
const query = criterion.value.trim();
|
|
7641
|
+
if (!query) {
|
|
7642
|
+
throw {
|
|
7643
|
+
message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
|
|
7644
|
+
operator,
|
|
7645
|
+
fieldName: criterion.fieldName
|
|
7646
|
+
};
|
|
7647
|
+
}
|
|
7648
|
+
if (operator === "CONTAINS" /* CONTAINS */) {
|
|
7649
|
+
return {
|
|
7650
|
+
mode: "exact",
|
|
7651
|
+
query
|
|
7652
|
+
};
|
|
7653
|
+
}
|
|
7654
|
+
if (operator === "STARTS_WITH" /* STARTS_WITH */) {
|
|
7655
|
+
return {
|
|
7656
|
+
mode: "lossy",
|
|
7657
|
+
query: `${query}*`
|
|
7658
|
+
};
|
|
7659
|
+
}
|
|
7660
|
+
return {
|
|
7661
|
+
mode: "lossy",
|
|
7662
|
+
query
|
|
7663
|
+
};
|
|
7664
|
+
};
|
|
7665
|
+
/**
|
|
7666
|
+
* @returns Auto full-text search plan for list criteria, if applicable.
|
|
7667
|
+
*/
|
|
7668
|
+
resolveAutoFullTextCriteriaPlan = (typeName, criteria) => {
|
|
7669
|
+
if (!criteria?.fieldCriteria?.length) {
|
|
7670
|
+
return void 0;
|
|
7671
|
+
}
|
|
7672
|
+
const configuredIndexFields = new Set(
|
|
7673
|
+
this.resolveFullTextIndexFields(typeName)
|
|
7674
|
+
);
|
|
7675
|
+
if (configuredIndexFields.size === 0) {
|
|
7676
|
+
return void 0;
|
|
7677
|
+
}
|
|
7678
|
+
const fullTextCandidates = [];
|
|
7679
|
+
let hasNonFullTextCriteria = false;
|
|
7680
|
+
for (const criterion of criteria.fieldCriteria) {
|
|
7681
|
+
const indexField = criterion.fieldName;
|
|
7682
|
+
if (!configuredIndexFields.has(indexField)) {
|
|
7683
|
+
hasNonFullTextCriteria = true;
|
|
7684
|
+
continue;
|
|
7685
|
+
}
|
|
7686
|
+
const plan = this.toFullTextSearchPlan(criterion);
|
|
7687
|
+
if (!plan) {
|
|
7688
|
+
hasNonFullTextCriteria = true;
|
|
7689
|
+
continue;
|
|
7690
|
+
}
|
|
7691
|
+
fullTextCandidates.push({
|
|
7692
|
+
...plan,
|
|
7693
|
+
indexField
|
|
7694
|
+
});
|
|
7695
|
+
}
|
|
7696
|
+
if (fullTextCandidates.length === 0) {
|
|
7697
|
+
return void 0;
|
|
7698
|
+
}
|
|
7699
|
+
if (hasNonFullTextCriteria || fullTextCandidates.length > 1 || criteria.logicalOperator === "OR" /* OR */) {
|
|
7700
|
+
throw {
|
|
7701
|
+
message: "INDEXING_UNSUPPORTED_COMBINATION" /* INDEXING_UNSUPPORTED_COMBINATION */,
|
|
7702
|
+
typeName
|
|
7703
|
+
};
|
|
7704
|
+
}
|
|
7705
|
+
return fullTextCandidates[0];
|
|
7706
|
+
};
|
|
7707
|
+
/**
|
|
7708
|
+
* @returns Encoded cursor for full-scan compare pagination.
|
|
7709
|
+
*/
|
|
7710
|
+
encodeFullScanCompareCursor = (offset) => JSON.stringify({ fullScanCompareOffset: offset });
|
|
7711
|
+
/**
|
|
7712
|
+
* @returns Decoded offset for full-scan compare pagination.
|
|
7713
|
+
*/
|
|
7714
|
+
decodeFullScanCompareCursor = (cursor) => {
|
|
7715
|
+
if (!cursor) {
|
|
7716
|
+
return 0;
|
|
7717
|
+
}
|
|
7718
|
+
try {
|
|
7719
|
+
const parsed = JSON.parse(cursor);
|
|
7720
|
+
const offset = parsed.fullScanCompareOffset;
|
|
7721
|
+
if (!Number.isFinite(offset) || offset < 0) {
|
|
7722
|
+
throw new Error("Invalid full scan cursor offset");
|
|
7723
|
+
}
|
|
7724
|
+
return offset;
|
|
7725
|
+
} catch (_error) {
|
|
7726
|
+
throw {
|
|
7727
|
+
message: "INVALID_CURSOR" /* INVALID_CURSOR */,
|
|
7728
|
+
cursor
|
|
7729
|
+
};
|
|
7382
7730
|
}
|
|
7383
|
-
return this.config.indexing?.fullText?.defaultIndexFieldByType?.[typeName];
|
|
7384
7731
|
};
|
|
7732
|
+
/**
|
|
7733
|
+
* Execute a criteria list via full scan + in-memory compare.
|
|
7734
|
+
*
|
|
7735
|
+
* This is the universal fallback strategy for criteria/operators that are not
|
|
7736
|
+
* supported by indexed query planners.
|
|
7737
|
+
*
|
|
7738
|
+
* @returns List results with cursor.
|
|
7739
|
+
*/
|
|
7740
|
+
async listByFullScanAndCompare(typeName, config, cleanSelectedFields, useDAC, context) {
|
|
7741
|
+
const driver = this.getDriverInternal(typeName);
|
|
7742
|
+
const { criteria, sortFields, itemsPerPage = 10, cursor } = config;
|
|
7743
|
+
const allItems = [];
|
|
7744
|
+
let scanCursor;
|
|
7745
|
+
while (true) {
|
|
7746
|
+
const page = await driver.listItems({
|
|
7747
|
+
itemsPerPage: 250,
|
|
7748
|
+
cursor: scanCursor
|
|
7749
|
+
});
|
|
7750
|
+
allItems.push(...page.items ?? []);
|
|
7751
|
+
if (!page.cursor || page.cursor === scanCursor) {
|
|
7752
|
+
break;
|
|
7753
|
+
}
|
|
7754
|
+
scanCursor = page.cursor;
|
|
7755
|
+
}
|
|
7756
|
+
const filtered = criteria ? getFilterTypeInfoDataItemsBySearchCriteria(
|
|
7757
|
+
criteria,
|
|
7758
|
+
allItems,
|
|
7759
|
+
typeName,
|
|
7760
|
+
this.config.typeInfoMap
|
|
7761
|
+
) : allItems;
|
|
7762
|
+
const sorted = getSortedItems(sortFields, filtered);
|
|
7763
|
+
let index = this.decodeFullScanCompareCursor(cursor);
|
|
7764
|
+
const cleanedItems = [];
|
|
7765
|
+
while (index < sorted.length && cleanedItems.length < itemsPerPage) {
|
|
7766
|
+
const item = sorted[index];
|
|
7767
|
+
index += 1;
|
|
7768
|
+
let fieldsResources;
|
|
7769
|
+
if (useDAC) {
|
|
7770
|
+
const {
|
|
7771
|
+
allowed: readAllowed,
|
|
7772
|
+
denied: readDenied,
|
|
7773
|
+
fieldsResources: nextFieldsResources = {}
|
|
7774
|
+
} = await this.getItemDACValidation(
|
|
7775
|
+
item,
|
|
7776
|
+
typeName,
|
|
7777
|
+
"READ" /* READ */,
|
|
7778
|
+
context
|
|
7779
|
+
);
|
|
7780
|
+
const listDenied = readDenied || !readAllowed;
|
|
7781
|
+
if (listDenied) {
|
|
7782
|
+
continue;
|
|
7783
|
+
}
|
|
7784
|
+
fieldsResources = nextFieldsResources;
|
|
7785
|
+
}
|
|
7786
|
+
cleanedItems.push(
|
|
7787
|
+
this.getCleanItem(
|
|
7788
|
+
typeName,
|
|
7789
|
+
item,
|
|
7790
|
+
fieldsResources,
|
|
7791
|
+
cleanSelectedFields
|
|
7792
|
+
)
|
|
7793
|
+
);
|
|
7794
|
+
}
|
|
7795
|
+
return {
|
|
7796
|
+
items: cleanedItems,
|
|
7797
|
+
cursor: index < sorted.length ? this.encodeFullScanCompareCursor(index) : void 0
|
|
7798
|
+
};
|
|
7799
|
+
}
|
|
7385
7800
|
/**
|
|
7386
7801
|
* @param value Value to check.
|
|
7387
7802
|
* @returns True when the value is a supported structured value.
|
|
@@ -7393,9 +7808,13 @@ var TypeInfoORMService = class {
|
|
|
7393
7808
|
buildStructuredFields = (typeName, item) => {
|
|
7394
7809
|
const typeInfo = this.getTypeInfo(typeName);
|
|
7395
7810
|
const fieldMap = this.config.indexing?.structured?.fieldMapByType?.[typeName];
|
|
7811
|
+
const indexedFields = this.resolveStructuredIndexedFields(typeName);
|
|
7396
7812
|
const withoutRefs = removeTypeReferenceFieldsFromDataItem(typeInfo, item);
|
|
7397
7813
|
const fields = {};
|
|
7398
7814
|
for (const [fieldName, value] of Object.entries(withoutRefs)) {
|
|
7815
|
+
if (!indexedFields.has(fieldName)) {
|
|
7816
|
+
continue;
|
|
7817
|
+
}
|
|
7399
7818
|
if (typeof value === "undefined") {
|
|
7400
7819
|
continue;
|
|
7401
7820
|
}
|
|
@@ -7444,88 +7863,94 @@ var TypeInfoORMService = class {
|
|
|
7444
7863
|
*/
|
|
7445
7864
|
async indexFullTextDocument(typeName, item, indexFieldOverride) {
|
|
7446
7865
|
const { fullText } = this.config.indexing ?? {};
|
|
7447
|
-
const
|
|
7866
|
+
const indexFields = this.resolveFullTextIndexFields(
|
|
7448
7867
|
typeName,
|
|
7449
7868
|
indexFieldOverride
|
|
7450
7869
|
);
|
|
7451
|
-
if (!fullText ||
|
|
7870
|
+
if (!fullText || indexFields.length === 0) {
|
|
7452
7871
|
return;
|
|
7453
7872
|
}
|
|
7454
|
-
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
7455
7873
|
const { primaryField } = this.getTypeInfo(typeName);
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7874
|
+
for (const indexField of indexFields) {
|
|
7875
|
+
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
7876
|
+
if (!(indexField in item)) {
|
|
7877
|
+
throw {
|
|
7878
|
+
message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
|
|
7879
|
+
typeName,
|
|
7880
|
+
indexField
|
|
7881
|
+
};
|
|
7882
|
+
}
|
|
7883
|
+
await indexDocument({
|
|
7884
|
+
backend: fullText.backend,
|
|
7885
|
+
document: item,
|
|
7886
|
+
primaryField: String(primaryField),
|
|
7887
|
+
indexField,
|
|
7888
|
+
indexFieldQualified: qualifiedIndexField
|
|
7889
|
+
});
|
|
7462
7890
|
}
|
|
7463
|
-
await indexDocument({
|
|
7464
|
-
backend: fullText.backend,
|
|
7465
|
-
document: item,
|
|
7466
|
-
primaryField: String(primaryField),
|
|
7467
|
-
indexField,
|
|
7468
|
-
indexFieldQualified: qualifiedIndexField
|
|
7469
|
-
});
|
|
7470
7891
|
}
|
|
7471
7892
|
/**
|
|
7472
7893
|
* @returns Promise resolved once removal is complete.
|
|
7473
7894
|
*/
|
|
7474
7895
|
async removeFullTextDocument(typeName, item, indexFieldOverride) {
|
|
7475
7896
|
const { fullText } = this.config.indexing ?? {};
|
|
7476
|
-
const
|
|
7897
|
+
const indexFields = this.resolveFullTextIndexFields(
|
|
7477
7898
|
typeName,
|
|
7478
7899
|
indexFieldOverride
|
|
7479
7900
|
);
|
|
7480
|
-
if (!fullText ||
|
|
7901
|
+
if (!fullText || indexFields.length === 0) {
|
|
7481
7902
|
return;
|
|
7482
7903
|
}
|
|
7483
|
-
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
7484
7904
|
const { primaryField } = this.getTypeInfo(typeName);
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7905
|
+
for (const indexField of indexFields) {
|
|
7906
|
+
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
7907
|
+
if (!(indexField in item)) {
|
|
7908
|
+
throw {
|
|
7909
|
+
message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
|
|
7910
|
+
typeName,
|
|
7911
|
+
indexField
|
|
7912
|
+
};
|
|
7913
|
+
}
|
|
7914
|
+
await removeDocument({
|
|
7915
|
+
backend: fullText.backend,
|
|
7916
|
+
document: item,
|
|
7917
|
+
primaryField: String(primaryField),
|
|
7918
|
+
indexField,
|
|
7919
|
+
indexFieldQualified: qualifiedIndexField
|
|
7920
|
+
});
|
|
7491
7921
|
}
|
|
7492
|
-
await removeDocument({
|
|
7493
|
-
backend: fullText.backend,
|
|
7494
|
-
document: item,
|
|
7495
|
-
primaryField: String(primaryField),
|
|
7496
|
-
indexField,
|
|
7497
|
-
indexFieldQualified: qualifiedIndexField
|
|
7498
|
-
});
|
|
7499
7922
|
}
|
|
7500
7923
|
/**
|
|
7501
7924
|
* @returns Promise resolved once replacement is complete.
|
|
7502
7925
|
*/
|
|
7503
7926
|
async replaceFullTextDocument(typeName, previousItem, nextItem, indexFieldOverride) {
|
|
7504
7927
|
const { fullText } = this.config.indexing ?? {};
|
|
7505
|
-
const
|
|
7928
|
+
const indexFields = this.resolveFullTextIndexFields(
|
|
7506
7929
|
typeName,
|
|
7507
7930
|
indexFieldOverride
|
|
7508
7931
|
);
|
|
7509
|
-
if (!fullText ||
|
|
7932
|
+
if (!fullText || indexFields.length === 0) {
|
|
7510
7933
|
return;
|
|
7511
7934
|
}
|
|
7512
|
-
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
7513
7935
|
const { primaryField } = this.getTypeInfo(typeName);
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7936
|
+
for (const indexField of indexFields) {
|
|
7937
|
+
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
7938
|
+
if (!(indexField in previousItem)) {
|
|
7939
|
+
throw {
|
|
7940
|
+
message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
|
|
7941
|
+
typeName,
|
|
7942
|
+
indexField
|
|
7943
|
+
};
|
|
7944
|
+
}
|
|
7945
|
+
await replaceFullTextDocument({
|
|
7946
|
+
backend: fullText.backend,
|
|
7947
|
+
previousDocument: previousItem,
|
|
7948
|
+
nextDocument: nextItem,
|
|
7949
|
+
primaryField: String(primaryField),
|
|
7950
|
+
indexField,
|
|
7951
|
+
indexFieldQualified: qualifiedIndexField
|
|
7952
|
+
});
|
|
7520
7953
|
}
|
|
7521
|
-
await replaceFullTextDocument({
|
|
7522
|
-
backend: fullText.backend,
|
|
7523
|
-
previousDocument: previousItem,
|
|
7524
|
-
nextDocument: nextItem,
|
|
7525
|
-
primaryField: String(primaryField),
|
|
7526
|
-
indexField,
|
|
7527
|
-
indexFieldQualified: qualifiedIndexField
|
|
7528
|
-
});
|
|
7529
7954
|
}
|
|
7530
7955
|
/**
|
|
7531
7956
|
* @returns Promise resolved once indexing is complete.
|
|
@@ -7549,6 +7974,12 @@ var TypeInfoORMService = class {
|
|
|
7549
7974
|
primaryFieldName
|
|
7550
7975
|
);
|
|
7551
7976
|
const fields = this.buildStructuredFields(typeName, item);
|
|
7977
|
+
this.emitStructuredIndexWrite(
|
|
7978
|
+
typeName,
|
|
7979
|
+
String(docId),
|
|
7980
|
+
"upsert",
|
|
7981
|
+
Object.keys(fields).length
|
|
7982
|
+
);
|
|
7552
7983
|
await structured.writer.write(docId, fields);
|
|
7553
7984
|
}
|
|
7554
7985
|
/**
|
|
@@ -7572,6 +8003,7 @@ var TypeInfoORMService = class {
|
|
|
7572
8003
|
item[primaryFieldName],
|
|
7573
8004
|
primaryFieldName
|
|
7574
8005
|
);
|
|
8006
|
+
this.emitStructuredIndexWrite(typeName, String(docId), "remove", 0);
|
|
7575
8007
|
await structured.writer.write(docId, {});
|
|
7576
8008
|
}
|
|
7577
8009
|
/**
|
|
@@ -8249,7 +8681,7 @@ var TypeInfoORMService = class {
|
|
|
8249
8681
|
this.validateReadOperation(typeName, cleanSelectedFields);
|
|
8250
8682
|
const { typeInfoMap, useDAC, indexing } = this.config;
|
|
8251
8683
|
this.getTypeInfo(typeName);
|
|
8252
|
-
const { criteria,
|
|
8684
|
+
const { criteria, itemsPerPage, cursor, sortFields } = config;
|
|
8253
8685
|
const { fieldCriteria = [] } = criteria || {};
|
|
8254
8686
|
const searchFieldValidationResults = validateSearchFields(
|
|
8255
8687
|
typeName,
|
|
@@ -8260,55 +8692,36 @@ var TypeInfoORMService = class {
|
|
|
8260
8692
|
const hasStructured = !!indexing?.structured?.reader;
|
|
8261
8693
|
const hasFullText = !!indexing?.fullText?.backend;
|
|
8262
8694
|
const hasCriteria = !!criteria && fieldCriteria.length > 0;
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
if (hasStructured || hasFullText) {
|
|
8266
|
-
if (!shouldUseIndexing) ; else {
|
|
8267
|
-
if (hasCriteria && hasText) {
|
|
8268
|
-
throw {
|
|
8269
|
-
message: "INDEXING_UNSUPPORTED_COMBINATION" /* INDEXING_UNSUPPORTED_COMBINATION */,
|
|
8270
|
-
typeName
|
|
8271
|
-
};
|
|
8272
|
-
}
|
|
8273
|
-
if (hasCriteria && !hasStructured) {
|
|
8274
|
-
throw {
|
|
8275
|
-
message: "INDEXING_MISSING_BACKEND" /* INDEXING_MISSING_BACKEND */,
|
|
8276
|
-
typeName,
|
|
8277
|
-
backend: "structured.reader"
|
|
8278
|
-
};
|
|
8279
|
-
}
|
|
8280
|
-
if (hasText && !hasFullText) {
|
|
8281
|
-
throw {
|
|
8282
|
-
message: "INDEXING_MISSING_BACKEND" /* INDEXING_MISSING_BACKEND */,
|
|
8283
|
-
typeName,
|
|
8284
|
-
backend: "fullText"
|
|
8285
|
-
};
|
|
8286
|
-
}
|
|
8695
|
+
if (hasCriteria && (hasStructured || hasFullText)) {
|
|
8696
|
+
try {
|
|
8287
8697
|
let docIds = [];
|
|
8288
8698
|
let nextCursor = void 0;
|
|
8289
|
-
|
|
8290
|
-
|
|
8699
|
+
const fullTextPlan = this.resolveAutoFullTextCriteriaPlan(
|
|
8700
|
+
typeName,
|
|
8701
|
+
criteria
|
|
8702
|
+
);
|
|
8703
|
+
if (fullTextPlan && hasFullText) {
|
|
8704
|
+
this.emitListRoutingDecision(
|
|
8291
8705
|
typeName,
|
|
8292
|
-
|
|
8706
|
+
"fullText",
|
|
8707
|
+
"fullTextPlan",
|
|
8708
|
+
fieldCriteria.length
|
|
8709
|
+
);
|
|
8710
|
+
const qualifiedIndexField = qualifyIndexField(
|
|
8711
|
+
typeName,
|
|
8712
|
+
fullTextPlan.indexField
|
|
8293
8713
|
);
|
|
8294
|
-
if (!indexField) {
|
|
8295
|
-
throw {
|
|
8296
|
-
message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
|
|
8297
|
-
typeName
|
|
8298
|
-
};
|
|
8299
|
-
}
|
|
8300
|
-
const qualifiedIndexField = qualifyIndexField(typeName, indexField);
|
|
8301
8714
|
const fullTextBackend = indexing?.fullText?.backend;
|
|
8302
|
-
const searchResult =
|
|
8715
|
+
const searchResult = fullTextPlan.mode === "exact" ? await searchExact({
|
|
8303
8716
|
backend: fullTextBackend,
|
|
8304
|
-
query:
|
|
8717
|
+
query: fullTextPlan.query,
|
|
8305
8718
|
indexField: qualifiedIndexField,
|
|
8306
8719
|
limit: itemsPerPage,
|
|
8307
8720
|
cursor,
|
|
8308
8721
|
limits: indexing?.limits
|
|
8309
8722
|
}) : await searchLossy({
|
|
8310
8723
|
backend: fullTextBackend,
|
|
8311
|
-
query:
|
|
8724
|
+
query: fullTextPlan.query,
|
|
8312
8725
|
indexField: qualifiedIndexField,
|
|
8313
8726
|
limit: itemsPerPage,
|
|
8314
8727
|
cursor,
|
|
@@ -8316,9 +8729,25 @@ var TypeInfoORMService = class {
|
|
|
8316
8729
|
});
|
|
8317
8730
|
docIds = searchResult.docIds;
|
|
8318
8731
|
nextCursor = searchResult.nextCursor;
|
|
8319
|
-
} else {
|
|
8320
|
-
|
|
8321
|
-
|
|
8732
|
+
} else if (hasStructured) {
|
|
8733
|
+
if (!this.canUseStructuredIndexForCriteria(typeName, criteria)) {
|
|
8734
|
+
throw {
|
|
8735
|
+
message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
|
|
8736
|
+
typeName
|
|
8737
|
+
};
|
|
8738
|
+
}
|
|
8739
|
+
this.emitListRoutingDecision(
|
|
8740
|
+
typeName,
|
|
8741
|
+
"structured",
|
|
8742
|
+
"structuredEligible",
|
|
8743
|
+
fieldCriteria.length
|
|
8744
|
+
);
|
|
8745
|
+
const tokenizer = indexing?.structured?.tokenizer;
|
|
8746
|
+
const whereWithTokenizer = criteriaToStructuredWhere(
|
|
8747
|
+
criteria,
|
|
8748
|
+
tokenizer
|
|
8749
|
+
);
|
|
8750
|
+
if (!whereWithTokenizer) {
|
|
8322
8751
|
throw {
|
|
8323
8752
|
message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
|
|
8324
8753
|
typeName
|
|
@@ -8326,7 +8755,7 @@ var TypeInfoORMService = class {
|
|
|
8326
8755
|
}
|
|
8327
8756
|
const mappedWhere = this.applyStructuredFieldMap(
|
|
8328
8757
|
typeName,
|
|
8329
|
-
|
|
8758
|
+
whereWithTokenizer,
|
|
8330
8759
|
indexing?.structured?.fieldMapByType?.[typeName]
|
|
8331
8760
|
);
|
|
8332
8761
|
const structuredReader = indexing?.structured?.reader;
|
|
@@ -8340,6 +8769,11 @@ var TypeInfoORMService = class {
|
|
|
8340
8769
|
);
|
|
8341
8770
|
docIds = page.candidateIds;
|
|
8342
8771
|
nextCursor = page.cursor;
|
|
8772
|
+
} else {
|
|
8773
|
+
throw {
|
|
8774
|
+
message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
|
|
8775
|
+
typeName
|
|
8776
|
+
};
|
|
8343
8777
|
}
|
|
8344
8778
|
const driver2 = this.getDriverInternal(typeName);
|
|
8345
8779
|
const items = [];
|
|
@@ -8392,8 +8826,37 @@ var TypeInfoORMService = class {
|
|
|
8392
8826
|
items: sortedItems,
|
|
8393
8827
|
cursor: nextCursor
|
|
8394
8828
|
};
|
|
8829
|
+
} catch (_error) {
|
|
8830
|
+
this.emitListRoutingDecision(
|
|
8831
|
+
typeName,
|
|
8832
|
+
"fullScanCompare",
|
|
8833
|
+
"indexedPathFailedOrUnsupported",
|
|
8834
|
+
fieldCriteria.length
|
|
8835
|
+
);
|
|
8836
|
+
return this.listByFullScanAndCompare(
|
|
8837
|
+
typeName,
|
|
8838
|
+
config,
|
|
8839
|
+
cleanSelectedFields,
|
|
8840
|
+
useDAC,
|
|
8841
|
+
context
|
|
8842
|
+
);
|
|
8395
8843
|
}
|
|
8396
8844
|
}
|
|
8845
|
+
if (hasCriteria) {
|
|
8846
|
+
this.emitListRoutingDecision(
|
|
8847
|
+
typeName,
|
|
8848
|
+
"fullScanCompare",
|
|
8849
|
+
"criteriaWithoutIndexedPath",
|
|
8850
|
+
fieldCriteria.length
|
|
8851
|
+
);
|
|
8852
|
+
return this.listByFullScanAndCompare(
|
|
8853
|
+
typeName,
|
|
8854
|
+
config,
|
|
8855
|
+
cleanSelectedFields,
|
|
8856
|
+
useDAC,
|
|
8857
|
+
context
|
|
8858
|
+
);
|
|
8859
|
+
}
|
|
8397
8860
|
const driver = this.getDriverInternal(typeName);
|
|
8398
8861
|
const fieldsResourcesCache = [];
|
|
8399
8862
|
const results = await executeDriverListItems(
|