UNPKG

itemsjs

Version:

Created to perform fast search on small json dataset (up to 1000 elements).

384 lines (283 loc) 9.17 kB
const _ = require('./../vendor/lodash'); const FastBitSet = require('fastbitset'); const clone = function(val) { try { return JSON.parse(JSON.stringify(val)); } catch (e) { return val; } }; const humanize = function (str) { return str .replace(/^[\s_]+|[\s_]+$/g, '') .replace(/[_\s]+/g, ' ') .replace(/^[a-z]/, function(m) { return m.toUpperCase(); }); }; const combination_indexes = function(facets, filters) { const indexes = {}; _.mapValues(filters, function(filter) { // filter is still array so disjunctive if (Array.isArray(filter[0])) { let facet_union = new FastBitSet([]); let filter_keys = []; _.mapValues(filter, function(disjunctive_filter) { const filter_key = disjunctive_filter[0]; const filter_val = disjunctive_filter[1]; filter_keys.push(filter_key); facet_union = facet_union.new_union(facets['bits_data'][filter_key][filter_val]); indexes[filter_key] = facet_union; }); } }); return indexes; } /* * returns facets and ids */ const matrix = function(facets, filters) { const temp_facet = _.clone(facets); filters = filters || []; _.mapValues(temp_facet['bits_data'], function(values, key) { _.mapValues(temp_facet['bits_data'][key], function(facet_indexes, key2) { temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data'][key][key2]; }); }); let conjunctive_index; let disjunctive_indexes = combination_indexes(facets, filters); /** * process only conjunctive filters */ _.mapValues(filters, function(filter) { if (!Array.isArray(filter[0])) { const filter_key = filter[0]; const filter_val = filter[1]; if (conjunctive_index && temp_facet['bits_data_temp'][filter_key][filter_val]) { conjunctive_index = temp_facet['bits_data_temp'][filter_key][filter_val].new_intersection(conjunctive_index); } else if (conjunctive_index && !temp_facet['bits_data_temp'][filter_key][filter_val]) { conjunctive_index = new FastBitSet([]); } else { conjunctive_index = temp_facet['bits_data_temp'][filter_key][filter_val]; } } }); // cross all facets with conjunctive index if (conjunctive_index) { _.mapValues(temp_facet['bits_data_temp'], function(values, key) { _.mapValues(temp_facet['bits_data_temp'][key], function(facet_indexes, key2) { temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_intersection(conjunctive_index); }); }); } // cross all combination indexes with conjunctive index /*if (conjunctive_index) { _.mapValues(disjunctive_indexes, function(disjunctive_index, disjunctive_key) { disjunctive_indexes[disjunctive_key] = conjunctive_index.new_intersection(disjunctive_indexes[disjunctive_key]); }); }*/ /** * process only negative filters */ _.mapValues(filters, function(filter) { if (filter.length === 3 && filter[1] === '-') { const filter_key = filter[0]; const filter_val = filter[2]; const negative_bits = temp_facet['bits_data_temp'][filter_key][filter_val].clone(); _.mapValues(temp_facet['bits_data_temp'], function(values, key) { _.mapValues(temp_facet['bits_data_temp'][key], function(facet_indexes, key2) { temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_difference(negative_bits); }); }); } }); // cross all facets with disjunctive index _.mapValues(temp_facet['bits_data_temp'], function(values, key) { _.mapValues(temp_facet['bits_data_temp'][key], function(facet_indexes, key2) { _.mapValues(disjunctive_indexes, function(disjunctive_index, disjunctive_key) { if (disjunctive_key !== key) { temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_intersection(disjunctive_index); } }); }); }); return temp_facet; }; const index = function(items, fields) { fields = fields || []; const facets = { data: {}, bits_data: {}, bits_data_temp: {}, }; let i = 1; items = _.map(items, item => { if (!item['_id']) { item['_id'] = i; ++i; } return item; }); // replace chain with forEach _.chain(items) .map(item => { fields.forEach(field => { //if (!item || !item[field]) { if (!item) { return; } if (!facets['data'][field]) { facets['data'][field] = {}; } if (Array.isArray(item[field])) { item[field].forEach(v => { if (!item[field]) { return; } if (!facets['data'][field][v]) { facets['data'][field][v] = []; } facets['data'][field][v].push(parseInt(item._id)); }); } else if (typeof item[field] !== 'undefined') { const v = item[field]; if (!facets['data'][field][v]) { facets['data'][field][v] = []; } facets['data'][field][v].push(parseInt(item._id)); } }); return item; }) .value(); facets['data'] = _.mapValues(facets['data'], function(values, field) { if (!facets['bits_data'][field]) { facets['bits_data'][field] = {}; facets['bits_data_temp'][field] = {}; } //console.log(values); return _.mapValues(values, function(indexes, filter) { const sorted_indexes = _.sortBy(indexes); facets['bits_data'][field][filter] = new FastBitSet(sorted_indexes); return sorted_indexes; }); }); return facets; }; /** * calculates ids for facets * if there is no facet input then return null to not save resources for OR calculation * null means facets haven't crossed searched items */ const facets_ids = function(facets_data, filters) { let output = new FastBitSet([]); let i = 0; _.mapValues(filters, function(filters, field) { //console.log(facets_data); filters.forEach(filter => { ++i; output = output.new_union(facets_data[field][filter]); }); }); if (i === 0) { return null; } return output; }; const getBuckets = function(data, input, aggregations) { let position = 1; return _.mapValues(data['bits_data_temp'], (v, k) => { let order; let sort; let size; let title; if (aggregations[k]) { order = aggregations[k].order; sort = aggregations[k].sort; size = aggregations[k].size; title = aggregations[k].title; } let buckets = _.chain(v) .toPairs().map(v2 => { let filters = []; if (input && input.filters && input.filters[k]) { filters = input.filters[k]; } return { key: v2[0], doc_count: v2[1].array().length, selected: filters.indexOf(v2[0]) !== -1 }; }) .value(); if (sort === 'term') { buckets = _.orderBy(buckets, ['selected', 'key'], ['desc', order || 'asc']); } else { buckets = _.orderBy(buckets, ['selected', 'doc_count', 'key'], ['desc', order || 'desc', 'asc']); } buckets = buckets.slice(0, size || 10); return { name: k, title: title || humanize(k), position: position++, buckets: buckets }; }); }; const mergeAggregations = function(aggregations, input) { return _.mapValues(clone(aggregations), (val, key) => { if (!val.field) { val.field = key; } let filters = []; if (input.filters && input.filters[key]) { filters = input.filters[key]; } val.filters = filters; let not_filters = []; if (input.not_filters && input.not_filters[key]) { not_filters = input.not_filters[key]; } if (input.exclude_filters && input.exclude_filters[key]) { not_filters = input.exclude_filters[key]; } val.not_filters = not_filters; return val; }); }; const input_to_facet_filters = function(input, config) { let filters = []; _.mapValues(input.filters, function(values, key) { if (values && values.length) { if (config[key].conjunction !== false) { _.mapValues(values, function(values2) { filters.push([key, values2]); }); } else { const temp = []; _.mapValues(values, function(values2) { temp.push([key, values2]); }); filters.push(temp); } } }); _.mapValues(input.not_filters, function(values, key) { if (values && values.length) { _.mapValues(values, function(values2) { filters.push([key, '-', values2]); }); } }); return filters; }; module.exports.input_to_facet_filters = input_to_facet_filters; module.exports.facets_ids = facets_ids; module.exports.clone = clone; module.exports.humanize = humanize; module.exports.index = index; module.exports.combination_indexes = combination_indexes; module.exports.matrix = matrix; module.exports.getBuckets = getBuckets; module.exports.getFacets = getBuckets; module.exports.mergeAggregations = mergeAggregations;