Skip to content

Commit 4d1b25b

Browse files
authored
feat(loki): simplesort leverages binary indices better when filtered (#83)
array intersection algorithm is used to leverage binary index on simplesort if the filtered documents is a least 10% of total documents from results See techfort/LokiJS@4e28e81
1 parent fdf1c62 commit 4d1b25b

File tree

6 files changed

+494
-236
lines changed

6 files changed

+494
-236
lines changed

packages/loki/spec/generic/dynamic_view.spec.ts

+153-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Loki} from "../../src/loki";
33
import {MemoryStorage} from "../../../memory-storage/src/memory_storage";
44
import {Collection} from "../../src/collection";
55
import {Doc} from "../../../common/types";
6+
import {LokiOps} from "../../src/result_set";
67

78
describe("dynamicviews", () => {
89
interface User {
@@ -291,10 +292,10 @@ describe("dynamicviews", () => {
291292
expect(resultsNoIndex.length).toEqual(0);
292293

293294
const resultsWithIndex = itc.find({
294-
"testindex": 4
295+
"testindex": 4
295296
});
296297
//it("no results found", () => {
297-
expect(resultsWithIndex.length).toEqual(0);
298+
expect(resultsWithIndex.length).toEqual(0);
298299
//});
299300
});
300301

@@ -343,4 +344,154 @@ describe("dynamicviews", () => {
343344
});
344345
});
345346
});
347+
348+
it("applySortCriteria", () => {
349+
const db = new Loki("dvtest.db");
350+
const coll = db.addCollection<User>("any");
351+
coll.insert([{
352+
name: "mjolnir",
353+
owner: "odin",
354+
maker: "dwarves",
355+
}, {
356+
name: "tyrfing",
357+
owner: "thor",
358+
maker: "dwarves",
359+
}, {
360+
name: "gungnir",
361+
owner: "odin",
362+
maker: "elves",
363+
}, {
364+
name: "draupnir",
365+
owner: "thor",
366+
maker: "elves",
367+
}]);
368+
const dv = coll.addDynamicView("test");
369+
370+
let result = dv.applySortCriteria(["owner", "maker"]).data();
371+
expect(result).toEqual(dv.applySortCriteria([["owner", false], ["maker", false]]).data());
372+
expect(result.length).toBe(4);
373+
expect(result[0].name).toBe("mjolnir");
374+
expect(result[1].name).toBe("gungnir");
375+
expect(result[2].name).toBe("tyrfing");
376+
expect(result[3].name).toBe("draupnir");
377+
378+
result = dv.applySortCriteria([["owner", false], ["maker", true]]).data();
379+
expect(result.length).toBe(4);
380+
expect(result[0].name).toBe("gungnir");
381+
expect(result[1].name).toBe("mjolnir");
382+
expect(result[2].name).toBe("draupnir");
383+
expect(result[3].name).toBe("tyrfing");
384+
385+
result = dv.applySortCriteria([["owner", true], ["maker", false]]).data();
386+
expect(result.length).toBe(4);
387+
expect(result[0].name).toBe("tyrfing");
388+
expect(result[1].name).toBe("draupnir");
389+
expect(result[2].name).toBe("mjolnir");
390+
expect(result[3].name).toBe("gungnir");
391+
392+
result = dv.applySortCriteria([["owner", true], ["maker", true]]).data();
393+
expect(result.length).toBe(4);
394+
expect(result[0].name).toBe("draupnir");
395+
expect(result[1].name).toBe("tyrfing");
396+
expect(result[2].name).toBe("gungnir");
397+
expect(result[3].name).toBe("mjolnir");
398+
399+
result = dv.applySortCriteria(["maker", "owner"]).data();
400+
expect(result).toEqual(dv.applySortCriteria([["maker", false], ["owner", false]]).data());
401+
expect(result.length).toBe(4);
402+
expect(result[0].name).toBe("mjolnir");
403+
expect(result[1].name).toBe("tyrfing");
404+
expect(result[2].name).toBe("gungnir");
405+
expect(result[3].name).toBe("draupnir");
406+
407+
result = dv.applySortCriteria([["maker", false], ["owner", true]]).data();
408+
expect(result.length).toBe(4);
409+
expect(result[0].name).toBe("tyrfing");
410+
expect(result[1].name).toBe("mjolnir");
411+
expect(result[2].name).toBe("draupnir");
412+
expect(result[3].name).toBe("gungnir");
413+
414+
result = dv.applySortCriteria([["maker", true], ["owner", false]]).data();
415+
expect(result.length).toBe(4);
416+
expect(result[0].name).toBe("gungnir");
417+
expect(result[1].name).toBe("draupnir");
418+
expect(result[2].name).toBe("mjolnir");
419+
expect(result[3].name).toBe("tyrfing");
420+
421+
result = dv.applySortCriteria([["maker", true], ["owner", true]]).data();
422+
expect(result.length).toBe(4);
423+
expect(result[0].name).toBe("draupnir");
424+
expect(result[1].name).toBe("gungnir");
425+
expect(result[2].name).toBe("tyrfing");
426+
expect(result[3].name).toBe("mjolnir");
427+
});
428+
429+
describe("dynamic view simplesort options work correctly", () => {
430+
it("works", function () {
431+
const db = new Loki("dvtest.db");
432+
let coll = db.addCollection<{ a: number, b: number }>("colltest", {indices: ["a", "b"]});
433+
434+
// add basic dv with filter on a and basic simplesort on b
435+
let dv = coll.addDynamicView("dvtest");
436+
dv.applyFind({a: {$lte: 20}});
437+
dv.applySimpleSort("b");
438+
439+
// data only needs to be inserted once since we are leaving collection intact while
440+
// building up and tearing down dynamic views within it
441+
coll.insert([{a: 1, b: 11}, {a: 2, b: 9}, {a: 8, b: 3}, {a: 6, b: 7}, {a: 2, b: 14}, {a: 22, b: 1}]);
442+
443+
// test whether results are valid
444+
let results = dv.data();
445+
expect(results.length).toBe(5);
446+
for (let idx = 0; idx < results.length - 1; idx++) {
447+
expect(LokiOps.$lte(results[idx]["b"], results[idx + 1]["b"]));
448+
}
449+
450+
// remove dynamic view
451+
coll.removeDynamicView("dvtest");
452+
453+
// add basic dv with filter on a and simplesort (with js fallback) on b
454+
dv = coll.addDynamicView("dvtest");
455+
dv.applyFind({a: {$lte: 20}});
456+
dv.applySimpleSort("b", {useJavascriptSorting: true});
457+
458+
// test whether results are valid
459+
// for our simple integer datatypes javascript sorting is same as loki sorting
460+
results = dv.data();
461+
expect(results.length).toBe(5);
462+
for (let idx = 0; idx < results.length - 1; idx++) {
463+
expect(results[idx]["b"] <= results[idx + 1]["b"]);
464+
}
465+
466+
// remove dynamic view
467+
coll.removeDynamicView("dvtest");
468+
469+
// add basic dv with filter on a and simplesort (forced js sort) on b
470+
dv = coll.addDynamicView("dvtest");
471+
dv.applyFind({a: {$lte: 20}});
472+
dv.applySimpleSort("b", {disableIndexIntersect: true, useJavascriptSorting: true});
473+
474+
// test whether results are valid
475+
results = dv.data();
476+
expect(results.length).toBe(5);
477+
for (let idx = 0; idx < results.length - 1; idx++) {
478+
expect(results[idx]["b"] <= results[idx + 1]["b"]);
479+
}
480+
481+
// remove dynamic view
482+
coll.removeDynamicView("dvtest");
483+
484+
// add basic dv with filter on a and simplesort (forced loki sort) on b
485+
dv = coll.addDynamicView("dvtest");
486+
dv.applyFind({a: {$lte: 20}});
487+
dv.applySimpleSort("b", {forceIndexIntersect: true});
488+
489+
// test whether results are valid
490+
results = dv.data();
491+
expect(results.length).toBe(5);
492+
for (let idx = 0; idx < results.length - 1; idx++) {
493+
expect(LokiOps.$lte(results[idx]["b"], results[idx + 1]["b"]));
494+
}
495+
});
496+
});
346497
});

0 commit comments

Comments
 (0)