@lde/search-typesense 0.50.1 → 0.51.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"query-compiler.d.ts","sourceRoot":"","sources":["../src/query-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEzE,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,QAAQ,EAEb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,UAAU,EAIhB,MAAM,aAAa,CAAC;AAqBrB;mDACmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,KACpB,UAAU,GAAG,SAAS,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,MAAM,CAAC,CAkCtB;AA2hBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
1
+ {"version":3,"file":"query-compiler.d.ts","sourceRoot":"","sources":["../src/query-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEzE,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,QAAQ,EAEb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,UAAU,EAIhB,MAAM,aAAa,CAAC;AAqBrB;mDACmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,KACpB,UAAU,GAAG,SAAS,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,wBAA6B,GACrC,YAAY,CAAC,MAAM,CAAC,CAkCtB;AA4jBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -78,13 +78,25 @@ function compileRangeFacet(name, ranges) {
78
78
  * folded `*_search` companion(s); a localized field’s active-locale companion
79
79
  * keeps its full weight while the other locale is gently demoted (−1, floored at
80
80
  * 1), so a match in the user’s language ranks higher while cross-language matches
81
- * still surface.
81
+ * still surface. A request with no language preference demotes nothing.
82
+ *
83
+ * The fields are ordered by weight, heaviest first, and among equal weights a
84
+ * leading companion – the request’s language, or the first tagged locale –
85
+ * comes first. Typesense stems the
86
+ * query once, with the settings of the highest-weighted field – the first one
87
+ * on a tie (https://github.com/typesense/typesense/issues/1815) – so an
88
+ * unstemmed companion in the lead leaves the query unstemmed, and the stemmed
89
+ * companions match only where the stem happens to equal the word. The order
90
+ * changes nothing else: every field carries an explicit weight.
82
91
  */
83
92
  function queryFields(searchType, locale, schema) {
84
- const names = [];
85
- const weights = [];
86
- collectSearchable(searchType, locale, schema, '', new Set(), names, weights);
87
- return { names, weights };
93
+ const fields = [];
94
+ collectSearchable(searchType, locale, schema, '', new Set(), fields);
95
+ fields.sort((left, right) => right.weight - left.weight || Number(right.leads) - Number(left.leads));
96
+ return {
97
+ names: fields.map((field) => field.name),
98
+ weights: fields.map((field) => field.weight),
99
+ };
88
100
  }
89
101
  /**
90
102
  * Collect the searchable physical fields of a type and of everything it nests,
@@ -105,7 +117,7 @@ function queryFields(searchType, locale, schema) {
105
117
  * boundary the collection's own walk cuts at, so no field is declared indexed
106
118
  * that this walk does not ask for.
107
119
  */
108
- function collectSearchable(searchType, locale, schema, prefix, onPath, names, weights) {
120
+ function collectSearchable(searchType, locale, schema, prefix, onPath, fields) {
109
121
  const walked = new Set(onPath).add(searchType.name);
110
122
  for (const field of searchableFields(searchType)) {
111
123
  const search = physicalFields(field).search;
@@ -113,20 +125,34 @@ function collectSearchable(searchType, locale, schema, prefix, onPath, names, we
113
125
  if (field.kind === 'text') {
114
126
  const locales = field.locales;
115
127
  const activeLocale = matchLanguage(locale, locales)[0];
128
+ const noPreference = activeLocale === undefined || activeLocale === 'und';
129
+ // Without a preference the first tagged locale stems the query; only an
130
+ // `und`-only field leads with `und`.
131
+ const leading = noPreference
132
+ ? (locales.find((candidate) => candidate !== 'und') ?? activeLocale)
133
+ : activeLocale;
116
134
  search.forEach((name, index) => {
117
- names.push(qualify(prefix, name));
118
- // The active locale keeps full weight; `und` is language-neutral, so
119
- // it is never demoted (an untagged-only field would otherwise always
120
- // rank below its declared weight).
121
- weights.push(locales[index] === activeLocale || locales[index] === 'und'
122
- ? baseWeight
123
- : Math.max(1, baseWeight - 1));
135
+ fields.push({
136
+ name: qualify(prefix, name),
137
+ // The active locale keeps full weight; `und` is language-neutral, so
138
+ // it is never demoted (an untagged-only field would otherwise always
139
+ // rank below its declared weight).
140
+ weight: noPreference ||
141
+ locales[index] === activeLocale ||
142
+ locales[index] === 'und'
143
+ ? baseWeight
144
+ : Math.max(1, baseWeight - 1),
145
+ leads: locales[index] === leading,
146
+ });
124
147
  });
125
148
  }
126
149
  else {
127
150
  for (const name of search) {
128
- names.push(qualify(prefix, name));
129
- weights.push(baseWeight);
151
+ fields.push({
152
+ name: qualify(prefix, name),
153
+ weight: baseWeight,
154
+ leads: false,
155
+ });
130
156
  }
131
157
  }
132
158
  }
@@ -155,7 +181,7 @@ function collectSearchable(searchType, locale, schema, prefix, onPath, names, we
155
181
  if (onPath.has(nestedType.name)) {
156
182
  continue;
157
183
  }
158
- collectSearchable(nestedType, locale, schema, qualify(prefix, field.name), walked, names, weights);
184
+ collectSearchable(nestedType, locale, schema, qualify(prefix, field.name), walked, fields);
159
185
  }
160
186
  }
161
187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-typesense",
3
- "version": "0.50.1",
3
+ "version": "0.51.1",
4
4
  "description": "Typesense implementation of the @lde/search SearchEngine port: collection-schema builder, query compiler, label-resolving result reconstruction, and blue/green index lifecycle. Engine-specific (Typesense) but domain-agnostic.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -36,6 +36,6 @@
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@lde/dataset": "^0.10.1",
39
- "@lde/pipeline": "^0.44.0"
39
+ "@lde/pipeline": "^0.45.0"
40
40
  }
41
41
  }