From 93b4031bcc4672697e605650bdc470d6a4625775 Mon Sep 17 00:00:00 2001 From: Mark Hahnenberg Date: Sun, 15 Jan 2017 14:39:00 -0800 Subject: [PATCH] [search-index] Add isSearchIndexed field to searchable models Summary: The isModelIndexed function was extremely slow because it was scanning the unindexed 'content_id' field in the search index table. This diff adds a field to the searchable models (currently Thread, Contact, and Event) that stores whether that particular model has been indexed or not, allowing us to avoid doing the O(n) scan of the search index table. Test Plan: Run locally, verify that isModelIndexed is no longer a bottleneck Reviewers: juan, evan Reviewed By: evan Differential Revision: https://phab.nylas.com/D3695 --- src/flux/models/contact.es6 | 5 +++++ src/flux/models/event.es6 | 5 +++++ src/flux/models/thread.es6 | 5 +++++ src/flux/stores/database-store.es6 | 24 ++++++++++++++---------- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/flux/models/contact.es6 b/src/flux/models/contact.es6 index a1ff31153..916b8a0f9 100644 --- a/src/flux/models/contact.es6 +++ b/src/flux/models/contact.es6 @@ -75,6 +75,11 @@ export default class Contact extends Model { company: Attributes.String({ modelKey: 'company', }), + + isSearchIndexed: Attributes.Boolean({ + modelKey: 'isSearchIndexed', + jsonKey: 'is_search_indexed', + }), }); static additionalSQLiteConfig = { diff --git a/src/flux/models/event.es6 b/src/flux/models/event.es6 index a00362a5c..c5fb5b57c 100644 --- a/src/flux/models/event.es6 +++ b/src/flux/models/event.es6 @@ -110,6 +110,11 @@ export default class Event extends Model { modelKey: 'end', jsonKey: '_end', }), + + isSearchIndexed: Attributes.Boolean({ + modelKey: 'isSearchIndexed', + jsonKey: 'is_search_indexed', + }), }); static additionalSQLiteConfig = { diff --git a/src/flux/models/thread.es6 b/src/flux/models/thread.es6 index 934b62052..4a49e99f3 100644 --- a/src/flux/models/thread.es6 +++ b/src/flux/models/thread.es6 @@ -105,6 +105,11 @@ class Thread extends ModelWithMetadata { modelKey: 'inAllMail', jsonKey: 'in_all_mail', }), + + isSearchIndexed: Attributes.Boolean({ + modelKey: 'isSearchIndexed', + jsonKey: 'is_search_indexed', + }), }) static naturalSortOrder = () => { diff --git a/src/flux/stores/database-store.es6 b/src/flux/stores/database-store.es6 index 8021b689c..35eee6e3c 100644 --- a/src/flux/stores/database-store.es6 +++ b/src/flux/stores/database-store.es6 @@ -772,13 +772,7 @@ class DatabaseStore extends NylasStore { if (isIndexed === true) { return Promise.resolve(true); } - const searchTableName = `${model.constructor.name}Search` - const exists = ( - `SELECT rowid FROM \`${searchTableName}\` WHERE \`${searchTableName}\`.\`content_id\` = ?` - ) - return this._query(exists, [model.id]).then((results) => - Promise.resolve(results.length > 0) - ) + return Promise.resolve(!!model.isSearchIndexed); } indexModel(model, indexData, isModelIndexed) { @@ -795,7 +789,10 @@ class DatabaseStore extends NylasStore { const sql = ( `INSERT INTO \`${searchTableName}\`(${keysSql}) VALUES (${valsSql})` ) - return this._query(sql, values); + return this._query(sql, values).then(() => { + model.isSearchIndexed = true; + return this.inTransaction((t) => t.persistModel(model)) + }); }); } @@ -825,17 +822,24 @@ class DatabaseStore extends NylasStore { const sql = ( `DELETE FROM \`${searchTableName}\` WHERE \`${searchTableName}\`.\`content_id\` = ?` ); - return this._query(sql, [model.id]); + return this._query(sql, [model.id]).then(() => { + model.isSearchIndexed = false; + return this.inTransaction((t) => t.persistModel(model)) + }); } unindexModelsForAccount(accountId, modelKlass) { const modelTable = modelKlass.name; const searchTableName = `${modelTable}Search`; + /* TODO: We don't correctly clean up the model tables right now, so we don't + * want to destroy the index until we do so. const sql = ( `DELETE FROM \`${searchTableName}\` WHERE \`${searchTableName}\`.\`content_id\` IN (SELECT \`id\` FROM \`${modelTable}\` WHERE \`${modelTable}\`.\`account_id\` = ?)` ); - return this._query(sql, [accountId]); + return this._query(sql, [accountId]) + */ + return Promise.resolve() } }