2

My database structure is some thing like this:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "score": 4,
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

I am trying to retrieve top 20 scores in descending order.

let queryRef = FIRDatabase.database().reference().child("users").queryOrderedByChild("score").queryLimitedToLast(20)
queryRef.observeSingleEventOfType(.Value, withBlock: { (querySnapShot) in
      print(querySnapShot.value)
})

i am trying to get output like

score": 4
score": 3
score": 2

or 

score": 2
score": 3
score": 4

or 

2
3
4

Please let me know how to solve this.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
ilvcs
  • 103
  • 1
  • 17

2 Answers2

7

When you request the children in a specific order, the resulting snapshot will contain both the data that matches the query and information about the order in which you requested them.

But when you request the .value of the snapshot, the keys+data are converted to a Dictionary<String,AnyObject>. Since a dictionary does not have an extra place to put the information about the order, that information is lost when converting to a dictionary.

The solution is to not convert to a dictionary prematurely and instead loop over the snapshot:

queryRef.observeSingleEventOfType(.Value, withBlock: { (querySnapShot) in
    for childSnapshot in querySnapShot.children {
        print(childSnapshot.value)
    }
})

You can also listen to the .ChildAdded event, instead of .Value, in which case the children will arrive in the correct value:

queryRef.observeSingleEventOfType(.ChildAdded, withBlock: { (childSnapshot) in
    print(childSnapshot.value)
})

Update

I just added this JSON to my database:

{
  "users" : {
    "alovelace" : {
      "name" : "Ada Lovelace",
      "score" : 4
    },
    "eclarke" : {
      "name" : "Emily Clarke",
      "score" : 5
    },
    "ghopper" : {
      "name" : "Grace Hopper",
      "score" : 2
    }
  }
}

And then ran this code:

let queryRef = ref.child("users").queryOrderedByChild("score").queryLimitedToLast(20);
queryRef.observeEventType(.ChildAdded) { (snapshot) in
    print(snapshot.key)
}

The output is:

ghopper

alovelace

eclarke

Which is the users in ascending order of score.

Update to add more on getting the scores in descending order

The above code gets the 20 highest scores in ascending order. There is no API call to return themthem in descending score.

But reversing 20 items client side is no performance concern, you just need to write the code for it. See for example this answer.

If you really are stuck on reversing them client side, you can add an inverted score. See this answer for an example of that.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Hi, neither worked for me. First method throwing an error "Ambiguous use of 'value'", second one is printing the values which are not sorted.score is not the directly child node of users. Please observe that "score" is in the sub directory of each user. – ilvcs Aug 30 '16 at 17:00
  • Hi, my apologies for asking question wrongly, i want top 20 scores from the users. I have updated the question now. – ilvcs Aug 31 '16 at 03:30
  • Added some links explanation and links for that. – Frank van Puffelen Aug 31 '16 at 03:39
5

Use method observeEventType instead of observeSingleEventOfType. Also, make FIRDataEventType to ChildAdded.

Last, If you want Top 20 items, use queryLimitedToFirst instead of queryLimitedToLast.

{
  "users" : {
    "alovelace" : {
      "name" : "Ada Lovelace",
      "score" : 4
    },
    "eclarke" : {
      "name" : "Emily Clarke",
      "score" : 5
    },
    "ghopper" : {
      "name" : "Grace Hopper",
      "score" : 2
    }
  }
}

For the dataset above

let queryRef = FIRDatabase.database().reference().child("users").queryOrderedByChild("score").queryLimitedToFirst(20)
queryRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
    print("key: \(snapshot.key), value: \(snapshot.value)")
})

key: ghopper, value: Optional({ name = Grace Hopper; score = 2; })

key: alovelace, value: Optional({ name = Ada Lovelace; score = 4; })

key: eclarke, value: Optional({ name = Emily Clarke; score = 5; })

Snapshot will returns the contents as native types. Data types returned:

  • NSDictionary
  • NSArray
  • NSNumber (also includes booleans)
  • NSString

So, you can get your scores this way.

    let queryRef = FIRDatabase.database().reference().child("users").queryOrderedByChild("score").queryLimitedToFirst(20)
queryRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
    if let scores = snapshot.value as? NSDictionary {
        print(scores["score"])
    }
})

Optional(2)

Optional(4)

Optional(5)

Moreover, the default of realtime database return everything in ascending order.

If you want descending order, you can make some tricks(4:40) in your database.

Willjay
  • 6,381
  • 4
  • 33
  • 58