Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase queryOrderedByChild() method not giving sorted data

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.

like image 346
ilvcs Avatar asked Nov 29 '25 03:11

ilvcs


2 Answers

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.

like image 135
Frank van Puffelen Avatar answered Dec 01 '25 16:12

Frank van Puffelen


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.

like image 29
Willjay Avatar answered Dec 01 '25 18:12

Willjay



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!