From 54b1b9f744ed03a97457719ff6a0cad60db5c4b0 Mon Sep 17 00:00:00 2001 From: "Rico Sta. Cruz" Date: Mon, 23 Oct 2017 13:38:51 +0800 Subject: [PATCH] Initial buggy implementation of search backend --- _js/search/index.js | 122 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + yarn.lock | 6 ++- 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 _js/search/index.js diff --git a/_js/search/index.js b/_js/search/index.js new file mode 100644 index 000000000..93789939b --- /dev/null +++ b/_js/search/index.js @@ -0,0 +1,122 @@ +/** + * Lunr-based searching + * + * @example + * + * + * + * s = new Search() + * s.inferFromMeta() + * s.search('hello').then(() => ...) + * + * s.loadFromData({ index, store }) + */ + +class Search { + constructor (opts) { + const once = require('once') + const Emitter = require('events') + + this.indexPath = opts && opts.index + this.load = once(this.load) + this.index = null + this.store = null + this.emitter = new Emitter() + } + + load () { + return this.loadRemote() + } + + /** + * Infers `indexPath` from meta tags. + */ + + inferFromMeta () { + // Safeguard, in case we're not loaded in a browser context + if (typeof document === 'undefined') return this + + const meta = document.querySelector('meta[name="search:index"]') + if (!meta) return this + + const value = meta.getAttribute('content') + this.indexPath = value + return this + } + + /** + * Loads the search index remotely (via `indexPath`). + */ + + loadRemote () { + return window.fetch(this.indexPath) + .then(res => res.json()) + .then(data => { + this.loadFromData(data) + }) + } + + /** + * Loads the search index locally. + */ + + loadFromData (data) { + const lunr = require('lunr') + + const { index, store } = data + this.index = lunr.Index.load(index) + this.store = store + this.emitter.emit('load') + } + + /** + * Waits until the data is loaded, then resolves. + * + * - When called when the data is still loading, it'll wait until the data is + * loaded. + */ + + loaded () { + return new Promise((resolve, reject) => { + if (this.index) return resolve(this) + + this.emitter.on('load', () => { + resolve(this) + }) + + this.load() + }) + } + + /* + * Searches for keywords. + */ + + async search (keywords) { + await this.loaded() + const results = this.index.search(keywords) + const fullResults = results.map(result => { + return { + ...result, + object: this.store[result.ref] + } + }) + + return fullResults + } +} + +/* + * The primary instance + */ + +const search = new Search().inferFromMeta() + +/* + * Export + */ + +module.exports = { + Search, + search +} diff --git a/package.json b/package.json index e99648117..bb83b7e9d 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "hint.css": "2.5.0", "isotope-layout": "3.0.4", "jquery": "3.2.1", + "lunr": "2.1.4", + "once": "1.4.0", "onmount": "1.3.0", "prismjs": "1.8.1", "sanitize.css": "5.0.0" diff --git a/yarn.lock b/yarn.lock index e645b3cf9..93007aac8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4536,6 +4536,10 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lunr@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.1.4.tgz#52be0a9d068321909a7fb46575620e24417d5591" + macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" @@ -4980,7 +4984,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.3.3, once@^1.4.0: +once@1.4.0, once@^1.3.0, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: