2
votes

I'm using firebase for a large-ish database, with each entry using an autoid key. To get, say, the last ten entries, I can use:

ref.queryLimitedToLast(10).observeSingleEventOfType(.Value, withBlock: { snapshot in
            for item in snapshot.children {
                //do some code to each item
            }
        })

However, I can't for the life of me work out how to then get just the ten entries before that. Eg. if the database had 100 entries, my code would return 90-100, but how would I then get entries 80-90 (without, for example, querying the last 20 and throwing half away, as it seems inefficient)?

Edit: I ended up using

ref.queryOrderedByChild("timecode").queryEndingAtValue(final).queryLimitedToLast(10).observeSingleEventOfType(.Value, withBlock: { snapshot in
for item in snapshot.children {
                //do some code to each item, including saving a new value of 'final'
            }
        })

and saving the value 'final' as the timecode of the last update. that is, first i would get results, 90-100, say, and save the timecode of 90 as final (minus one second), then use this for the ending value, etc... to find results 80-89. Just as Jay describes below, but using a timestamp instead of an index number (as it was already in there)

Edit 2: Also, to get it working better, I also added ".indexOn": "timecode" to the firebase rules for the database

2
There's a couple of ways to do this but a super simple solution is to simply keep a total_count in another node and and index within each node. Then use starting at and ending at. You could also leverge the priority variable on your nodes to store the index. You can also use a transation to count your nodes and then use starter and end at with the index. - Jay
Thanks for that. I've had a crack at that but can't get it working. I'd like to query just from the top keys if possible. I think what i'm after is called 'pagination', which i can't find a simple way to do on iOS, i don't see how to do that with data structured by autoid keys. - Bullwinkle
I posted an answer to assist in getting it working. It's fairly straight-forward but let me know if you have questions. - Jay
@Richie, what is final in your code? Is it a number or a timestamp? - IvanPavliuk

2 Answers

1
votes

There's a couple of ways to do this but an easy solution is to keep a total_count in another node and an index within each node.

Then use queryStartingAtValue and queryEndingAtValue to query the range of child nodes you are interested in.

When you add a child to your 'posts' node for example, add one to the total_count node and save it. Over time you'll have 100 posts and the total_count node will have a value of 100. You can then query for any range of posts: .queryStartingAtValue(80) and . queryEndingAtValue(89), or .queryStartingAt(20) and .queryEndingAt(30)

For example, assume there's 45 posts (showing just 4 of them here)

posts
  ...
  post_1024
    text: "my post!"
    index: 42
  post_1025
    text: "another post"
    index: 43
  post_1026
    text: "yippee"
    index: 44
  post_1027
    text: "Stuff and Things"
    index: 45

and then a node to track them

post_info
   total_count: 45

and the code to query for the middle two nodes

let ref = myRootRef.childByAppendingPath("posts"
ref.queryOrderedByChild("index").queryStartingAtValue(43).queryEndingAtValue(44)
   .observeEventType(.Value, withBlock: { snapshot in
    print(snapshot.key)
})

and the output would be

  post_1025
    text: "another post"
    index: 43
  post_1026
    text: "yippee"
    index: 44

That being said, this may be slightly redundant depending on what happens to your data. If you never delete posts, then you're set. However, if you delete posts then obviously there's a gap in your indexes (42, 43, .. 45) so other factors need to be taken into consideration.

You may not even need a total_count - it just depends on how your app works.

You could also leverage the priority variable on your nodes to store the index instead of having it be a child node.

Transitions and .observeSingleEvent with .Value and .numChildren can be also be used to obtain a live node count.

1
votes

Have you tried stacking queries?

ref.queryLimitedToLast(20).queryLimitedToFirst(10).observeSingleEventOfType(.Value, withBlock: { snapshot in
    for item in snapshot.children {
        //do some code to each item
    }
})

Just a thought, haha.