Skip to content

Commit 8fbc174

Browse files
authored
feat: integrate full-text-search to loki (insert/update/remove + search)
* make full text search available in Collection/Resultset * add an unit test for full text search with Loki * implement insert/remove/update documents and chained search unit test with full text search * make common module with a small plugin system and common types * support elasticsearch 5 and 6 inside the unit test
1 parent d47f190 commit 8fbc174

22 files changed

+518
-154
lines changed

packages/common/plugin.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function getGlobal(): any {
2+
let glob;
3+
(function (global) {
4+
glob = global;
5+
})(typeof global !== "undefined" && global || this);
6+
return glob;
7+
}
8+
9+
10+
function create(): void {
11+
const global = getGlobal();
12+
const sym = Symbol.for("LOKI") as any;
13+
if (global[sym] === undefined) {
14+
global[sym] = {
15+
};
16+
}
17+
return global[sym];
18+
}
19+
20+
export const PLUGINS = create();

packages/loki/src/types.ts packages/common/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Loki} from "./loki";
1+
import {Loki} from "../loki/src/loki";
22

33
export type ANY = any;
44

packages/fs-storage/spec/node/fs_storage.spec.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ describe("testing fs storage", function () {
88
name: string;
99
}
1010

11+
beforeAll(() => {
12+
LokiFSStorage.register();
13+
});
14+
15+
afterAll(() => {
16+
LokiFSStorage.deregister();
17+
});
18+
1119
it("LokiFSStorage", function (done) {
1220
const db = new Loki("myTestApp");
1321
const adapter = {adapter: new LokiFSStorage()};
@@ -18,7 +26,16 @@ describe("testing fs storage", function () {
1826
})
1927
.then(() => {
2028
const db2 = new Loki("myTestApp");
21-
return db2.initializePersistence(adapter)
29+
return db2.initializePersistence()
30+
.then(() => {
31+
return db2.loadDatabase();
32+
}).then(() => {
33+
expect(db2.getCollection<Name>("myColl").find()[0].name).toEqual("Hello World");
34+
});
35+
})
36+
.then(() => {
37+
const db2 = new Loki("myTestApp");
38+
return db2.initializePersistence({persistenceMethod: Loki.PersistenceMethod.FS_STORAGE})
2239
.then(() => {
2340
return db2.loadDatabase();
2441
}).then(() => {
@@ -27,7 +44,7 @@ describe("testing fs storage", function () {
2744
})
2845
.then(() => {
2946
const db3 = new Loki("other");
30-
return db3.initializePersistence(adapter)
47+
return db3.initializePersistence()
3148
.then(() => {
3249
return db3.loadDatabase();
3350
}).then(() => {

packages/fs-storage/src/fs_storage.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
import {Loki} from "../../loki/src/loki";
2-
import {StorageAdapter} from "../../loki/src/types";
1+
import {PLUGINS} from "../../common/plugin";
2+
import {StorageAdapter} from "../../common/types";
33
import * as fs from "fs";
44

55
/**
66
* A loki persistence adapter which persists using node fs module.
77
*/
88
export class LokiFSStorage implements StorageAdapter {
9+
/**
10+
* Registers the fs storage as plugin.
11+
*/
12+
static register(): void {
13+
PLUGINS["LokiFSStorage"] = LokiFSStorage;
14+
}
15+
16+
/**
17+
* Deregisters the fs storage as plugin.
18+
*/
19+
static deregister(): void {
20+
delete PLUGINS["LokiFSStorage"];
21+
}
22+
923
/**
1024
* Load data from file, will throw an error if the file does not exist
1125
* @param {string} dbname - the filename of the database to load
@@ -75,6 +89,4 @@ export class LokiFSStorage implements StorageAdapter {
7589
}
7690
}
7791

78-
Loki["LokiFSStorage"] = LokiFSStorage;
79-
8092
export default LokiFSStorage;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/* global describe, ddescribe, it, expect */
2+
import {Loki} from "../../../loki/src/loki";
3+
import {QueryBuilder} from "../../src/query_builder";
4+
import {LokiMemoryAdapter} from "../../../loki/src/memory_adapter";
5+
import {Collection} from "../../../loki/src/collection";
6+
import {FullTextSearch} from "../../src/full_text_search";
7+
8+
describe("full text search", () => {
9+
FullTextSearch.register();
10+
11+
interface User {
12+
name: string;
13+
id: number;
14+
}
15+
16+
let db: Loki;
17+
let coll: Collection;
18+
19+
beforeEach(() => {
20+
db = new Loki("MyDB");
21+
coll = db.addCollection<User>("User", {fullTextSearch: [{name: "name"}]});
22+
23+
coll.insert([
24+
{name: "quark", id: 1},
25+
{name: "quarrk", id: 2},
26+
{name: "quak", id: 3},
27+
{name: "quask", id: 4},
28+
]);
29+
});
30+
31+
it("usage", () => {
32+
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
33+
expect(coll.find({"$fts": query}).length).toBe(3);
34+
});
35+
36+
it("chained", () => {
37+
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
38+
39+
expect(
40+
coll.find({"$fts": query})).not.toEqual(
41+
coll.find({"id": {"$in": [1, 2, 3]}}));
42+
43+
expect(
44+
coll
45+
.chain()
46+
.find({"id": {"$in": [1, 2, 3]}})
47+
.find({"$fts": query})
48+
.data().length)
49+
.toBe(2);
50+
51+
expect(
52+
coll
53+
.chain()
54+
.find({"$fts": query})
55+
.find({"id": {"$in": [1, 2, 3]}})
56+
.data().length)
57+
.toBe(2);
58+
});
59+
60+
it("update", () => {
61+
coll.updateWhere((user: User) => {
62+
return user.name == "quak";
63+
}, (user: User) => {
64+
user.name = "quaaak";
65+
return user;
66+
});
67+
68+
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
69+
expect(coll.find({"$fts": query}).length).toBe(2);
70+
});
71+
72+
it("remove", () => {
73+
coll.removeWhere((user: User) => {
74+
return user.name == "quak";
75+
});
76+
77+
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
78+
expect(coll.find({"$fts": query}).length).toBe(2);
79+
});
80+
81+
it("clear", () => {
82+
coll.clear();
83+
84+
coll.insert([
85+
{name: "abcd", id: 1},
86+
{name: "abcde", id: 2},
87+
{name: "abcdef", id: 3},
88+
{name: "abcdefg", id: 4},
89+
]);
90+
91+
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
92+
expect(coll.find({"$fts": query}).length).toBe(0);
93+
94+
query = new QueryBuilder().fuzzy("name", "abcde").fuzziness(1).build();
95+
expect(coll.find({"$fts": query}).length).toBe(3);
96+
});
97+
98+
it("save/load", (done) => {
99+
const adapter = {adapter: new LokiMemoryAdapter()};
100+
db.initializePersistence(adapter)
101+
.then(() => {
102+
return db.saveDatabase();
103+
})
104+
.then(() => {
105+
const db2 = new Loki("MyDB");
106+
return db2.initializePersistence(adapter)
107+
.then(() => {
108+
return db2.loadDatabase();
109+
}).then(() => {
110+
const coll2 = db2.getCollection<User>("User");
111+
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
112+
expect(coll2.find({"$fts": query}).length).toBe(3);
113+
done();
114+
});
115+
})
116+
.catch(() => {
117+
expect(true).toBe(false);
118+
done();
119+
});
120+
});
121+
});

0 commit comments

Comments
 (0)