Home > Blockchain >  Swift Firebase Database: How to filter values by child key
Swift Firebase Database: How to filter values by child key

Time:01-25

I don't understand how to filter the data lists

my db has the following form

{
    "numbers": [0, 1, 2, 3, 4, 5],
    "models": {
        "random_string_key": {
            "name": "model_name",
            "value": "some_value"
        },
        "random_string_key-2": {
            "name": "Name",
            "value": "any value"
        }
    }
}
  1. How i can get array from numbers where all values < 3?
  2. How i can filter models and get array models where value == "some_value"?

I would like to understand what I am doing wrong

let ref = Database.database().reference()
let refNumbers = ref.child("numbers")
refNumbers
    .getData(completion: { error, snapshot in
        // snapshot.value == [0, 1, 2, 3, 4, 5]
        // OK
    })

refNumbers
    .queryOrderedByValue()
    .queryEnding(beforeValue: 2)
    .getData(completion: { error, snapshot in
        /*
        error:  Unable to get latest value for query FQuerySpec (path: /numbers, params: {
            en = "[MIN_NAME]";
            ep = 2;
            i = ".value";
        }), client offline with no active listeners and no matching disk cache entries

        why???
        */
    })
let modelsRef = ref.child("models")
modelsRef
    .getData(completion: { error, snapshot in
        // snapshot.value == NSDictionary
        // OK
        /*
        [
            "key": String
            "value": NSDictionary
        ]
        */
    })

modelsRef
    .queryEqual(toValue: "some_value", childKey: "value")
    .getData(completion: { error, snapshot in
        /*
        error: null
        snapshot.value == nil

        why???
        */
    })

modelsRef
    .queryOrdered(byChild: "value")
    .queryEqual(toValue: "some_value")
    .getData(completion: { error, snapshot in
        /*
        Unable to get latest value for query FQuerySpec (path: /models, params: {
            ep = some_value;
            i = value;
            sp = some_value;
        }), client offline with no active listeners and no matching disk cache entries

        why???
        */
    })

I tried all the options that I found on the Internet but the result is 0 Either I get all the data from the list and filter it in the app, or I get nothing Is it possible to filter the data upon receipt?

CodePudding user response:

It is a bit difficult to give you a solution to the issues you described above but I can tell you where you're going wrong.

For #1, when you do the below

...
refNumbers
    .queryOrderedByValue()
    .queryEnding(beforeValue: 2)
    .getData(completion: { error, snapshot in
        ...
    })

refNumbers is not an array of numbers, it is an 'object'. And queryOrderedByValue() will not work on this 'single object', neither will .queryEnding(beforeValue: 2). You either need to do what you're doing, which is to get the entire data, convert to swift native types and filter, or you need to restructure your data on the DB side.

Similarly, in-case of #2, the object modelsRef is composed of a number of objects with random keys. So, when you perform a .queryEqual(toValue: "some_value", childKey: "value") operation, it will not find the child-key named 'value'. This child key is actually a child-key for the objects that modelsRef is composed of. So, again, either you need to get all this data, type cast to native swift types and then filter, or somehow restructure your data.

So, the answer to your question is essentially either continue what you're doing (get data to the app and filter using native swift API which may present scalability challenges later depending on the amount of data), or, restructure your data.

CodePudding user response:

Example with queryStarting & queryEnding

As requested, here is what works for me.

Database design

I have an event based app, using Firebase Realtime Database, with one parent node, lets call it events for now, because the real example is mainly in German. Under events there is one child for each event, obviously:

{
  "events": {
    "-L_nMRK8mzXal47IE54x": {
      "endDate": 1568715118,
      "lat": 48.4382387,
      "lon": 10.0499972044298,
      "name": "Exampletown - event",
      "city": "Exampletown",
      "zip": "12345",
      "startDate": 1568325600,
      "street": "Street 11"
    },
    "-L_nMRK8mzXal47IE54y": {
      "endDate": 1568715118,
      "lat": 49.4382387,
      "lon": 10.0499972044298,
      "name": "Exampletown - event 2",
      "city": "Exampletown",
      "zip": "12345",
      "startDate": 1568325600,
      "street": "Street 12"
    }, 
  }
}

Swift 4.2

Inside a method I use the following to query the database for all past events based on the current timestamp:

let ref = Database.database().reference()
let query = ref.child("events").queryOrdered(byChild: "endDate").queryStarting(atValue: diffInterval, childKey: "endDate").queryEnding(atValue: now, childKey: "endDate")

// observe single event is sufficient for my app
query.observeSingleEvent(of: .value) { (snapshot) in
    if snapshot.childrenCount > 0 {
        for snap in snapshot.children {
            // create an object for each of the objects of the snapshot
            guard let eventData = self.getEventsFromSnapshot(snap: snap as! DataSnapshot) else {return}
            // do something with eventData
        }
    } else {
        // custom logging and return of empty array
    }
}
  • Related