diff options
author | Dmitri Plotnikov <dplotnikov@google.com> | 2011-02-11 16:06:35 -0800 |
---|---|---|
committer | Dmitri Plotnikov <dplotnikov@google.com> | 2011-02-11 16:06:35 -0800 |
commit | 155accbcb95fc13b984cf0ea8e5498a9c619cbf5 (patch) | |
tree | 4e84cd7eb3f735f3bb30b94e3c2005584bf152d6 | |
parent | 92ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100 (diff) | |
download | packages_providers_ContactsProvider-155accbcb95fc13b984cf0ea8e5498a9c619cbf5.zip packages_providers_ContactsProvider-155accbcb95fc13b984cf0ea8e5498a9c619cbf5.tar.gz packages_providers_ContactsProvider-155accbcb95fc13b984cf0ea8e5498a9c619cbf5.tar.bz2 |
Full text search: email suggestions
Also, removing a bunch of dead code
Bug: 2078420
Change-Id: I2c0a9ddd8e60624049b39ac018f87bfabc6574c2
7 files changed, 98 insertions, 159 deletions
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java index a772201..6fc936f 100644 --- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java +++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java @@ -92,7 +92,7 @@ import java.util.Locale; * 500-599 Honeycomb-MR1 * </pre> */ - static final int DATABASE_VERSION = 502; + static final int DATABASE_VERSION = 503; private static final String DATABASE_NAME = "contacts2.db"; private static final String DATABASE_PRESENCE = "presence_db"; @@ -485,6 +485,7 @@ import java.util.Locale; public static final class SearchIndexColumns { public static final String CONTACT_ID = "contact_id"; public static final String CONTENT = "content"; + public static final String NAME = "name"; public static final String TOKENS = "tokens"; } @@ -1096,6 +1097,7 @@ import java.util.Locale; + " USING FTS4 (" + SearchIndexColumns.CONTACT_ID + " INTEGER REFERENCES contacts(_id) NOT NULL," + SearchIndexColumns.CONTENT + " TEXT, " + + SearchIndexColumns.NAME + " TEXT, " + SearchIndexColumns.TOKENS + " TEXT" + ")"); } @@ -1537,6 +1539,7 @@ import java.util.Locale; boolean upgradeViewsAndTriggers = false; boolean upgradeNameLookup = false; boolean upgradeLegacyApiSupport = false; + boolean upgradeSearchIndex = false; if (oldVersion == 99) { upgradeViewsAndTriggers = true; @@ -1768,20 +1771,26 @@ import java.util.Locale; // Honeycomb-MR1 upgrades if (oldVersion < 500) { - upgradeToVersion500(db); - oldVersion = 500; + upgradeSearchIndex = true; } if (oldVersion < 501) { + upgradeSearchIndex = true; upgradeToVersion501(db); oldVersion = 501; } if (oldVersion < 502) { + upgradeSearchIndex = true; upgradeToVersion502(db); oldVersion = 502; } + if (oldVersion < 503) { + upgradeSearchIndex = true; + oldVersion = 503; + } + if (upgradeViewsAndTriggers) { createContactsViews(db); createGroupsView(db); @@ -1800,6 +1809,10 @@ import java.util.Locale; rebuildNameLookup(db); } + if (upgradeSearchIndex) { + setProperty(db, SearchIndexManager.PROPERTY_SEARCH_INDEX_VERSION, "0"); + } + if (oldVersion != newVersion) { throw new IllegalStateException( "error upgrading the database to version " + newVersion); @@ -2798,20 +2811,14 @@ import java.util.Locale; " (" + PhoneLookupColumns.DATA_ID + ", " + PhoneLookupColumns.MIN_MATCH + ");"); } - private void upgradeToVersion500(SQLiteDatabase db) { - setProperty(db, SearchIndexManager.PROPERTY_SEARCH_INDEX_VERSION, "0"); - } - private void upgradeToVersion501(SQLiteDatabase db) { // Remove organization rows from the name lookup, we now use search index for that db.execSQL("DELETE FROM name_lookup WHERE name_type=5"); - setProperty(db, SearchIndexManager.PROPERTY_SEARCH_INDEX_VERSION, "0"); } private void upgradeToVersion502(SQLiteDatabase db) { // Remove Chinese and Korean name lookup - this data is now in the search index db.execSQL("DELETE FROM name_lookup WHERE name_type IN (6, 7)"); - setProperty(db, SearchIndexManager.PROPERTY_SEARCH_INDEX_VERSION, "0"); } public String extractHandleFromEmailAddress(String email) { diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 1a8178d..7dd5e2a 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -3533,8 +3533,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun boolean orNeeded = false; String normalizedName = NameNormalizer.normalize(filterParam); if (normalizedName.length() > 0) { - sb.append(Data.RAW_CONTACT_ID + " IN "); - appendRawContactsByNormalizedNameFilter(sb, normalizedName, false); + sb.append(Data.RAW_CONTACT_ID + " IN " + + "(SELECT " + RawContactsColumns.CONCRETE_ID + + " FROM " + Tables.SEARCH_INDEX + + " JOIN " + Tables.RAW_CONTACTS + + " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID + + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" + + " WHERE " + SearchIndexColumns.NAME + " MATCH "); + DatabaseUtils.appendEscapedSQLString(sb, filterParam + "*"); + sb.append(")"); orNeeded = true; hasCondition = true; } @@ -3618,25 +3625,20 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun sb.append(" AND " + Data.DATA1 + " LIKE "); DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%'); if (!filterParam.contains("@")) { - String normalizedName = NameNormalizer.normalize(filterParam); - if (normalizedName.length() > 0) { - - /* - * Using a UNION instead of an "OR" to make SQLite use the right - * indexes. We need it to use the (mimetype,data1) index for the - * email lookup (see above), but not for the name lookup. - * SQLite is not smart enough to use the index on one side of an OR - * but not on the other. Using two separate nested queries - * and a UNION between them does the job. - */ - sb.append( - " UNION SELECT " + Data._ID + - " FROM " + Tables.DATA + - " WHERE +" + DataColumns.MIMETYPE_ID + "="); - sb.append(mDbHelper.getMimeTypeIdForEmail()); - sb.append(" AND " + Data.RAW_CONTACT_ID + " IN "); - appendRawContactsByNormalizedNameFilter(sb, normalizedName, false); - } + sb.append( + " UNION SELECT " + Data._ID + + " FROM " + Tables.DATA + + " WHERE +" + DataColumns.MIMETYPE_ID + "="); + sb.append(mDbHelper.getMimeTypeIdForEmail()); + sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " + + "(SELECT " + RawContactsColumns.CONCRETE_ID + + " FROM " + Tables.SEARCH_INDEX + + " JOIN " + Tables.RAW_CONTACTS + + " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID + + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" + + " WHERE " + SearchIndexColumns.NAME + " MATCH "); + DatabaseUtils.appendEscapedSQLString(sb, filterParam + "*"); + sb.append(")"); } sb.append(")"); qb.appendWhere(sb); @@ -5024,119 +5026,6 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun " IN(" + CONTACT_LOOKUP_NAME_TYPES + "))"); } - public String getRawContactsByFilterAsNestedQuery(String filterParam) { - StringBuilder sb = new StringBuilder(); - appendRawContactsByFilterAsNestedQuery(sb, filterParam); - return sb.toString(); - } - - public void appendRawContactsByFilterAsNestedQuery(StringBuilder sb, String filterParam) { - appendRawContactsByNormalizedNameFilter(sb, NameNormalizer.normalize(filterParam), true); - } - - private void appendRawContactsByNormalizedNameFilter(StringBuilder sb, String normalizedName, - boolean allowEmailMatch) { - if (TextUtils.isEmpty(normalizedName)) { - // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here - sb.append("(0)"); - } else { - sb.append("(" + - "SELECT " + NameLookupColumns.RAW_CONTACT_ID + - " FROM " + Tables.NAME_LOOKUP + - " WHERE " + NameLookupColumns.NORMALIZED_NAME + - " GLOB '"); - // Should not use a "?" argument placeholder here, because - // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME. - sb.append(normalizedName); - sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN (" - + NameLookupType.NAME_COLLATION_KEY + "," - + NameLookupType.NICKNAME); - if (allowEmailMatch) { - sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME); - } - sb.append("))"); - } - } - - - public boolean appendEmailBasedDataFilter(StringBuilder sb, String filter) { - if (filter.indexOf('@') == -1) { - return false; - } - - String address = mDbHelper.extractAddressFromEmailAddress(filter); - if (TextUtils.isEmpty(address)) { - return false; - } - - sb.append(DataColumns.MIMETYPE_ID + " IN ("); - sb.append(mDbHelper.getMimeTypeIdForEmail()); - sb.append(","); - sb.append(mDbHelper.getMimeTypeIdForIm()); - sb.append(","); - sb.append(mDbHelper.getMimeTypeIdForSip()); - sb.append(") AND " + Data.DATA1 + " LIKE("); - DatabaseUtils.appendEscapedSQLString(sb, address + '%'); - sb.append(")"); - return true; - } - - public boolean appendPhoneNumberBasedDataFilter(StringBuilder sb, String filter) { - if (!isPhoneNumber(filter)) { - return false; - } - - String number = PhoneNumberUtils.normalizeNumber(filter); - sb.append(DataColumns.CONCRETE_ID + " IN " + - "(SELECT " + PhoneLookupColumns.DATA_ID - + " FROM " + Tables.PHONE_LOOKUP - + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '"); - sb.append(number); - sb.append("%'"); - - String numberE164 = PhoneNumberUtils.formatNumberToE164(number, mDbHelper.getCountryIso()); - if (!TextUtils.isEmpty(numberE164)) { - sb.append(" OR " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '"); - sb.append(numberE164); - sb.append("%'"); - } - sb.append(")"); - - String normalizedFilter = NameNormalizer.normalize(filter); - if (TextUtils.isEmpty(normalizedFilter)) { - return true; - } - - sb.append(" OR " + DataColumns.CONCRETE_RAW_CONTACT_ID + " IN " + - "(SELECT " + NameLookupColumns.RAW_CONTACT_ID + - " FROM " + Tables.NAME_LOOKUP + - " WHERE " + NameLookupColumns.NORMALIZED_NAME + - " GLOB '"); - sb.append(normalizedFilter); - sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN (" - + CONTACT_LOOKUP_NAME_TYPES + "))"); - return true; - } - - public boolean appendNameBasedRawContactFilter(StringBuilder sb, String filter) { - String normalizedFilter = NameNormalizer.normalize(filter); - if (TextUtils.isEmpty(normalizedFilter)) { - return false; - } - - sb.append(DataColumns.CONCRETE_RAW_CONTACT_ID + " IN " + - "(SELECT " + NameLookupColumns.RAW_CONTACT_ID + - " FROM " + Tables.NAME_LOOKUP + - " WHERE " + NameLookupColumns.NORMALIZED_NAME + - " GLOB '"); - // Should not use a "?" argument placeholder here, because - // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME. - sb.append(normalizedFilter); - sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN (" - + CONTACT_LOOKUP_NAME_TYPES + "))"); - return true; - } - public boolean isPhoneNumber(String filter) { boolean atLeastOneDigit = false; int len = filter.length(); diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java index 55aeec8..51c61bf 100644 --- a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java +++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java @@ -207,18 +207,18 @@ public class DataRowHandlerForStructuredName extends DataRowHandler { || !TextUtils.isEmpty(phoneticGiven)) { mSb.setLength(0); if (!TextUtils.isEmpty(phoneticFamily)) { - builder.appendToken(phoneticFamily); + builder.appendName(phoneticFamily); mSb.append(phoneticFamily); } if (!TextUtils.isEmpty(phoneticMiddle)) { - builder.appendToken(phoneticMiddle); + builder.appendName(phoneticMiddle); mSb.append(phoneticMiddle); } if (!TextUtils.isEmpty(phoneticGiven)) { - builder.appendToken(phoneticGiven); + builder.appendName(phoneticGiven); mSb.append(phoneticGiven); } - builder.appendToken(mSb.toString().trim()); + builder.appendName(mSb.toString().trim()); } } } diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java index e5a3a2b..a51728e 100644 --- a/src/com/android/providers/contacts/LegacyApiSupport.java +++ b/src/com/android/providers/contacts/LegacyApiSupport.java @@ -19,6 +19,8 @@ import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns; import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType; import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns; import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; @@ -59,6 +61,7 @@ import android.provider.ContactsContract.Groups; import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.Settings; import android.provider.ContactsContract.StatusUpdates; +import android.text.TextUtils; import android.util.Log; import java.util.HashMap; @@ -1632,7 +1635,7 @@ public class LegacyApiSupport { applyRawContactsAccount(qb); String filterParam = uri.getPathSegments().get(2); qb.appendWhere(" AND " + People._ID + " IN " - + mContactsProvider.getRawContactsByFilterAsNestedQuery(filterParam)); + + getRawContactsByFilterAsNestedQuery(filterParam)); break; } @@ -1998,6 +2001,32 @@ public class LegacyApiSupport { + DatabaseUtils.sqlEscapeString(systemId) + "))"; } + private String getRawContactsByFilterAsNestedQuery(String filterParam) { + StringBuilder sb = new StringBuilder(); + String normalizedName = NameNormalizer.normalize(filterParam); + if (TextUtils.isEmpty(normalizedName)) { + // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here + sb.append("(0)"); + } else { + sb.append("(" + + "SELECT " + NameLookupColumns.RAW_CONTACT_ID + + " FROM " + Tables.NAME_LOOKUP + + " WHERE " + NameLookupColumns.NORMALIZED_NAME + + " GLOB '"); + // Should not use a "?" argument placeholder here, because + // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME. + sb.append(normalizedName); + sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN (" + + NameLookupType.NAME_COLLATION_KEY + "," + + NameLookupType.NICKNAME); + if (true) { + sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME); + } + sb.append("))"); + } + return sb.toString(); + } + /** * Called when a change has been made. * diff --git a/src/com/android/providers/contacts/NameLookupBuilder.java b/src/com/android/providers/contacts/NameLookupBuilder.java index 68c84e8..5ebbcd1 100644 --- a/src/com/android/providers/contacts/NameLookupBuilder.java +++ b/src/com/android/providers/contacts/NameLookupBuilder.java @@ -146,7 +146,7 @@ public abstract class NameLookupBuilder { } for (int i = 0; i < tokenCount; i++) { - builder.appendToken(mNames[i]); + builder.appendName(mNames[i]); } appendNameShorthandLookup(builder, name, fullNameStyle); @@ -162,7 +162,7 @@ public abstract class NameLookupBuilder { NameSplitter.Name name = new NameSplitter.Name(); mSplitter.split(name, fullName, fullNameStyle); if (name.givenNames != null) { - builder.appendToken(name.givenNames); + builder.appendName(name.givenNames); appendKoreanNameConsonantsLookup(builder, name.givenNames); } appendKoreanNameConsonantsLookup(builder, fullName); @@ -218,7 +218,7 @@ public abstract class NameLookupBuilder { // At least, insert consonants when Korean characters are two or more. // Only one character cases are covered by NAME_COLLATION_KEY if (consonantLength > 1) { - builder.appendToken(mStringBuilder.toString()); + builder.appendName(mStringBuilder.toString()); } } @@ -324,7 +324,7 @@ public abstract class NameLookupBuilder { ContactLocaleUtils.getIntance().getNameLookupKeys(name, fullNameStyle); if (it != null) { while (it.hasNext()) { - builder.appendToken(it.next()); + builder.appendName(it.next()); } } } diff --git a/src/com/android/providers/contacts/SearchIndexManager.java b/src/com/android/providers/contacts/SearchIndexManager.java index 7555c99..d198337 100644 --- a/src/com/android/providers/contacts/SearchIndexManager.java +++ b/src/com/android/providers/contacts/SearchIndexManager.java @@ -66,6 +66,7 @@ public class SearchIndexManager { public static final int SEPARATOR_COMMA = 3; private StringBuilder mSbContent = new StringBuilder(); + private StringBuilder mSbName = new StringBuilder(); private StringBuilder mSbTokens = new StringBuilder(); private StringBuilder mSbElementContent = new StringBuilder(); private HashSet<String> mUniqueElements = new HashSet<String>(); @@ -78,6 +79,7 @@ public class SearchIndexManager { void reset() { mSbContent.setLength(0); mSbTokens.setLength(0); + mSbName.setLength(0); mSbElementContent.setLength(0); mUniqueElements.clear(); } @@ -86,6 +88,10 @@ public class SearchIndexManager { return mSbContent.length() == 0 ? null : mSbContent.toString(); } + public String getName() { + return mSbName.length() == 0 ? null : mSbName.toString(); + } + public String getTokens() { return mSbTokens.length() == 0 ? null : mSbTokens.toString(); } @@ -100,7 +106,7 @@ public class SearchIndexManager { @Override public String toString() { - return "Content: " + mSbContent + "\n Tokens: " + mSbTokens; + return "Content: " + mSbContent + "\n Name: " + mSbTokens + "\n Tokens: " + mSbTokens; } public void commit() { @@ -162,10 +168,6 @@ public class SearchIndexManager { } } - public void appendTokenFromColumn(String columnName) { - appendToken(getString(columnName)); - } - public void appendToken(String token) { if (TextUtils.isEmpty(token)) { return; @@ -176,6 +178,17 @@ public class SearchIndexManager { } mSbTokens.append(token); } + + public void appendName(String name) { + if (TextUtils.isEmpty(name)) { + return; + } + + if (mSbName.length() != 0) { + mSbName.append(' '); + } + mSbName.append(name); + } } private final ContactsProvider2 mContactsProvider; @@ -309,6 +322,7 @@ public class SearchIndexManager { SQLiteDatabase db, long contactId, IndexBuilder builder, boolean replace) { 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); diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index f308f1c..790d294 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -1090,7 +1090,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot"); assertStoredValuesWithProjection(filterUri2, values); - Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hottamale"); + Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale"); assertStoredValuesWithProjection(filterUri3, values); Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme"); @@ -2965,7 +2965,7 @@ public class ContactsProvider2Test extends BaseContactsProvider2Test { Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); long photoId = ContentUris.parseId(dataUri); - assertNull(getStoredValue(twigUri, Data._ID)); + assertEquals(0, getCount(twigUri, null, null)); values.clear(); values.put(Photo.PHOTO, loadTestPhoto()); |