mirror of
https://github.com/samsonjs/spirit-tracker.git
synced 2026-03-25 09:25:51 +00:00
feat: SMWS suggestions
This commit is contained in:
parent
bffb5475ff
commit
0d3cc8b70b
1 changed files with 27 additions and 12 deletions
|
|
@ -41,6 +41,17 @@ const SIM_STOP_TOKENS = new Set([
|
||||||
"old",
|
"old",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const SMWS_WORD_RE = /\bsmws\b/i;
|
||||||
|
const SMWS_CODE_RE = /\b(\d{1,3}\.\d{1,4})\b/;
|
||||||
|
|
||||||
|
function smwsKeyFromName(name) {
|
||||||
|
const s = String(name || "");
|
||||||
|
if (!SMWS_WORD_RE.test(s)) return "";
|
||||||
|
const m = s.match(SMWS_CODE_RE);
|
||||||
|
return m ? m[1] : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function isNumberToken(t) {
|
function isNumberToken(t) {
|
||||||
return /^\d+$/.test(String(t || ""));
|
return /^\d+$/.test(String(t || ""));
|
||||||
}
|
}
|
||||||
|
|
@ -292,19 +303,13 @@ function topSuggestions(allAgg, limit, otherPinnedSku, mappedSkus) {
|
||||||
return scored.slice(0, limit).map((x) => x.it);
|
return scored.slice(0, limit).map((x) => x.it);
|
||||||
}
|
}
|
||||||
|
|
||||||
function recommendSimilar(
|
function recommendSimilar(allAgg, pinned, limit, otherPinnedSku, mappedSkus, isIgnoredPairFn) {
|
||||||
allAgg,
|
|
||||||
pinned,
|
|
||||||
limit,
|
|
||||||
otherPinnedSku,
|
|
||||||
mappedSkus,
|
|
||||||
isIgnoredPairFn
|
|
||||||
) {
|
|
||||||
if (!pinned || !pinned.name)
|
if (!pinned || !pinned.name)
|
||||||
return topSuggestions(allAgg, limit, otherPinnedSku, mappedSkus);
|
return topSuggestions(allAgg, limit, otherPinnedSku, mappedSkus);
|
||||||
|
|
||||||
const base = String(pinned.name || "");
|
const base = String(pinned.name || "");
|
||||||
const pinnedSku = String(pinned.sku || "");
|
const pinnedSku = String(pinned.sku || "");
|
||||||
|
const pinnedSmws = smwsKeyFromName(pinned.name || "");
|
||||||
const scored = [];
|
const scored = [];
|
||||||
|
|
||||||
for (const it of allAgg) {
|
for (const it of allAgg) {
|
||||||
|
|
@ -314,12 +319,21 @@ function recommendSimilar(
|
||||||
if (otherPinnedSku && String(it.sku) === String(otherPinnedSku)) continue;
|
if (otherPinnedSku && String(it.sku) === String(otherPinnedSku)) continue;
|
||||||
if (storesOverlap(pinned, it)) continue;
|
if (storesOverlap(pinned, it)) continue;
|
||||||
|
|
||||||
if (
|
if (typeof isIgnoredPairFn === "function" && isIgnoredPairFn(pinnedSku, String(it.sku || "")))
|
||||||
typeof isIgnoredPairFn === "function" &&
|
|
||||||
isIgnoredPairFn(pinnedSku, String(it.sku || ""))
|
|
||||||
)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// SMWS exact NUM.NUM match => force to top (requires SMWS + code match)
|
||||||
|
if (pinnedSmws) {
|
||||||
|
const k = smwsKeyFromName(it.name || "");
|
||||||
|
if (k && k === pinnedSmws) {
|
||||||
|
const stores = it.stores ? it.stores.size : 0;
|
||||||
|
const hasPrice = it.cheapestPriceNum != null ? 1 : 0;
|
||||||
|
const s = 1e9 + stores * 10 + hasPrice; // tie-break within exact matches
|
||||||
|
scored.push({ it, s });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let s = similarityScore(base, it.name || "");
|
let s = similarityScore(base, it.name || "");
|
||||||
|
|
||||||
// Small boost if either side is an unknown sku (u:...)
|
// Small boost if either side is an unknown sku (u:...)
|
||||||
|
|
@ -329,6 +343,7 @@ function recommendSimilar(
|
||||||
|
|
||||||
if (s > 0) scored.push({ it, s });
|
if (s > 0) scored.push({ it, s });
|
||||||
}
|
}
|
||||||
|
|
||||||
scored.sort((a, b) => b.s - a.s);
|
scored.sort((a, b) => b.s - a.s);
|
||||||
return scored.slice(0, limit).map((x) => x.it);
|
return scored.slice(0, limit).map((x) => x.it);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue