From a620e5b57c4289c610b1a54badc269680ee9306a Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Wed, 9 Jun 2021 18:18:18 +0200 Subject: [PATCH] feat(getFacetValues): process facetOrdering DX-2075 --- src/SearchResults/index.js | 134 +- .../getFacetValues-facetOrdering.js | 1486 +++++++++++++++++ .../getFacetValues/disjunctive.json | 2 +- .../getRefinements/hierarchical-cards.json | 5 +- 4 files changed, 1598 insertions(+), 29 deletions(-) create mode 100644 test/spec/SearchResults/getFacetValues-facetOrdering.js diff --git a/src/SearchResults/index.js b/src/SearchResults/index.js index 9747ba755..015e69675 100644 --- a/src/SearchResults/index.js +++ b/src/SearchResults/index.js @@ -659,20 +659,30 @@ function extractNormalizedFacetValues(results, attribute) { } /** - * Sort nodes of a hierarchical facet results + * Sort nodes of a hierarchical or disjunctive facet results * @private - * @param {HierarchicalFacet} node node to upon which we want to apply the sort + * @param {function} sortFn + * @param {HierarchicalFacet|Array} node node to upon which we want to apply the sort + * @param {string[]} names attribute names + * @param {number} [level=0] current index in the names array */ -function recSort(sortFn, node) { +function recSort(sortFn, node, names, level) { + level = level || 0; + + if (Array.isArray(node)) { + return sortFn(node, names[level]); + } + if (!node.data || node.data.length === 0) { return node; } var children = node.data.map(function(childNode) { - return recSort(sortFn, childNode); + return recSort(sortFn, childNode, names, level + 1); }); - var sortedChildren = sortFn(children); - var newNode = merge({}, node, {data: sortedChildren}); + var sortedChildren = sortFn(children, names[level]); + var newNode = merge({}, node); + newNode.data = sortedChildren; return newNode; } @@ -682,6 +692,66 @@ function vanillaSortFn(order, data) { return data.sort(order); } +/** + * @typedef FacetOrdering + * @type {Object} + * @property {string[]} [order] + * @property {'count' | 'alpha' | 'hidden'} [sortRemainingBy] + */ + +/** + * Sorts facet arrays via their facet ordering + * @param {Array} facetValues the values + * @param {FacetOrdering} facetOrdering the ordering + * @returns {Array} + */ +function sortViaFacetOrdering(facetValues, facetOrdering) { + var pinnedFacets = []; + var unpinnedFacets = []; + + var pinned = facetOrdering.order || []; + var reversePinned = pinned.reduce(function(acc, name, i) { + acc[name] = i; + return acc; + }, {}); + + facetValues.forEach(function(item) { + if (reversePinned[item.name] !== undefined) { + pinnedFacets[reversePinned[item.name]] = item; + } else { + unpinnedFacets.push(item); + } + }); + + var sortRemainingBy = facetOrdering.sortRemainingBy; + var ordering; + if (sortRemainingBy === 'hidden') { + return pinnedFacets; + } else if (sortRemainingBy === 'alpha') { + ordering = [['name'], ['asc']]; + } else { + ordering = [['count'], ['desc']]; + } + + return pinnedFacets.concat( + orderBy(unpinnedFacets, ordering[0], ordering[1]) + ); +} + +/** + * @param {SearchResults} results a results class (this) + * @param {string} attribute the attribute to retrieve ordering of + * @returns {FacetOrdering=} + */ +function getFacetOrdering(results, attribute) { + return ( + results.renderingContent && + results.renderingContent.facetOrdering && + results.renderingContent.facetOrdering.values && + results.renderingContent.facetOrdering.values[attribute] + ); +} + /** * Get a the list of values for a given facet attribute. Those values are sorted * refinement first, descending count (bigger value on top), and name ascending @@ -694,6 +764,9 @@ function vanillaSortFn(order, data) { * might not be respected if you have facet values that are already refined. * @param {string} attribute attribute name * @param {object} opts configuration options. + * @param {boolean} [opts.facetOrdering=false] + * Force the use of facetOrdering from the result if a sortBy is present. If + * sortBy isn't present, facetOrdering will be used automatically. * @param {Array. | function} opts.sortBy * When using strings, it consists of * the name of the [FacetValue](#SearchResults.FacetValue) or the @@ -735,28 +808,37 @@ SearchResults.prototype.getFacetValues = function(attribute, opts) { var options = defaultsPure({}, opts, {sortBy: SearchResults.DEFAULT_SORT}); - if (Array.isArray(options.sortBy)) { - var order = formatSort(options.sortBy, SearchResults.DEFAULT_SORT); - if (Array.isArray(facetValues)) { - return orderBy(facetValues, order[0], order[1]); - } - // If facetValues is not an array, it's an object thus a hierarchical facet object - return recSort(function(hierarchicalFacetValues) { - return orderBy(hierarchicalFacetValues, order[0], order[1]); - }, facetValues); - } else if (typeof options.sortBy === 'function') { - if (Array.isArray(facetValues)) { - return facetValues.sort(options.sortBy); + var results = this; + var attributes; + if (Array.isArray(facetValues)) { + attributes = [attribute]; + } else { + var config = results._state.getHierarchicalFacetByName(facetValues.name); + attributes = config.attributes; + } + + return recSort(function(data, facetName) { + // if no sortBy is given, attempt to sort based on facetOrdering + // if it is given, we still allow to sort via facet ordering first + if (!opts || !opts.sortBy || opts.facetOrdering) { + var facetOrdering = getFacetOrdering(results, facetName); + if (Boolean(facetOrdering)) { + return sortViaFacetOrdering(data, facetOrdering); + } } - // If facetValues is not an array, it's an object thus a hierarchical facet object - return recSort(function(data) { + + if (Array.isArray(options.sortBy)) { + var order = formatSort(options.sortBy, SearchResults.DEFAULT_SORT); + + return orderBy(data, order[0], order[1]); + } else if (typeof options.sortBy === 'function') { return vanillaSortFn(options.sortBy, data); - }, facetValues); - } - throw new Error( - 'options.sortBy is optional but if defined it must be ' + - 'either an array of string (predicates) or a sorting function' - ); + } + throw new Error( + 'options.sortBy is optional but if defined it must be ' + + 'either an array of string (predicates) or a sorting function' + ); + }, facetValues, attributes); }; /** diff --git a/test/spec/SearchResults/getFacetValues-facetOrdering.js b/test/spec/SearchResults/getFacetValues-facetOrdering.js new file mode 100644 index 000000000..0a5c39656 --- /dev/null +++ b/test/spec/SearchResults/getFacetValues-facetOrdering.js @@ -0,0 +1,1486 @@ +'use strict'; + +var SearchResults = require('../../../src/SearchResults'); +var SearchParameters = require('../../../src/SearchParameters'); + +describe('disjunctive facet', function() { + test.each([ + [ + 'nothing pinned', + { + values: { + brand: { + order: [] + } + } + }, + [ + {count: 551, isRefined: false, name: 'Insignia™'}, + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 386, isRefined: true, name: 'Apple'} + ] + ], + [ + 'all pinned', + { + values: { + brand: { + order: ['Samsung', 'Apple', 'Insignia™'] + } + } + }, + [ + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 386, isRefined: true, name: 'Apple'}, + {count: 551, isRefined: false, name: 'Insignia™'} + ] + ], + [ + 'one item pinned (implicit sort)', + { + values: { + brand: { + order: ['Samsung'] + } + } + }, + [ + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 551, isRefined: false, name: 'Insignia™'}, + {count: 386, isRefined: true, name: 'Apple'} + ] + ], + [ + 'one item pinned (sort by count)', + { + values: { + brand: { + order: ['Samsung'], + sortRemainingBy: 'count' + } + } + }, + [ + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 551, isRefined: false, name: 'Insignia™'}, + {count: 386, isRefined: true, name: 'Apple'} + ] + ], + [ + 'one item pinned (sort by alpha)', + { + values: { + brand: { + order: ['Samsung'], + sortRemainingBy: 'alpha' + } + } + }, + [ + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 386, isRefined: true, name: 'Apple'}, + {count: 551, isRefined: false, name: 'Insignia™'} + ] + ], + [ + 'one item pinned (sort by hidden)', + { + values: { + brand: { + order: ['Samsung'], + sortRemainingBy: 'hidden' + } + } + }, + [{count: 511, isRefined: false, name: 'Samsung'}] + ] + ])('%p', function(_name, facetOrdering, expected) { + var data = require('./getFacetValues/disjunctive.json'); + var order = { + renderingContent: { + facetOrdering: facetOrdering + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('brand'); + + expect(facetValues).toEqual(expected); + }); + + test('sortBy overrides facetOrdering', function() { + var data = require('./getFacetValues/disjunctive.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + brand: { + order: ['Samsung', 'Apple', 'Insignia™'] + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('brand', {sortBy: ['name:desc']}); + + var expected = [ + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 551, isRefined: false, name: 'Insignia™'}, + {count: 386, isRefined: true, name: 'Apple'} + ]; + + expect(facetValues).toEqual(expected); + }); + + test('facetOrdering: true overrides sortBy', function() { + var data = require('./getFacetValues/disjunctive.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + brand: { + order: ['Samsung', 'Apple', 'Insignia™'] + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('brand', { + sortBy: ['name:desc'], + facetOrdering: true + }); + + var expected = [ + {count: 511, isRefined: false, name: 'Samsung'}, + {count: 386, isRefined: true, name: 'Apple'}, + {count: 551, isRefined: false, name: 'Insignia™'} + ]; + + expect(facetValues).toEqual(expected); + }); + + test('without facetOrdering, nor sortBy', function() { + var data = require('./getFacetValues/disjunctive.json'); + var order = { + renderingContent: {} + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('brand'); + + var expected = [ + {count: 386, isRefined: true, name: 'Apple'}, + {count: 551, isRefined: false, name: 'Insignia™'}, + {count: 511, isRefined: false, name: 'Samsung'} + ]; + + expect(facetValues).toEqual(expected); + }); +}); + +describe('hierarchical facet', function() { + test('empty facet ordering', function() { + var data = require('./getRefinements/hierarchical-cards.json'); + var order = { + renderingContent: { + facetOrdering: {} + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('hierarchicalCategories'); + + var expected = { + name: 'hierarchicalCategories', + count: null, + isRefined: true, + path: null, + exhaustive: true, + data: [ + { + name: 'Best Buy Gift Cards', + path: 'Best Buy Gift Cards', + count: 80, + isRefined: true, + exhaustive: true, + data: [ + { + count: 17, + data: null, + exhaustive: true, + isRefined: true, + name: 'Entertainment Gift Cards', + path: 'Best Buy Gift Cards > Entertainment Gift Cards' + }, + { + name: 'Swag Gift Cards', + path: 'Best Buy Gift Cards > Swag Gift Cards', + count: 20, + isRefined: false, + exhaustive: true, + data: null + } + ] + }, + { + name: 'Cell Phones', + path: 'Cell Phones', + count: 1920, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Computers & Tablets', + path: 'Computers & Tablets', + count: 1858, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Appliances', + path: 'Appliances', + count: 1533, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Audio', + path: 'Audio', + count: 1010, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Cameras & Camcorders', + path: 'Cameras & Camcorders', + count: 753, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'TV & Home Theater', + path: 'TV & Home Theater', + count: 617, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Car Electronics & GPS', + path: 'Car Electronics & GPS', + count: 509, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Health, Fitness & Beauty', + path: 'Health, Fitness & Beauty', + count: 385, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office & School Supplies', + path: 'Office & School Supplies', + count: 302, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Video Games', + path: 'Video Games', + count: 178, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Housewares', + path: 'Housewares', + count: 135, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Electronics', + path: 'Office Electronics', + count: 122, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Toys, Games & Drones', + path: 'Toys, Games & Drones', + count: 104, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Connected Home', + path: 'Connected Home', + count: 96, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Telephones & Communication', + path: 'Telephones & Communication', + count: 76, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Wearable Technology', + path: 'Wearable Technology', + count: 68, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Name Brands', + path: 'Name Brands', + count: 55, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Paper', + path: 'Paper', + count: 48, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Musical Instruments', + path: 'Musical Instruments', + count: 40, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Household Essentials', + path: 'Household Essentials', + count: 32, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Scanners, Faxes & Copiers', + path: 'Scanners, Faxes & Copiers', + count: 25, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Furniture & Storage', + path: 'Office Furniture & Storage', + count: 14, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Movies & Music', + path: 'Movies & Music', + count: 13, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Magnolia Home Theater', + path: 'Magnolia Home Theater', + count: 10, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Furniture & Decor', + path: 'Furniture & Decor', + count: 6, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Batteries & Power', + path: 'Batteries & Power', + count: 5, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Carfi Instore Only', + path: 'Carfi Instore Only', + count: 4, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Home', + path: 'Home', + count: 1, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Microwaves', + path: 'Microwaves', + count: 1, + isRefined: false, + exhaustive: true, + data: null + } + ] + }; + + expect(facetValues).toEqual(expected); + }); + + test('root pinned (no sortRemainingBy)', function() { + var data = require('./getRefinements/hierarchical-cards.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + 'hierarchicalCategories.lvl0': { + order: ['Appliances', 'Audio', 'Scanners, Faxes & Copiers'] + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('hierarchicalCategories'); + + var expected = { + name: 'hierarchicalCategories', + count: null, + isRefined: true, + path: null, + exhaustive: true, + data: [ + { + name: 'Appliances', + path: 'Appliances', + count: 1533, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Audio', + path: 'Audio', + count: 1010, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Scanners, Faxes & Copiers', + path: 'Scanners, Faxes & Copiers', + count: 25, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Cell Phones', + path: 'Cell Phones', + count: 1920, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Computers & Tablets', + path: 'Computers & Tablets', + count: 1858, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Cameras & Camcorders', + path: 'Cameras & Camcorders', + count: 753, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'TV & Home Theater', + path: 'TV & Home Theater', + count: 617, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Car Electronics & GPS', + path: 'Car Electronics & GPS', + count: 509, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Health, Fitness & Beauty', + path: 'Health, Fitness & Beauty', + count: 385, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office & School Supplies', + path: 'Office & School Supplies', + count: 302, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Video Games', + path: 'Video Games', + count: 178, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Housewares', + path: 'Housewares', + count: 135, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Electronics', + path: 'Office Electronics', + count: 122, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Toys, Games & Drones', + path: 'Toys, Games & Drones', + count: 104, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Connected Home', + path: 'Connected Home', + count: 96, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Best Buy Gift Cards', + path: 'Best Buy Gift Cards', + count: 80, + isRefined: true, + exhaustive: true, + data: [ + { + count: 17, + data: null, + exhaustive: true, + isRefined: true, + name: 'Entertainment Gift Cards', + path: 'Best Buy Gift Cards > Entertainment Gift Cards' + }, + { + name: 'Swag Gift Cards', + path: 'Best Buy Gift Cards > Swag Gift Cards', + count: 20, + isRefined: false, + exhaustive: true, + data: null + } + ] + }, + { + name: 'Telephones & Communication', + path: 'Telephones & Communication', + count: 76, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Wearable Technology', + path: 'Wearable Technology', + count: 68, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Name Brands', + path: 'Name Brands', + count: 55, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Paper', + path: 'Paper', + count: 48, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Musical Instruments', + path: 'Musical Instruments', + count: 40, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Household Essentials', + path: 'Household Essentials', + count: 32, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Furniture & Storage', + path: 'Office Furniture & Storage', + count: 14, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Movies & Music', + path: 'Movies & Music', + count: 13, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Magnolia Home Theater', + path: 'Magnolia Home Theater', + count: 10, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Furniture & Decor', + path: 'Furniture & Decor', + count: 6, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Batteries & Power', + path: 'Batteries & Power', + count: 5, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Carfi Instore Only', + path: 'Carfi Instore Only', + count: 4, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Home', + path: 'Home', + count: 1, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Microwaves', + path: 'Microwaves', + count: 1, + isRefined: false, + exhaustive: true, + data: null + } + ] + }; + + expect(facetValues).toEqual(expected); + }); + + test('root pinned (sortRemainingBy count)', function() { + var data = require('./getRefinements/hierarchical-cards.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + 'hierarchicalCategories.lvl0': { + order: ['Appliances', 'Audio', 'Scanners, Faxes & Copiers'], + sortRemainingBy: 'count' + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('hierarchicalCategories'); + + var expected = { + name: 'hierarchicalCategories', + count: null, + isRefined: true, + path: null, + exhaustive: true, + data: [ + { + name: 'Appliances', + path: 'Appliances', + count: 1533, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Audio', + path: 'Audio', + count: 1010, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Scanners, Faxes & Copiers', + path: 'Scanners, Faxes & Copiers', + count: 25, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Cell Phones', + path: 'Cell Phones', + count: 1920, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Computers & Tablets', + path: 'Computers & Tablets', + count: 1858, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Cameras & Camcorders', + path: 'Cameras & Camcorders', + count: 753, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'TV & Home Theater', + path: 'TV & Home Theater', + count: 617, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Car Electronics & GPS', + path: 'Car Electronics & GPS', + count: 509, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Health, Fitness & Beauty', + path: 'Health, Fitness & Beauty', + count: 385, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office & School Supplies', + path: 'Office & School Supplies', + count: 302, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Video Games', + path: 'Video Games', + count: 178, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Housewares', + path: 'Housewares', + count: 135, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Electronics', + path: 'Office Electronics', + count: 122, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Toys, Games & Drones', + path: 'Toys, Games & Drones', + count: 104, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Connected Home', + path: 'Connected Home', + count: 96, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Best Buy Gift Cards', + path: 'Best Buy Gift Cards', + count: 80, + isRefined: true, + exhaustive: true, + data: [ + { + count: 17, + data: null, + exhaustive: true, + isRefined: true, + name: 'Entertainment Gift Cards', + path: 'Best Buy Gift Cards > Entertainment Gift Cards' + }, + { + name: 'Swag Gift Cards', + path: 'Best Buy Gift Cards > Swag Gift Cards', + count: 20, + isRefined: false, + exhaustive: true, + data: null + } + ] + }, + { + name: 'Telephones & Communication', + path: 'Telephones & Communication', + count: 76, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Wearable Technology', + path: 'Wearable Technology', + count: 68, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Name Brands', + path: 'Name Brands', + count: 55, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Paper', + path: 'Paper', + count: 48, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Musical Instruments', + path: 'Musical Instruments', + count: 40, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Household Essentials', + path: 'Household Essentials', + count: 32, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Furniture & Storage', + path: 'Office Furniture & Storage', + count: 14, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Movies & Music', + path: 'Movies & Music', + count: 13, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Magnolia Home Theater', + path: 'Magnolia Home Theater', + count: 10, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Furniture & Decor', + path: 'Furniture & Decor', + count: 6, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Batteries & Power', + path: 'Batteries & Power', + count: 5, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Carfi Instore Only', + path: 'Carfi Instore Only', + count: 4, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Home', + path: 'Home', + count: 1, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Microwaves', + path: 'Microwaves', + count: 1, + isRefined: false, + exhaustive: true, + data: null + } + ] + }; + + expect(facetValues).toEqual(expected); + }); + + test('root pinned (sortRemainingBy alpha)', function() { + var data = require('./getRefinements/hierarchical-cards.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + 'hierarchicalCategories.lvl0': { + order: ['Appliances', 'Audio', 'Scanners, Faxes & Copiers'], + sortRemainingBy: 'alpha' + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('hierarchicalCategories'); + + var expected = { + name: 'hierarchicalCategories', + count: null, + isRefined: true, + path: null, + exhaustive: true, + data: [ + // pinned + { + name: 'Appliances', + path: 'Appliances', + count: 1533, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Audio', + path: 'Audio', + count: 1010, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Scanners, Faxes & Copiers', + path: 'Scanners, Faxes & Copiers', + count: 25, + isRefined: false, + exhaustive: true, + data: null + }, + // ordered alphabetically + { + name: 'Batteries & Power', + path: 'Batteries & Power', + count: 5, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Best Buy Gift Cards', + path: 'Best Buy Gift Cards', + count: 80, + isRefined: true, + exhaustive: true, + data: [ + { + count: 17, + data: null, + exhaustive: true, + isRefined: true, + name: 'Entertainment Gift Cards', + path: 'Best Buy Gift Cards > Entertainment Gift Cards' + }, + { + name: 'Swag Gift Cards', + path: 'Best Buy Gift Cards > Swag Gift Cards', + count: 20, + isRefined: false, + exhaustive: true, + data: null + } + ] + }, + { + name: 'Cameras & Camcorders', + path: 'Cameras & Camcorders', + count: 753, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Car Electronics & GPS', + path: 'Car Electronics & GPS', + count: 509, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Carfi Instore Only', + path: 'Carfi Instore Only', + count: 4, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Cell Phones', + path: 'Cell Phones', + count: 1920, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Computers & Tablets', + path: 'Computers & Tablets', + count: 1858, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Connected Home', + path: 'Connected Home', + count: 96, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Furniture & Decor', + path: 'Furniture & Decor', + count: 6, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Health, Fitness & Beauty', + path: 'Health, Fitness & Beauty', + count: 385, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Home', + path: 'Home', + count: 1, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Household Essentials', + path: 'Household Essentials', + count: 32, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Housewares', + path: 'Housewares', + count: 135, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Magnolia Home Theater', + path: 'Magnolia Home Theater', + count: 10, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Microwaves', + path: 'Microwaves', + count: 1, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Movies & Music', + path: 'Movies & Music', + count: 13, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Musical Instruments', + path: 'Musical Instruments', + count: 40, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Name Brands', + path: 'Name Brands', + count: 55, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office & School Supplies', + path: 'Office & School Supplies', + count: 302, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Electronics', + path: 'Office Electronics', + count: 122, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Office Furniture & Storage', + path: 'Office Furniture & Storage', + count: 14, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Paper', + path: 'Paper', + count: 48, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'TV & Home Theater', + path: 'TV & Home Theater', + count: 617, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Telephones & Communication', + path: 'Telephones & Communication', + count: 76, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Toys, Games & Drones', + path: 'Toys, Games & Drones', + count: 104, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Video Games', + path: 'Video Games', + count: 178, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Wearable Technology', + path: 'Wearable Technology', + count: 68, + isRefined: false, + exhaustive: true, + data: null + } + ] + }; + + expect(facetValues).toEqual(expected); + }); + + test('root pinned (sortRemainingBy hidden)', function() { + var data = require('./getRefinements/hierarchical-cards.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + 'hierarchicalCategories.lvl0': { + order: ['Appliances', 'Audio', 'Scanners, Faxes & Copiers'], + sortRemainingBy: 'hidden' + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('hierarchicalCategories'); + + var expected = { + name: 'hierarchicalCategories', + count: null, + isRefined: true, + path: null, + exhaustive: true, + data: [ + { + name: 'Appliances', + path: 'Appliances', + count: 1533, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Audio', + path: 'Audio', + count: 1010, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Scanners, Faxes & Copiers', + path: 'Scanners, Faxes & Copiers', + count: 25, + isRefined: false, + exhaustive: true, + data: null + } + ] + }; + + expect(facetValues).toEqual(expected); + }); + + test('two levels pinned (sortRemainingBy count)', function() { + var data = require('./getRefinements/hierarchical-cards.json'); + var order = { + renderingContent: { + facetOrdering: { + values: { + 'hierarchicalCategories.lvl0': { + order: ['Best Buy Gift Cards'], + sortRemainingBy: 'hidden' + }, + 'hierarchicalCategories.lvl1': { + order: ['Swag Gift Cards'], + sortRemainingBy: 'count' + } + } + } + } + }; + var results = data.content.results.slice(); + results[0] = Object.assign(order, results[0]); + + var searchParams = new SearchParameters(data.state); + var result = new SearchResults(searchParams, results); + + var facetValues = result.getFacetValues('hierarchicalCategories'); + + var expected = { + name: 'hierarchicalCategories', + count: null, + isRefined: true, + path: null, + exhaustive: true, + data: [ + { + name: 'Best Buy Gift Cards', + path: 'Best Buy Gift Cards', + count: 80, + isRefined: true, + exhaustive: true, + data: [ + { + name: 'Swag Gift Cards', + path: 'Best Buy Gift Cards > Swag Gift Cards', + count: 20, + isRefined: false, + exhaustive: true, + data: null + }, + { + name: 'Entertainment Gift Cards', + path: 'Best Buy Gift Cards > Entertainment Gift Cards', + count: 17, + isRefined: true, + exhaustive: true, + data: null + } + ] + } + ] + }; + + expect(facetValues).toEqual(expected); + }); +}); diff --git a/test/spec/SearchResults/getFacetValues/disjunctive.json b/test/spec/SearchResults/getFacetValues/disjunctive.json index 06aff5c27..2248cfab9 100644 --- a/test/spec/SearchResults/getFacetValues/disjunctive.json +++ b/test/spec/SearchResults/getFacetValues/disjunctive.json @@ -1128,4 +1128,4 @@ "page": 0 }, "error": null -} \ No newline at end of file +} diff --git a/test/spec/SearchResults/getRefinements/hierarchical-cards.json b/test/spec/SearchResults/getRefinements/hierarchical-cards.json index 671aa6cd9..724e084ad 100644 --- a/test/spec/SearchResults/getRefinements/hierarchical-cards.json +++ b/test/spec/SearchResults/getRefinements/hierarchical-cards.json @@ -937,7 +937,8 @@ "Best Buy Gift Cards": 80 }, "hierarchicalCategories.lvl1": { - "Best Buy Gift Cards > Entertainment Gift Cards": 17 + "Best Buy Gift Cards > Entertainment Gift Cards": 17, + "Best Buy Gift Cards > Swag Gift Cards": 20 } }, "exhaustiveFacetsCount": true, @@ -1031,4 +1032,4 @@ "page": 0 }, "error": null -} \ No newline at end of file +}