mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-10-11 13:46:51 +08:00
fix(search): Prevent from adding duplicate threads to the search index
- We have to manually check if we are inserting a thread to the index that already exists because the virtual table does not support unique indexes - Add versioning to the index to be able to rebuild it for the next update
This commit is contained in:
parent
db0640a614
commit
42bce855ed
3 changed files with 42 additions and 25 deletions
|
@ -13,7 +13,7 @@ const MAX_INDEX_SIZE = 30000
|
||||||
const CHUNKS_PER_ACCOUNT = 10
|
const CHUNKS_PER_ACCOUNT = 10
|
||||||
const INDEXING_WAIT = 1000
|
const INDEXING_WAIT = 1000
|
||||||
const MESSAGE_BODY_LENGTH = 50000
|
const MESSAGE_BODY_LENGTH = 50000
|
||||||
|
const INDEX_VERSION = 1
|
||||||
|
|
||||||
class SearchIndexStore {
|
class SearchIndexStore {
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ class SearchIndexStore {
|
||||||
|
|
||||||
this.accountIds = _.pluck(AccountStore.accounts(), 'id')
|
this.accountIds = _.pluck(AccountStore.accounts(), 'id')
|
||||||
this.initializeIndex()
|
this.initializeIndex()
|
||||||
|
.then(() => {
|
||||||
|
NylasEnv.config.set('threadSearchIndexVersion', INDEX_VERSION)
|
||||||
|
return Promise.resolve()
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log(`Thread Search: Index built successfully in ${((Date.now() - date) / 1000)}s`)
|
console.log(`Thread Search: Index built successfully in ${((Date.now() - date) / 1000)}s`)
|
||||||
this.unsubscribers = [
|
this.unsubscribers = [
|
||||||
|
@ -48,6 +52,11 @@ class SearchIndexStore {
|
||||||
* before sync completes
|
* before sync completes
|
||||||
*/
|
*/
|
||||||
initializeIndex() {
|
initializeIndex() {
|
||||||
|
if (NylasEnv.config.get('threadSearchIndexVersion') !== INDEX_VERSION) {
|
||||||
|
return this.clearIndex()
|
||||||
|
.then(() => this.buildIndex(this.accountIds))
|
||||||
|
}
|
||||||
|
|
||||||
return DatabaseStore.searchIndexSize(Thread)
|
return DatabaseStore.searchIndexSize(Thread)
|
||||||
.then((size) => {
|
.then((size) => {
|
||||||
console.log(`Thread Search: Current index size is ${(size || 0)} threads`)
|
console.log(`Thread Search: Current index size is ${(size || 0)} threads`)
|
||||||
|
@ -56,12 +65,7 @@ class SearchIndexStore {
|
||||||
}
|
}
|
||||||
return this.getUnindexedAccounts()
|
return this.getUnindexedAccounts()
|
||||||
})
|
})
|
||||||
.then((accountIds) => {
|
.then((accountIds) => this.buildIndex(accountIds))
|
||||||
if (accountIds.length > 0) {
|
|
||||||
return this.buildIndex(accountIds)
|
|
||||||
}
|
|
||||||
return Promise.resolve()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,6 +141,7 @@ class SearchIndexStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildIndex = (accountIds) => {
|
buildIndex = (accountIds) => {
|
||||||
|
if (!accountIds || accountIds.length === 0) { return Promise.resolve() }
|
||||||
const sizePerAccount = Math.floor(INDEX_SIZE / accountIds.length)
|
const sizePerAccount = Math.floor(INDEX_SIZE / accountIds.length)
|
||||||
return Promise.resolve(accountIds)
|
return Promise.resolve(accountIds)
|
||||||
.each((accountId) => (
|
.each((accountId) => (
|
||||||
|
|
|
@ -75,11 +75,11 @@ class Matcher
|
||||||
when '>=' then return modelValue >= matcherValue
|
when '>=' then return modelValue >= matcherValue
|
||||||
when 'in' then return modelValue in matcherValue
|
when 'in' then return modelValue in matcherValue
|
||||||
when 'contains'
|
when 'contains'
|
||||||
!!modelArrayContainsValue(modelValue, matcherValue)
|
modelArrayContainsValue(modelValue, matcherValue)
|
||||||
|
|
||||||
when 'containsAny'
|
when 'containsAny'
|
||||||
_.any matcherValue, (submatcherValue) ->
|
_.any matcherValue, (submatcherValue) ->
|
||||||
!!modelArrayContainsValue(modelValue, submatcherValue)
|
modelArrayContainsValue(modelValue, submatcherValue)
|
||||||
|
|
||||||
when 'startsWith' then return modelValue.startsWith(matcherValue)
|
when 'startsWith' then return modelValue.startsWith(matcherValue)
|
||||||
when 'like' then modelValue.search(new RegExp(".*#{matcherValue}.*", "gi")) >= 0
|
when 'like' then modelValue.search(new RegExp(".*#{matcherValue}.*", "gi")) >= 0
|
||||||
|
|
|
@ -574,8 +574,23 @@ class DatabaseStore extends NylasStore
|
||||||
sql = "DROP TABLE IF EXISTS `#{searchTableName}`"
|
sql = "DROP TABLE IF EXISTS `#{searchTableName}`"
|
||||||
@_query(sql)
|
@_query(sql)
|
||||||
|
|
||||||
indexModel: (model, indexData) =>
|
isModelIndexed: (model, isIndexed) =>
|
||||||
|
return Promise.resolve(true) if isIndexed is true
|
||||||
searchTableName = "#{model.constructor.name}Search"
|
searchTableName = "#{model.constructor.name}Search"
|
||||||
|
exists = (
|
||||||
|
"SELECT rowid FROM `#{searchTableName}` WHERE `#{searchTableName}`.`content_id` = ?"
|
||||||
|
)
|
||||||
|
return @_query(exists, [model.id]).then((results) =>
|
||||||
|
return Promise.resolve(results.length > 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
indexModel: (model, indexData, isModelIndexed) =>
|
||||||
|
searchTableName = "#{model.constructor.name}Search"
|
||||||
|
@isModelIndexed(model, isModelIndexed)
|
||||||
|
.then((isIndexed) =>
|
||||||
|
if (isIndexed)
|
||||||
|
return @updateModelIndex(model, indexData, isIndexed)
|
||||||
|
|
||||||
indexFields = Object.keys(indexData)
|
indexFields = Object.keys(indexData)
|
||||||
keysSql = 'content_id, ' + indexFields.join(", ")
|
keysSql = 'content_id, ' + indexFields.join(", ")
|
||||||
valsSql = '?, ' + indexFields.map(=> '?').join(", ")
|
valsSql = '?, ' + indexFields.map(=> '?').join(", ")
|
||||||
|
@ -584,17 +599,14 @@ class DatabaseStore extends NylasStore
|
||||||
"INSERT INTO `#{searchTableName}`(#{keysSql}) VALUES (#{valsSql})"
|
"INSERT INTO `#{searchTableName}`(#{keysSql}) VALUES (#{valsSql})"
|
||||||
)
|
)
|
||||||
return @_query(sql, values)
|
return @_query(sql, values)
|
||||||
|
|
||||||
updateModelIndex: (model, indexData) =>
|
|
||||||
searchTableName = "#{model.constructor.name}Search"
|
|
||||||
exists = (
|
|
||||||
"SELECT rowid FROM `#{searchTableName}` WHERE `#{searchTableName}`.`content_id` = ?"
|
|
||||||
)
|
)
|
||||||
return @_query(exists, [model.id])
|
|
||||||
.then((results) =>
|
updateModelIndex: (model, indexData, isModelIndexed) =>
|
||||||
isIndexed = results.length > 0
|
searchTableName = "#{model.constructor.name}Search"
|
||||||
|
@isModelIndexed(model, isModelIndexed)
|
||||||
|
.then((isIndexed) =>
|
||||||
if (not isIndexed)
|
if (not isIndexed)
|
||||||
return @indexModel(model, indexData)
|
return @indexModel(model, indexData, isIndexed)
|
||||||
|
|
||||||
indexFields = Object.keys(indexData)
|
indexFields = Object.keys(indexData)
|
||||||
values = indexFields.map((key) => indexData[key]).concat([model.id])
|
values = indexFields.map((key) => indexData[key]).concat([model.id])
|
||||||
|
|
Loading…
Add table
Reference in a new issue