lib/database-setup-query-builder.js
- /* eslint global-require:0 */
- import ModelRegistry from './model-registry';
- import {tableNameForJoin} from './utils';
- import Attributes from './attributes';
-
- const {AttributeCollection, AttributeJoinedData} = Attributes;
-
- /**
- The factory methods in this class assemble SQL queries that build Model
- tables based on their attribute schema.
-
- @private
- */
- export default class DatabaseSetupQueryBuilder {
-
- setupQueries() {
- let queries = []
- for (const klass of ModelRegistry.getAllConstructors()) {
- queries = queries.concat(this.setupQueriesForTable(klass));
- }
- return queries;
- }
-
- analyzeQueries() {
- const queries = [];
-
- for (const klass of ModelRegistry.getAllConstructors()) {
- const attributes = Object.keys(klass.attributes).map(k => klass.attributes[k]);
- const collectionAttributes = attributes.filter((attr) =>
- attr.queryable && attr instanceof AttributeCollection
- )
-
- queries.push(`ANALYZE \`${klass.name}\``);
- collectionAttributes.forEach((attribute) => {
- queries.push(`ANALYZE \`${tableNameForJoin(klass, attribute.itemClass)}\``)
- });
- }
- return queries;
- }
-
- setupQueriesForTable(klass) {
- const attributes = Object.keys(klass.attributes).map(k => klass.attributes[k]);
- let queries = [];
-
- // Identify attributes of this class that can be matched against. These
- // attributes need their own columns in the table
- const columnAttributes = attributes.filter(attr =>
- attr.queryable && attr.columnSQL && attr.jsonKey !== 'id'
- );
-
- const columns = ['id TEXT PRIMARY KEY', 'data BLOB']
- columnAttributes.forEach(attr => columns.push(attr.columnSQL()));
-
- const columnsSQL = columns.join(',');
- queries.unshift(`CREATE TABLE IF NOT EXISTS \`${klass.name}\` (${columnsSQL})`);
- queries.push(`CREATE UNIQUE INDEX IF NOT EXISTS \`${klass.name}_id\` ON \`${klass.name}\` (\`id\`)`);
-
- // Identify collection attributes that can be matched against. These require
- // JOIN tables. (Right now the only one of these is Thread.folders or
- // Thread.categories)
- const collectionAttributes = attributes.filter(attr =>
- attr.queryable && attr instanceof AttributeCollection
- );
- collectionAttributes.forEach((attribute) => {
- const joinTable = tableNameForJoin(klass, attribute.itemClass);
- const joinColumns = attribute.joinQueryableBy.map((name) =>
- klass.attributes[name].columnSQL()
- );
- joinColumns.unshift('id TEXT KEY', '`value` TEXT');
-
- queries.push(`CREATE TABLE IF NOT EXISTS \`${joinTable}\` (${joinColumns.join(',')})`);
- queries.push(`CREATE INDEX IF NOT EXISTS \`${joinTable.replace('-', '_')}_id\` ON \`${joinTable}\` (\`id\` ASC)`);
- queries.push(`CREATE UNIQUE INDEX IF NOT EXISTS \`${joinTable.replace('-', '_')}_val_id\` ON \`${joinTable}\` (\`value\` ASC, \`id\` ASC)`);
- });
-
- const joinedDataAttributes = attributes.filter(attr =>
- attr instanceof AttributeJoinedData
- )
-
- joinedDataAttributes.forEach((attribute) => {
- queries.push(`CREATE TABLE IF NOT EXISTS \`${attribute.modelTable}\` (id TEXT PRIMARY KEY, \`value\` TEXT)`);
- });
-
- if (klass.additionalSQLiteConfig && klass.additionalSQLiteConfig.setup) {
- queries = queries.concat(klass.additionalSQLiteConfig.setup());
- }
-
- if (klass.searchable === true) {
- queries.push(this.createSearchIndexSql(klass));
- }
-
- return queries;
- }
-
- createSearchIndexSql(klass) {
- if (!klass) {
- throw new Error(`RxDatabase::createSearchIndex - You must provide a class`);
- }
- if (!klass.searchFields) {
- throw new Error(`RxDatabase::createSearchIndex - ${klass.name} must expose an array of \`searchFields\``);
- }
- const searchTableName = `${klass.name}Search`;
- const searchFields = klass.searchFields;
- return (
- `CREATE VIRTUAL TABLE IF NOT EXISTS \`${searchTableName}\` ` +
- `USING fts5(
- tokenize='porter unicode61',
- content_id UNINDEXED,
- ${searchFields.join(', ')}
- )`
- );
- }
- }