Open giorgiopiatti opened 7 years ago
Thanks for the issue report! I put together a demo to show what I found:
It looks like the query is filtering the results if you're only reading from Firebase (online and offline), but there are a few issues if writing to Firebase while offline:
equalTo
does not get applied until reconnecting)I'll see what I can do to fix this. Let me know if you experience anything else. Thanks!
Yes, I have the same problem.
this.afo.database.list(`/chats-messages/${this.chatId}` , {
query: {
orderByChild: 'createdAt',
limitToLast: 1
}
}).subscribe((data : any) => {
if (data.length > 0) {
this.lastKey = data.$key;
} else {
this.lastKey = '';
}
});
this.messages = this.afo.database.list(`/chats-messages/${this.chatId}`, {
query: {
orderByChild: 'createdAt',
limitToLast: 10
}
});
My logic want to select lastest data, then select 10 lastest record. But data always 1 record is lastest.
However, I used angularfire2, that's OK. No problem.
I have the same problem with AFO 2.0.3.
I have a dashboard, where I fetch all available tasks for all projects:
this.afo.database.list('/tasks').subscribe(...
On a project detail page, I have the tasks listed by project:
this.afo.database.list('/tasks', {
query: {
orderByChild: 'project',
equalTo: key
}
}).subscribe(...
I get the whole task list of all projects for each project, when I first visit the dashboard. When I first visit a project page I always get the result of this query for all later queries on tasks.
I digged a bit into the code of AFO. I AngularFireOfflineDatabase.prototype.setupList
the query is applied and the result is cached. Maybe it's useful to ignore the query for building up the cache and somehow mirror the query capabilities in the "list" method.
This should be fixed as of AngularFire2 Offline v4.1.1 🎉🎉
Please post if you're still experiencing issues. Thanks!
Hi @adriancarriger I update angularfire2 offline to v4.1.1, but the problem still working.
I'm using equalTo in my query.
Hi @rodrifmed, I just tried the equalTo
query and I can't reproduce your issue. Can you post your relevant code and data structure? Thanks! 👍
Hi @adriancarriger, The problema occurs when you execute the query with a equalTo, and them execute the same query with another value. The query always return the first value
Like this example:
Data:
"test":{
"X": {
"child_test":"A"
}
"Y":{
"child_test":"A"
}
"Z":{
"child_test":"B"
}
}
1) For example this query is returning a list like that [X,Y]
this.afo.database.list('/test', {
query: {
orderByChild: 'child_test',
equalTo: "A"
}
2) When you make the second query like:
this.afo.database.list('/test', {
query: {
orderByChild: 'child_test',
equalTo: "B"
}
the hight return is [z], but is returning [X,Y]
I just made an update that supports multiple queries to the same Firebase reference in AngularFire2 Offline version 4.1.2
Here is a live demo that has an equalTo
query and an unfiltered example on the same page.
Here is the code for the example. Let me know if this is working for you. Thanks!
@adriancarriger Still doenst work. Now the second query doenst return any value.
@rodrifmed thanks for hanging in there! 👍 I hope this will help solve your issue:
^4.1.2
git clone https://github.com/adriancarriger/angularfire2-offline.git
cd angularfire2-offline
git checkout gh-pages
cd issues/9.3
npm install
npm start
If you can view and run the demo without an issue, then maybe there are some specific implementation details in your code that are affecting queries. If this is the case, please share as much of your code as possible or feel free to create a minimal example of the issue. Thanks!
I had the same problem and can confirm with the update applied it works great. Thanks so much
Hi @adriancarriger
This example is more real:
I have to make two querys, but one inside another, is because I have to wait the two querys finish.
this.query(this.urlPath, "READ")
.subscribe((readResultList) => {
console.log(readResultList)
this.query(this.urlPath, "NOT_READ")
.subscribe((unReadList) => {
console.log(unReadList)
})
})
The query is:
function query(urlPath:string, status:string) : Observable<any[]>{
return this.dbOff.list(urlPath,
{
query: {
orderByChild: 'categoryId',
equalTo: status,
limitToFirst: 15
}
})
.first()
}
@adriancarriger You forgot to put first() on your example. I try without first and it's works partially, the first request brings the two lists, but the second when paging doesnt bring nothing.
And the console is showing twice the not read list.
@rodrifmed I've updated the demo (and code) by adding the .first()
method.
Also, my console is only showing one log per query. You may need to refresh the page a few times to see any new updates I posted. When you view the demo do you get different results? Here's a screenshot of what I'm seeing:
It sounds like your setup is more complex than the demo I posted. Can you share the part of your code that involves paging?
Hi @adriancarriger If you clear your storage data you will see the second query showing twice.
I didnt say nothing about the pagination because I thought that if the second query had returned value, the problem would be solved.
I'm using ionic infinity scroll to trigger the method to call the query with new parameters:
But you could make a timeout to reproduce:
queryNext(urlPath: string, category: string, lastStoreKey: string) {
console.log(urlPath)
return this.dbOff.list(urlPath,
{
query: {
orderByChild: "categoryId",
startAt: { value: category, key: lastStoreKey },
limitToFirst: 15 + 1
}
})
.first()
}
setTimeout(() => {
// call queryNext
}, 500);
I upgrade to new version, and the first problem with the second query returning empty list is happening again
Hi @rodrifmed I can confirm that the nested query is returning empty if I first clear the device's storage, and I will be working on fixing the issue. Thanks!
@rodrifmed thanks again for pointing out this interesting issue! First I'll explain what I found, and then propose a solution. Anyone interested in the issue is welcome to chime in.
Also, if you just want to know how to use Ionic infinite scroll, just go to the end of this post.
If you clear app storage before each demo you'll notice that:
The only difference is that 9.4 uses .first()
and 9.5 doesn't.
READ
is sent to Firebasesubscribe
returns the expected resultsNOT_READ
is sent to FirebaseREAD
subscribe
returns the results available (empty array)NOT_READ
)
.first()
method)subscribe
returns the expected resultsSome apps may prioritize speed over query breadth and vise versa.
Afo cannot know ahead of time what queries will be made to the same reference. I suggest an optional param called largestQuery
telling Afo the largest query that will be made to Firebase. If given this param, then the request will be made to Firebase once and the remaining queries would run locally.
The proposed solution would look something like this:
this.afoDatabase.list('firebase/ref', {
query: {
limitToFirst: 15
},
largestQuery: {
limitToFirst: 500
}
});
Another solution would be to add a boolean param called waitForFirebase
. When true, Afo would wait for Firebase to return data if the breadth of the combined queries to a single reference increases.
this.afoDatabase.list('firebase/ref', {
query: {
limitToFirst: 15
},
waitForFirebase : true
});
Both solutions can be implemented without conflict allowing developers to use what is best for their use case.
Setup for infinite scroll/paging can already be done using Observables with a single query like this:
// Properties
items: AfoListObservable<any[]>;
limit = 20;
limitObservable = new ReplaySubject(20);
constructor(private afoDatabase: AngularFireOfflineDatabase) {
// Set initial limit
this.limitObservable.next(this.limit);
// Subscribe to list
this.items = afoDatabase.list('/issues/9/9-6', {
query: {
orderByChild: 'categoryId',
limitToFirst: this.limitObservable
}
});
}
doInfinite(infiniteScroll) {
// Add 10 per page load
this.limit += 10;
// Update subject
this.limitObservable.next(this.limit);
// Call complete
setTimeout(() => infiniteScroll.complete(), 1500);
}
Very good @adriancarriger! Could you explaine more the possible solution 2 ?
@rodrifmed here's how solution 2 would work:
When multiple queries are made to a single reference they share the same "pool" of local data. During an offline write, all queries pointing to the same reference are updated without a network connection because they are all looking at the same pool of data.
If there are multiple queries to a single reference and they all only need some of the total data belonging to that reference, then Afo tries to only ask Firebase for as much data as necessary to run all existing queries. For example if one query has limitToFirst: 5
and another has limitToFirst: 10
, then Afo's query will be limitToFirst: 10
.
If a new query comes along (e.g. limitToFirst: 15
), then Afo will need to expand the query it actually sends to Firebase. Currently, queries run immediately without waiting for Firebase to return the expanding query data. This is good, because it can potentially be hours before someone gets an internet connection.
Solution 2 would allow the developer to have more control over this behavior by indicating that all updates to a specific query should wait for Firebase to return updated data if the query scope has increased.
I wanted to allow people to comment on their use cases and get feedback before implementing because this is a feature that strays from the primary list of AngularFire2 features. That said, this does seem important for managing data offline. Feel free to comment. Thanks!
Sorry for my stupid question: How can i count all the "milk" items? Thank you for your help.
Hi @adriancarriger, any evolution about this topic?
I've tried to query using orderByChild: "people/" + queryEmail, equalTo: "true"
but it doesn't work. No results are returned while the correct results are returned using AngularFire2
@rodrifmed do you have a preferred option (1 or 2 from above) for your use case?
@ix-xerri can you show code of a full component that reproduces the issue?
I'm simply trying to get a list of subscribed chats
let queryEmail = user.email.replace(/\./g,',');
this.events = this.db.list("/events/", {
query: {
orderByChild: "people/" + queryEmail,
equalTo: "true"
}
});
this.events.subscribe((chats) => {
if (chats) {
this.storage.set("chats", chats);
}
if (chats.length === 0) {
return this.showError = true;
}
});
It does return chats if using AngularFire but none if I use you package.
@adriancarriger I think solution 2 better. What do you think?
I think they both can be useful for different cases. Hopefully I can find some time to add support for solution 2 soon! 👍
@adriancarriger I'm sorry but I'm pretty new to Ionic and angular but I'm trying to implement this solution to the app I'm developing: https://angularfirebase.com/lessons/infinite-scroll-with-firebase-data-and-angular-animation/
It works fine but with afoDatabase
it doesn't work properly and after first query second returns only one element. I tried your solution of infinite scroll above but for some reason it makes this weird jumps to the bottom of the page from the scroll position it was before when it loads new elements. As well as it loads whole lot of elements, not just new ones and I don't know how to determine when there is no more elements to load.
I believe the issue I'm encountering is somehow connected to the query problem.
Using the same pattern code of this example the data aren't queried as expected.
this.moData = this.db.list('/personal/routines/'+ this.authService.userId, {query: {orderByChild: 'day',equalTo: 1}});
This code gets all the data fine but the query doesn't work.JSON of the db
Ionic info Ionic Framework: 2.0.0 Ionic Native: 2.4.1 Ionic App Scripts: 1.1.3 Angular Core: 2.2.1 Angular Compiler CLI: 2.2.1 Node: 7.4.0 OS Platform: Windows 10 Navigator Platform: Win32 User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 `