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
Related
I am using GreenDao for Android application, with some specification, for example, I have a Contact Model with some information like name, avatar, phone number, etc...
Right now the need is to change from only one phone number to a multiphone number.
Instead of creating two tables (table for numbers, and table for contacts), I really need just one information is the number so in my backend the contact numbers is stocked on a DC2type, (a json array saved as a string).
Do we have a possibility to do that using GreenDao?
i search for a solution or a DC2type implementation , etc ... and nothing is found
so i decide to created by my self , and this is what i did :
using the #Convert annotation presented of GreenDao 3 :
#Property(nameInDb = "phoneNumbers")
#Convert(converter = PhoneNumbersConverter.class, columnType = String.class)
private List<String> phoneNumbers;
static class PhoneNumbersConverter implements PropertyConverter<List<String>, String> {
#Override
public List<String> convertToEntityProperty(String databaseValue) {
List<String> listOfStrings = new Gson().fromJson(databaseValue,List.class);
return listOfStrings;
}
#Override
public String convertToDatabaseValue(List<String> entityProperty) {
String json = new Gson().toJson(entityProperty);
return json;
}
}
short story long , i create a json to array parser
thanks to myself to helped me :D
I have a table that looks like following
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product
{
#PrimaryKey
#ColumnInfo(name = "ID")
#JsonProperty("ID")
public int id;
#ColumnInfo(name = "Name")
#JsonProperty("Name")
public String name;
#ColumnInfo(name = "Documents")
#JsonProperty("Documents")
#TypeConverters(DocumentConverter.class)
public List<Document> documents;
}
//...
#TypeConverters(DocumentConverter.class)
#JsonIgnoreProperties( ignoreUnknown = true )
#JsonTypeName("Documents")
public class Document
{
#JsonProperty("Name")
public String name;
#JsonProperty("URL")
public String url;
}
I am able to retrieve a product based on its name by doing something like this
#Query("SELECT * FROM Product WHERE Name = :name")
List<Product> getProducts(String name);
And I would then be able to access the list of documents from each Product object. However I would also like to only deal with Products that has certain documents. I could get all Products via a query like above, then manually filter for the documents that I want, but it becomes quite a pain when I'm only looking for very specific documents.
Is it possible to also query based on Document variables without it being a separate table?
Something like...
#Query("SELECT * FROM Product WHERE Name = :name AND Document.name = :documentName")
List<Product> getProducts(String name, String documentName);
Thanks.
You could use LIKE sql statement to search inside your json column with converted documents list. Example:
Assume that we have document converted like this for storing in db:
{
name: "Title",
url: "Your_url"
}
So your query for product with such document in list should be like this:
SELECT * FROM Product WHERE Name = :name AND Documents LIKE :documentLikeExpr
Where
String documentLikeExpr = "%name: \"Title\"%";
% in expression mean zero, one or multiple characters.
So the only thing we are doing here - is searching for part of string inside column using SQL language features.
You cannot query a Document class variables as it is not stored as a separate table. #TypeConverter annotation converts your Document list to some predefined data types such as String. Basically it stores list of Document as a string Gson in a column of Product table, So we cannot access the field name of Document class in SQL query like Document.name.
Read the Option #2 given by #CommonsWare here
So, to access it you have to create a separate table for Document.
I would like to create a Realm Android query, but dont know how to do this.
My realm Object is the following.
public class test extends RealmObject {
#PrimaryKey
int id;
String test1;
String test2;
}
My query should search in test1 and test2. So if either test1 or test2 is equals to the filter String the RealmObject should be inserted into my list. It is possible that test1 and test2 both have the same value. If this is the case the Object should only appear once in my list and not twice.
My query which is not working:
List<test> games = db.where(test.class)
.contains("test1", charText,
Case.INSENSITIVE).distinct().where(test.class)
contains("test2", charText, Case.INSENSITIVE)
.distinct();
At last I want to sort the list after test1.
You could use RealmQuery::or to combine the conditions:
List<test> games = db.where(test.class)
.contains("test1", charText, Case.INSENSITIVE)
.or()
.contains("test2", charText, Case.INSENSITIVE)
.findAllSorted("test1");
You can find other examples of this type of query in the documentation
Even if the test1 and test2 contain the same value, the same record will not appear twice in the list so you do not need to add the distinct constraint on your query.
I am working on an application in which I want to save a list of list. I save all the data perfectly but when I fetch the data this come, but not in order.
For example, I want to save a book which has many chapters and a chapter have many paragraphs and each paragraph have a set of question and a question have one question one answer and four option.
I made the schema for all table like chapters, paragraphs,question and also relate them very well but I when I save the data this is saved in the separate table but not relatively. like chapter list contains only chapter name and paragraph list only contain paragraphs and similarly question.
So my question is this how to relate them? For example, I have a list of books which contain chapters and so on.
Thanks in advance.
Entity course = schema.addEntity("Course");
course.setTableName("Courses");
course.addIdProperty().primaryKey().autoincrement();
course.addStringProperty("course_name").notNull();
course.addStringProperty("course_id").notNull();
//chapter schema
Entity chapter = schema.addEntity("ChapterList");
chapter.setTableName("Chapters");
chapter.addIdProperty().primaryKey().autoincrement();
chapter.addStringProperty("chapter_name").notNull();
//relation chapter to course
Property chapterDate = chapter.addDateProperty("cahpter_add_date").getProperty();
Property courseId = chapter.addLongProperty("course_id").getProperty();
chapter.addToOne(course,courseId);
//relation course to chapter
ToMany courseToChapter = course.addToMany(chapter, courseId);
courseToChapter.setName("chapter_list");
courseToChapter.orderAsc(chapterDate);
This is the code for table creation and this code saving data.
public static void getCourseFromBackend(ParseObject p) {
DaoSession daoSession = WWApplication.getDaoSession();
CourseDao courseDao = daoSession.getCourseDao();
Course course = new Course();
String courseId = p.getString(Constants.ParseKeys.COURSE_ID);
course.setCourse_name(p.getString(Constants.ParseKeys.COURSE_NAME));
course.setCourse_id(courseId);
List<ParseObject> chapterList = p.getList(Constants.ParseKeys.CHAPTER_LIST);
if (chapterList != null) {
for (ParseObject bChapter : chapterList) {
saveChapterFromBackend(bChapter, course, courseId);
}
}
courseDao.insert(course);
}
public static void saveChapterFromBackend(ParseObject object, Course course, String courseId){
DaoSession daoSession = WWApplication.getDaoSession();
ChapterListDao chapterListDao = daoSession.getChapterListDao();
ChapterList chapter = new ChapterList();
String chapName = object.getString(Constants.ParseKeys.CHAPTER_NAME);
chapter.setChapter_name(chapName);
chapter.setCourse(course);
chapter.setCourse_id(course.getId());
chapterListDao.insert(chapter);
}
But when I retrieve data chapterList is null. Where is the problem: saving or fetching?
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.