46 lines
1.2 KiB
JavaScript
46 lines
1.2 KiB
JavaScript
function normalizeTitle(str) {
|
|
return str ? str.toLowerCase().replace(/[^a-z0-9]/g, '') : '';
|
|
}
|
|
|
|
// Compute match score: exact > startsWith > includes > other
|
|
function getMatchScore(film, query) {
|
|
const nTitle = normalizeTitle(film.title);
|
|
const nQuery = normalizeTitle(query);
|
|
if (nTitle === nQuery) return 100;
|
|
if (nTitle.startsWith(nQuery)) return 80;
|
|
if (nTitle.includes(nQuery)) return 60;
|
|
return 10;
|
|
}
|
|
|
|
// Merge and rank by match
|
|
function mergeResults(arrays, query = '', limit = 5) {
|
|
const map = {};
|
|
arrays.flat().forEach(entry => {
|
|
const key = normalizeTitle(entry.title) + (entry.year ? '|' + entry.year : '');
|
|
if (!map[key]) {
|
|
map[key] = {
|
|
title: entry.title,
|
|
year: entry.year,
|
|
results: [],
|
|
__raw: entry // For tie-break
|
|
};
|
|
}
|
|
map[key].results.push({
|
|
source: entry.source,
|
|
...entry
|
|
});
|
|
});
|
|
let out = Object.values(map);
|
|
if (query) {
|
|
out.forEach(f => f.__score = getMatchScore(f, query));
|
|
out = out.sort((a, b) => b.__score - a.__score);
|
|
}
|
|
// Remove internals, trim to limit
|
|
return out.slice(0, limit).map(f => {
|
|
delete f.__score; delete f.__raw;
|
|
return f;
|
|
});
|
|
}
|
|
|
|
module.exports = { mergeResults };
|