aboutsummaryrefslogtreecommitdiff
path: root/ext/bg/js/database.js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-06-28 17:24:06 -0400
committerGitHub <noreply@github.com>2020-06-28 17:24:06 -0400
commit441c23bf3be1bc4f14e17ec3956a8c90b1a674e8 (patch)
tree2ff8aef4f401f333df2d487814b741a005ca35d3 /ext/bg/js/database.js
parent7590055d4e809ab857b2d491dab256e66f1b34b6 (diff)
Rename Database to DictionaryDatabase (#633)
Diffstat (limited to 'ext/bg/js/database.js')
-rw-r--r--ext/bg/js/database.js474
1 files changed, 0 insertions, 474 deletions
diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js
deleted file mode 100644
index 47f1ebdd..00000000
--- a/ext/bg/js/database.js
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * Copyright (C) 2016-2020 Yomichan Authors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-/* global
- * GenericDatabase
- * dictFieldSplit
- */
-
-class Database {
- constructor() {
- this._db = new GenericDatabase();
- this._dbName = 'dict';
- this._schemas = new Map();
- }
-
- // Public
-
- async prepare() {
- await this._db.open(
- this._dbName,
- 60,
- [
- {
- version: 20,
- stores: {
- terms: {
- primaryKey: {keyPath: 'id', autoIncrement: true},
- indices: ['dictionary', 'expression', 'reading']
- },
- kanji: {
- primaryKey: {autoIncrement: true},
- indices: ['dictionary', 'character']
- },
- tagMeta: {
- primaryKey: {autoIncrement: true},
- indices: ['dictionary']
- },
- dictionaries: {
- primaryKey: {autoIncrement: true},
- indices: ['title', 'version']
- }
- }
- },
- {
- version: 30,
- stores: {
- termMeta: {
- primaryKey: {autoIncrement: true},
- indices: ['dictionary', 'expression']
- },
- kanjiMeta: {
- primaryKey: {autoIncrement: true},
- indices: ['dictionary', 'character']
- },
- tagMeta: {
- primaryKey: {autoIncrement: true},
- indices: ['dictionary', 'name']
- }
- }
- },
- {
- version: 40,
- stores: {
- terms: {
- primaryKey: {keyPath: 'id', autoIncrement: true},
- indices: ['dictionary', 'expression', 'reading', 'sequence']
- }
- }
- },
- {
- version: 50,
- stores: {
- terms: {
- primaryKey: {keyPath: 'id', autoIncrement: true},
- indices: ['dictionary', 'expression', 'reading', 'sequence', 'expressionReverse', 'readingReverse']
- }
- }
- },
- {
- version: 60,
- stores: {
- media: {
- primaryKey: {keyPath: 'id', autoIncrement: true},
- indices: ['dictionary', 'path']
- }
- }
- }
- ]
- );
- }
-
- async close() {
- this._db.close();
- }
-
- isPrepared() {
- return this._db.isOpen();
- }
-
- async purge() {
- if (this._db.isOpening()) {
- throw new Error('Cannot purge database while opening');
- }
- if (this._db.isOpen()) {
- this._db.close();
- }
- await GenericDatabase.deleteDatabase(this._dbName);
- await this.prepare();
- }
-
- async deleteDictionary(dictionaryName, progressSettings, onProgress) {
- const targets = [
- ['dictionaries', 'title'],
- ['kanji', 'dictionary'],
- ['kanjiMeta', 'dictionary'],
- ['terms', 'dictionary'],
- ['termMeta', 'dictionary'],
- ['tagMeta', 'dictionary'],
- ['media', 'dictionary']
- ];
-
- const {rate} = progressSettings;
- const progressData = {
- count: 0,
- processed: 0,
- storeCount: targets.length,
- storesProcesed: 0
- };
-
- const filterKeys = (keys) => {
- ++progressData.storesProcesed;
- progressData.count += keys.length;
- onProgress(progressData);
- return keys;
- };
- const onProgress2 = () => {
- const processed = progressData.processed + 1;
- progressData.processed = processed;
- if ((processed % rate) === 0 || processed === progressData.count) {
- onProgress(progressData);
- }
- };
-
- const promises = [];
- for (const [objectStoreName, indexName] of targets) {
- const query = IDBKeyRange.only(dictionaryName);
- const promise = this._db.bulkDelete(objectStoreName, indexName, query, filterKeys, onProgress2);
- promises.push(promise);
- }
- await Promise.all(promises);
- }
-
- findTermsBulk(termList, dictionaries, wildcard) {
- return new Promise((resolve, reject) => {
- const results = [];
- const count = termList.length;
- if (count === 0) {
- resolve(results);
- return;
- }
-
- const visited = new Set();
- const useWildcard = !!wildcard;
- const prefixWildcard = wildcard === 'prefix';
-
- const transaction = this._db.transaction(['terms'], 'readonly');
- const terms = transaction.objectStore('terms');
- const index1 = terms.index(prefixWildcard ? 'expressionReverse' : 'expression');
- const index2 = terms.index(prefixWildcard ? 'readingReverse' : 'reading');
-
- const count2 = count * 2;
- let completeCount = 0;
- for (let i = 0; i < count; ++i) {
- const inputIndex = i;
- const term = prefixWildcard ? stringReverse(termList[i]) : termList[i];
- const query = useWildcard ? IDBKeyRange.bound(term, `${term}\uffff`, false, false) : IDBKeyRange.only(term);
-
- const onGetAll = (rows) => {
- for (const row of rows) {
- if (dictionaries.has(row.dictionary) && !visited.has(row.id)) {
- visited.add(row.id);
- results.push(this._createTerm(row, inputIndex));
- }
- }
- if (++completeCount >= count2) {
- resolve(results);
- }
- };
-
- this._db.getAll(index1, query, onGetAll, reject);
- this._db.getAll(index2, query, onGetAll, reject);
- }
- });
- }
-
- findTermsExactBulk(termList, readingList, dictionaries) {
- return new Promise((resolve, reject) => {
- const results = [];
- const count = termList.length;
- if (count === 0) {
- resolve(results);
- return;
- }
-
- const transaction = this._db.transaction(['terms'], 'readonly');
- const terms = transaction.objectStore('terms');
- const index = terms.index('expression');
-
- let completeCount = 0;
- for (let i = 0; i < count; ++i) {
- const inputIndex = i;
- const reading = readingList[i];
- const query = IDBKeyRange.only(termList[i]);
-
- const onGetAll = (rows) => {
- for (const row of rows) {
- if (row.reading === reading && dictionaries.has(row.dictionary)) {
- results.push(this._createTerm(row, inputIndex));
- }
- }
- if (++completeCount >= count) {
- resolve(results);
- }
- };
-
- this._db.getAll(index, query, onGetAll, reject);
- }
- });
- }
-
- findTermsBySequenceBulk(sequenceList, mainDictionary) {
- return new Promise((resolve, reject) => {
- const results = [];
- const count = sequenceList.length;
- if (count === 0) {
- resolve(results);
- return;
- }
-
- const transaction = this._db.transaction(['terms'], 'readonly');
- const terms = transaction.objectStore('terms');
- const index = terms.index('sequence');
-
- let completeCount = 0;
- for (let i = 0; i < count; ++i) {
- const inputIndex = i;
- const query = IDBKeyRange.only(sequenceList[i]);
-
- const onGetAll = (rows) => {
- for (const row of rows) {
- if (row.dictionary === mainDictionary) {
- results.push(this._createTerm(row, inputIndex));
- }
- }
- if (++completeCount >= count) {
- resolve(results);
- }
- };
-
- this._db.getAll(index, query, onGetAll, reject);
- }
- });
- }
-
- findTermMetaBulk(termList, dictionaries) {
- return this._findGenericBulk('termMeta', 'expression', termList, dictionaries, this._createTermMeta.bind(this));
- }
-
- findKanjiBulk(kanjiList, dictionaries) {
- return this._findGenericBulk('kanji', 'character', kanjiList, dictionaries, this._createKanji.bind(this));
- }
-
- findKanjiMetaBulk(kanjiList, dictionaries) {
- return this._findGenericBulk('kanjiMeta', 'character', kanjiList, dictionaries, this._createKanjiMeta.bind(this));
- }
-
- findTagForTitle(name, title) {
- const query = IDBKeyRange.only(name);
- return this._db.find('tagMeta', 'name', query, (row) => (row.dictionary === title), null);
- }
-
- getMedia(targets) {
- return new Promise((resolve, reject) => {
- const count = targets.length;
- const results = new Array(count).fill(null);
- if (count === 0) {
- resolve(results);
- return;
- }
-
- let completeCount = 0;
- const transaction = this._db.transaction(['media'], 'readonly');
- const objectStore = transaction.objectStore('media');
- const index = objectStore.index('path');
-
- for (let i = 0; i < count; ++i) {
- const inputIndex = i;
- const {path, dictionaryName} = targets[i];
- const query = IDBKeyRange.only(path);
-
- const onGetAll = (rows) => {
- for (const row of rows) {
- if (row.dictionary !== dictionaryName) { continue; }
- results[inputIndex] = this._createMedia(row, inputIndex);
- }
- if (++completeCount >= count) {
- resolve(results);
- }
- };
-
- this._db.getAll(index, query, onGetAll, reject);
- }
- });
- }
-
- getDictionaryInfo() {
- return new Promise((resolve, reject) => {
- const transaction = this._db.transaction(['dictionaries'], 'readonly');
- const objectStore = transaction.objectStore('dictionaries');
- this._db.getAll(objectStore, null, resolve, reject);
- });
- }
-
- getDictionaryCounts(dictionaryNames, getTotal) {
- return new Promise((resolve, reject) => {
- const targets = [
- ['kanji', 'dictionary'],
- ['kanjiMeta', 'dictionary'],
- ['terms', 'dictionary'],
- ['termMeta', 'dictionary'],
- ['tagMeta', 'dictionary'],
- ['media', 'dictionary']
- ];
- const objectStoreNames = targets.map(([objectStoreName]) => objectStoreName);
- const transaction = this._db.transaction(objectStoreNames, 'readonly');
- const databaseTargets = targets.map(([objectStoreName, indexName]) => {
- const objectStore = transaction.objectStore(objectStoreName);
- const index = objectStore.index(indexName);
- return {objectStore, index};
- });
-
- const countTargets = [];
- if (getTotal) {
- for (const {objectStore} of databaseTargets) {
- countTargets.push([objectStore, null]);
- }
- }
- for (const dictionaryName of dictionaryNames) {
- const query = IDBKeyRange.only(dictionaryName);
- for (const {index} of databaseTargets) {
- countTargets.push([index, query]);
- }
- }
-
- const onCountComplete = (results) => {
- const resultCount = results.length;
- const targetCount = targets.length;
- const counts = [];
- for (let i = 0; i < resultCount; i += targetCount) {
- const countGroup = {};
- for (let j = 0; j < targetCount; ++j) {
- countGroup[targets[j][0]] = results[i + j];
- }
- counts.push(countGroup);
- }
- const total = getTotal ? counts.shift() : null;
- resolve({total, counts});
- };
-
- this._db.bulkCount(countTargets, onCountComplete, reject);
- });
- }
-
- async dictionaryExists(title) {
- const query = IDBKeyRange.only(title);
- const result = await this._db.find('dictionaries', 'title', query);
- return typeof result !== 'undefined';
- }
-
- bulkAdd(objectStoreName, items, start, count) {
- return this._db.bulkAdd(objectStoreName, items, start, count);
- }
-
- // Private
-
- async _findGenericBulk(objectStoreName, indexName, indexValueList, dictionaries, createResult) {
- return new Promise((resolve, reject) => {
- const results = [];
- const count = indexValueList.length;
- if (count === 0) {
- resolve(results);
- return;
- }
-
- const transaction = this._db.transaction([objectStoreName], 'readonly');
- const terms = transaction.objectStore(objectStoreName);
- const index = terms.index(indexName);
-
- let completeCount = 0;
- for (let i = 0; i < count; ++i) {
- const inputIndex = i;
- const query = IDBKeyRange.only(indexValueList[i]);
-
- const onGetAll = (rows) => {
- for (const row of rows) {
- if (dictionaries.has(row.dictionary)) {
- results.push(createResult(row, inputIndex));
- }
- }
- if (++completeCount >= count) {
- resolve(results);
- }
- };
-
- this._db.getAll(index, query, onGetAll, reject);
- }
- });
- }
-
- _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
- };
- }
-
- _createKanji(row, index) {
- return {
- index,
- character: row.character,
- onyomi: dictFieldSplit(row.onyomi),
- kunyomi: dictFieldSplit(row.kunyomi),
- tags: dictFieldSplit(row.tags),
- glossary: row.meanings,
- stats: row.stats,
- dictionary: row.dictionary
- };
- }
-
- _createTermMeta({expression, mode, data, dictionary}, index) {
- return {expression, mode, data, dictionary, index};
- }
-
- _createKanjiMeta({character, mode, data, dictionary}, index) {
- return {character, mode, data, dictionary, index};
- }
-
- _createMedia(row, index) {
- return Object.assign({}, row, {index});
- }
-}