Skip to content

Commit 58f9099

Browse files
committed
Fixed disjunctive facets.
In faceting, multi-value facet filters often send filters in the following format: ``` { all: [ { any: [ { brand: 'a' }, { brand: 'c' }, { brand: 'c' } ] }, { any: [ { size: 'small' }, { brand: 'medium' } ] } ] } ``` When determining the fields that are currently being filterd, our faceting logic was only looking at keys at the top level, so faceting logic was not being applied when it should have. We are now recursing down the filter tree to find values nested in this way.
1 parent 4a5a5a6 commit 58f9099

File tree

2 files changed

+178
-31
lines changed

2 files changed

+178
-31
lines changed

src/filters.js

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
function addPropertyIfKeysDoNotMatch(object, keyToAdd, valueToAdd, skipIfKey) {
2-
return {
3-
...object,
4-
...(keyToAdd !== skipIfKey && { [keyToAdd]: valueToAdd })
5-
};
6-
}
7-
8-
function removeObjectsFromArrayIfTheyHaveKey(array, key) {
9-
return array.filter(arrayValue => {
10-
return !Object.entries(arrayValue).find(([valueKey]) => key === valueKey);
11-
});
12-
}
13-
141
/**
152
* A helper for working with the JSON structure which represent
163
* filters in API requests.
@@ -20,30 +7,59 @@ export default class Filters {
207
this.filtersJSON = filtersJSON;
218
}
229

23-
removeFilter(filterKey) {
24-
const filtersJSON = Object.entries(this.filtersJSON).reduce(
25-
(acc, [key, value]) => {
26-
if (!["all", "any", "none"].includes(key)) {
27-
return addPropertyIfKeysDoNotMatch(acc, key, value, filterKey);
28-
}
29-
return {
30-
...acc,
31-
[key]: removeObjectsFromArrayIfTheyHaveKey(value, filterKey)
32-
};
33-
},
34-
{}
35-
);
36-
return new Filters(filtersJSON);
10+
removeFilter(filterKey, filtersMap = this.filtersJSON) {
11+
function go(filterKey, filtersMap) {
12+
const filtered = Object.entries(filtersMap).reduce(
13+
(acc, [filterName, filterValue]) => {
14+
if (filterName === filterKey) {
15+
return acc;
16+
}
17+
18+
if (["all", "any", "none"].includes(filterName)) {
19+
const nestedFiltersArray = filterValue;
20+
filterValue = nestedFiltersArray.reduce((acc, nestedFiltersMap) => {
21+
const updatedNestedFiltersMap = go(filterKey, nestedFiltersMap);
22+
if (updatedNestedFiltersMap) {
23+
return acc.concat(updatedNestedFiltersMap);
24+
} else {
25+
return acc;
26+
}
27+
}, []);
28+
}
29+
30+
return {
31+
...acc,
32+
[filterName]: filterValue
33+
};
34+
},
35+
{}
36+
);
37+
38+
if (Object.keys(filtered).length === 0) {
39+
return;
40+
}
41+
return filtered;
42+
}
43+
44+
const filtered = go(filterKey, filtersMap);
45+
return new Filters(filtered);
3746
}
3847

39-
getListOfAppliedFilters() {
40-
const set = Object.entries(this.filtersJSON).reduce((acc, [key, value]) => {
48+
getListOfAppliedFilters(filters = this.filtersJSON) {
49+
const set = Object.entries(filters).reduce((acc, [key, value]) => {
4150
if (!["all", "any", "none"].includes(key)) {
4251
acc.add(key);
4352
} else {
4453
value.forEach(nestedValue => {
4554
Object.keys(nestedValue).forEach(nestedKey => {
46-
acc.add(nestedKey);
55+
if (!["all", "any", "none"].includes(nestedKey)) {
56+
acc.add(nestedKey);
57+
} else {
58+
acc = new Set([
59+
...acc,
60+
...this.getListOfAppliedFilters(nestedValue)
61+
]);
62+
}
4763
});
4864
});
4965
}

tests/filters.spec.js

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe("Filters", () => {
2828
});
2929
});
3030

31-
test("can remove a nested filters", () => {
31+
test("can remove a filter from any, all, or none", () => {
3232
const filters = new Filters({
3333
all: [{ c: "c" }, { d: "d" }],
3434
none: [{ e: "e" }, { f: "f" }],
@@ -46,9 +46,90 @@ describe("Filters", () => {
4646
any: []
4747
});
4848
});
49+
50+
test("can remove nested filters", () => {
51+
const filters = new Filters({
52+
all: [
53+
{
54+
all: [{ c: "c" }, { d: "d" }],
55+
none: [{ e: "e" }, { e: "e1" }, { f: "f" }],
56+
any: [
57+
{
58+
g: "g"
59+
},
60+
{
61+
all: [
62+
{
63+
h: "h"
64+
},
65+
{
66+
any: [
67+
{
68+
i: "i"
69+
}
70+
]
71+
}
72+
]
73+
}
74+
]
75+
},
76+
{ j: "j" }
77+
],
78+
any: [
79+
{
80+
all: [{ k: "k" }],
81+
any: [{ l: "l" }]
82+
}
83+
]
84+
});
85+
86+
expect(
87+
filters
88+
.removeFilter("k")
89+
.removeFilter("i")
90+
.removeFilter("e").filtersJSON
91+
).toEqual({
92+
all: [
93+
{
94+
all: [{ c: "c" }, { d: "d" }],
95+
none: [{ f: "f" }],
96+
any: [
97+
{
98+
g: "g"
99+
},
100+
{
101+
all: [
102+
{
103+
h: "h"
104+
},
105+
{
106+
any: []
107+
}
108+
]
109+
}
110+
]
111+
},
112+
{ j: "j" }
113+
],
114+
any: [
115+
{
116+
all: [],
117+
any: [{ l: "l" }]
118+
}
119+
]
120+
});
121+
});
49122
});
50123

51124
describe("#getListOfAppliedFilters", () => {
125+
test("it should return a single top level value filter", () => {
126+
const filters = new Filters({
127+
b: "b"
128+
});
129+
130+
expect(filters.getListOfAppliedFilters()).toEqual(["b"]);
131+
});
132+
52133
test("it should return a list of top level filters", () => {
53134
const filters = new Filters({
54135
b: ["b", "b1"]
@@ -72,5 +153,55 @@ describe("Filters", () => {
72153
"g"
73154
]);
74155
});
156+
157+
test("it should return nested filters", () => {
158+
const filters = new Filters({
159+
all: [
160+
{
161+
all: [{ c: "c" }, { d: "d" }],
162+
none: [{ e: "e" }, { e: "e1" }, { f: "f" }],
163+
any: [
164+
{
165+
g: "g"
166+
},
167+
{
168+
all: [
169+
{
170+
h: "h"
171+
},
172+
{
173+
any: [
174+
{
175+
i: "i"
176+
}
177+
]
178+
}
179+
]
180+
}
181+
]
182+
},
183+
{ j: "j" }
184+
],
185+
any: [
186+
{
187+
all: [{ k: "k" }],
188+
any: [{ l: "l" }]
189+
}
190+
]
191+
});
192+
193+
expect(filters.getListOfAppliedFilters()).toEqual([
194+
"c",
195+
"d",
196+
"e",
197+
"f",
198+
"g",
199+
"h",
200+
"i",
201+
"j",
202+
"k",
203+
"l"
204+
]);
205+
});
75206
});
76207
});

0 commit comments

Comments
 (0)