is eager load supported ?
i want load one or all relation field when query completed
it's good when we want use entities to populate a recyclerview and use relation field for list items
thanks
We were thinking to add eager loading to the query API to make it flexible (eager only when needed) in a post 1.0 release. Any thoughts are welcome.
yes i think must be add to query api. are you familiar with laravel php framework ? i love eloquent relationship and eager load pattern, if possible check it
https://laravel.com/docs/5.4/eloquent-relationships
p.s: have you any idea to solve recyclerview lag for lazy relationship in current version before release version 1 ?
I think it goes in a similar direction. API could look like this:
queryBuilder.eager(Customer_.order);
at a later stage maybe even with a limit of entities retrieved:
queryBuilder.eager(Customer_.order, 100);
Did you profile the lag? How many entities are loaded?
yes for eager it's good, it's good if you can use this api for relations :
sample for toMany :
@Entity
public class Customer extend ObjectBoxModel {
@Id private Long id;
private String tag;
private List<Order> orders;
public QueryBuilder<Order> orders() {
return this.toMany(Order.class, "source_id", "target_id");
}
}
in laravel we can access to relation query to find/ filter/ sort or ... in relations of model, when use customer.orders we can access to all relations objects, and when we want filter / sort / or ... it :
customer.orders().greater(Customer_.id, 100).build().list();
note: this is lazy result
or it can be defined when use eager :
queryBuilder.eager(Customer_.order, 100, query -> {
return query.greater(Customer_.id, 100).orderDesc(Order_.id);
});
or it can be defined for all query of Customer entity :
@Entity
public class Customer extend ObjectBoxModel {
@Id private Long id;
private String tag;
private List<Order> orders;
public QueryBuilder<Order> orders() {
return this.toMany(Order.class, "source_id", "target_id").greater(Customer_.id, 100).orderDesc(Order_.id);
}
}
it's not beautiful and simple ?
for lag that i have, it's small, but when database grow can be a problem :
problem is i want relaton object to render recyclerview item, because for each item must query to database in ui thread, it's caused a small lag
@greenrobot do you plan to change api like this ?
this api is very similar to ActiveAndroid library
@DJafari There are some nice ideas here. Will think about it. I think we should keep relations and queries separate. Still, we can think about making it easier to mix queries with entities.
Can you share a bit more about the lag? For example, can you pinpoint the lag to a single lazy fetched relation?
Eager loading just landed with 0.9.16-RC.
Example:
customers = queryBuilder.eager(Customer_.order).build().list();
You can also specify a limit and multiple relations, for example
customers = queryBuilder.eager(100, Customer_.order, Customer_.addresses).build().list();
Feedback welcome!
@greenrobot visibility of relationInfo field is incorrect, because when i want use it as eager parameter , android studio hint it with : group is not public in News_; cannot be accessed from outside package
in News_.class :
static final RelationInfo<NewsGroup> group =
new RelationInfo<>(News_.__INSTANCE, NewsGroup_.__INSTANCE, groupId, new ToOneGetter<News>() {
@Override
public ToOne<NewsGroup> getToOne(News entity) {
return entity.group;
}
});
my lag will be fixed with eager, before you reply, i must set it manually in background thread for solved lag
about changing api for relation, i agree with you and trust you, you will apply it with better and beautiful way, I'm sure :)
@DJafari Thanks for spotting this, we just fixed visibility to public. Will let you know when a new release is out. Also thanks for your kind words! :)
@greenrobot yw :)
can i know what is your algorithm for eager load ?
for each row of result automatically query with another box ?
in laravel eager caused multiple query into 2 query, examples in sql :
SELECT * FROM `news`
get relation field ids ( ex: [1,3,4,10] ) and run :
SELECT * FROM `newsGroups` WHERE `id` IN (1,3,4,10)
first query get all result for news table, and get ids of result query, and second run query in relation table and get all related items from second table
result : for n item in parent table, without eager must be n + 1 query, but with eager it's converted to only 2 query.
@DJafari it is super simple: the query calls the ToOne/ToMany objects to initialize their data. Thus it's N lookups by ID. This is where ObjectBox is super fast. It's not the same like a SQL query (ObjectBox does not have rows/columns). Will illustrate that with some performance numbers once there is time for that.
@greenrobot but i check performance time with / without eager, with eager result time is bigger than without eager, i think this is must be reverse
tested code :
static final RelationInfo<Test> tests = new RelationInfo<>(My_.__INSTANCE, Test_.__INSTANCE,
new ToManyGetter<My>() {
@Override
public List<Test> getToMany(My entity) {
return entity.tests;
}
}, 1);
@DebugLog
private void listWithEager() {
List<My> myList = this.myBox.query().eager(tests).build().find();
for(My my : myList) {
for(Test test : my.tests) {
}
}
}
@DebugLog
private void listWithoutEager() {
List<My> myList = this.myBox.query().build().find();
for(My my : myList) {
for(Test test : my.tests) {
}
}
}
@DJafari Can you share the logged times? Can you try to reserve the order of execution; it might influence the resulting times.
@greenrobot
1 ⇠insertToMany [135,312ms] [INSERT 1000 MY AND 1000 TEST PER MY : 1000 MY ROW - 1000 * 1000 TEST ROW]
2 ⇠listWithEager [40,711ms]
3 ⇠listWithoutEager [45,938ms]
---------
1 ⇠listWithEager [53,764ms]
2 ⇠listWithoutEager [60,397ms]
---------
1 ⇠listWithoutEager [61,413ms]
2 ⇠listWithEager [61,192ms]
--------
1 ⇠listWithEager [62,459ms]
--------
1 ⇠listWithoutEager [61,161ms]
p.s : ---- means i killed app and run test again
as you seen using from eager not surprise me too much, i think must be better
@DJafari Thanks for sharing! So, with eager it's a bit faster, right?
With eager loading the time spent is moved to the query (which can be executed in the background, etc). Thus, the time spend inside your for loops should have significant timing differences. So far it looks like expected behavior, no?
Btw, how many entities do you handle in your app? Is it actually in the range of millions?
@greenrobot yw, yes but only a bit faster ! I expect it must be better
loops exists inside two methods, i thinks comparison is correct
yes i have 1,000 entities in my box and 1,000,000 entities in test box
@DJafari The measured time should correlate more strongly to number of objects than to number of look-ups (internal "queries"). Still, it may be worthwhile to check if we can squeeze out more performance after 1.0.
Fix for missing public is deployed with 0.9.16-RC2 - let us know if we missed something.
A{
ToMany<B> list;
}
B{
ToOne<C> c;
}
C{
...
}
The relationship of ABC is 1:N:1
store.boxFor(A.class).query().eager(A_.list).build().find()
Using the above method, I cannot immediately fill in the contents of C.
store.boxFor(A.class).query().eager(A_.list,B_.c).build().find()
This will give an error
A cannot be cast to B
Question:
With eager, how can I get all the data of the associated table?
@GuiwenChen Not yet, please open a new issue for your feature request.
ok,thx.new issue https://github.com/objectbox/objectbox-java/issues/383