Skip to content

Commit

Permalink
feat: integrate full-text-search to loki (insert/update/remove + search)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Viatorus authored Nov 21, 2017
1 parent d47f190 commit 8fbc174
Show file tree
Hide file tree
Showing 22 changed files with 518 additions and 154 deletions.
20 changes: 20 additions & 0 deletions packages/common/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function getGlobal(): any {
let glob;
(function (global) {
glob = global;
})(typeof global !== "undefined" && global || this);
return glob;
}


function create(): void {
const global = getGlobal();
const sym = Symbol.for("LOKI") as any;
if (global[sym] === undefined) {
global[sym] = {
};
}
return global[sym];
}

export const PLUGINS = create();
2 changes: 1 addition & 1 deletion packages/loki/src/types.ts → packages/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Loki} from "./loki";
import {Loki} from "../loki/src/loki";

export type ANY = any;

Expand Down
21 changes: 19 additions & 2 deletions packages/fs-storage/spec/node/fs_storage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ describe("testing fs storage", function () {
name: string;
}

beforeAll(() => {
LokiFSStorage.register();
});

afterAll(() => {
LokiFSStorage.deregister();
});

it("LokiFSStorage", function (done) {
const db = new Loki("myTestApp");
const adapter = {adapter: new LokiFSStorage()};
Expand All @@ -18,7 +26,16 @@ describe("testing fs storage", function () {
})
.then(() => {
const db2 = new Loki("myTestApp");
return db2.initializePersistence(adapter)
return db2.initializePersistence()
.then(() => {
return db2.loadDatabase();
}).then(() => {
expect(db2.getCollection<Name>("myColl").find()[0].name).toEqual("Hello World");
});
})
.then(() => {
const db2 = new Loki("myTestApp");
return db2.initializePersistence({persistenceMethod: Loki.PersistenceMethod.FS_STORAGE})
.then(() => {
return db2.loadDatabase();
}).then(() => {
Expand All @@ -27,7 +44,7 @@ describe("testing fs storage", function () {
})
.then(() => {
const db3 = new Loki("other");
return db3.initializePersistence(adapter)
return db3.initializePersistence()
.then(() => {
return db3.loadDatabase();
}).then(() => {
Expand Down
20 changes: 16 additions & 4 deletions packages/fs-storage/src/fs_storage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import {Loki} from "../../loki/src/loki";
import {StorageAdapter} from "../../loki/src/types";
import {PLUGINS} from "../../common/plugin";
import {StorageAdapter} from "../../common/types";
import * as fs from "fs";

/**
* A loki persistence adapter which persists using node fs module.
*/
export class LokiFSStorage implements StorageAdapter {
/**
* Registers the fs storage as plugin.
*/
static register(): void {
PLUGINS["LokiFSStorage"] = LokiFSStorage;
}

/**
* Deregisters the fs storage as plugin.
*/
static deregister(): void {
delete PLUGINS["LokiFSStorage"];
}

/**
* Load data from file, will throw an error if the file does not exist
* @param {string} dbname - the filename of the database to load
Expand Down Expand Up @@ -75,6 +89,4 @@ export class LokiFSStorage implements StorageAdapter {
}
}

Loki["LokiFSStorage"] = LokiFSStorage;

export default LokiFSStorage;
121 changes: 121 additions & 0 deletions packages/full-text-search/spec/generic/full_text_search.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* global describe, ddescribe, it, expect */
import {Loki} from "../../../loki/src/loki";
import {QueryBuilder} from "../../src/query_builder";
import {LokiMemoryAdapter} from "../../../loki/src/memory_adapter";
import {Collection} from "../../../loki/src/collection";
import {FullTextSearch} from "../../src/full_text_search";

describe("full text search", () => {
FullTextSearch.register();

interface User {
name: string;
id: number;
}

let db: Loki;
let coll: Collection;

beforeEach(() => {
db = new Loki("MyDB");
coll = db.addCollection<User>("User", {fullTextSearch: [{name: "name"}]});

coll.insert([
{name: "quark", id: 1},
{name: "quarrk", id: 2},
{name: "quak", id: 3},
{name: "quask", id: 4},
]);
});

it("usage", () => {
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
expect(coll.find({"$fts": query}).length).toBe(3);
});

it("chained", () => {
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();

expect(
coll.find({"$fts": query})).not.toEqual(
coll.find({"id": {"$in": [1, 2, 3]}}));

expect(
coll
.chain()
.find({"id": {"$in": [1, 2, 3]}})
.find({"$fts": query})
.data().length)
.toBe(2);

expect(
coll
.chain()
.find({"$fts": query})
.find({"id": {"$in": [1, 2, 3]}})
.data().length)
.toBe(2);
});

it("update", () => {
coll.updateWhere((user: User) => {
return user.name == "quak";
}, (user: User) => {
user.name = "quaaak";
return user;
});

let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
expect(coll.find({"$fts": query}).length).toBe(2);
});

it("remove", () => {
coll.removeWhere((user: User) => {
return user.name == "quak";
});

let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
expect(coll.find({"$fts": query}).length).toBe(2);
});

it("clear", () => {
coll.clear();

coll.insert([
{name: "abcd", id: 1},
{name: "abcde", id: 2},
{name: "abcdef", id: 3},
{name: "abcdefg", id: 4},
]);

let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
expect(coll.find({"$fts": query}).length).toBe(0);

query = new QueryBuilder().fuzzy("name", "abcde").fuzziness(1).build();
expect(coll.find({"$fts": query}).length).toBe(3);
});

it("save/load", (done) => {
const adapter = {adapter: new LokiMemoryAdapter()};
db.initializePersistence(adapter)
.then(() => {
return db.saveDatabase();
})
.then(() => {
const db2 = new Loki("MyDB");
return db2.initializePersistence(adapter)
.then(() => {
return db2.loadDatabase();
}).then(() => {
const coll2 = db2.getCollection<User>("User");
let query = new QueryBuilder().fuzzy("name", "quak").fuzziness(1).build();
expect(coll2.find({"$fts": query}).length).toBe(3);
done();
});
})
.catch(() => {
expect(true).toBe(false);
done();
});
});
});
Loading

0 comments on commit 8fbc174

Please sign in to comment.