UNPKG

contexture-mongo

Version:
225 lines (224 loc) 8.44 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var facet_exports = {}; __export(facet_exports, { default: () => facet_default }); module.exports = __toCommonJS(facet_exports); var import_futil = __toESM(require("futil")); var import_fp = __toESM(require("lodash/fp.js")); var import_mongodb = require("mongodb"); let projectStageFromLabelFields = (node) => ({ $project: { count: 1, ...import_futil.default.arrayToObject( (fieldName) => `label.${fieldName}`, import_fp.default.constant(1) )(import_fp.default.flow(import_fp.default.get("label.fields"), import_fp.default.castArray)(node)) } }); let sortAndLimitIfSearching = (shouldSortAndLimit, limit) => shouldSortAndLimit ? [{ $sort: { count: -1 } }, limit !== 0 && { $limit: limit || 10 }] : []; let sortAndLimitIfNotSearching = (should, limit) => sortAndLimitIfSearching(!should, limit); let getSearchableKeysList = import_fp.default.flow( import_fp.default.getOr("_nodeFieldStr", "label.fields"), import_fp.default.castArray, import_fp.default.map((label) => label === "_nodeFieldStr" ? label : `label.${label}`) ); let getMatchesForMultipleKeywords = (list, filterWords) => ({ $and: import_fp.default.map( (option) => ({ $or: import_fp.default.map( (key) => ({ [key]: { $regex: option, $options: "i" } }), list ) }), filterWords ) }); let setMatchOperators = (node) => { const list = getSearchableKeysList(node); const filterWords = import_fp.default.compact(import_fp.default.split(/\s+/, node.optionsFilter)); return list.length > 1 ? getMatchesForMultipleKeywords(list, filterWords) : { $and: import_fp.default.map( (option) => ({ [import_fp.default.first(list)]: { $options: "i", $regex: option } }), filterWords ) }; }; let mapKeywordFilters = (node) => [ // Cast field we're searching on to string to enable regex matches on it. { $addFields: { _nodeFieldStr: { $toString: "$_id" } } }, { $match: setMatchOperators(node) } ]; let lookupLabel = (node) => import_fp.default.get("label", node) ? [ { $lookup: { from: import_fp.default.get("label.collection", node), as: "label", localField: "_id", foreignField: import_fp.default.get("label.foreignField", node) } }, { $unwind: { path: "$label", preserveNullAndEmptyArrays: true } } ] : []; let facetValueLabel = (node, label) => { if (!node.label) { return {}; } if (!node.label.fields || import_fp.default.isArray(node.label.fields)) { return { label }; } return { label: import_fp.default.flow(import_fp.default.values, import_fp.default.first)(label) }; }; let unwindPropOrField = (node) => import_fp.default.map( (field) => ({ $unwind: `$${field}` }), import_fp.default.castArray(node.unwind || node.field) ); let runSearch = ({ options, getSchema, getProvider }, node) => (filters, aggs) => getProvider(node).runSearch( options, node, getSchema(node.schema), filters, aggs ); var facet_default = { hasValue: import_fp.default.get("values.length"), filter: (node) => ({ [node.field]: { [node.mode === "exclude" ? "$nin" : "$in"]: node.isMongoId ? import_fp.default.map((v) => new import_mongodb.ObjectId(v), node.values) : node.values } }), async result(node, search, schema, config = {}) { let valueIds = import_fp.default.get("values", node); let optionsFilterAggs = import_fp.default.compact([ ...lookupLabel(node), import_fp.default.get("label.fields", node) && projectStageFromLabelFields(node), ...node.optionsFilter ? mapKeywordFilters(node) : [] ]); let results = await Promise.all([ search( import_fp.default.compact([ // Unwind allows supporting array and non array fields - for non arrays, it will treat as an array with 1 value // https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#non-array-field-path ...unwindPropOrField(node), { $group: { _id: `$${node.field}`, count: { $sum: 1 } } }, ...sortAndLimitIfNotSearching(node.optionsFilter, node.size), ...optionsFilterAggs, ...sortAndLimitIfSearching(node.optionsFilter, node.size) ]) ), search([ ...unwindPropOrField(node), { $group: { _id: `$${node.field}` } }, ...optionsFilterAggs, { $group: { _id: 1, count: { $sum: 1 } } } ]) ]).then(([options, cardinality]) => ({ cardinality: import_fp.default.get("0.count", cardinality), options: import_fp.default.map( ({ _id, label, count }) => import_futil.default.omitNil({ name: _id, count, ...facetValueLabel(node, label) }), options ) })); let lostIds = import_fp.default.difference( valueIds, import_fp.default.map( ({ name }) => import_futil.default.when(node.isMongoId, import_fp.default.toString, name), results.options ) ); let maybeMapObjectId = import_futil.default.when( node.isMongoId, import_fp.default.map((x) => new import_mongodb.ObjectId(x)) ); if (!import_fp.default.isEmpty(lostIds)) { let lostOptions = await search( import_fp.default.compact([ ...unwindPropOrField(node), { $match: { [node.field]: { $in: maybeMapObjectId(lostIds) } } }, { $group: { _id: `$${node.field}`, count: { $sum: 1 } } }, ...sortAndLimitIfNotSearching(node.optionsFilter, node.size), ...optionsFilterAggs ]) ); let zeroCountIds = import_fp.default.difference( //when values are numeric values, stringify missedValues to avoid the bug. import_fp.default.map(import_futil.default.unless(import_fp.default.isBoolean, import_fp.default.toString), lostIds), import_fp.default.map(({ _id }) => import_fp.default.toString(_id), lostOptions) ); let zeroCountOptions = []; if (!import_fp.default.isEmpty(zeroCountIds)) { zeroCountOptions = import_fp.default.map( ({ _id, label }) => ({ _id, label, count: 0 }), await runSearch(config, node)( { [node.field]: { $in: maybeMapObjectId(zeroCountIds) } }, import_fp.default.compact([ { $group: { _id: `$${node.field}` } }, ...lookupLabel(node), import_fp.default.get("label.fields", node) && projectStageFromLabelFields(node) ]) ) ); } let totalMissedOptions = import_fp.default.map(({ _id, label, count }) => ({ name: _id, count, ...facetValueLabel(node, label) }))(import_fp.default.concat(lostOptions, zeroCountOptions)); results.options = import_fp.default.concat(totalMissedOptions, results.options); } return results; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = {});