UNPKG

@orama/orama

Version:

A complete search engine and RAG pipeline in your browser, server, or edge network with support for full-text, vector, and hybrid search in less than 2kb.

131 lines 5.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFacets = getFacets; const errors_js_1 = require("../errors.js"); const utils_js_1 = require("../utils.js"); function sortAsc(a, b) { return a[1] - b[1]; } function sortDesc(a, b) { return b[1] - a[1]; } function sortingPredicateBuilder(order = 'desc') { return order.toLowerCase() === 'asc' ? sortAsc : sortDesc; } function getFacets(orama, results, facetsConfig) { const facets = {}; const allIDs = results.map(([id]) => id); const allDocs = orama.documentsStore.getMultiple(orama.data.docs, allIDs); const facetKeys = Object.keys(facetsConfig); const properties = orama.index.getSearchablePropertiesWithTypes(orama.data.index); for (const facet of facetKeys) { let values; // Hack to guarantee the same order of ranges as specified by the user // TODO: Revisit this once components land if (properties[facet] === 'number') { const { ranges } = facetsConfig[facet]; const rangesLength = ranges.length; const tmp = Array.from({ length: rangesLength }); for (let i = 0; i < rangesLength; i++) { const range = ranges[i]; tmp[i] = [`${range.from}-${range.to}`, 0]; } values = Object.fromEntries(tmp); } facets[facet] = { count: 0, values: values ?? {} }; } const allDocsLength = allDocs.length; for (let i = 0; i < allDocsLength; i++) { const doc = allDocs[i]; for (const facet of facetKeys) { const facetValue = facet.includes('.') ? (0, utils_js_1.getNested)(doc, facet) : doc[facet]; const propertyType = properties[facet]; const facetValues = facets[facet].values; switch (propertyType) { case 'number': { const ranges = facetsConfig[facet].ranges; calculateNumberFacetBuilder(ranges, facetValues)(facetValue); break; } case 'number[]': { const alreadyInsertedValues = new Set(); const ranges = facetsConfig[facet].ranges; const calculateNumberFacet = calculateNumberFacetBuilder(ranges, facetValues, alreadyInsertedValues); for (const v of facetValue) { calculateNumberFacet(v); } break; } case 'boolean': case 'enum': case 'string': { calculateBooleanStringOrEnumFacetBuilder(facetValues, propertyType)(facetValue); break; } case 'boolean[]': case 'enum[]': case 'string[]': { const alreadyInsertedValues = new Set(); const innerType = propertyType === 'boolean[]' ? 'boolean' : 'string'; const calculateBooleanStringOrEnumFacet = calculateBooleanStringOrEnumFacetBuilder(facetValues, innerType, alreadyInsertedValues); for (const v of facetValue) { calculateBooleanStringOrEnumFacet(v); } break; } default: throw (0, errors_js_1.createError)('FACET_NOT_SUPPORTED', propertyType); } } } // TODO: We are looping again with the same previous keys, should we creat a single loop instead? for (const facet of facetKeys) { const currentFacet = facets[facet]; // Count the number of values for each facet currentFacet.count = Object.keys(currentFacet.values).length; // Sort only string-based facets if (properties[facet] === 'string') { const stringFacetDefinition = facetsConfig[facet]; const sortingPredicate = sortingPredicateBuilder(stringFacetDefinition.sort); currentFacet.values = Object.fromEntries(Object.entries(currentFacet.values) .sort(sortingPredicate) .slice(stringFacetDefinition.offset ?? 0, stringFacetDefinition.limit ?? 10)); } } return facets; } function calculateNumberFacetBuilder(ranges, values, alreadyInsertedValues) { return (facetValue) => { for (const range of ranges) { const value = `${range.from}-${range.to}`; if (alreadyInsertedValues?.has(value)) { continue; } if (facetValue >= range.from && facetValue <= range.to) { if (values[value] === undefined) { values[value] = 1; } else { values[value]++; alreadyInsertedValues?.add(value); } } } }; } function calculateBooleanStringOrEnumFacetBuilder(values, propertyType, alreadyInsertedValues) { const defaultValue = propertyType === 'boolean' ? 'false' : ''; return (facetValue) => { // String or boolean based facets const value = facetValue?.toString() ?? defaultValue; if (alreadyInsertedValues?.has(value)) { return; } values[value] = (values[value] ?? 0) + 1; alreadyInsertedValues?.add(value); }; } //# sourceMappingURL=facets.js.map