Speed up search query update after each transaction
Contains two optimizations, which will greatly affect initial sync for
an account with a large number of contacts.
- When selecting from search_index, we used two columns from different tables:
'raw_contacts.contact_id IN (...) OR data.raw_contact_id IN (...)'.
But this is really equivalent to
'... OR raw_contacts._id IN (...)' as we inner join the two tables.
The latter is pretty much faster as both columns are in the same table and
each has an index.
Bug 5842620
- When we replace a search_index row, we first try UPDATE and if no rows are
affected then INSERT. But this UPDATE is pretty slow; on a test device it can
easily take >10ms, and with a 10K contacts database it took >80ms. (INSERT
is pretty fast; it's typically ~1ms.)
Let's first bulk DELETE all affected rows, then INSERT. The DELETE takes
roughly the same time as the previous UPDATE, but we only need 1 DELETE
per transaction, not per row contact.
Bug 5887648
Change-Id: I549ea2b47d7058bfba96a861236295f682d88c10
diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java
index c989fdc..2174f69 100644
--- a/src/com/android/providers/contacts/SearchIndexManager.java
+++ b/src/com/android/providers/contacts/SearchIndexManager.java
@@ -17,6 +17,7 @@
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
+import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.SearchIndexColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
@@ -30,6 +31,7 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.ProviderStatus;
+import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.util.Log;
@@ -240,7 +242,7 @@
int count = 0;
try {
mDbHelper.createSearchIndexTable(db);
- count = buildIndex(db, null, false);
+ count = buildAndInsertIndex(db, null);
} finally {
mContactsProvider.setProviderStatus(ProviderStatus.STATUS_NORMAL);
@@ -254,7 +256,7 @@
mSb.setLength(0);
mSb.append("(");
if (!contactIds.isEmpty()) {
- mSb.append(Data.CONTACT_ID + " IN (");
+ mSb.append(RawContacts.CONTACT_ID + " IN (");
for (Long contactId : contactIds) {
mSb.append(contactId).append(",");
}
@@ -266,7 +268,7 @@
if (!contactIds.isEmpty()) {
mSb.append(" OR ");
}
- mSb.append(Data.RAW_CONTACT_ID + " IN (");
+ mSb.append(RawContactsColumns.CONCRETE_ID + " IN (");
for (Long rawContactId : rawContactIds) {
mSb.append(rawContactId).append(",");
}
@@ -275,10 +277,25 @@
}
mSb.append(")");
- buildIndex(mDbHelper.getWritableDatabase(), mSb.toString(), true);
+
+ // The selection to select raw_contacts.
+ final String rawContactsSelection = mSb.toString();
+
+ // Remove affected search_index rows.
+ final SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ final int deleted = db.delete(Tables.SEARCH_INDEX,
+ SearchIndexColumns.CONTACT_ID + " IN (SELECT " +
+ RawContacts.CONTACT_ID +
+ " FROM " + Tables.RAW_CONTACTS +
+ " WHERE " + rawContactsSelection +
+ ")"
+ , null);
+
+ // Then rebuild index for them.
+ buildAndInsertIndex(db, rawContactsSelection);
}
- private int buildIndex(SQLiteDatabase db, String selection, boolean replace) {
+ private int buildAndInsertIndex(SQLiteDatabase db, String selection) {
mSb.setLength(0);
mSb.append(Data.CONTACT_ID + ", ");
mSb.append("(CASE WHEN " + DataColumns.MIMETYPE_ID + "=");
@@ -307,7 +324,7 @@
long contactId = cursor.getLong(0);
if (contactId != currentContactId) {
if (currentContactId != -1) {
- saveContactIndex(db, currentContactId, mIndexBuilder, replace);
+ insertIndexRow(db, currentContactId, mIndexBuilder);
count++;
}
currentContactId = contactId;
@@ -321,7 +338,7 @@
}
}
if (currentContactId != -1) {
- saveContactIndex(db, currentContactId, mIndexBuilder, replace);
+ insertIndexRow(db, currentContactId, mIndexBuilder);
count++;
}
} finally {
@@ -330,24 +347,13 @@
return count;
}
- private void saveContactIndex(
- SQLiteDatabase db, long contactId, IndexBuilder builder, boolean replace) {
+ private void insertIndexRow(SQLiteDatabase db, long contactId, IndexBuilder builder) {
mValues.clear();
mValues.put(SearchIndexColumns.CONTENT, builder.getContent());
mValues.put(SearchIndexColumns.NAME, builder.getName());
mValues.put(SearchIndexColumns.TOKENS, builder.getTokens());
- if (replace) {
- mSelectionArgs1[0] = String.valueOf(contactId);
- int count = db.update(Tables.SEARCH_INDEX, mValues,
- SearchIndexColumns.CONTACT_ID + "=CAST(? AS int)", mSelectionArgs1);
- if (count == 0) {
- mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
- db.insert(Tables.SEARCH_INDEX, null, mValues);
- }
- } else {
- mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
- db.insert(Tables.SEARCH_INDEX, null, mValues);
- }
+ mValues.put(SearchIndexColumns.CONTACT_ID, contactId);
+ db.insert(Tables.SEARCH_INDEX, null, mValues);
}
private int getSearchIndexVersion() {
return Integer.parseInt(mDbHelper.getProperty(PROPERTY_SEARCH_INDEX_VERSION, "0"));