polymerfire icon indicating copy to clipboard operation
polymerfire copied to clipboard

Firebase Query Returns Empty Array even though there are objects in the endpoint

Open ahilans opened this issue 9 years ago • 21 comments

Description

The firebase-query element's data observer method, which has parameters newData and oldData returns an empty array, preventing transformation of the data in the observer.

Expected outcome

I have an observer _onDataReceived(newData, oldData) function that gets called on change of a firebase-query's data property. This data is retrieved from a path that only has two items.

I expected the observer function to return an array of two items so that I can transform it in the observer method.

Actual outcome

What actually happens when stepping through observer method is that it gets called two times, but both times newData is an empty array. However, the observer method console.log's newData both times, and running the observer method without breakpoints shows that the second time the observer method gets called, newData is an array of two objects.

The code is run as if there is no data in newData (both times newData is an empty array)

Steps to reproduce

  1. create a firebase app with an endpoint /test2 that has two objects in it.
  2. create a firebase-test element:
<link rel="import" href="/bower/polymer/polymer.html">
<link rel="import" href="/bower/polymerfire/firebase-auth.html">
<link rel="import" href="/bower/polymerfire/firebase-app.html">
<link rel="import" href="/bower/polymerfire/firebase-query.html">


<dom-module id="test-firebase">
    <template>
        <firebase-app auth-domain="YOUR_AUTH_DOMAIN"
            database-url="YOUR_DATABASE_URL"
            api-key="YOUR_FIREBASE_API_KEY">
        </firebase-app>

        <firebase-auth id="firebaseAuth"
            location="YOUR_DATABASE_LOCATION"
            user="{{user}}">
        </firebase-auth>

        <firebase-query
            path='/test2'
            data="{{data}}">
        </firebase-query>

    </template>

    <script>
        Polymer({
            is: "test-firebase",

            properties: {
                data: {
                    type: Object,
                    observer: '_onDataReceived'
                }
            },

            _onDataReceived: function (newData, oldData) {
                console.log("objects in newData:", newData);
                this.convertedData = [];
                for (item in newData) { 
                    this.push('convertedData', { 'id': item}); 
                }
                console.log('convertedObjects', this.convertedData);
            }
        });
    </script>

</dom-module>

  1. Put a firebase-test element in the page.
  2. Open the page in a web browser.
  3. Inspect Element and look at console.
  4. Add a breakpoint at console.log("objects in newData:", newData);
  5. Refresh page and step through both invocations of the observer method. Notice the value of newData
  6. remove breakpoint.
  7. refresh page. Keep eye on console log.
  8. Notice the value of the second log of newData. The two objects magically appear even though they weren't initially there in the step through and the observer method.

I tried the data.splices workaround (https://github.com/firebase/polymerfire/issues/67), but that retrieves the same data from firebase following this pattern: retrieves an array of size 1, then an array of size 2 (object 1 and object 2), then an array of size 3 (object 1,2 and 3) etc.

This unnecessarily retrieves the same data multiple time, resulting in a long load time.

-->

Browsers Affected

  • [ X] Chrome

ahilans avatar Sep 27 '16 21:09 ahilans

#67 is not a workaround but how Polymer really works. firebase-query uses this.push('data', {value of child added}) to the data attribute. This is because if we use this:

  this.data = [];
  var newArr = [];
  for (var i in dataLoaded) {
    newArr.push(dataLoaded[i])
  }
  this.data = newArr;

Then you will have a constant flicker of an array with no elements then an array of 1, 2, 3, 4, 5 elements every time a child_added event is being called.

Even if we change the behavior of "observer" to allow detection of object.splices, it will still have that same behavior of having an array of object 1, then 2, then 3, then 4 because of how firebase (not polymerfire) work in sending frames to your browser.

What you can do is you can use the value used in the limit-to-start or limit-to-end attribute of firebase-query, and use a condition that if data.length >= limitValue then you just start running the function.

tjmonsi avatar Oct 03 '16 01:10 tjmonsi

I'm having the same problem. I was wondering what version of Polymer you're using?

phidias51 avatar Nov 05 '16 00:11 phidias51

Did you load the firebase-database-script.html along with the firebase-app-script and firebase-app?

On Sat, 5 Nov 2016 08:13 Mark Fortner, [email protected] wrote:

I'm having the same problem. I was wondering what version of Polymer you're using?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/firebase/polymerfire/issues/126#issuecomment-258576812, or mute the thread https://github.com/notifications/unsubscribe-auth/AChe1jSQY5KK1ULuiWRMoZPn_zYG16KDks5q68oigaJpZM4KILRW .

tjmonsi avatar Nov 05 '16 00:11 tjmonsi

@tjmonsi I must have missed that in the documentation. I've loaded the polymerfire.html element which is supposed to have all of the dependencies that you need.

phidias51 avatar Nov 05 '16 17:11 phidias51

I think it was not in the docs either. I just figured it out while debugging my simple code as to why firebase query and firebase document doesnt work

On Sun, 6 Nov 2016 01:32 Mark Fortner, [email protected] wrote:

@tjmonsi https://github.com/tjmonsi I must have missed that in the documentation. I've loaded the polymerfire.html element which is supposed to have all of the dependencies that you need.

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/firebase/polymerfire/issues/126#issuecomment-258628335, or mute the thread https://github.com/notifications/unsubscribe-auth/AChe1pGQUNlvaosHX1OwE5BSn_zLuep0ks5q7L3HgaJpZM4KILRW .

tjmonsi avatar Nov 06 '16 00:11 tjmonsi

If they add it to the docs it will definitely clear up some confusion. Better yet, have all dependencies in the element.

phidias51 avatar Nov 06 '16 01:11 phidias51

I think i understand the idea of not putting in all the scripts when you only need is auth or database and not storage and messaging. But if you really need it all, you can just import poylmerfire/firebase.html before importing polymerfire/firebase-app

On Sun, 6 Nov 2016 09:12 Mark Fortner, [email protected] wrote:

If they add it to the docs it will definitely clear up some confusion. Better yet, have all dependencies in the element.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/firebase/polymerfire/issues/126#issuecomment-258653504, or mute the thread https://github.com/notifications/unsubscribe-auth/AChe1gKQQS1ThXMfNeYTUFKRRNt0n6Qxks5q7SmXgaJpZM4KILRW .

tjmonsi avatar Nov 06 '16 01:11 tjmonsi

Apparently though, these are required dependencies and are needed in order for the elements to function. The browsers automatically cache the imports so duplicate references don't cause a performance hit.

phidias51 avatar Nov 06 '16 01:11 phidias51

But the ordering of import also matters... Like when i loaded firebase-auth-script before firebase-app doesnt work.

And if you have initialized firebase-app before lazy loading a page that has firebase-document which in turn loads firebase-database-script, it somehow doesnt work

On Sun, 6 Nov 2016 09:19 Mark Fortner, [email protected] wrote:

Apparently though, these are required dependencies and are needed in order for the elements to function. The browsers automatically cache the imports so duplicate references don't cause a performance hit.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/firebase/polymerfire/issues/126#issuecomment-258653721, or mute the thread https://github.com/notifications/unsubscribe-auth/AChe1icWg4wx3ycOIcLgVHw8kqxMDtDRks5q7SsygaJpZM4KILRW .

tjmonsi avatar Nov 06 '16 01:11 tjmonsi

I can help address this.

On Sat, Nov 5, 2016, 6:27 PM Toni-Jan Keith Monserrat < [email protected]> wrote:

But the ordering of import also matters... Like when i loaded firebase-auth-script before firebase-app doesnt work.

And if you have initialized firebase-app before lazy loading a page that has firebase-document which in turn loads firebase-database-script, it somehow doesnt work

On Sun, 6 Nov 2016 09:19 Mark Fortner, [email protected] wrote:

Apparently though, these are required dependencies and are needed in order for the elements to function. The browsers automatically cache the imports so duplicate references don't cause a performance hit.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub < https://github.com/firebase/polymerfire/issues/126#issuecomment-258653721 , or mute the thread < https://github.com/notifications/unsubscribe-auth/AChe1icWg4wx3ycOIcLgVHw8kqxMDtDRks5q7SsygaJpZM4KILRW

.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/firebase/polymerfire/issues/126#issuecomment-258653977, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAD_jpFreEV3CPVbby3CSQ0Wk2zqNoPks5q7S0PgaJpZM4KILRW .

mbleigh avatar Nov 06 '16 19:11 mbleigh

@tjmonsi The issue with the limit approach for my use case is that the data at an endpoint can range from 2 points to thousands of points. So if I set an "at least" limit before running the array pushing function, I miss out on all the endpoints where there might not be that many points.

Additionally, setting the "at least" limit to a smaller number will still take a long time to load due to the dataLoaded loading 1, then 2, then 3 etc.

I have also added the two script files and the loading is still very long. It takes me 4-5 seconds to load an endpoint with 200 data objects.

I am confused as to how this is a firebase issue because another client application using the mobile firebase sdk is obtaining the same data almost instantly.

ahilans avatar Nov 07 '16 16:11 ahilans

@ahilans I think what I can suggest is not to use the firebase-query because it uses the child_added event rather than the value event. If you are not adding a limit value and you want to load everything inside the endpoint, you can just use firebase-document.

<firebase-document
    path="/test2"
    data="{{data}}"
></firebase-document>

then in your script do this:

observers: [
    '_onDataReceived(data.*)'
]

_onDataReceived: function() {
    var newData = this.data;
    var convertedData = [];
    for (var item in newData) { 
        convertedData.push({ 'id': item});    
    }
    this.convertedData = convertedData;
    console.log('convertedObjects', this.convertedData);
}

This would instantaneously (I hope) show the data. The slow down would be of course if you have a thousand points in an endpoint which is caused by the latency of loading a lot of points

tjmonsi avatar Nov 08 '16 01:11 tjmonsi

Basically the whole example reported in https://beta.webcomponents.org/element/firebase/polymerfire/v0.10.3/firebase-query doesn't work. Since no change in {{data}} is detected then the list of < sticky-note > is not updated. I think this is a big issue.

fairsayan avatar Nov 12 '16 14:11 fairsayan

@mbleigh can you comment on the dependency ordering questions above?

There was another report on slack that importing firebase-app in one file and then importing firebase-auth and firebase-query (for example) in a second file (child element) wasn't working. However, this is approximately what the demo does.

If there's an ordering dependency here, it's not apparent from the docs.

arthurevans avatar Dec 01 '16 20:12 arthurevans

i changed all data to _data & add _data in properties

          _data: {
            notify: true,
          },

now it's working, all result getting in _data

hedcet avatar Dec 11 '16 08:12 hedcet

@arthurevans I think it is a case of a race-condition. I have experienced it and come up with my own personal solution...

Apparently, importing the firebase-app only imports firebase-app-script.html, which imports the firebase.js shell script. But because you didn't import the firebase-auth.js and firebase-database.js files, initializing firebase-app doesn't do anything.

That means even if you have imported firebase-auth, which in turn imports firebase-auth-script.html; and firebase-query, which in turn imports firebase-document-script.html, it was only imported after you have initialized firebase-app...

Here's what is happening i think...

// parent-element

<link rel="import" href="bower_components/polymerfire/firebase-app.html">
<link rel="import" href="child-element.html">
...
<firebase-app ... ></firebase-app>
<child-element></child-element>

// child-element

<link rel="import" href="bower_components/polymerfire/firebase-app.html">
<link rel="import" href="bower_components/polymerfire/firebase-query.html">
...
<firebase-auth></firebase-auth>
<firebase-query></firebase-query>

Sequence

  1. parent-element is loaded and parsed
  2. parent-element is executed
  3. firebase-app is loaded
  4. child-element is loaded
  5. firebase-app is parsed and executed through call of firebase-app element
  6. child-element is parsed and executed
  7. firebase-auth is loaded
  8. firebase-query is loaded
  9. firebase-auth is parsed and executed through call of firebase-auth element
  10. firebase-query is parsed and executed through call of firebase-query element

tjmonsi avatar Dec 11 '16 12:12 tjmonsi

I have bumped into this issue as well, though it's probably because I'm using firebase-query in an unexpected way.

I'm currently simulating typeahead with firebase query using the following code:

<firebase-query
        id="game-query"
        app-name="games"
        limit-to-first={{resultSize}}
        order-by-child="name"
        start-at="{{gameName}}"
        end-at="{{gameName}}zz"
        path="/games"
        data="{{data}}">
    </firebase-query>

In this case, gameName is input from the user in a search box. Generally speaking, this actually works quite well. The problem is I want to animate the creation of the children, which requires me to know when firebase-query has returned all of it's results.

Should I be using something other than firebase-query for this? I still have not come up with a workaround. This currently causes other problems such as preventing me from being able to ignore "the" in the name of games or handle case sensitivity. I'm extremely open to other approaches. I'm clearly not a firebase expert.

luciddr34m3r avatar Jan 04 '17 19:01 luciddr34m3r

i have the same problem

jurerick avatar May 29 '17 05:05 jurerick

With an empty array being outputted, how can I count the number of objects in the data of firebase-query element?

dshukertjr avatar Jun 04 '17 00:06 dshukertjr

It's not only empty, but I can't even access smth like data[0]. Any fix or workaround?

artemik avatar Jun 20 '17 09:06 artemik

I'm experiencing the same issue. The observer on the data received from Firebase is fired up and I get an empty array first, then the observer is fired up a second time with the right data from the database. It's quite a problem for implementing a loader for exemple.

jonadeline avatar Oct 29 '17 20:10 jonadeline