Skip to content
This repository has been archived by the owner on Sep 4, 2020. It is now read-only.

Querying lists doesn't work #9

Open
giorgiopiatti opened this issue Mar 4, 2017 · 29 comments
Open

Querying lists doesn't work #9

giorgiopiatti opened this issue Mar 4, 2017 · 29 comments
Assignees

Comments

@giorgiopiatti
Copy link

giorgiopiatti commented Mar 4, 2017

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

{
    "routines" : {
      "TaMPvE9d0nSM6iLqBWoz3wTdJ0E3" : {
        "-KeQBZCteClbOZKwieZb" : {
          "day" : 0,
          "description" : "",
          "timestamp" : 1488662446111,
          "title" : "Test",
        }
      }
    }
  }

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
`

@adriancarriger
Copy link
Owner

Thanks for the issue report! I put together a demo to show what I found:

Example

  • Code
  • Demo - this purposely doesn't allow writes to Firebase, but feel free to test using your own database

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:

Issues

  • Queries are not applied to writes (in this example equalTo does not get applied until reconnecting)
  • If you have two Afo lists with different queries pointing to the same Firebase reference, the first query gets used for both

I'll see what I can do to fix this. Let me know if you experience anything else. Thanks!

@adriancarriger adriancarriger self-assigned this Mar 6, 2017
@lamquangphuc
Copy link

lamquangphuc commented Mar 13, 2017

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.

@davemecha
Copy link

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.

@adriancarriger
Copy link
Owner

This should be fixed as of AngularFire2 Offline v4.1.1 🎉🎉

Please post if you're still experiencing issues. Thanks!

@rodrifmed
Copy link

Hi @adriancarriger I update angularfire2 offline to v4.1.1, but the problem still working.

I'm using equalTo in my query.

siliconxp pushed a commit to siliconxp/angularfire2-offline that referenced this issue May 18, 2017
@adriancarriger
Copy link
Owner

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! 👍

@rodrifmed
Copy link

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"
	}
  1. 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]

@adriancarriger
Copy link
Owner

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!

@rodrifmed
Copy link

@adriancarriger Still doenst work. Now the second query doenst return any value.

@adriancarriger
Copy link
Owner

@rodrifmed thanks for hanging in there! 👍 I hope this will help solve your issue:

Demos

Steps to solve

  • Please make sure you've cleared application storage in Chrome dev tools (image) for each site/demo that you are viewing to rule out old versions affecting what you see
  • Make sure any AngularFire2 Offline code you're running is using version ^4.1.2
  • Check if the above demos are working
  • Run demo 9.3 using these commands:
    • git clone https://github.com/adriancarriger/angularfire2-offline.git
    • cd angularfire2-offline
    • git checkout gh-pages
    • cd issues/9.3
    • npm install
    • npm start

Next steps

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!

@terrycollinson
Copy link

I had the same problem and can confirm with the update applied it works great. Thanks so much

@rodrifmed
Copy link

rodrifmed commented May 23, 2017

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
Copy link
Owner

@rodrifmed thanks for finding this! It should now be fixed in version 4.1.4. Here is a demo (with code).

Can you confirm that this solves your issue?

@rodrifmed
Copy link

@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.

@adriancarriger
Copy link
Owner

Updated demo

@rodrifmed I've updated the demo (and code) by adding the .first() method.

Console

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:

screen shot 2017-05-24 at 7 19 59 pm

Paging

It sounds like your setup is more complex than the demo I posted. Can you share the part of your code that involves paging?

@rodrifmed
Copy link

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);

@rodrifmed
Copy link

I upgrade to new version, and the first problem with the second query returning empty list is happening again

@adriancarriger
Copy link
Owner

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!

@adriancarriger
Copy link
Owner

@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.

What is happening

Demos

If you clear app storage before each demo you'll notice that:

  • Demo 9.4 only returns results matching READ
  • Demo 9.5 returns results for READ and NOT_READ

The only difference is that 9.4 uses .first() and 9.5 doesn't.

Step by step

  1. The first request for items matching READ is sent to Firebase
  2. subscribe returns the expected results
  3. The second request for items matching NOT_READ is sent to Firebase
  4. Afo checks the in-memory cache (not device storage)
    • because both queries belong to the same reference it finds the last dataset from Firebase which only contains results matching READ
    • Afo runs the query locally
    • subscribe returns the results available (empty array)
  5. Afo receives Firebase data for the second query (items matching NOT_READ)
    • Demo 9.4 ignores the new data (because of the .first() method)
    • Demo 9.5 subscribe returns the expected results

Is this a bug?

  • Running a query on the first dataset available may be desired for some use cases, especially when the app may lose connection at any time.
  • However, an app may need a guarantee that queries run against data of a specific scope.

Some apps may prioritize speed over query breadth and vise versa.

Possible solution 1

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.

Proposed usage

The proposed solution would look something like this:

this.afoDatabase.list('firebase/ref', {
  query: {
    limitToFirst: 15
  },
  largestQuery: {
    limitToFirst: 500
  }
});

Possible solution 2

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.

Proposed usage

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.

Ionic infinite scroll paging

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);
}

@rodrifmed
Copy link

Very good @adriancarriger! Could you explaine more the possible solution 2 ?

@adriancarriger
Copy link
Owner

@rodrifmed here's how solution 2 would work:

Solution 2 details

Data sharing

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.

Shared query scope

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.

Increasing shared scope

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

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.

Next steps

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!

@ghost
Copy link

ghost commented Jun 6, 2017

Sorry for my stupid question:
How can i count all the "milk" items?
Thank you for your help.

@rodrifmed
Copy link

Hi @adriancarriger, any evolution about this topic?

@ix-xerri
Copy link

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

@adriancarriger
Copy link
Owner

@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?

@ix-xerri
Copy link

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.

@rodrifmed
Copy link

@adriancarriger I think solution 2 better. What do you think?

@adriancarriger
Copy link
Owner

I think they both can be useful for different cases. Hopefully I can find some time to add support for solution 2 soon! 👍

@eolant
Copy link

eolant commented Aug 24, 2017

@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.

davemecha pushed a commit to davemecha/angularfire2-offline that referenced this issue Aug 3, 2018
davemecha pushed a commit to davemecha/angularfire2-offline that referenced this issue Aug 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants