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