1

I am trying to just retrieve a list of records, sorted by one of the fields in each record, and I would like to be able to retrieve the data in pages.

According to the Firebase documentation, the reference.startAt() method has an optional 2nd parameter, which is:

The child key to start at. This argument is only allowed if ordering by child, value, or priority.

First off, here is the data:

{
  "products" : {
    "-KlsqFgVWwUrA-j0VsZS" : {
      "name" : "Product 4",
      "price" : 666
    },
    "-Klst-cLSckuwAuNAJF8" : {
      "name" : "Product 1",
      "price" : 100
    },
    "-Klst7IINdt8YeMmauRz" : {
      "name" : "Product 2",
      "price" : 50
    },
    "-Klst9KfM2QWp8kXrOlR" : {
      "name" : "Product 6",
      "price" : 30
    },
    "-KlstB51ap1L2tcK8cL6" : {
      "name" : "Product 5",
      "price" : 99
    },
    "-KlstDR5cCayGH0XKtZ0" : {
      "name" : "Product 3",
      "price" : 500
    }
  }
}

And here is the code which is able to retrieve Page 1 (lowest priced item + 2nd lowest priced + 3rd lowest priced):

(I am using Firebase JS SDK 4.1.1)

'use strict';
var firebase = require('firebase');

firebase.initializeApp({
    apiKey: "your-api-key",
    authDomain: "your-firebase-domain",
    databaseURL: "https://your-db.firebaseio.com",
    projectId: "your-project",
    storageBucket: "your-bucket.appspot.com",
    messagingSenderId: "your-sender-id"
})

firebase.database().ref('products')
.orderByChild('price')
.limitToFirst(3)
.on('child_added', function (snapshot) {
    var key = snapshot.key;
    var data = snapshot.val();
    console.log(key + ': ' + JSON.stringify(data))
})

Output:

TJ:database tjwoon$ node test.js

-Klst9KfM2QWp8kXrOlR: {"name":"Product 6","price":30}
-Klst7IINdt8YeMmauRz: {"name":"Product 2","price":50}
-KlstB51ap1L2tcK8cL6: {"name":"Product 5","price":99}

Page 2 should be the 3rd lowest, 4th lowest, and 5th lowest priced products, which means my code need one additional line:

firebase.database().ref('products')
.orderByChild('price')
.startAt(null, '-KlstB51ap1L2tcK8cL6')
.limitToFirst(3)
.on('child_added', function (snapshot) {
    var key = snapshot.key;
    var data = snapshot.val();
    console.log(key + ': ' + JSON.stringify(data))
})

Output:

TJ:database tjwoon$ node test.js
-Klst9KfM2QWp8kXrOlR: {"name":"Product 6","price":30}
-Klst7IINdt8YeMmauRz: {"name":"Product 2","price":50}
-KlstB51ap1L2tcK8cL6: {"name":"Product 5","price":99}

The problem is that it is returning Page 1 again. If the documentation is correct, the results should start at the record with key -KlstB51ap1L2tcK8cL6.

I have tried adding an .indexOn rule for the price field, but the results remained identical.

If I remove the orderByChild() line, then the results do start from the given key, but of course the sorting is incorrect, plus it behaves contrary to the documentation...

I found these other Stack Overflow posts which describe the same problem:

However there are no answers to those questions and quite few responses. There are no issues in the Github repository which match the search term startat.

Is anyone else facing the same issue? This problem makes it impossible to retrieve a sorted list in a paginated manner...

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
tjwoon
  • 13
  • 5
  • 2nd StackOverflow question: https://stackoverflow.com/questions/36779947/firebase-querying-when-combining-orderbychild-startat-and-limittofirst?rq=1 – tjwoon Jun 06 '17 at 15:38

1 Answers1

6

You're almost there!

The problem is in how you call startAt for the second page:

.startAt(null, '-KlstB51ap1L2tcK8cL6')

To get the correct results you need to pass in both the price and the key of the so-called anchor item:

.startAt(99, '-KlstB51ap1L2tcK8cL6')

With that Firebase will find all items with price 99 and then return items starting at key '-KlstB51ap1L2tcK8cL6'.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 1
    Thanks! Looks like the 1st parameter is the "main" startAt criteria, with the 2nd parameter serving as a kind of disambiguation value in case multiple children have the same value. This is awesome, thanks a lot! – tjwoon Jun 07 '17 at 14:14
  • I'd like to know how to use this feature via REST. If anyone can help, this is what I ask at https://stackoverflow.com/questions/45909798/how-use-firebase-startat-method-with-value-and-key-in-rest-request – Alex Oliveira Aug 27 '17 at 22:17