diff options
author | Alex Yatskov <FooSoft@users.noreply.github.com> | 2019-10-05 09:18:09 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-05 09:18:09 -0700 |
commit | a369f8d0a41338a0d4aa85d904f0f20c71eb7817 (patch) | |
tree | 5d0c5f2503472070699e408f338c1c9e41080552 /ext/bg/js/database.js | |
parent | d3d162ea785457d9533d57f861afa82d2c2b0e01 (diff) | |
parent | 44119eea2c8ad4c8eed38070ef1a3ce27fa9359e (diff) |
Merge pull request #229 from toasted-nutbread/database-optimizations
Database optimizations
Diffstat (limited to 'ext/bg/js/database.js')
-rw-r--r-- | ext/bg/js/database.js | 180 |
1 files changed, 134 insertions, 46 deletions
diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 093ec102..e8214c3c 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -68,24 +68,39 @@ class Database { const results = []; await this.db.terms.where('expression').equals(term).or('reading').equals(term).each(row => { if (titles.includes(row.dictionary)) { - results.push({ - expression: row.expression, - reading: row.reading, - definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''), - termTags: dictFieldSplit(row.termTags || ''), - rules: dictFieldSplit(row.rules), - glossary: row.glossary, - score: row.score, - dictionary: row.dictionary, - id: row.id, - sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence - }); + results.push(Database.createTerm(row)); } }); return results; } + async findTermsBulk(terms, titles) { + const promises = []; + const visited = {}; + const results = []; + const createResult = Database.createTerm; + const filter = (row) => titles.includes(row.dictionary); + + const db = this.db.backendDB(); + const dbTransaction = db.transaction(['terms'], 'readonly'); + const dbTerms = dbTransaction.objectStore('terms'); + const dbIndex1 = dbTerms.index('expression'); + const dbIndex2 = dbTerms.index('reading'); + + for (let i = 0; i < terms.length; ++i) { + const only = IDBKeyRange.only(terms[i]); + promises.push( + Database.getAll(dbIndex1, only, i, visited, filter, createResult, results), + Database.getAll(dbIndex2, only, i, visited, filter, createResult, results) + ); + } + + await Promise.all(promises); + + return results; + } + async findTermsExact(term, reading, titles) { if (!this.db) { throw 'Database not initialized'; @@ -94,18 +109,7 @@ class Database { const results = []; await this.db.terms.where('expression').equals(term).each(row => { if (row.reading === reading && titles.includes(row.dictionary)) { - results.push({ - expression: row.expression, - reading: row.reading, - definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''), - termTags: dictFieldSplit(row.termTags || ''), - rules: dictFieldSplit(row.rules), - glossary: row.glossary, - score: row.score, - dictionary: row.dictionary, - id: row.id, - sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence - }); + results.push(Database.createTerm(row)); } }); @@ -120,18 +124,7 @@ class Database { const results = []; await this.db.terms.where('sequence').equals(sequence).each(row => { if (row.dictionary === mainDictionary) { - results.push({ - expression: row.expression, - reading: row.reading, - definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''), - termTags: dictFieldSplit(row.termTags || ''), - rules: dictFieldSplit(row.rules), - glossary: row.glossary, - score: row.score, - dictionary: row.dictionary, - id: row.id, - sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence - }); + results.push(Database.createTerm(row)); } }); @@ -157,6 +150,28 @@ class Database { return results; } + async findTermMetaBulk(terms, titles) { + const promises = []; + const visited = {}; + const results = []; + const createResult = Database.createTermMeta; + const filter = (row) => titles.includes(row.dictionary); + + const db = this.db.backendDB(); + const dbTransaction = db.transaction(['termMeta'], 'readonly'); + const dbTerms = dbTransaction.objectStore('termMeta'); + const dbIndex = dbTerms.index('expression'); + + for (let i = 0; i < terms.length; ++i) { + const only = IDBKeyRange.only(terms[i]); + promises.push(Database.getAll(dbIndex, only, i, visited, filter, createResult, results)); + } + + await Promise.all(promises); + + return results; + } + async findKanji(kanji, titles) { if (!this.db) { throw 'Database not initialized'; @@ -199,23 +214,30 @@ class Database { return results; } + findTagForTitleCached(name, title) { + if (this.tagCache.hasOwnProperty(title)) { + const cache = this.tagCache[title]; + if (cache.hasOwnProperty(name)) { + return cache[name]; + } + } + } + async findTagForTitle(name, title) { if (!this.db) { throw 'Database not initialized'; } - this.tagCache[title] = this.tagCache[title] || {}; + const cache = (this.tagCache.hasOwnProperty(title) ? this.tagCache[title] : (this.tagCache[title] = {})); - let result = this.tagCache[title][name]; - if (!result) { - await this.db.tagMeta.where('name').equals(name).each(row => { - if (title === row.dictionary) { - result = row; - } - }); + let result = null; + await this.db.tagMeta.where('name').equals(name).each(row => { + if (title === row.dictionary) { + result = row; + } + }); - this.tagCache[title][name] = result; - } + cache[name] = result; return result; } @@ -489,4 +511,70 @@ class Database { return summary; } + + static createTerm(row, index) { + return { + index, + expression: row.expression, + reading: row.reading, + definitionTags: dictFieldSplit(row.definitionTags || row.tags || ''), + termTags: dictFieldSplit(row.termTags || ''), + rules: dictFieldSplit(row.rules), + glossary: row.glossary, + score: row.score, + dictionary: row.dictionary, + id: row.id, + sequence: typeof row.sequence === 'undefined' ? -1 : row.sequence + }; + } + + static createTermMeta(row, index) { + return { + index, + mode: row.mode, + data: row.data, + dictionary: row.dictionary + }; + } + + static getAll(dbIndex, query, index, visited, filter, createResult, results) { + const fn = typeof dbIndex.getAll === 'function' ? Database.getAllFast : Database.getAllUsingCursor; + return fn(dbIndex, query, index, visited, filter, createResult, results); + } + + static getAllFast(dbIndex, query, index, visited, filter, createResult, results) { + return new Promise((resolve, reject) => { + const request = dbIndex.getAll(query); + request.onerror = (e) => reject(e); + request.onsuccess = (e) => { + for (const row of e.target.result) { + if (filter(row, index) && !visited.hasOwnProperty(row.id)) { + visited[row.id] = true; + results.push(createResult(row, index)); + } + } + resolve(); + }; + }); + } + + static getAllUsingCursor(dbIndex, query, index, visited, filter, createResult, results) { + return new Promise((resolve, reject) => { + const request = dbIndex.openCursor(query, 'next'); + request.onerror = (e) => reject(e); + request.onsuccess = (e) => { + const cursor = e.target.result; + if (cursor) { + const row = cursor.value; + if (filter(row, index) && !visited.hasOwnProperty(row.id)) { + visited[row.id] = true; + results.push(createResult(row, index)); + } + cursor.continue(); + } else { + resolve(); + } + }; + }); + } } |