0

I want to get suggestions from my DB table while using SearchView.

I get IllegalArgumentException: Invalid column (name of the column)

    private static final HashMap<String, String> mColumnMap = buildColumnMap();

    //Build a map for all columns that may be requested, which will be given to SQLiteQueryBuilder.
    //This allow ContentProvider to request columns without the need to know real column names and
    //create the alias itself
    private static HashMap<String, String> buildColumnMap() {
        HashMap<String, String> map = new HashMap<String, String>();

        map.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "rowid AS " +
                CookingContract.FoodEntry.COLUMN_NAME);
        map.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "rowid AS " +
                CookingContract.FoodEntry.COLUMN_DESCRIPTION);
        map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + CookingContract.FoodEntry._ID);


        return map;
    }


    //Return a Cursor over all words that match the given query
    public Cursor getWordMatches(String query, String[] columns) {
        String selection = CookingContract.FoodEntry.COLUMN_NAME + " LIKE ?";
        String[] selectionArgs = new String[]{"%" + query + "%"};

        return query(selection, selectionArgs, columns);
    }

    //The SQLiteBuilder provides a map for all possible columns requested to actual columns in the
    //DB, creating a simple column alias mechanism by which the ContentProvider doesn't need to know
    //the real column names
    private Cursor query(String selection, String[] selectionArgs, String[] columns) {
        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
        builder.setTables(CookingContract.FoodEntry.TABLE_NAME);
        builder.setProjectionMap(mColumnMap);


        Cursor cursor = builder.query(getReadableDatabase(),
                columns, selection, selectionArgs, null, null, null);

        if (cursor == null) {
            return null;
        } else if (!cursor.moveToFirst()) {
            cursor.close();
            return null;
        }
        return cursor;
    }
}

Here's part of the code of my ContentProvider

public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    Cursor retCursor;
    switch (sUriMatcher.match(uri)) {

        case SEARCH_SUGGEST:

            if (selectionArgs == null) {
                throw new IllegalArgumentException(
                        "selectionArgs must be provided for the Uri: +"uri");
            }
            return getSuggestions(selectionArgs[0]);

        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }

    retCursor.setNotificationUri(getContext().getContentResolver(), uri);
    return retCursor;
}

private Cursor getSuggestions(String query) {
    query = query.toLowerCase();
    String[] columns = new String[]{
           CookingContract.FoodEntry._ID,
           CookingContract.FoodEntry.COLUMN_NAME,
           CookingContract.FoodEntry.COLUMN_DESCRIPTION,

    };
    return cookingDBHelper.getWordMatches(query, columns);
}

Using setProjectionMap I should be able to tell Android what name my columns have and to use it respecting the SearchManager constraints... What do I do wrong?

Alex
  • 1,447
  • 7
  • 23
  • 48

1 Answers1

0

I think your projection map (mColumnMap) is not correct, what you have is this:

map.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "rowid AS " +
            CookingContract.FoodEntry.COLUMN_NAME);
map.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "rowid AS " +
            CookingContract.FoodEntry.COLUMN_DESCRIPTION);
map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + 
            CookingContract.FoodEntry._ID);

This means you are mapping the columns in the following way:

SUGGEST_COLUMN_NAME -> rowid AS COLUMN_NAME
SUGGEST_COLUMN_TEXT_2 -> rowid AS COLUMN_DESCRIPTION
SUGGEST_COLUMN_INTENT_DATA_ID -> rowid AS _ID

When the query builder uses your map it will rename the column names to the ones given as the values on the map, but according to the columns you are providing as keys on the map, none of the current columns you are querying will be correctly mapped.

String[] columns = new String[]{
       CookingContract.FoodEntry._ID,
       CookingContract.FoodEntry.COLUMN_NAME,
       CookingContract.FoodEntry.COLUMN_DESCRIPTION,

};

The columns above cannot be correctly mapped because none of them exist as the key on the projection map.

Some examples on map projection and why it's useful can be found on this question.

Taking into consideration the guide on custom suggestions, the cursor to be returned is expected to contain at least 2 columns: _ID and SUGGEST_COLUMN_TEXT_1, thus I would suggest you to change your map to the following one:

map.put(CookingContract.FoodEntry._ID, 
            CookingContract.FoodEntry._ID + " as " + SearchManager._ID);
map.put(CookingContract.FoodEntry.COLUMN_NAME,
            CookingContract.FoodEntry.COLUMN_NAME + " as " + SearchManager.SUGGEST_COLUMN_TEXT_1);
map.put(CookingContract.FoodEntry.COLUMN_DESCRIPTION,
            CookingContract.FoodEntry.COLUMN_DESCRIPTION + " as " + SearchManager.SUGGEST_COLUMN_TEXT_2);

With this map the columns your are providing will be mapped in the following way:

FoodEntry._ID -> _ID
FoodEntry.COLUMN_NAME -> FoodEntry.COLUMN_NAME as SUGGEST_COLUMN_TEXT_1
FoodEntry.COLUMN_DESCRIPTION -> FoodEntry.COLUMN_DESCRIPTION as SUGGEST_COLUMN_TEXT_2

Notice that I omitted the SUGGEST_COLUMN_INTENT_DATA_ID column, because I don't know which column you are using to query the database for a single record, it could either be rowId or _ID, you can adjust the projection map according to your needs.

Also please make sure you are configuring the android:searchSuggestIntentAction in your manifest to handle an action properly.

Hope this makes thinks clearer about the exception.

Community
  • 1
  • 1
LaloLoop
  • 2,143
  • 3
  • 12
  • 18
  • I edited my code and now I get the following error: Search suggestions query threw an exception. `android.database.sqlite.SQLiteException: no such column: suggest_text_1 (code 1): , while compiling: SELECT _id, suggest_text_1, suggest_text_2 FROM food WHERE (name LIKE ?)` – Alex May 23 '16 at 09:29
  • It works! Thank you, the problem was as you said the aliases – Alex May 23 '16 at 10:05