0

I have the following query to get the newest 16 articles since the last fetch:

db.ref("articles).startAt(null, "last_fetched_key").orderByChild("published").limitToLast(16);

So it should start at the last article I have, then order those by the child key "published", then limit those results to 16.

It works as long as I don't do orderByChild("published") but if I do, I always get 16 items?

Note: I see this behavior on both nodejs (firebase-admin 5.1.0) and browser (firebase-sdk 4.2.0)

Example: (see below for data example)

function countObj(obj) { var c=0; for (var k in obj) { c++; } return c; }
function getLastKey(obj) { var lk=null; for (var k in obj) { lk = k; } return lk; }

firebase.database().ref("articles/-KnLhC7S0wtNV17R-w2I")
  .orderByChild("published")
  .limitToLast(4) // limit 4, 
  .once("value", function(snap) {

    var data = snap.val();

    console.log("#1 Results: "+ countObj(data.val())); // Results: 4

    var lastKey = getLastKey(data);

    // With .orderByChild("published")
    // Should return 1 result because articles contains 4, we pass the 4th key to startAt()
    firebase.database().ref("articles/-KnLhC7S0wtNV17R-w2I")
      .startAt(null, lastKey) // use startAt
      .orderByChild("published")
      .limitToLast(4)
      .once("value", function(snap) {
        var data = snap.val();
        console.log("#2 Results: "+ countObj(data.val())); // Results: 4
    });

    // Without .orderByChild("published")
    // Returns 1 result because articles contains 4, we pass the 4th key to startAt()
    firebase.database().ref("articles/-KnLhC7S0wtNV17R-w2I")
      .startAt(null, lastKey) // use startAt
      .limitToLast(4)
      .once("value", function(snap) {
        var data = snap.val();
        console.log("#3 Results: "+ countObj(data.val())); // Results: 1
    });


  });

Data:

{
  "articles" : {
    "-KnLhC7S0wtNV17R-w2I" : {
      "1498230632000-3M7VnZESptDyGP4iN6BXlNqi6ukG4HgWkX4KvwiWEKs%3D_15cd582aa38%3A50c8a%3A35f03cd7" : {
        "published" : 1498230632000
      },
      "1498230632001-3M7VnZESptDyGP4iN6BXlNqi6ukG4HgWkX4KvwiWEKs%3D_15cd582aa38%3A50c8a%3A35f03cd7" : {
        "published": 1000
      },
      "1498230632002-3M7VnZESptDyGP4iN6BXlNqi6ukG4HgWkX4KvwiWEKs%3D_15cd582aa38%3A50c8a%3A35f03cd7" : {
        "published": 900
      },
      "1498230632003-3M7VnZESptDyGP4iN6BXlNqi6ukG4HgWkX4KvwiWEKs%3D_15cd582aa38%3A50c8a%3A35f03cd7" : {
        "published": 1100
      },
    }
  }
}
KENdi
  • 7,576
  • 2
  • 16
  • 31
REJH
  • 3,273
  • 5
  • 31
  • 56
  • 1
    Please share the minimum JSON that is needed to reproduce the problem (as text, no screenshots). You can get this by clicking the "Export JSON" link in your [Firebase Database console](https://console.firebase.google.com/project/_/database/data). – Frank van Puffelen Aug 12 '17 at 14:01
  • Done! (I hope my example code is clear enough.. typed it here on SO so no checks .. :)) – REJH Aug 12 '17 at 15:53

1 Answers1

2

You seem to misunderstand how the parameters for startAt() work. There are three steps here:

  1. First the data is ordered as your query instructs. If you don't specify any ordering, the data is ordered by priority (which is the same for all data in your case)
  2. The query then finds the first child for which the value that it sorted on matches what you specify as the first parameter.
  3. The query then takes the (optional) second and removes any items whose key is before that.

So in your query, if you want the next 4 items after 1498230632003...:

ref
  .orderByChild("published")
  .startAt(1100, "1498230632003-3M7VnZESptDyGP4iN6BXlNqi6ukG4HgWkX4KvwiWEKs%3D_15cd582aa38%3A50c8a%3A35f03cd7")
  .once("value", function(snap) {
    console.log("#2 Results: "+snap.numChildren()); // Results: 2
    snap.forEach(function(child) { 
      console.log(child.key); // 1498230632003..., 1498230632000...
    });
  });

If you want to determine where to start the query results based on the key, you should use orderByKey():

ref
  .orderByKey()
  .startAt("1498230632003-3M7VnZESptDyGP4iN6BXlNqi6ukG4HgWkX4KvwiWEKs%3D_15cd582aa38%3A50c8a%3A35f03cd7")
  .once("value", function(snap) {
  console.log("#2 Results: "+snap.numChildren()); // Results: 1
  snap.forEach(function(child) { 
    console.log(child.key); // 1498230632003...
  });
});

I'm not completely certain, but it seems like you're trying to get a query to order on a property and filter on the key too. This is not possible within the Firebase Database query model. For your options, see http://stackoverflow.com/questions/26700924/query-based-on-multiple-where-clauses-in-firebase

My jsbin for testing: https://jsbin.com/xuwaxaq/edit?js,console

A few snippets from your code are hard to understand. I recommend replacing them with my more idiomatic variants:

countObj(data.val())

Is the same as:

snap.numChildren()

And:

var lastKey = getLastKey(data);

Is the same as:

var lastKey;
snap.forEach(function(child) { lastKey = child.key; });
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks, I'll give this a go and mark as answered as soon as I'm sure I understand :) – REJH Aug 13 '17 at 10:43