I have three parse subclasses: Recipe, Ingredient, and RecipeIngredient. RecipeIngredient has a pointer to a Recipe, and a pointer to an Ingredient.
When I am trying to create a QueryFactory to get all the ingredients for a recipe. I am trying to do this with whereMatchesKeyInQuery, but the objectIds aren't matching. From the docs, it appears that this should be legal. What am I missing?
public MeatIngredientListAdapter(Context context, final String recipeName) {
super(context, new ParseQueryAdapter.QueryFactory<Ingredient>() {
public ParseQuery<Ingredient> create() {
ParseQuery<Ingredient> query = ParseQuery.getQuery(Ingredient.class);
query.whereEqualTo("isMeatOrFat", true);
ParseQuery<RecipeIngredient> riQuery = ParseQuery.getQuery(RecipeIngredient.class);
riQuery.whereEqualTo("recipeName", recipeName);
riQuery.include("ingredient");
riQuery.whereEqualTo("isMeatOrFat", true);
query.whereMatchesKeyInQuery("objectId", "ingredient.objectId", riQuery);
return query;
}
});
}
In your case the use of whereMatchesKeyInQuery is overkill. I might not have enough information to make this call about your app but is seems that you would be able to cut out the need for RecipeIngredient all together if you just create a Relation of the Ingredient class inside the Recipe class. This will simplify your queries and make your app more scalable and give you features (explained below). If you had a data structure like this:
Recipe Class
- Name (String)
- ingredients (Relation of the Ingredient class)
Ingredient Class
- <Columns to describe the ingredient that you already have in place>
Now you can store one recipe that "points" (using relations) to many ingredients.
So an example entry might look like this:
Recipe
Name
PB&J
ingredients
Peanut Butter //this is a relation to the Peanut Butter Ingredient object
Jelly //this is a relation to the Jelly Ingredient object
Ingredient
Name
Peanut Butter
Calories
...
Cost
...
And here in code we add the data to the classes:
ParseObject ingredient1 = new ParseObject(Ingredient.class);
ingredient1.put("Name", "Peanut Butter");
ParseObject ingredient2 = new ParseObject(Ingredient.class);
ingredient1.put("Name", "Jelly");
ParseObject recipe = new ParseObject("Recipe");
recipe.put("Name", "PB&J");
ParseRelation<ParseObject> relation = recipe.getRelation("ingredients");
relation.add(ingredient1);
relation.add(ingredient2);
recipe.saveInBackground();
The magic behind this setup is that we can now specify a recipe by name and get all ingredients like you wanted but we can also retrieve all recipes that have certain ingredient(s) in them (this is the beauty of a many-to-many relationship) and on top of that it simplifies your queries.
Now for the original query you wanted with this new setup:
ParseObject recipe = ...; // "PB&J" Recipe object.
ParseRelation relation = recipe.getRelation("ingredients");
// generate a query based on that relation
ParseQuery query = relation.getQuery();
query will hold all of the ingredients for the recipe object when the query is executed.
Now suppose you want to create a query where you get all of the recipes that contain a certain ingredient:
ParseObject ingredient = ...
ParseQuery<ParseObject> query = ParseQuery.getQuery("Recipe");
query.whereEqualTo("ingredients", ingredient); //use whereContainedIn for multiple ingredients
query will contain all Recipe objects that have the specified ingredient in their ingredients relation column when the query is executed.
I hope this helped you. Please let me know if I severely misunderstood the structure of your app - if so I will revise my answer if you give me new information but honestly I think the "middle man" RecipeIngredient is forcing you to complicate your app.
Related
I am struggling with Parse Relational Queries.
What I want to do: To query all Recipes that current ParseUser likes.
Models (Pseudo code to simplify)
There are two classes. One is Recipe class, another one is Like class that has information of who likes which recipe.
#ParseClassName("Like")
public class Like ParseObject{
"fromUser" : ParserUser
"toRecipe": Recipe
}
#ParseClassName("Recipe")
public class Recipe : ParseObject {
"objectId": String // from ParseObject
// some other field.
}
I have tried both whereMatchesQuery() and whereMatchesKeyInQuery() methods. But both query results are an empty list. Does someone know how to query to meet my requirement?
Case of using ParseQuery.whereMatchesQuery()
ParseQuery<Like> likesQuery = ParseQuery.getQuery(Like.class);
likesQuery.whereEqualTo("fromUser", ParseUser.getCurrentUser());
ParseQuery<Recipe> recipesQuery = ParseQuery.getQuery(Recipe.class);
recipesQuery.whereMatchesQuery("toRecipe", likesQuery);
recipesQuery.find() <- Return Empty List
Case of using ParseQuery.whereMatchesKeyInQuery()
ParseQuery<Like> likesQuery = ParseQuery.getQuery(Like.class);
likesQuery.whereEqualTo("fromUser", ParseUser.getCurrentUser());
ParseQuery<Recipe> recipesQuery = ParseQuery.getQuery(Recipe.class);
recipesQuery.ParseQuerywhereMatchesKeyInQuery("objectId", toRecipe", likesQuery);
recipesQuery.find() <- Return Empty List
With the current design, it seems you can only fetch likes and then get Recipes with get:
ParseQuery<Like> likesQuery = ParseQuery.getQuery(Like.class);
likesQuery.whereEqualTo("fromUser", ParseUser.getCurrentUser());
List<Like> likes = likesQuery.find();
List<Recipe> recipes = new ArrayList(likes.size());
for (Like like : likes) {
recipes.add(like.getRecipe());
}
You can use query.include() to include the recipe object in the query results.
Another option is to add a recipe id field to likes:
#ParseClassName("Like")
public class Like ParseObject {
"fromUser" : ParserUser
“toRecipe” : Recipe
"toRecipeId": String
}
If you do this, you can use your second suggestion with whereMatchesKeyInQuery.
I am new to Greendao.I am writing a generator for generating entities in greendao.So,I have two entities Hospital and patient. They have a one to many relationship between them. So,a hospital can have many patients but one patient can have only one hospital. Now Property hospitalId = patient.addLongProperty("hospitalId").getProperty(); this adds a hospitalid column to patient table. and
ToMany hospitalToPatients = hospital.addToMany(patient, hospitalId);
This line creates a one to many relationship between hospital and patient.So what is the use of the lines patient.addToOne(hospital, hospitalId); and hospitalToPatients.setName("patients"); How to implement one to one,one to many,many to one and many to many relationships in greendao ?
PS: I copied this code from http://www.vertabelo.com/blog/technical-articles/a-comparison-of-android-orms
public class ProjectGenerator {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1, "com.example.project");
// hospital table
Entity hospital = schema.addEntity("Hospital");
hospital.addIdProperty();
hospital.addStringProperty("name");
// patient table
Entity patient = schema.addEntity("Patient");
patient.addIdProperty();
patient.addStringProperty("name");
Property hospitalId = patient.addLongProperty("hospitalId").getProperty();
// patient has a one assigned hospital
patient.addToOne(hospital, hospitalId);
// hospital has many patients
ToMany hospitalToPatients = hospital.addToMany(patient, hospitalId);
hospitalToPatients.setName("patients");
// trigger generation with path to the Android project
new DaoGenerator().generateAll(schema, "../project/src/main/java");
}
}
So what is the use of the lines patient.addToOne(hospital, hospitalId)
This line is creating a oneToOne relation between hospital and patient .
hospitalToPatients.setName("patients")
This is just setting the name of foreign key .
As you can see, you have already implemented implement one to one,one to many relationship in your example . patient.addToOne is an example of OneToOne relationships . hospital.addToMany is an example of OneToMany relationships . And greenDao doesn't support ManyToMany relationship directly for more details you can read this .
I have a list of Teachers that each contain a list of Student objects. Each Student contains a list of schoolbooks that he has to bring each day. It looks like this:
Teacher {
String teacherName;
RealmList<Student> students = new RealmList<>();
}
Student {
String studentName;
RealmList<SchoolDay> schooldays = new RealmList<>();
}
SchoolDay {
String day;
RealmList<RealmString> schoolbooks;
}
(RealmString is simply primitive String wrapped as a RealmObject)
I want to extract the list of schoolbooks for a certain student on a certain day - several students might have the same schoolbooks, but I'm only interested in the books for one particular student on one particular day (for example, Sunday). A student might be in the classes of several teachers, but I'm only interested in the result for one of them as the weekly booklist will be different for each one. Sample query data might be:
teacher : steven
student : austin
day : sunday
This is where I get stuck - how do I subquery this? To get the teacher that I'm interested in:
RealmResults<Teacher> = realm.where(Teacher.class).equalTo("teacherName", "steven").findAll();
However, I then have to run a subquery on the teacher and a subquery on the student - or better yet, run all of them in the same query somehow. What I want to get as my final result is just the string representing the schoolbooks for that one particular student. How can I do this?
I would propose an easier option.
You can make use of inverse relationships:
The models will look like:
Teacher {
String teacherName;
RealmList<Student> students = new RealmList<>();
}
Student {
String studentName;
RealmList<SchoolDay> schooldays = new RealmList<>();
#LinkingObjects("students")
final RealmResults<Teacher> teacher = null;
}
SchoolDay {
String day;
RealmList<SchoolBook> schoolbooks;
#LinkingObjects("schooldays")
final RealmResults<Student> student = null;
}
SchoolBook {
String bookName;
#LinkingObjects("schoolbooks")
final RealmResults<SchoolDay> day = null;
}
And the query will be as simple as:
RealmResults<SchoolBook> = realm
.where(SchoolBook.class)
.equalTo("day.student.studentName", "austin")
.findAll();
Can you try like that :
realm.where(Teacher.class)
.equalTo("teacherName",teachername)
.equalTo("students.studentName",studentname)
.equalTo("students.schooldays.day",day).findAll();
After all you have Teacher object(s) and you can get variables in one query :
RealmResults<Teacher> teachers= your query above;
for(Teacher teacher:teachers){
//remember still you can have multiple students for given teacher
for(Student student:teacher.getStudents()){
for(Schoolday schoolday:student.getSchooldays()){
schoolday.schoolbooks bla bla bla...
}
}
}
Why we using for loop : because findAll() method can return multiple results, if you want single Teacher object use findFirst()
This will return you Teachers of given teachername attribute that contains/includes students with given student name : studentname and those students have schooldays with given: day name.
I think the last explaination is quite a bit hard to understand, now I explain it with examples:
In first query you are getting Teachers with name= "Yasin".
Assume that after this query you got 5 Teachers with name "Yasin".
Then in second query you are searching this 5 Yasin teachers; if their one of students name is "Jon". Assume that you have 3 Yasin teachers after that query.
Then in the last query you searching of that 3 "Yasin" teachers; if their one of students school day is "Sunday".
You can take a look at this question : how-to-make-a-nested-query-in-realm
Also this question is good for referencing ; it was helpful for me: realm-android-nested-query
i have basic question. Iam using parse android SDK to retrieve some data from backend. I have 2 collections : "User" and "Photo". What i want to do is that i want to query "Photo" collection which contains some data about image and join that against "User" collection and get some info about owner of picture. Please see code below:
ParseQuery<ParseUser> queryInner = ParseUser.getQuery();
ParseQuery<ParseObject> query = ParseQuery.getQuery("Photo");
query.whereMatchesKeyInQuery("owner", "username", queryInner);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> resultSet, ParseException exception) {
if (exception == null) {
parsqQueryResult = new ArrayList<ObjectUserPhoto>();
ObjectUserPhoto row = new ObjectUserPhoto();
Log.e("size",String.valueOf(resultSet.size()));
for (int a = 0; a < resultSet.size(); a++) {
row = new ObjectUserPhoto();
//this works
row.setDescription(resultSet.get(a).getString(Constants.DB_COL_USER_PHOTOS_DESCRIPTION));
//this is not working
Log.e("username",String.valueOf(resultSet.get(a).getString("col_from_user_table")));
parsqQueryResult.add(row);
}
}
}
});
So basically iam able to acces columns from "Photo" collection, but iam not able to acces any column from "User" collection. How can i acces data from "User" collection ??
Thanks
Martin
In your comment you mentioned that you are not using pointers. I assume that you are just storing the id of the ParseUser objects.
Given a ParseObject's ID, you can retrieve an object by issuing a ParseQuery and passing the ID. Refer to this document.
However, I do not see any reason why you would want to store just the ID and not the Pointer to the object. Imagine if you want to display the information on a list then that means for every item you will need to perform a ParseQuery.
If you store a Pointer then you can leverage on the "include" method (see it here at the bottom part of the "Relational Queries"). This allows you to include the actual object the Pointer is pointing to. So in your case, the results of the Photo query will include the User objects too.
And lastly, I suggest that you read through at least the whole Objects and Queries sections of the documentation as you would learn the basics on how to use the SDK.
I'm using greenDao and I need to extract data from several tables similar to left-join funcionality. Here's a cite from my schema generator:
private static void genRetailers(Schema schema) {
// create retailer entity
Entity retailerEntity = schema.addEntity("Retailer");
retailerEntity.addIdProperty().notNull();
retailerEntity.addStringProperty("title");
Entity shopEntity = schema.addEntity("Shop");
shopEntity.addIdProperty().notNull();
shopEntity.addStringProperty("address");
Property retailerId = shopEntity.addLongProperty("retailerId")
.getProperty();
// (1) Retailer < - > (*) Shop
retailerEntity.addToMany(shopEntity, retailerId);
}
When I do:
return (ArrayList<Retailer>) mDaoSession.getRetailerDao()
.queryBuilder().list();
I only get contents of the Retailer table itself, however I also need Shop entity values which're null. Only after I call getShops() the entities get filled. I need to fill entities right at the first query. How is it done ?
Thanks.
Use the queryDeep method:
return mDaoSession.getRetailerDao().queryDeep(null);