Skip to content

Commit 9b426a4

Browse files
committed
feat(loki): allow sorting on nested properties (#31)
(from techfort/LokiJS#574)
1 parent 4f4a182 commit 9b426a4

File tree

4 files changed

+102
-15
lines changed

4 files changed

+102
-15
lines changed

packages/loki/spec/generic/sortingIndexing.spec.js

+41
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,23 @@ describe("sorting and indexing", () => {
6363
});
6464
});
6565

66+
describe("resultset simplesort on nested properties", () => {
67+
it("works", function () {
68+
const rss = db.addCollection("rssort");
69+
70+
rss.insert({foo: {a: 4, b: 2}});
71+
rss.insert({foo: {a: 7, b: 1}});
72+
rss.insert({foo: {a: 3, b: 4}});
73+
rss.insert({foo: {a: 9, b: 5}});
74+
75+
const results = rss.chain().simplesort("foo.a").data();
76+
expect(results[0].foo.a).toBe(3);
77+
expect(results[1].foo.a).toBe(4);
78+
expect(results[2].foo.a).toBe(7);
79+
expect(results[3].foo.a).toBe(9);
80+
});
81+
});
82+
6683
describe("resultset simplesort with dates", () => {
6784
it("works", () => {
6885
const now = new Date().getTime();
@@ -131,6 +148,30 @@ describe("sorting and indexing", () => {
131148
});
132149
});
133150

151+
describe("resultset compoundsort on nested properties works correctly", () => {
152+
it("works", function () {
153+
const db = new loki("test.db");
154+
const coll = db.addCollection("coll");
155+
156+
coll.insert([{a: 1, z: {y: {b: 9, c: "first"}}}, {a: 5, z: {y: {b: 7, c: "second"}}}, {
157+
a: 2,
158+
z: {y: {b: 9, c: "third"}}
159+
}]);
160+
161+
let result = coll.chain().compoundsort(["z.y.b", "z.y.c"]).data();
162+
expect(result.length).toEqual(3);
163+
expect(result[0].a).toEqual(5);
164+
expect(result[1].a).toEqual(1);
165+
expect(result[2].a).toEqual(2);
166+
167+
result = coll.chain().compoundsort(["z.y.b", ["z.y.c", true]]).data();
168+
expect(result.length).toEqual(3);
169+
expect(result[0].a).toEqual(5);
170+
expect(result[1].a).toEqual(2);
171+
expect(result[2].a).toEqual(1);
172+
});
173+
});
174+
134175
describe("collection indexing", () => {
135176
it("mixed types sort as expected", () => {
136177
const coll = db.addCollection("coll");

packages/loki/src/collection.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -729,12 +729,20 @@ export class Collection extends LokiEventEmitter {
729729
this.binaryIndices[property] = index;
730730

731731
const wrappedComparer =
732-
(((p, data) => (a, b) => {
733-
const objAp = data[a][p];
734-
const objBp = data[b][p];
735-
if (objAp !== objBp) {
736-
if (ltHelper(objAp, objBp, false)) return -1;
737-
if (gtHelper(objAp, objBp, false)) return 1;
732+
(((prop, data) => (a, b) => {
733+
let val1, val2, arr;
734+
if (~prop.indexOf(".")) {
735+
arr = prop.split(".");
736+
val1 = arr.reduce(function(obj, i) { return obj && obj[i] || undefined; }, data[a]);
737+
val2 = arr.reduce(function(obj, i) { return obj && obj[i] || undefined; }, data[b]);
738+
} else {
739+
val1 = data[a][prop];
740+
val2 = data[b][prop];
741+
}
742+
743+
if (val1 !== val2) {
744+
if (ltHelper(val1, val2, false)) return -1;
745+
if (gtHelper(val1, val2, false)) return 1;
738746
}
739747
return 0;
740748
}))(property, this.data);

packages/loki/src/resultset.js

+30-3
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,23 @@ function compoundeval(properties, obj1, obj2) {
224224
let res = 0;
225225
let prop;
226226
let field;
227+
let val1, val2, arr;
227228
for (let i = 0, len = properties.length; i < len; i++) {
228229
prop = properties[i];
229230
field = prop[0];
230-
res = sortHelper(obj1[field], obj2[field], prop[1]);
231+
if (~field.indexOf(".")) {
232+
arr = field.split(".");
233+
val1 = arr.reduce((obj, i) => {
234+
return obj && obj[i] || undefined;
235+
}, obj1);
236+
val2 = arr.reduce((obj, i) => {
237+
return obj && obj[i] || undefined;
238+
}, obj2);
239+
} else {
240+
val1 = obj1[field];
241+
val2 = obj2[field];
242+
}
243+
res = sortHelper(val1, val2, prop[1]);
231244
if (res !== 0) {
232245
return res;
233246
}
@@ -514,8 +527,22 @@ export class Resultset {
514527
}
515528
}
516529

517-
const wrappedComparer =
518-
(((prop, desc, data) => (a, b) => sortHelper(data[a][prop], data[b][prop], desc)))(propname, isdesc, this.collection.data);
530+
const wrappedComparer = ((prop, desc, data) => (a, b) => {
531+
let val1, val2, arr;
532+
if (~prop.indexOf(".")) {
533+
arr = prop.split(".");
534+
val1 = arr.reduce(function (obj, i) {
535+
return obj && obj[i] || undefined;
536+
}, data[a]);
537+
val2 = arr.reduce(function (obj, i) {
538+
return obj && obj[i] || undefined;
539+
}, data[b]);
540+
} else {
541+
val1 = data[a][prop];
542+
val2 = data[b][prop];
543+
}
544+
return sortHelper(val1, val2, desc);
545+
})(propname, isdesc, this.collection.data);
519546

520547
this.filteredrows.sort(wrappedComparer);
521548

packages/partitioning-adapter/spec/generic/partitioning.spec.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ describe("partitioning adapter", () => {
6666
it("verify partioning adapter with paging mode enabled works", (done) => {
6767
const mem = new LokiMemoryAdapter();
6868

69-
// we will use an exceptionally low page size (128bytes) to test with small dataset
70-
const adapter = new LokiPartitioningAdapter(mem, {paging: true, pageSize: 128});
69+
// we will use an exceptionally low page size (64bytes) to test with small dataset
70+
// every object will serialize to over 64bytes so that is not a hard limit but when
71+
// we exceed that we will stop adding to page (so for this test 1 doc per page)
72+
const adapter = new LokiPartitioningAdapter(mem, {paging: true, pageSize: 64});
7173

7274
const db = new Loki("sandbox.db");
7375
let db2;
@@ -87,15 +89,19 @@ describe("partitioning adapter", () => {
8789
// for purposes of our memory adapter it is pretty much synchronous
8890
db.saveDatabase().then(() => {
8991
// should have partitioned the data
90-
expect(Object.keys(mem.hashStore).length).toEqual(4);
92+
expect(Object.keys(mem.hashStore).length).toEqual(6);
9193
expect(mem.hashStore.hasOwnProperty("sandbox.db")).toEqual(true);
9294
expect(mem.hashStore.hasOwnProperty("sandbox.db.0.0")).toEqual(true);
9395
expect(mem.hashStore.hasOwnProperty("sandbox.db.0.1")).toEqual(true);
96+
expect(mem.hashStore.hasOwnProperty("sandbox.db.0.2")).toEqual(true);
97+
expect(mem.hashStore.hasOwnProperty("sandbox.db.0.3")).toEqual(true);
9498
expect(mem.hashStore.hasOwnProperty("sandbox.db.1.0")).toEqual(true);
9599
// all partitions should have been saved once each
96100
expect(mem.hashStore["sandbox.db"].savecount).toEqual(1);
97101
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(1);
98102
expect(mem.hashStore["sandbox.db.0.1"].savecount).toEqual(1);
103+
expect(mem.hashStore["sandbox.db.0.2"].savecount).toEqual(1);
104+
expect(mem.hashStore["sandbox.db.0.3"].savecount).toEqual(1);
99105
expect(mem.hashStore["sandbox.db.1.0"].savecount).toEqual(1);
100106

101107
// so let's go ahead and update one of our collections to make it dirty
@@ -109,7 +115,8 @@ describe("partitioning adapter", () => {
109115
expect(mem.hashStore["sandbox.db"].savecount).toEqual(2);
110116
// we didn't change this
111117
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(1);
112-
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(1);
118+
expect(mem.hashStore["sandbox.db.0.2"].savecount).toEqual(1);
119+
expect(mem.hashStore["sandbox.db.0.3"].savecount).toEqual(1);
113120
// we updated this collection so it should have been saved again
114121
expect(mem.hashStore["sandbox.db.1.0"].savecount).toEqual(2);
115122

@@ -121,7 +128,9 @@ describe("partitioning adapter", () => {
121128
}).then(() => {
122129
expect(mem.hashStore["sandbox.db"].savecount).toEqual(3);
123130
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(2);
124-
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(2);
131+
expect(mem.hashStore["sandbox.db.0.1"].savecount).toEqual(2);
132+
expect(mem.hashStore["sandbox.db.0.2"].savecount).toEqual(2);
133+
expect(mem.hashStore["sandbox.db.0.3"].savecount).toEqual(2);
125134
expect(mem.hashStore["sandbox.db.1.0"].savecount).toEqual(2);
126135

127136
// ok now lets load from it
@@ -141,7 +150,9 @@ describe("partitioning adapter", () => {
141150
}).then(() => {
142151
expect(mem.hashStore["sandbox.db"].savecount).toEqual(4);
143152
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(2);
144-
expect(mem.hashStore["sandbox.db.0.0"].savecount).toEqual(2);
153+
expect(mem.hashStore["sandbox.db.0.1"].savecount).toEqual(2);
154+
expect(mem.hashStore["sandbox.db.0.2"].savecount).toEqual(2);
155+
expect(mem.hashStore["sandbox.db.0.3"].savecount).toEqual(2);
145156
expect(mem.hashStore["sandbox.db.1.0"].savecount).toEqual(2);
146157
expect(mem.hashStore["sandbox.db.2.0"].savecount).toEqual(1);
147158

0 commit comments

Comments
 (0)