SQLite query where selection in the column? - android

How would I write a query that selects where the selection is IN the column? I've only seen it where the column is in the selection like this:
String[] names = { "name1", "name2" }; // do whatever is needed first
String query = "SELECT * FROM table"
+ " WHERE name IN (" + makePlaceholders(names.length) + ")";
Cursor cursor = mDb.rawQuery(query, names);
I basically want the opposite of this.
My app is a recipe finding app so there is one column that has a string of ingredients for every entry. I want to be able to select the recipe where the input selection (let's say "chicken rice") was in the string of ingredients ("chicken rice onions...").
Is it possible to do this?
this is what my query looks like now
mCursor = mDb.query(true, SQLITE_TABLE, new String[] {COLUMN_ROWID,
COLUMN_NAME, COLUMN_TYPE, COLUMN_INGRED, COLUMN_SPECIAL, COLUMN_DESCRIPT, COLUMN_ALLINGRED, COLUMN_INSTRUCT, COLUMN_IMGPATH},
COLUMN_ALLINGRED + "like '%" + inputText + "%'",
null, null, null, null, null);

Is your second query not working? You're not doing a "selection in column", it's just a simple text search. But it has many issues (besides performance). What if your ingredients are in a different order? For ex. rice chicken. You probably want to store each ingredient on its own and tokenize inputText and search for all matches of either of the tokens, and prioritize both. Text search is not easy, you'd be much better off offloading this to a service and making calls from the client. You shouldn't have so much offline data anyway, presumably you'll want to update it frequently.

Related

Android Search Match 2 columns SQL Statement

I am following the Android SearchableDictionary tutorial here and code here, I get everything to work, and understand the code. However, for the life of me I cannot make the search look in 2 columns of the searchable sqliteDB. See the comment below for what I want to accomplish
public Cursor getWordMatches(String query, String[] columns) {
//This (original code) searches your query in the KEY_WORD column, which is the SUGGEST_COLUMN_TEXT_1 column.
//String selection = KEY_WORD + " MATCH ?";
// This searches your query across ALL columns of the table, not what I want because I have added additional columns to the sqlitedb table
//String selection = FTS_VIRTUAL_TABLE + " MATCH ?";
// This is what I want, to search my query in both the keyword and keydefinition columns, which is the 1 and 2 suggest column text
String selection = KEY_WORD +"OR" + KEY_DEFINITION+ " MATCH ?";
String[] selectionArgs = new String[] {query+"*"};
return query(selection, selectionArgs, columns);
/* This builds a query that looks like:
* SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*'
* which is an FTS3 search for the query text (plus a wildcard) inside the word column.
Things I have tried (there are more, but they are tiny modifications of the following formats):
String selection = KEY_WORD +"OR" + KEY_DEFINITION+ " MATCH ?";
String selection = FTS_VIRTUAL_TABLE+ " MATCH 'suggest_text_1:? OR suggest_text_2:?'";
I have also read the relevant sections of this, it did not help me because it again gave example of searching in 1 column and searching in all columns using the hidden db named column. I need to search for more than 1 column in a single SQL statement.

How should I write a SQLite query so it searches for strings in a database that match strings in a string array?

I have a database that holds recipes. I want to be able to search the database for recipes by ingredients. So, if I search for "apple", it should pull up all the recipes that have the word "apple" in the column "ingredients."
Example of table
Name: "Apple pie"
Ingredients: "apple flour sugar etc."
At first, when it searched the database, I had it set up so that the inputText was just a string. For example, the inputText would be like "apple flour." But, I thought maybe it would make more sense if it took the inputText and put it in a String Array (but I could be totally wrong). So, it would be more like this: ("apple", "flour").
But, my issue is that I don't know how I would write the query so that it looks in the ingredients column for any string that matches one in the array.
Also, should I make it so that the strings in the ingredients column are arrays as well? Because right now they're just strings separated by spaces.
Here's what my query looks like now, which I know isn't right
public Cursor fetchRecipesByName(String inputText) throws SQLException {
SQLiteDatabase mDb = this.getWritableDatabase();
Log.w(TAG, inputText);
Cursor mCursor = null;
if (inputText == null || inputText.length () == 0) {
mCursor = mDb.query(SQLITE_TABLE, new String[] {COLUMN_ROWID,
COLUMN_NAME, COLUMN_TYPE, COLUMN_INGRED, COLUMN_SPECIAL, COLUMN_DESCRIPT, COLUMN_ALLINGRED, COLUMN_INSTRUCT, COLUMN_IMGPATH},
null, null, null, null, null);
}
else {
mCursor = mDb.query(true, SQLITE_TABLE, new String[] {COLUMN_ROWID,
COLUMN_NAME, COLUMN_TYPE, COLUMN_INGRED, COLUMN_SPECIAL, COLUMN_DESCRIPT, COLUMN_ALLINGRED, COLUMN_INSTRUCT, COLUMN_IMGPATH},
COLUMN_INGRED + " like '%" + inputText + "%'",
null, null, null, null, null);
}
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
The correct way
What you have is a multi to multi relationship. Ie, you have many recipes which can each have many ingredients... or you have many ingredients which can be in many recipes.
The best way to handle the multi-to-multi relationship is by having 3 tables. One for your recipes, one for your ingredients, and one for relations.
For your Recipes Table, let's say you have columns _id and name (and others, but those aren't relevant)
For Ingredients Table, you would also have _id and name (and again, lets ignore other columns for this example)
For the relations table, say RecipesAndIngredients, you would have columns recipe_id and ingredient_id
Then to search for ingredients named 'apple' and 'flour', your end-result
SQL statement would be:
SELECT _id,name FROM Recipes
JOIN RecipesAndIngredients
ON RecipesAndIngredients.recipe_id=Recipes._id
JOIN Ingredients
ON Ingredients.id = RecipesAndIngredients.ingredient_id
WHERE Ingredients.name LIKE 'apple%' OR Ingredients.name LIKE 'flour%'
GROUP BY Recipes._id
I'm not going to work this into an Android-friendly example, but it is the correct way to do it.
The easy way
Use someone else's answer.
Why not use the easy way?
Performance. Search will be slower because indexes cannot be used for LIKE
'%abc', and you will have to use LIKE '%abc%' for everything since
you wouldn't know WHERE in the ingredients list the 'abc' is.
Consistency. There are a finite number of ingredients, and storing strings means you could end up with 'flour', 'floor', 'fluur' or whatever else. By using a separate table, users could first search for an ingredient before adding it.
Data access. I'm not gonna get into it, but you have way more options for accessing your data, such as "how many recipes have apples in them?" or "What recipes have 3 of the same ingredients that are in THIS RECIPE?".
Learning. Using my way will help you learn databases as well as Android. The easy way will only really help with learning Android.
Why use the easy way?
It's easier. You already have the code written.
Less Learning. It will also keep you from learning about databases, which I see as a bad thing because databases are great. But, if you don't want to learn databases right now, then my approach won't be very good for you.
Overwhelming...ness. Learning how databases work is different than learning how Java/Android works, and trying to understand it can be a bit overwhelming.
Notes
There are third party database libraries/tools available. I
personally recommend using one, such as Greendao or SugarORM. Then you won't have to do any real databasing.
Threading/Synchronization sucks with Android SQL, and seemingly random errors pop up as a result. I made this project to solve that problem.
Lets say you named the array which contains ingredients you want to search, ings. I mean ings is the string array you build from splitting inputText.
You can do this:
String qr = "";
for(int i=0; i<ings.length; i++{
String tmp = COLUMN_INGRED + " LIKE ?"
ings[i] = "%" + ings[i] + "%";
qr += tmp;
if(i != ings.size()-1) qr += " OR ";
}
mCursor = mDb.query(true, SQLITE_TABLE, new String[] {COLUMN_ROWID,
COLUMN_NAME, COLUMN_TYPE,
COLUMN_INGRED, COLUMN_SPECIAL,
COLUMN_DESCRIPT, COLUMN_ALLINGRED,
COLUMN_INSTRUCT, COLUMN_IMGPATH},
qr,
ings, null, null, null, null);
I again mention that the type of ings is String[].
You missed to write the question mark after the "like" part and the percent % after the searched word, so your mCursor must look something like this:
Cursor mCursor = mDb.query(SQLITE_TABLE, new String[] {COLUMN_ROWID, COLUMN_NAME,
COLUMN_TYPE, COLUMN_INGRED, COLUMN_SPECIAL, COLUMN_DESCRIPT,
COLUMN_ALLINGRED, COLUMN_INSTRUCT, COLUMN_IMGPATH},
COLUMN_INGRED + " LIKE '?'", new String[]{inputText+"%"},
null, null, null);
Although I have never used it, your usage of the LIKE clause looks fine. There may be some ways you could improve on your method, but it should work. If you are looking to search for several ingredients that appear in a String[], then perhaps you could try something like this (this is untested, just an idea):
To get your String[] composed of your user's search input, split into separate search terms:
String[] splitStringBySpace(String userEditTextInput){
if (userEditTextInput.trim().equals("") {
return new String[0];
}
return userEditTextInput.trim().split("\\s+");
}
Then use it inside your revised method:
public Cursor fetchRecipesByName(String whereArgsNotSplit) throws SQLException {
String[] whereArgs = splitStringBySpace(whereArgsNotSplit);
Cursor returnCursor = null;
if (whereArgs == null || whereArgs.length == 0) {
return returnCursor;
}
/*
*Exit early if whereArgs is empty or null. If you want to return a full cursor here,
*then you can return a query() with all parameters set to null except for the
*SQLITE_TABLE.
*/
SQLiteDatabase mDb = this.getReadableDatabase(); //We are only reading the database
StringBuilder selection = new StringBuilder();
for (int i = 0; i < whereArgs.length; i++) {
whereArgs[i] = "'%" + whereArgs[i].trim() + "%'"
if (i == 0) {
selection.append(COLUMN_INGRED + " LIKE ?");
} else {
selection.append(" OR LIKE ?")
}
}
//This for loop will construct the data for our WHERE clause in the query method.
returnCursor = mDb.query(true, SQLITE_TABLE, null,
selection.toString(),
whereArgs, null, null, null, null); //Using whereArgs parameter
}
return returnCursor;
}

Android - Database SQLite - convert boolean values

I am working on an Android project with a SQLite database and have a column in the database consisting of a boolean value (which store 1s and 0s) of whether an individual in the database is important.
What I am trying to do is output into a textview listview a "!" if an individual is important, and " " if they are not important using a cursor and cursor adaptor. I can get the "1" and "0"s to appear in the listview, but my question is how/where do I convert these to "!" and " "?
The query I am currently using is
return database.query("people", new String[] {"_id", "important"}, null, null, null, null, "important" + " DESC, " + "name" + " ASC");
The easiest way is to do this in the database query.
The query function is too inflexible for that, so you have to use rawQuery instead:
db.rawQuery("SELECT _id, " +
"CASE WHEN important THEN '!' ELSE ' ' END AS important " +
"FROM people " +
"ORDER BY important DESC, name ASC", null);
If it is a custom ListView which I am pretty sure it is, then you'll have to do that in your CustomAdapter for the ListView, which takes care of populating the ListView with the data you pass to it.Check this out.
Doing it is pretty simple, just loop through your Cursor and do if else statement.

Android- SQLite query to Order Results by Day using the query builder

At the moment i'm currently using the query builder to pull all my records from my database and they are currently organised by day however I its sorts in althabetical order,
public Cursor fetchAll() {
return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_MODULECODE,
KEY_DAY, KEY_TIME, KEY_DURATION, KEY_TYPE, KEY_ROOM},null, null, null, null,"Day");
}
I know after some googling, that I need to replace the final section with a custom search order.
something along the lines of :
select _id, busnum,
case cast (strftime('%w', servdate) as integer)
when 0 then 'Sunday'
when 1 then 'Monday'
when 2 then 'Tuesday'
when 3 then 'Wednesday'
when 4 then 'Thursday'
when 5 then 'Friday'
else 'Saturday' end as servdayofweek
from tb1
where ...
From:
http://stackoverflow.com/questions/4319302/sqlite-return-as-day-of-week
so far i have been unable to figure out how tocombine the two and anyhelp in the right direction would be much aprechiated.
Edit Soloution
thank you for the help I went with declaring the string and linking to it in the query for anyone that needs something simple this is my final code for it
public Cursor fetchAll() {
String OrderBy ="case Day"+
" when 'Monday' then 0 "+
" when 'Tuesday' then 1"+
" when 'Wednesday' then 2"+
" when 'Thursday' then 3"+
" when 'Friday' then 5"+
" end";
return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_MODULECODE,
KEY_DAY, KEY_TIME, KEY_DURATION, KEY_TYPE, KEY_ROOM},null, null, null, null,OrderBy );
}
If your Day values are the day names then you want something like this:
order by case Day
when 'Sunday' then 0
when 'Monday' then 1
when 'Tuesday' then 2
...
when 'Saturday' then 6
end
in the SQL; keep in mind that you can hand an SQL ORDER BY pretty much any expression. The orderBy argument to query can be any SQL ORDER BY clause (without the order by) so you want this big ugly string :
"case Day when 'Sunday' then 0 when 'Monday' then 1 ... when 'Saturday' then 6 end"
as the last argument to query. You'll have to properly fill in the ... of course.
If you cannot figure out how to pass a complex query through SQLiteDatabase.query(... lots of variables...) just pass your select statement as a string with SQLiteDatabase.rawQuery().
For example:
String query = "select _id, busnum, " +
" case cast cast (strftime('%w', servdate) as integer) " +
...
String[] selectionArgs = new String() { yada, yada, yada }
mDb.rawQuery(query, selectionArgs);

SQLITE Selection and order by causing errors

i'm trying to get a query that returns rows only with a specified name, and sort descending by week (integer).
Everytime I try and run it it gives me a FC and logcat says
ERROR/AndroidRuntime(728): android.database.sqlite.SQLiteException: no such column: New: , while compiling: SELECT Name, Week, Total FROM notes WHERE Name=New ORDER BY WeekDESC LIMIT 10
public Cursor graphQuery(String name){
return mDb.query(DATABASE_TABLE, new String[] {KEY_NAME,
KEY_WEEK, KEY_TOTAL}, KEY_NAME + "=" + name, null, null, null, KEY_WEEK + "DESC","10");
}
It says that there is no such column, which doesn't make sense because it should be looking at the names in the row and returning the ones that match. Also if I put KEY_NAME + "=" + name as null, it says that there is no such column WeekDESC which doesn't make sense either cause it is just supposed to be sorting by an integer.
I have gotten this code working before, but I misplaced it and can't seem to get it working again... This is getting frustrating, any help is very much appreciated!
If you search by string, you should use a question mark as a placeholder, i.e.
return mDb.query(DATABASE_TABLE, new String[] {KEY_NAME,
KEY_WEEK, KEY_TOTAL}, KEY_NAME + "=?", new String[] { name }, null, null, null, KEY_WEEK + "DESC","10");
Or else it will break if the name contains spaces or special characters.
(If you really don't want to you the question mark, you need to put the string in quotes or double-quotes, but using a question mark is far more elegant and safe.)
Also: There is no space between "DESC" and the key you're sorting by. Use " DESC".

Categories

Resources