elasticlink 0.8.0-beta → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +631 -819
  2. package/dist/index.cjs +1528 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +1890 -0
  5. package/dist/index.d.cts.map +1 -0
  6. package/dist/index.d.ts +1889 -19
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1474 -18
  9. package/dist/index.js.map +1 -0
  10. package/package.json +25 -19
  11. package/dist/aggregation.builder.d.ts +0 -6
  12. package/dist/aggregation.builder.d.ts.map +0 -1
  13. package/dist/aggregation.builder.js +0 -64
  14. package/dist/aggregation.types.d.ts +0 -173
  15. package/dist/aggregation.types.d.ts.map +0 -1
  16. package/dist/aggregation.types.js +0 -6
  17. package/dist/bulk.builder.d.ts +0 -28
  18. package/dist/bulk.builder.d.ts.map +0 -1
  19. package/dist/bulk.builder.js +0 -51
  20. package/dist/bulk.types.d.ts +0 -46
  21. package/dist/bulk.types.d.ts.map +0 -1
  22. package/dist/bulk.types.js +0 -6
  23. package/dist/field.helpers.d.ts +0 -167
  24. package/dist/field.helpers.d.ts.map +0 -1
  25. package/dist/field.helpers.js +0 -282
  26. package/dist/field.types.d.ts +0 -255
  27. package/dist/field.types.d.ts.map +0 -1
  28. package/dist/field.types.js +0 -6
  29. package/dist/index-management.builder.d.ts +0 -33
  30. package/dist/index-management.builder.d.ts.map +0 -1
  31. package/dist/index-management.builder.js +0 -64
  32. package/dist/index-management.types.d.ts +0 -126
  33. package/dist/index-management.types.d.ts.map +0 -1
  34. package/dist/index-management.types.js +0 -6
  35. package/dist/mapping.builder.d.ts +0 -46
  36. package/dist/mapping.builder.d.ts.map +0 -1
  37. package/dist/mapping.builder.js +0 -39
  38. package/dist/mapping.types.d.ts +0 -160
  39. package/dist/mapping.types.d.ts.map +0 -1
  40. package/dist/mapping.types.js +0 -6
  41. package/dist/multi-search.builder.d.ts +0 -22
  42. package/dist/multi-search.builder.d.ts.map +0 -1
  43. package/dist/multi-search.builder.js +0 -39
  44. package/dist/multi-search.types.d.ts +0 -36
  45. package/dist/multi-search.types.d.ts.map +0 -1
  46. package/dist/multi-search.types.js +0 -6
  47. package/dist/query.builder.d.ts +0 -4
  48. package/dist/query.builder.d.ts.map +0 -1
  49. package/dist/query.builder.js +0 -264
  50. package/dist/query.types.d.ts +0 -324
  51. package/dist/query.types.d.ts.map +0 -1
  52. package/dist/query.types.js +0 -7
  53. package/dist/settings.presets.d.ts +0 -98
  54. package/dist/settings.presets.d.ts.map +0 -1
  55. package/dist/settings.presets.js +0 -115
  56. package/dist/suggester.builder.d.ts +0 -23
  57. package/dist/suggester.builder.d.ts.map +0 -1
  58. package/dist/suggester.builder.js +0 -51
  59. package/dist/suggester.types.d.ts +0 -50
  60. package/dist/suggester.types.d.ts.map +0 -1
  61. package/dist/suggester.types.js +0 -6
  62. package/dist/vector.types.d.ts +0 -17
  63. package/dist/vector.types.d.ts.map +0 -1
  64. package/dist/vector.types.js +0 -6
package/dist/index.cjs ADDED
@@ -0,0 +1,1528 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/aggregation.builder.ts
3
+ const sharedAggMethods = (state, rebuild) => ({
4
+ terms: (name, field, options) => rebuild({
5
+ ...state,
6
+ [name]: { terms: {
7
+ field,
8
+ ...options
9
+ } }
10
+ }),
11
+ dateHistogram: (name, field, options) => rebuild({
12
+ ...state,
13
+ [name]: { date_histogram: {
14
+ field,
15
+ ...options
16
+ } }
17
+ }),
18
+ range: (name, field, options) => rebuild({
19
+ ...state,
20
+ [name]: { range: {
21
+ field,
22
+ ...options
23
+ } }
24
+ }),
25
+ dateRange: (name, field, options) => rebuild({
26
+ ...state,
27
+ [name]: { date_range: {
28
+ field,
29
+ ...options
30
+ } }
31
+ }),
32
+ filters: (name, filters, options) => rebuild({
33
+ ...state,
34
+ [name]: { filters: {
35
+ filters,
36
+ ...options
37
+ } }
38
+ }),
39
+ significantTerms: (name, field, options) => rebuild({
40
+ ...state,
41
+ [name]: { significant_terms: {
42
+ field,
43
+ ...options
44
+ } }
45
+ }),
46
+ histogram: (name, field, options) => rebuild({
47
+ ...state,
48
+ [name]: { histogram: {
49
+ field,
50
+ ...options
51
+ } }
52
+ }),
53
+ avg: (name, field, options) => rebuild({
54
+ ...state,
55
+ [name]: { avg: {
56
+ field,
57
+ ...options
58
+ } }
59
+ }),
60
+ sum: (name, field, options) => rebuild({
61
+ ...state,
62
+ [name]: { sum: {
63
+ field,
64
+ ...options
65
+ } }
66
+ }),
67
+ min: (name, field, options) => rebuild({
68
+ ...state,
69
+ [name]: { min: {
70
+ field,
71
+ ...options
72
+ } }
73
+ }),
74
+ max: (name, field, options) => rebuild({
75
+ ...state,
76
+ [name]: { max: {
77
+ field,
78
+ ...options
79
+ } }
80
+ }),
81
+ cardinality: (name, field, options) => rebuild({
82
+ ...state,
83
+ [name]: { cardinality: {
84
+ field,
85
+ ...options
86
+ } }
87
+ }),
88
+ percentiles: (name, field, options) => rebuild({
89
+ ...state,
90
+ [name]: { percentiles: {
91
+ field,
92
+ ...options
93
+ } }
94
+ }),
95
+ stats: (name, field, options) => rebuild({
96
+ ...state,
97
+ [name]: { stats: {
98
+ field,
99
+ ...options
100
+ } }
101
+ }),
102
+ valueCount: (name, field, options) => rebuild({
103
+ ...state,
104
+ [name]: { value_count: {
105
+ field,
106
+ ...options
107
+ } }
108
+ }),
109
+ extendedStats: (name, field, options) => rebuild({
110
+ ...state,
111
+ [name]: { extended_stats: {
112
+ field,
113
+ ...options
114
+ } }
115
+ }),
116
+ topHits: (name, options) => rebuild({
117
+ ...state,
118
+ [name]: { top_hits: { ...options } }
119
+ }),
120
+ autoDateHistogram: (name, field, options) => rebuild({
121
+ ...state,
122
+ [name]: { auto_date_histogram: {
123
+ field,
124
+ ...options
125
+ } }
126
+ }),
127
+ composite: (name, sources, options) => rebuild({
128
+ ...state,
129
+ [name]: { composite: {
130
+ sources,
131
+ ...options
132
+ } }
133
+ }),
134
+ filter: (name, query) => rebuild({
135
+ ...state,
136
+ [name]: { filter: query }
137
+ }),
138
+ rareTerms: (name, field, options) => rebuild({
139
+ ...state,
140
+ [name]: { rare_terms: {
141
+ field,
142
+ ...options
143
+ } }
144
+ }),
145
+ multiTerms: (name, options) => rebuild({
146
+ ...state,
147
+ [name]: { multi_terms: { ...options } }
148
+ }),
149
+ geoDistance: (name, field, options) => rebuild({
150
+ ...state,
151
+ [name]: { geo_distance: {
152
+ field,
153
+ ...options
154
+ } }
155
+ }),
156
+ geohashGrid: (name, field, options) => rebuild({
157
+ ...state,
158
+ [name]: { geohash_grid: {
159
+ field,
160
+ ...options
161
+ } }
162
+ }),
163
+ geotileGrid: (name, field, options) => rebuild({
164
+ ...state,
165
+ [name]: { geotile_grid: {
166
+ field,
167
+ ...options
168
+ } }
169
+ }),
170
+ geoBounds: (name, field, options) => rebuild({
171
+ ...state,
172
+ [name]: { geo_bounds: {
173
+ field,
174
+ ...options
175
+ } }
176
+ }),
177
+ geoCentroid: (name, field, options) => rebuild({
178
+ ...state,
179
+ [name]: { geo_centroid: {
180
+ field,
181
+ ...options
182
+ } }
183
+ }),
184
+ missing: (name, field, options) => rebuild({
185
+ ...state,
186
+ [name]: { missing: {
187
+ field,
188
+ ...options
189
+ } }
190
+ }),
191
+ topMetrics: (name, options) => rebuild({
192
+ ...state,
193
+ [name]: { top_metrics: { ...options } }
194
+ }),
195
+ weightedAvg: (name, options) => rebuild({
196
+ ...state,
197
+ [name]: { weighted_avg: { ...options } }
198
+ }),
199
+ bucketScript: (name, options) => rebuild({
200
+ ...state,
201
+ [name]: { bucket_script: { ...options } }
202
+ }),
203
+ bucketSelector: (name, options) => rebuild({
204
+ ...state,
205
+ [name]: { bucket_selector: { ...options } }
206
+ }),
207
+ derivative: (name, options) => rebuild({
208
+ ...state,
209
+ [name]: { derivative: { ...options } }
210
+ }),
211
+ cumulativeSum: (name, options) => rebuild({
212
+ ...state,
213
+ [name]: { cumulative_sum: { ...options } }
214
+ }),
215
+ build: () => state
216
+ });
217
+ const attachSubAgg = (state, subAggState) => {
218
+ const lastKey = Object.keys(state).at(-1);
219
+ const existing = state[lastKey].aggs ?? {};
220
+ return {
221
+ ...state,
222
+ [lastKey]: {
223
+ ...state[lastKey],
224
+ aggs: {
225
+ ...existing,
226
+ ...subAggState
227
+ }
228
+ }
229
+ };
230
+ };
231
+ function createNestedEntryBuilder(state, rebuild) {
232
+ return {
233
+ ...sharedAggMethods(state, rebuild),
234
+ global: (name) => createAggregationBuilder({
235
+ ...state,
236
+ [name]: { global: {} }
237
+ }),
238
+ nested: (name, path) => createNestedEntryBuilder({
239
+ ...state,
240
+ [name]: { nested: { path } }
241
+ }, rebuild),
242
+ subAgg: (fn) => rebuild(attachSubAgg(state, fn(createNestedAggregationBuilder()).build()))
243
+ };
244
+ }
245
+ function createNestedAggregationBuilder(state = {}) {
246
+ return {
247
+ ...sharedAggMethods(state, createNestedAggregationBuilder),
248
+ nested: (name, path) => createNestedEntryBuilder({
249
+ ...state,
250
+ [name]: { nested: { path } }
251
+ }, createNestedAggregationBuilder),
252
+ reverseNested: (name, path) => createNestedAggregationBuilder({
253
+ ...state,
254
+ [name]: { reverse_nested: path ? { path } : {} }
255
+ }),
256
+ subAgg: (fn) => createNestedAggregationBuilder(attachSubAgg(state, fn(createNestedAggregationBuilder()).build()))
257
+ };
258
+ }
259
+ function createAggregationBuilder(state = {}) {
260
+ return {
261
+ ...sharedAggMethods(state, createAggregationBuilder),
262
+ global: (name) => createAggregationBuilder({
263
+ ...state,
264
+ [name]: { global: {} }
265
+ }),
266
+ nested: (name, path) => createNestedEntryBuilder({
267
+ ...state,
268
+ [name]: { nested: { path } }
269
+ }, createAggregationBuilder),
270
+ subAgg: (fn) => createAggregationBuilder(attachSubAgg(state, fn(createAggregationBuilder()).build()))
271
+ };
272
+ }
273
+ const aggregations = (_schema) => createAggregationBuilder();
274
+ //#endregion
275
+ //#region src/suggester.builder.ts
276
+ /**
277
+ * Creates a suggester builder
278
+ * @returns SuggesterBuilder instance
279
+ */
280
+ const createSuggesterBuilder = (state = {}) => ({
281
+ term: (name, text, options) => {
282
+ return createSuggesterBuilder({
283
+ ...state,
284
+ [name]: {
285
+ text,
286
+ term: options
287
+ }
288
+ });
289
+ },
290
+ phrase: (name, text, options) => {
291
+ return createSuggesterBuilder({
292
+ ...state,
293
+ [name]: {
294
+ text,
295
+ phrase: options
296
+ }
297
+ });
298
+ },
299
+ completion: (name, prefix, options) => {
300
+ return createSuggesterBuilder({
301
+ ...state,
302
+ [name]: {
303
+ prefix,
304
+ completion: options
305
+ }
306
+ });
307
+ },
308
+ build: () => ({ suggest: state })
309
+ });
310
+ /**
311
+ * Factory function to create a new suggester builder
312
+ * @example
313
+ * ```typescript
314
+ * const suggestions = suggest(productMappings)
315
+ * .term('name-suggestions', 'laptop', { field: 'name', size: 5 })
316
+ * .build();
317
+ * ```
318
+ */
319
+ const suggest = (_schema) => createSuggesterBuilder();
320
+ //#endregion
321
+ //#region src/query.builder.ts
322
+ const resolveCondition = (c) => typeof c === "function" ? resolveCondition(c()) : typeof c === "boolean" ? c : c != null;
323
+ const createClauseMethods = (wrap, qualifyField = (f) => f) => ({
324
+ matchAll: () => wrap({ match_all: {} }),
325
+ matchNone: () => wrap({ match_none: {} }),
326
+ match: (field, value, options) => wrap({ match: { [qualifyField(field)]: options ? {
327
+ query: value,
328
+ ...options
329
+ } : value } }),
330
+ multiMatch: (fields, query, options) => wrap({ multi_match: {
331
+ fields: fields.map(qualifyField),
332
+ query,
333
+ ...options
334
+ } }),
335
+ matchPhrase: (field, query) => wrap({ match_phrase: { [qualifyField(field)]: query } }),
336
+ matchPhrasePrefix: (field, value, options) => wrap({ match_phrase_prefix: { [qualifyField(field)]: options ? {
337
+ query: value,
338
+ ...options
339
+ } : value } }),
340
+ term: (field, value) => wrap({ term: { [qualifyField(field)]: value } }),
341
+ terms: (field, value) => wrap({ terms: { [qualifyField(field)]: value } }),
342
+ range: (field, conditions) => wrap({ range: { [qualifyField(field)]: conditions } }),
343
+ exists: (field) => wrap({ exists: { field: qualifyField(field) } }),
344
+ prefix: (field, value) => wrap({ prefix: { [qualifyField(field)]: value } }),
345
+ wildcard: (field, value) => wrap({ wildcard: { [qualifyField(field)]: value } }),
346
+ fuzzy: (field, value, options) => wrap({ fuzzy: { [qualifyField(field)]: options ? {
347
+ value,
348
+ ...options
349
+ } : { value } } }),
350
+ ids: (values) => wrap({ ids: { values } }),
351
+ script: (options) => {
352
+ const { source, lang = "painless", params, boost } = options;
353
+ return wrap({ script: {
354
+ script: {
355
+ source,
356
+ lang,
357
+ ...params !== void 0 && { params }
358
+ },
359
+ ...boost !== void 0 && { boost }
360
+ } });
361
+ },
362
+ combinedFields: (fields, query, options) => wrap({ combined_fields: {
363
+ fields: fields.map(qualifyField),
364
+ query,
365
+ ...options
366
+ } }),
367
+ queryString: (query, options) => wrap({ query_string: {
368
+ query,
369
+ ...options
370
+ } }),
371
+ simpleQueryString: (query, options) => wrap({ simple_query_string: {
372
+ query,
373
+ ...options
374
+ } }),
375
+ moreLikeThis: (fields, like, options) => wrap({ more_like_this: {
376
+ fields: fields.map(qualifyField),
377
+ like: Array.isArray(like) ? like : [like],
378
+ ...options
379
+ } }),
380
+ matchBoolPrefix: (field, value, options) => wrap({ match_bool_prefix: { [qualifyField(field)]: options ? {
381
+ query: value,
382
+ ...options
383
+ } : value } }),
384
+ regexp: (field, value, options) => wrap({ regexp: { [qualifyField(field)]: options ? {
385
+ value,
386
+ ...options
387
+ } : value } }),
388
+ geoDistance: (field, center, options) => wrap({ geo_distance: {
389
+ [qualifyField(field)]: center,
390
+ ...options
391
+ } }),
392
+ geoBoundingBox: (field, options) => wrap({ geo_bounding_box: { [qualifyField(field)]: options } }),
393
+ geoPolygon: (field, options) => wrap({ geo_polygon: { [qualifyField(field)]: options } }),
394
+ geoShape: (field, shape, options) => wrap({ geo_shape: { [qualifyField(field)]: {
395
+ shape,
396
+ ...options
397
+ } } }),
398
+ distanceFeature: (field, options) => wrap({ distance_feature: {
399
+ field: qualifyField(field),
400
+ ...options
401
+ } }),
402
+ rankFeature: (field, options) => wrap({ rank_feature: {
403
+ field: qualifyField(field),
404
+ ...options
405
+ } }),
406
+ sparseVector: (field, options) => wrap({ sparse_vector: {
407
+ field: qualifyField(field),
408
+ ...options
409
+ } }),
410
+ intervals: (field, options) => wrap({ intervals: { [qualifyField(field)]: options } }),
411
+ spanTerm: (field, value) => wrap({ span_term: { [qualifyField(field)]: { value } } }),
412
+ spanNear: (clauses, options) => wrap({ span_near: {
413
+ clauses: [...clauses],
414
+ ...options
415
+ } }),
416
+ spanOr: (clauses) => wrap({ span_or: { clauses: [...clauses] } }),
417
+ spanNot: (include, exclude, options) => wrap({ span_not: {
418
+ include,
419
+ exclude,
420
+ ...options
421
+ } }),
422
+ spanFirst: (match, end) => wrap({ span_first: {
423
+ match,
424
+ end
425
+ } }),
426
+ spanContaining: (big, little) => wrap({ span_containing: {
427
+ big,
428
+ little
429
+ } }),
430
+ spanWithin: (big, little) => wrap({ span_within: {
431
+ big,
432
+ little
433
+ } }),
434
+ spanMultiTerm: (query) => wrap({ span_multi: { match: query } }),
435
+ spanFieldMasking: (field, query) => wrap({ span_field_masking: {
436
+ field: qualifyField(field),
437
+ query
438
+ } })
439
+ });
440
+ const createClauseBuilder = (prefix) => {
441
+ const qualifyField = prefix ? (f) => `${prefix}.${f}` : (f) => f;
442
+ return {
443
+ ...createClauseMethods((dsl) => dsl, qualifyField),
444
+ knn: (field, queryVector, options) => {
445
+ const { k, num_candidates, ...restOptions } = options;
446
+ return { knn: {
447
+ field: qualifyField(field),
448
+ query_vector: queryVector,
449
+ k,
450
+ num_candidates,
451
+ ...restOptions
452
+ } };
453
+ },
454
+ nested: (path, fn, options) => {
455
+ const nestedPath = prefix ? `${prefix}.${path}` : path;
456
+ const nestedQuery = fn(createClauseBuilder(nestedPath));
457
+ return nestedQuery === void 0 ? void 0 : { nested: {
458
+ path: nestedPath,
459
+ query: nestedQuery,
460
+ ...options
461
+ } };
462
+ },
463
+ functionScore: (queryFn, options) => {
464
+ return { function_score: {
465
+ query: queryFn(createClauseBuilder(prefix)) ?? { match_all: {} },
466
+ ...options
467
+ } };
468
+ },
469
+ hasChild: (type, queryFn, options) => {
470
+ return { has_child: {
471
+ type,
472
+ query: queryFn(createClauseBuilder()) ?? { match_all: {} },
473
+ ...options
474
+ } };
475
+ },
476
+ hasParent: (type, queryFn, options) => {
477
+ return { has_parent: {
478
+ parent_type: type,
479
+ query: queryFn(createClauseBuilder()) ?? { match_all: {} },
480
+ ...options
481
+ } };
482
+ },
483
+ parentId: (type, id, options) => ({ parent_id: {
484
+ type,
485
+ id,
486
+ ...options
487
+ } }),
488
+ when: (condition, thenFn) => resolveCondition(condition) ? thenFn(createClauseBuilder(prefix)) : void 0
489
+ };
490
+ };
491
+ const createQueryBuilder = (state = {}) => {
492
+ const q = state.query ?? {};
493
+ return {
494
+ ...createClauseMethods((dsl) => createQueryBuilder({
495
+ ...state,
496
+ query: dsl
497
+ })),
498
+ bool: () => createQueryBuilder({
499
+ ...state,
500
+ query: { bool: {} }
501
+ }),
502
+ must: (builderFn) => {
503
+ const clause = builderFn(createClauseBuilder());
504
+ const existing = q.bool?.must ?? [];
505
+ return clause === void 0 ? createQueryBuilder(state) : createQueryBuilder({
506
+ ...state,
507
+ query: { bool: {
508
+ ...q.bool,
509
+ must: [...existing, clause]
510
+ } }
511
+ });
512
+ },
513
+ mustNot: (builderFn) => {
514
+ const clause = builderFn(createClauseBuilder());
515
+ const existing = q.bool?.must_not ?? [];
516
+ return clause === void 0 ? createQueryBuilder(state) : createQueryBuilder({
517
+ ...state,
518
+ query: { bool: {
519
+ ...q.bool,
520
+ must_not: [...existing, clause]
521
+ } }
522
+ });
523
+ },
524
+ should: (builderFn) => {
525
+ const clause = builderFn(createClauseBuilder());
526
+ const existing = q.bool?.should ?? [];
527
+ return clause === void 0 ? createQueryBuilder(state) : createQueryBuilder({
528
+ ...state,
529
+ query: { bool: {
530
+ ...q.bool,
531
+ should: [...existing, clause]
532
+ } }
533
+ });
534
+ },
535
+ filter: (builderFn) => {
536
+ const clause = builderFn(createClauseBuilder());
537
+ const existing = q.bool?.filter ?? [];
538
+ return clause === void 0 ? createQueryBuilder(state) : createQueryBuilder({
539
+ ...state,
540
+ query: { bool: {
541
+ ...q.bool,
542
+ filter: [...existing, clause]
543
+ } }
544
+ });
545
+ },
546
+ minimumShouldMatch: (value) => createQueryBuilder({
547
+ ...state,
548
+ query: { bool: {
549
+ ...q.bool,
550
+ minimum_should_match: value
551
+ } }
552
+ }),
553
+ knn: (field, queryVector, options) => {
554
+ const { k, num_candidates, ...restOptions } = options;
555
+ return createQueryBuilder({
556
+ ...state,
557
+ knn: {
558
+ field,
559
+ query_vector: queryVector,
560
+ k,
561
+ num_candidates,
562
+ ...restOptions
563
+ }
564
+ });
565
+ },
566
+ nested: (path, fn, options) => {
567
+ const nestedQuery = fn(createClauseBuilder(path));
568
+ return nestedQuery === void 0 ? createQueryBuilder(state) : createQueryBuilder({
569
+ ...state,
570
+ query: { nested: {
571
+ path,
572
+ query: nestedQuery,
573
+ ...options
574
+ } }
575
+ });
576
+ },
577
+ scriptScore: (queryFn, script, options) => {
578
+ const innerQuery = queryFn(createClauseBuilder()) ?? { match_all: {} };
579
+ const { source, lang = "painless", params } = script;
580
+ return createQueryBuilder({
581
+ ...state,
582
+ query: { script_score: {
583
+ query: innerQuery,
584
+ script: {
585
+ source,
586
+ lang,
587
+ ...params !== void 0 && { params }
588
+ },
589
+ ...options?.min_score !== void 0 && { min_score: options.min_score },
590
+ ...options?.boost !== void 0 && { boost: options.boost }
591
+ } }
592
+ });
593
+ },
594
+ percolate: (options) => createQueryBuilder({
595
+ ...state,
596
+ query: { percolate: { ...options } }
597
+ }),
598
+ functionScore: (queryFn, options) => {
599
+ const innerQuery = queryFn(createClauseBuilder()) ?? { match_all: {} };
600
+ return createQueryBuilder({
601
+ ...state,
602
+ query: { function_score: {
603
+ query: innerQuery,
604
+ ...options
605
+ } }
606
+ });
607
+ },
608
+ hasChild: (type, queryFn, options) => {
609
+ const childQuery = queryFn(createClauseBuilder()) ?? { match_all: {} };
610
+ return createQueryBuilder({
611
+ ...state,
612
+ query: { has_child: {
613
+ type,
614
+ query: childQuery,
615
+ ...options
616
+ } }
617
+ });
618
+ },
619
+ hasParent: (type, queryFn, options) => {
620
+ const parentQuery = queryFn(createClauseBuilder()) ?? { match_all: {} };
621
+ return createQueryBuilder({
622
+ ...state,
623
+ query: { has_parent: {
624
+ parent_type: type,
625
+ query: parentQuery,
626
+ ...options
627
+ } }
628
+ });
629
+ },
630
+ parentId: (type, id, options) => createQueryBuilder({
631
+ ...state,
632
+ query: { parent_id: {
633
+ type,
634
+ id,
635
+ ...options
636
+ } }
637
+ }),
638
+ when: (condition, thenFn) => resolveCondition(condition) ? thenFn(createQueryBuilder(state)) : createQueryBuilder(state),
639
+ sort: (field, direction = "asc") => {
640
+ const existing = state.sort || [];
641
+ return createQueryBuilder({
642
+ ...state,
643
+ sort: [...existing, { [field]: direction }]
644
+ });
645
+ },
646
+ from: (from) => createQueryBuilder({
647
+ ...state,
648
+ from
649
+ }),
650
+ size: (size) => createQueryBuilder({
651
+ ...state,
652
+ size
653
+ }),
654
+ _source: (_source) => createQueryBuilder({
655
+ ...state,
656
+ _source
657
+ }),
658
+ sourceIncludes: (paths) => createQueryBuilder({
659
+ ...state,
660
+ _source_includes: paths
661
+ }),
662
+ sourceExcludes: (paths) => createQueryBuilder({
663
+ ...state,
664
+ _source_excludes: paths
665
+ }),
666
+ runtimeMappings: (runtime_mappings) => createQueryBuilder({
667
+ ...state,
668
+ runtime_mappings
669
+ }),
670
+ docValueFields: (docvalue_fields) => createQueryBuilder({
671
+ ...state,
672
+ docvalue_fields
673
+ }),
674
+ fields: (fields) => createQueryBuilder({
675
+ ...state,
676
+ fields
677
+ }),
678
+ postFilter: (fn) => {
679
+ const clause = fn(createClauseBuilder());
680
+ return clause === void 0 ? createQueryBuilder(state) : createQueryBuilder({
681
+ ...state,
682
+ post_filter: clause
683
+ });
684
+ },
685
+ scriptFields: (script_fields) => createQueryBuilder({
686
+ ...state,
687
+ script_fields
688
+ }),
689
+ timeout: (timeout) => createQueryBuilder({
690
+ ...state,
691
+ timeout
692
+ }),
693
+ trackScores: (track_scores) => createQueryBuilder({
694
+ ...state,
695
+ track_scores
696
+ }),
697
+ explain: (explain) => createQueryBuilder({
698
+ ...state,
699
+ explain
700
+ }),
701
+ minScore: (min_score) => createQueryBuilder({
702
+ ...state,
703
+ min_score
704
+ }),
705
+ version: (version) => createQueryBuilder({
706
+ ...state,
707
+ version
708
+ }),
709
+ seqNoPrimaryTerm: (seq_no_primary_term) => createQueryBuilder({
710
+ ...state,
711
+ seq_no_primary_term
712
+ }),
713
+ trackTotalHits: (track_total_hits = true) => createQueryBuilder({
714
+ ...state,
715
+ track_total_hits
716
+ }),
717
+ highlight: (fields, options) => {
718
+ const { pre_tags, post_tags, ...fieldOptions } = options || {};
719
+ const fieldValue = Object.keys(fieldOptions).length > 0 ? fieldOptions : {};
720
+ const highlightFields = Object.fromEntries(fields.map((field) => [field, fieldValue]));
721
+ return createQueryBuilder({
722
+ ...state,
723
+ highlight: {
724
+ fields: highlightFields,
725
+ ...pre_tags && { pre_tags },
726
+ ...post_tags && { post_tags }
727
+ }
728
+ });
729
+ },
730
+ constantScore: (fn, options) => {
731
+ const clause = fn(createClauseBuilder());
732
+ return createQueryBuilder({
733
+ ...state,
734
+ query: { constant_score: {
735
+ filter: clause,
736
+ ...options
737
+ } }
738
+ });
739
+ },
740
+ searchAfter: (values) => createQueryBuilder({
741
+ ...state,
742
+ search_after: values
743
+ }),
744
+ preference: (value) => createQueryBuilder({
745
+ ...state,
746
+ preference: value
747
+ }),
748
+ collapse: (field, options) => createQueryBuilder({
749
+ ...state,
750
+ collapse: {
751
+ field,
752
+ ...options
753
+ }
754
+ }),
755
+ rescore: (queryFn, windowSize, options) => {
756
+ const rescoreQuery = queryFn(createClauseBuilder());
757
+ return createQueryBuilder({
758
+ ...state,
759
+ rescore: {
760
+ window_size: windowSize,
761
+ query: {
762
+ rescore_query: rescoreQuery,
763
+ ...options
764
+ }
765
+ }
766
+ });
767
+ },
768
+ storedFields: (fields) => createQueryBuilder({
769
+ ...state,
770
+ stored_fields: fields
771
+ }),
772
+ terminateAfter: (count) => createQueryBuilder({
773
+ ...state,
774
+ terminate_after: count
775
+ }),
776
+ pit: (id, keepAlive) => createQueryBuilder({
777
+ ...state,
778
+ pit: {
779
+ id,
780
+ keep_alive: keepAlive
781
+ }
782
+ }),
783
+ indicesBoost: (boosts) => createQueryBuilder({
784
+ ...state,
785
+ indices_boost: boosts
786
+ }),
787
+ aggs: (fn) => {
788
+ const builtAggs = fn(createAggregationBuilder()).build();
789
+ return createQueryBuilder({
790
+ ...state,
791
+ aggs: builtAggs
792
+ });
793
+ },
794
+ suggest: (fn) => {
795
+ const builtSuggestions = fn(createSuggesterBuilder()).build();
796
+ return createQueryBuilder({
797
+ ...state,
798
+ suggest: builtSuggestions.suggest
799
+ });
800
+ },
801
+ build: () => {
802
+ const { _includeQuery, ...rest } = state;
803
+ if (_includeQuery === false) {
804
+ const { query: _q, ...noQuery } = rest;
805
+ return noQuery;
806
+ }
807
+ return rest;
808
+ }
809
+ };
810
+ };
811
+ //#endregion
812
+ //#region src/mapping.builder.ts
813
+ const mappings = (fields, options) => {
814
+ return {
815
+ _fieldTypes: void 0,
816
+ _fields: void 0,
817
+ properties: { ...fields },
818
+ _mappingOptions: {
819
+ dynamic: "strict",
820
+ ...options
821
+ }
822
+ };
823
+ };
824
+ //#endregion
825
+ //#region src/field.helpers.ts
826
+ const text = (options) => ({
827
+ type: "text",
828
+ ...options
829
+ });
830
+ const keyword = (options) => ({
831
+ type: "keyword",
832
+ ...options
833
+ });
834
+ const long = (options) => ({
835
+ type: "long",
836
+ ...options
837
+ });
838
+ const integer = (options) => ({
839
+ type: "integer",
840
+ ...options
841
+ });
842
+ const short = (options) => ({
843
+ type: "short",
844
+ ...options
845
+ });
846
+ const byte = (options) => ({
847
+ type: "byte",
848
+ ...options
849
+ });
850
+ const double = (options) => ({
851
+ type: "double",
852
+ ...options
853
+ });
854
+ const float = (options) => ({
855
+ type: "float",
856
+ ...options
857
+ });
858
+ const halfFloat = (options) => ({
859
+ type: "half_float",
860
+ ...options
861
+ });
862
+ const scaledFloat = (options) => ({
863
+ type: "scaled_float",
864
+ ...options
865
+ });
866
+ const date = (options) => ({
867
+ type: "date",
868
+ ...options
869
+ });
870
+ /**
871
+ * Date field stored with nanosecond precision. Same API as `date()` but supports sub-millisecond timestamps.
872
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/date_nanos.html
873
+ */
874
+ const dateNanos = (options) => ({
875
+ type: "date_nanos",
876
+ ...options
877
+ });
878
+ const boolean = (options) => ({
879
+ type: "boolean",
880
+ ...options
881
+ });
882
+ const binary = () => ({ type: "binary" });
883
+ const ip = (options) => ({
884
+ type: "ip",
885
+ ...options
886
+ });
887
+ const denseVector = (options) => ({
888
+ type: "dense_vector",
889
+ ...options
890
+ });
891
+ /**
892
+ * Quantized dense vector field — wraps `denseVector()` with `int8_hnsw` index type.
893
+ *
894
+ * Quantizes float32 vectors to int8 at index time, saving ~75% memory.
895
+ * Recommended for vectors with dims >= 384. Original float vectors are retained
896
+ * in the index, enabling a two-phase search pattern:
897
+ *
898
+ * 1. Fast approximate search using quantized int8 vectors
899
+ * 2. Precise rescore of top-k results using retained float vectors
900
+ *
901
+ * @example
902
+ * // Index mapping
903
+ * const schema = mappings({
904
+ * title: text(),
905
+ * embedding: quantizedDenseVector({ dims: 768, similarity: 'cosine' }),
906
+ * }, {
907
+ * _source: { excludes: ['embedding'] },
908
+ * });
909
+ *
910
+ * // Two-phase search: fast kNN + precise rescore
911
+ * const result = queryBuilder(schema)
912
+ * .knn('embedding', queryVector, { k: 100, num_candidates: 200 })
913
+ * .rescore(
914
+ * (q) => q.scriptScore(
915
+ * (inner) => inner.matchAll(),
916
+ * { source: "cosineSimilarity(params.v, 'embedding') + 1.0", params: { v: queryVector } }
917
+ * ),
918
+ * 100
919
+ * )
920
+ * .build();
921
+ */
922
+ const quantizedDenseVector = (options) => ({
923
+ type: "dense_vector",
924
+ ...options,
925
+ index_options: {
926
+ type: "int8_hnsw",
927
+ ...options?.index_options
928
+ }
929
+ });
930
+ /**
931
+ * Sparse vector field — stores token-weight pairs for sparse retrieval (e.g. ELSER/BM25-style models).
932
+ * Complement to `denseVector()`. Query with the `sparse_vector` query.
933
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/sparse-vector.html
934
+ */
935
+ const sparseVector = () => ({ type: "sparse_vector" });
936
+ /**
937
+ * Rank feature field — a single numeric feature used by the `rank_feature` query to boost relevance.
938
+ * Use when each document has one named signal (e.g. `pagerank`, `popularity_score`).
939
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/rank-feature.html
940
+ */
941
+ const rankFeature = (options) => ({
942
+ type: "rank_feature",
943
+ ...options
944
+ });
945
+ /**
946
+ * Rank features field — a sparse map of numeric features used by the `rank_feature` query.
947
+ * Use when each document has many named signals (e.g. topic scores).
948
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/rank-features.html
949
+ */
950
+ const rankFeatures = (options) => ({
951
+ type: "rank_features",
952
+ ...options
953
+ });
954
+ /**
955
+ * Semantic text field (ES 9.x) — ML-powered text field for semantic and hybrid search.
956
+ * Automatically generates and stores embeddings at index time using the configured inference endpoint.
957
+ *
958
+ * @example
959
+ * const schema = mappings({
960
+ * title: text(),
961
+ * body: semanticText({ inference_id: 'my-elser-endpoint' }),
962
+ * });
963
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html
964
+ */
965
+ const semanticText = (options) => ({
966
+ type: "semantic_text",
967
+ ...options
968
+ });
969
+ /**
970
+ * Unsigned long field (ES 9.0+) — stores unsigned 64-bit integer values (0 to 2^64-1).
971
+ * Use when values exceed the `long` range.
972
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/number.html
973
+ */
974
+ const unsignedLong = (options) => ({
975
+ type: "unsigned_long",
976
+ ...options
977
+ });
978
+ const geoPoint = (options) => ({
979
+ type: "geo_point",
980
+ ...options
981
+ });
982
+ const geoShape = (options) => ({
983
+ type: "geo_shape",
984
+ ...options
985
+ });
986
+ const completion = (options) => ({
987
+ type: "completion",
988
+ ...options
989
+ });
990
+ /**
991
+ * Object field — for JSON-like structured documents where sub-fields are queried with dot-notation.
992
+ *
993
+ * The most common way to model structured data (e.g. `{ address: { city, zip } }`).
994
+ * Sub-fields are indexed inline within the parent document — no special query wrapper needed.
995
+ * Query sub-fields directly using dot-notation: `.term('address.city', 'NYC')`.
996
+ *
997
+ * Use `nested()` instead when you have **arrays of objects** and need cross-field queries
998
+ * within each element to be accurate (e.g. tags with both a label and weight).
999
+ *
1000
+ * @example
1001
+ * const m = mappings({
1002
+ * address: object({
1003
+ * street: text(),
1004
+ * city: keyword(),
1005
+ * zip: keyword(),
1006
+ * }),
1007
+ * });
1008
+ * queryBuilder(m).term('address.city', 'NYC').build();
1009
+ */
1010
+ function object(fields, options) {
1011
+ return {
1012
+ type: "object",
1013
+ ...options ?? {},
1014
+ properties: fields
1015
+ };
1016
+ }
1017
+ /**
1018
+ * Nested field — for **arrays of objects** where cross-field queries within each element must be accurate.
1019
+ *
1020
+ * Each nested object is stored as a separate hidden Elasticsearch document, preserving the
1021
+ * relationship between sub-fields within each element. Without `nested`, Elasticsearch flattens
1022
+ * array sub-fields and loses which values belong to the same element.
1023
+ *
1024
+ * Queries on nested fields **must** use the `.nested()` query builder method — direct dot-notation
1025
+ * queries will not find nested documents.
1026
+ *
1027
+ * Use `object()` instead for single structured objects (addresses, names, etc.) — it is simpler,
1028
+ * more efficient, and does not require a query wrapper.
1029
+ *
1030
+ * @example
1031
+ * const m = mappings({
1032
+ * tags: nested({
1033
+ * label: keyword(),
1034
+ * weight: float(),
1035
+ * }),
1036
+ * });
1037
+ * queryBuilder(m).nested('tags', q => q.term('label', 'sale')).build();
1038
+ */
1039
+ function nested(fields, options) {
1040
+ return {
1041
+ type: "nested",
1042
+ ...options ?? {},
1043
+ properties: fields
1044
+ };
1045
+ }
1046
+ const alias = (options) => ({
1047
+ type: "alias",
1048
+ ...options
1049
+ });
1050
+ const percolator = () => ({ type: "percolator" });
1051
+ const integerRange = (options) => ({
1052
+ type: "integer_range",
1053
+ ...options
1054
+ });
1055
+ const floatRange = (options) => ({
1056
+ type: "float_range",
1057
+ ...options
1058
+ });
1059
+ const longRange = (options) => ({
1060
+ type: "long_range",
1061
+ ...options
1062
+ });
1063
+ const doubleRange = (options) => ({
1064
+ type: "double_range",
1065
+ ...options
1066
+ });
1067
+ const dateRange = (options) => ({
1068
+ type: "date_range",
1069
+ ...options
1070
+ });
1071
+ /**
1072
+ * IP range field — stores a range of IPv4/IPv6 addresses.
1073
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/range.html
1074
+ */
1075
+ const ipRange = (options) => ({
1076
+ type: "ip_range",
1077
+ ...options
1078
+ });
1079
+ /**
1080
+ * No-score text field — faster and uses less disk than `text()`.
1081
+ * Use when you only need filter/match but not relevance scoring (e.g., logs, simple field matches).
1082
+ */
1083
+ const matchOnlyText = (options) => ({
1084
+ type: "match_only_text",
1085
+ ...options
1086
+ });
1087
+ /**
1088
+ * Autocomplete / typeahead field. Creates sub-fields for edge n-gram matching
1089
+ * out of the box. Query with `multi_match` targeting the generated sub-fields.
1090
+ */
1091
+ const searchAsYouType = (options) => ({
1092
+ type: "search_as_you_type",
1093
+ ...options
1094
+ });
1095
+ /**
1096
+ * Field where every document has the same value. Useful for multi-index queries
1097
+ * to identify the index type (e.g., `constantKeyword({ value: 'product' })`).
1098
+ */
1099
+ const constantKeyword = (options) => ({
1100
+ type: "constant_keyword",
1101
+ ...options
1102
+ });
1103
+ /**
1104
+ * Optimized for grep-like wildcard/regexp queries on high-cardinality or large fields.
1105
+ * Use instead of `keyword()` when leading wildcards (`*foo`) are needed.
1106
+ */
1107
+ const wildcardField = (options) => ({
1108
+ type: "wildcard",
1109
+ ...options
1110
+ });
1111
+ /**
1112
+ * Flattens complex/dynamic objects into a single field. Faster than `nested()`,
1113
+ * but only supports keyword-level queries on inner values.
1114
+ */
1115
+ const flattened = (options) => ({
1116
+ type: "flattened",
1117
+ ...options
1118
+ });
1119
+ /**
1120
+ * Token count field — stores the number of tokens produced by an analyzer.
1121
+ * Useful for enforcing minimum/maximum field lengths via queries or aggregations.
1122
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/token-count.html
1123
+ */
1124
+ const tokenCount = (options) => ({
1125
+ type: "token_count",
1126
+ ...options
1127
+ });
1128
+ /**
1129
+ * Murmur3 hash field — computes and stores a murmur3 hash of field values at index time.
1130
+ * Requires the `mapper-murmur3` plugin.
1131
+ * @see https://www.elastic.co/guide/en/elasticsearch/plugins/current/mapper-murmur3.html
1132
+ */
1133
+ const murmur3Hash = () => ({ type: "murmur3" });
1134
+ /**
1135
+ * Join field — defines parent/child relationships within a single index.
1136
+ * `relations` is required: maps parent names to one or more child names.
1137
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html
1138
+ *
1139
+ * @example
1140
+ * const m = mappings({
1141
+ * title: text(),
1142
+ * relation: join({ relations: { question: 'answer' } }),
1143
+ * });
1144
+ */
1145
+ const join = (options) => ({
1146
+ type: "join",
1147
+ ...options
1148
+ });
1149
+ //#endregion
1150
+ //#region src/multi-search.builder.ts
1151
+ /**
1152
+ * Creates a multi-search builder
1153
+ * @returns MSearchBuilder instance
1154
+ */
1155
+ const createMSearchBuilder = (searches = [], params = {}) => ({
1156
+ add: (request) => {
1157
+ return createMSearchBuilder([...searches, request], params);
1158
+ },
1159
+ addQuery: (body, header = {}) => {
1160
+ return createMSearchBuilder([...searches, {
1161
+ header,
1162
+ body
1163
+ }], params);
1164
+ },
1165
+ addQueryBuilder: (qb, header = {}) => {
1166
+ return createMSearchBuilder([...searches, {
1167
+ header,
1168
+ body: qb.build()
1169
+ }], params);
1170
+ },
1171
+ withParams: (next) => {
1172
+ return createMSearchBuilder(searches, {
1173
+ ...params,
1174
+ ...next
1175
+ });
1176
+ },
1177
+ build: () => {
1178
+ return `${searches.map(({ header, body }) => {
1179
+ return `${JSON.stringify(header || {})}\n${JSON.stringify(body)}`;
1180
+ }).join("\n")}\n`;
1181
+ },
1182
+ buildArray: () => {
1183
+ return searches.flatMap(({ header, body }) => [header || {}, body]);
1184
+ },
1185
+ buildParams: () => params
1186
+ });
1187
+ /**
1188
+ * Create a new multi-search builder
1189
+ * @example
1190
+ * const ms = msearch(productMappings)
1191
+ * .addQueryBuilder(queryBuilder(productMappings).match('name', 'shoe'), { index: 'products' })
1192
+ * .addQueryBuilder(queryBuilder(productMappings).match('name', 'shirt'), { index: 'products' })
1193
+ * .withParams({ max_concurrent_searches: 5, typed_keys: true })
1194
+ * .build();
1195
+ */
1196
+ const msearch = (_schema) => createMSearchBuilder();
1197
+ //#endregion
1198
+ //#region src/bulk.builder.ts
1199
+ /**
1200
+ * Creates a bulk operations builder
1201
+ * @returns BulkBuilder instance
1202
+ */
1203
+ const createBulkBuilder = (operations = []) => ({
1204
+ index: (doc, meta = {}) => {
1205
+ return createBulkBuilder([
1206
+ ...operations,
1207
+ { index: meta },
1208
+ doc
1209
+ ]);
1210
+ },
1211
+ create: (doc, meta = {}) => {
1212
+ return createBulkBuilder([
1213
+ ...operations,
1214
+ { create: meta },
1215
+ doc
1216
+ ]);
1217
+ },
1218
+ update: (meta) => {
1219
+ const { doc, script, upsert, doc_as_upsert, detect_noop, scripted_upsert, _source, ...header } = meta;
1220
+ const updateDoc = {
1221
+ ...doc && { doc },
1222
+ ...script && { script },
1223
+ ...upsert && { upsert },
1224
+ ...doc_as_upsert !== void 0 && { doc_as_upsert },
1225
+ ...detect_noop !== void 0 && { detect_noop },
1226
+ ...scripted_upsert !== void 0 && { scripted_upsert },
1227
+ ..._source !== void 0 && { _source }
1228
+ };
1229
+ return createBulkBuilder([
1230
+ ...operations,
1231
+ { update: header },
1232
+ updateDoc
1233
+ ]);
1234
+ },
1235
+ delete: (meta) => {
1236
+ return createBulkBuilder([...operations, { delete: meta }]);
1237
+ },
1238
+ build: () => {
1239
+ return `${operations.map((op) => JSON.stringify(op)).join("\n")}\n`;
1240
+ },
1241
+ buildArray: () => operations
1242
+ });
1243
+ /**
1244
+ * Create a new bulk operations builder.
1245
+ * `.build()` returns an NDJSON string (newline-delimited JSON) ready to POST to `/_bulk`.
1246
+ * `.buildArray()` returns the raw operation objects if you need to inspect or transform them.
1247
+ * @example
1248
+ * const ndjson = bulk(productMappings)
1249
+ * .index({ id: '1', name: 'Product 1' }, { _index: 'products', _id: '1' })
1250
+ * .create({ id: '2', name: 'Product 2' }, { _index: 'products', _id: '2' })
1251
+ * .update({ _index: 'products', _id: '3', doc: { name: 'Updated' } })
1252
+ * .delete({ _index: 'products', _id: '4' })
1253
+ * .build(); // POST to /_bulk with Content-Type: application/x-ndjson
1254
+ */
1255
+ const bulk = (_schema) => createBulkBuilder();
1256
+ //#endregion
1257
+ //#region src/index-management.builder.ts
1258
+ const isMappingsSchema = (input) => "_fieldTypes" in input;
1259
+ /**
1260
+ * Creates an index builder.
1261
+ *
1262
+ * The optional generic `M` carries the field-type map from any mapping schema attached
1263
+ * via `.mappings(schema)`. `.mappings()` rebinds the generic to the incoming schema's
1264
+ * field-type map, so callers typically rely on inference rather than specifying `M` directly.
1265
+ *
1266
+ * @returns IndexBuilder instance
1267
+ */
1268
+ const createIndexBuilder = (state = {}) => ({
1269
+ mappings: (schemaOrFields, inlineOptions) => {
1270
+ const rawProperties = isMappingsSchema(schemaOrFields) ? schemaOrFields.properties : schemaOrFields;
1271
+ const properties = Object.fromEntries(Object.entries(rawProperties).filter(([, v]) => v !== void 0));
1272
+ const mappingOptions = {
1273
+ dynamic: "strict",
1274
+ ...isMappingsSchema(schemaOrFields) ? schemaOrFields._mappingOptions : void 0,
1275
+ ...inlineOptions
1276
+ };
1277
+ return createIndexBuilder({
1278
+ ...state,
1279
+ mappings: {
1280
+ properties,
1281
+ ...mappingOptions.dynamic !== void 0 && { dynamic: mappingOptions.dynamic },
1282
+ ...mappingOptions._source !== void 0 && { _source: mappingOptions._source },
1283
+ ...mappingOptions._meta && { _meta: mappingOptions._meta }
1284
+ }
1285
+ });
1286
+ },
1287
+ settings: (settings) => createIndexBuilder({
1288
+ ...state,
1289
+ settings
1290
+ }),
1291
+ analysis: (config) => createIndexBuilder({
1292
+ ...state,
1293
+ settings: {
1294
+ ...state.settings,
1295
+ analysis: config
1296
+ }
1297
+ }),
1298
+ alias: (name, options = {}) => {
1299
+ const aliases = state.aliases || {};
1300
+ return createIndexBuilder({
1301
+ ...state,
1302
+ aliases: {
1303
+ ...aliases,
1304
+ [name]: options
1305
+ }
1306
+ });
1307
+ },
1308
+ build: () => state
1309
+ });
1310
+ /**
1311
+ * Create a new index builder for Elasticsearch index configuration.
1312
+ *
1313
+ * Typical lifecycle:
1314
+ * 1. **Create index:** `indexBuilder().mappings(schema).settings(productionSearchSettings()).build()`
1315
+ * 2. **Bulk ingest:** Apply `fastIngestSettings()` via ES `_settings` API → bulk index →
1316
+ * apply `productionSearchSettings()` → `POST /index/_refresh`
1317
+ * 3. **Normal operations:** Query, update individual docs via `_update` API
1318
+ * 4. **Migration with mapping changes:** Create new index with new mappings → `POST _reindex`
1319
+ * from old → atomically swap alias via `POST _aliases` → delete old index
1320
+ *
1321
+ * After bulk loading, remember to refresh the index with `POST /index/_refresh`
1322
+ * to make all documents searchable.
1323
+ *
1324
+ * @example
1325
+ * const indexConfig = indexBuilder()
1326
+ * .mappings(productMappings)
1327
+ * .settings(productionSearchSettings())
1328
+ * .alias('products')
1329
+ * .build();
1330
+ */
1331
+ const indexBuilder = () => createIndexBuilder();
1332
+ //#endregion
1333
+ //#region src/settings.presets.ts
1334
+ /**
1335
+ * Balanced production settings for search workloads.
1336
+ *
1337
+ * Defaults:
1338
+ * - `number_of_replicas: 1` — one replica for redundancy
1339
+ * - `refresh_interval: '5s'` — near-real-time search without excessive refresh overhead
1340
+ *
1341
+ * @param overrides - Override or extend any setting. Applied after defaults.
1342
+ *
1343
+ * @example
1344
+ * const index = indexBuilder()
1345
+ * .mappings(schema)
1346
+ * .settings(productionSearchSettings())
1347
+ * .build();
1348
+ *
1349
+ * @example
1350
+ * // With best_compression for disk savings
1351
+ * productionSearchSettings({ codec: 'best_compression' })
1352
+ *
1353
+ * @example
1354
+ * // Restore production settings after bulk ingest
1355
+ * await client.indices.putSettings({
1356
+ * index: 'my-index',
1357
+ * body: productionSearchSettings(),
1358
+ * });
1359
+ * await client.indices.refresh({ index: 'my-index' });
1360
+ */
1361
+ const productionSearchSettings = (overrides) => ({
1362
+ number_of_replicas: 1,
1363
+ refresh_interval: "5s",
1364
+ ...overrides
1365
+ });
1366
+ /**
1367
+ * Generate index sort settings from a field→direction (or full spec) map.
1368
+ *
1369
+ * Index-time sorting improves compression (similar values stored together) and
1370
+ * enables early termination when query sort matches index sort. Combine with
1371
+ * `trackTotalHits(false)` for fastest sorted queries.
1372
+ *
1373
+ * The returned object lives at the top level of `IndicesIndexSettings` — spread it
1374
+ * directly into a settings object (do NOT wrap it under an `index` key, the
1375
+ * `@elastic/elasticsearch` client normalises that form).
1376
+ *
1377
+ * Pass a bare `'asc' | 'desc'` for simple cases; pass `{ order, mode?, missing? }`
1378
+ * when you need `mode` (`min`/`max`/`median`/`avg`) or `missing` (`_first`/`_last`).
1379
+ *
1380
+ * @param fields - Map of field names to sort direction or full spec.
1381
+ * @returns A `{ sort: IndicesIndexSegmentSort }` fragment to spread into your settings.
1382
+ *
1383
+ * @example
1384
+ * const index = indexBuilder()
1385
+ * .mappings(schema)
1386
+ * .settings({
1387
+ * ...productionSearchSettings(),
1388
+ * ...indexSortSettings({ timestamp: 'desc', status: 'asc' }),
1389
+ * })
1390
+ * .build();
1391
+ *
1392
+ * @example
1393
+ * indexSortSettings({
1394
+ * price: { order: 'asc', mode: 'min', missing: '_last' },
1395
+ * created_at: 'desc'
1396
+ * })
1397
+ */
1398
+ const indexSortSettings = (fields) => {
1399
+ const entries = Object.entries(fields);
1400
+ const normalize = (spec) => typeof spec === "string" ? { order: spec } : spec;
1401
+ const hasAnyMode = entries.some(([, spec]) => typeof spec !== "string" && spec.mode !== void 0);
1402
+ const hasAnyMissing = entries.some(([, spec]) => typeof spec !== "string" && spec.missing !== void 0);
1403
+ return { sort: {
1404
+ field: entries.map(([name]) => name),
1405
+ order: entries.map(([, spec]) => normalize(spec).order),
1406
+ ...hasAnyMode && { mode: entries.map(([, spec]) => normalize(spec).mode ?? "min") },
1407
+ ...hasAnyMissing && { missing: entries.map(([, spec]) => normalize(spec).missing ?? "_last") }
1408
+ } };
1409
+ };
1410
+ /**
1411
+ * Settings optimized for maximum indexing speed during bulk operations.
1412
+ *
1413
+ * Apply via the ES `_settings` API before starting bulk ingest, then revert
1414
+ * with `productionSearchSettings()` afterward. Do not use these as permanent
1415
+ * index settings — they trade durability and search availability for speed.
1416
+ *
1417
+ * Defaults:
1418
+ * - `refresh_interval: '-1'` — disables refresh (can improve reindex time by 70%+)
1419
+ * - `number_of_replicas: 0` — no replication overhead during ingest
1420
+ * - `translog.durability: 'async'` — async translog for faster writes
1421
+ * - `translog.sync_interval: '30s'` — less frequent translog sync
1422
+ *
1423
+ * @param overrides - Override or extend any setting. Applied after defaults.
1424
+ * `translog` overrides are deep-merged so individual translog keys can be
1425
+ * changed without clobbering the other defaults.
1426
+ *
1427
+ * @example
1428
+ * // Apply before bulk ingest via ES client
1429
+ * await client.indices.putSettings({
1430
+ * index: 'my-index',
1431
+ * body: fastIngestSettings(),
1432
+ * });
1433
+ *
1434
+ * @example
1435
+ * // With custom overrides
1436
+ * fastIngestSettings({ number_of_shards: 3 })
1437
+ */
1438
+ const fastIngestSettings = (overrides) => {
1439
+ const { translog, ...rest } = overrides ?? {};
1440
+ return {
1441
+ number_of_replicas: 0,
1442
+ refresh_interval: "-1",
1443
+ translog: {
1444
+ durability: "async",
1445
+ sync_interval: "30s",
1446
+ ...translog
1447
+ },
1448
+ ...rest
1449
+ };
1450
+ };
1451
+ //#endregion
1452
+ //#region src/index.ts
1453
+ const queryBuilder = (_schema, includeQuery = true) => createQueryBuilder({ _includeQuery: includeQuery });
1454
+ /**
1455
+ * Type inference helper for vanilla JavaScript.
1456
+ *
1457
+ * **Type-only — do not use the returned value at runtime.** This function always returns
1458
+ * `undefined`; its declared return type is a lie used to carry `Infer<typeof schema>` into
1459
+ * JSDoc `@type` annotations. Accessing properties on the return value (e.g. `inferType(s).name`)
1460
+ * will throw `TypeError`.
1461
+ *
1462
+ * @example
1463
+ * const schema = mappings({ name: text() });
1464
+ * const _Product = inferType(schema); // undefined at runtime; type-only
1465
+ *
1466
+ * /** @type {typeof _Product} *\/
1467
+ * const doc = { name: 'Laptop' };
1468
+ *
1469
+ * In TypeScript, prefer `type Product = Infer<typeof schema>` directly.
1470
+ */
1471
+ const inferType = (_schema) => void 0;
1472
+ //#endregion
1473
+ exports.aggregations = aggregations;
1474
+ exports.alias = alias;
1475
+ exports.binary = binary;
1476
+ exports.boolean = boolean;
1477
+ exports.bulk = bulk;
1478
+ exports.byte = byte;
1479
+ exports.completion = completion;
1480
+ exports.constantKeyword = constantKeyword;
1481
+ exports.date = date;
1482
+ exports.dateNanos = dateNanos;
1483
+ exports.dateRange = dateRange;
1484
+ exports.denseVector = denseVector;
1485
+ exports.double = double;
1486
+ exports.doubleRange = doubleRange;
1487
+ exports.fastIngestSettings = fastIngestSettings;
1488
+ exports.flattened = flattened;
1489
+ exports.float = float;
1490
+ exports.floatRange = floatRange;
1491
+ exports.geoPoint = geoPoint;
1492
+ exports.geoShape = geoShape;
1493
+ exports.halfFloat = halfFloat;
1494
+ exports.indexBuilder = indexBuilder;
1495
+ exports.indexSortSettings = indexSortSettings;
1496
+ exports.inferType = inferType;
1497
+ exports.integer = integer;
1498
+ exports.integerRange = integerRange;
1499
+ exports.ip = ip;
1500
+ exports.ipRange = ipRange;
1501
+ exports.join = join;
1502
+ exports.keyword = keyword;
1503
+ exports.long = long;
1504
+ exports.longRange = longRange;
1505
+ exports.mappings = mappings;
1506
+ exports.matchOnlyText = matchOnlyText;
1507
+ exports.msearch = msearch;
1508
+ exports.murmur3Hash = murmur3Hash;
1509
+ exports.nested = nested;
1510
+ exports.object = object;
1511
+ exports.percolator = percolator;
1512
+ exports.productionSearchSettings = productionSearchSettings;
1513
+ exports.quantizedDenseVector = quantizedDenseVector;
1514
+ exports.queryBuilder = queryBuilder;
1515
+ exports.rankFeature = rankFeature;
1516
+ exports.rankFeatures = rankFeatures;
1517
+ exports.scaledFloat = scaledFloat;
1518
+ exports.searchAsYouType = searchAsYouType;
1519
+ exports.semanticText = semanticText;
1520
+ exports.short = short;
1521
+ exports.sparseVector = sparseVector;
1522
+ exports.suggest = suggest;
1523
+ exports.text = text;
1524
+ exports.tokenCount = tokenCount;
1525
+ exports.unsignedLong = unsignedLong;
1526
+ exports.wildcardField = wildcardField;
1527
+
1528
+ //# sourceMappingURL=index.cjs.map