I have a Firebase that its structured as follows:
"zipcodes"
12345
1
"city": "test"
"state": "XX"
98765
2
"city": "test2"
"state": "AA"
3
"city": "test3"
"state": "BB"
I am trying to query my database by the user's zip code; however, when I try to view the snapshot that it found, it is null. Here is the code I currently have:
func firebaseSearch(zipCode: String) {
let conditionRef = FIRDatabase.database().reference().child("zipcodes")
let query = conditionRef.queryEqualToValue(zipCode)
query.observeSingleEventOfType(.Value, withBlock: {snapshot in
print(snapshot.value)
for child in snapshot.children {
print(child)
}
})
}
When I run the code, I get this:
(/zipcodes {
ep = 12345;
sp = 12345;
})
Optional(<null>)
My database has around 77k entries, so I was worried iterating through all entries and trying to find the child with my zipCode value would take a large amount of time, so I am trying to use the query feature to see if it's any quicker. I appreciate any help, thank you very much!
You don't need query for this, use .child(zipCode) instead of .queryEqualToValue(zipCode). That will return the expected result.
Hope this helps!!
Related
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"
}
}
}
How i can get array from numbers where all values < 3?
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?
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.
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
}
}
JSON data are following:
{
"Questions": {
"1": {
"questiontext": "Which choice best describes what happens in the\npassage?",
"A": "One character argues with another character who intrudes on her home.",
"B": "One character receives a surprising request from\nanother character",
"C": "One character reminisces about choices she has\nmade over the years.",
"D": "One character criticizes another character for\npursuing an unexpected course of action.",
"E": "null"
},
"2": {
"questiontext": "Which choice best describes the developmental pattern of the passage?",
"A": "A careful analysis of a traditional practice",
"B": "A detailed depiction of a meaningful encounter",
"C": "A definitive response to a series of questions",
"D": "A cheerful recounting of an ammusing anecdote",
"E": "null"
}
}
}
You may find there are some challenges with your structure so that will probably need to be changed in the future.
Since your question is very specific about the included structure, here's how to access the value of Question 1, Answer A (or B, C, D etc)
let q1Ref = self.ref.child("Questions").child("1")
let q1Ref_a_ref = q1Ref.child("A")
q1Ref_a_ref.observeSingleEvent(of: .value, with: { snapshot in
let answer = snapshot.value as! String
print(answer)
})
The output is
One character argues with another character who intrudes on her home.
Now, if you want to get all the child nodes A-E, then here's the code
let q1Ref = self.ref.child("Questions").child("1")
q1Ref.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
let value = snap.value as! String
print("Answer \(key) is \(value)")
}
})
and the output will is
Answer A is One character argues with another character who intrudes on her home.
Answer B is One character receives a surprising request from\nanother character
Answer C is One character reminisces about choices she has\nmade over the years.
Answer D is One character criticizes another character for\npursuing an unexpected course of action.
Answer E is null
To address a comment; this assumes your Firebase root ref is defined thusly:
class ViewController: UIViewController {
var ref: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
and your firebase structure keys are 1, 2, 3 (as in an array*) and not "1", "2", "3"
You can tell the difference by looking at your Firebase console and seeing if the keys are surrounded by quotes; 1 is different than "1"
It would be a good idea to use .childByAutoId to create parent node keys instead of numerics (array)
*Arrays should be avoided in Firebase. See Arrays
if let question = jsonData["Questions"] as? [String: Any] {
//Question 1
if let questionOne = question["1"] as? [String: Any] {
for (key, value) in questionOne {
print("\(key):\(value)")
}
}
//Question 2
if let questionTwo = question["2"] as? [String: Any]{
for (key, value) in questionTwo {
print("\(key):\(value)")
}
}
}
I have a database scheme that is essentially the same that the one is the documentation :
// An index to track Ada's memberships
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
I would like to create a DatabaseQuery that gets all users that take part of a specific group (for instance users whose "groups" contain "techpionneers")
How should I proceed ? I tried a Database.database().reference(withPath: "users").queryOrdered(byChild: "groups").queryEqual(toValue: "techpioneers") but that does not work (obviously).
To load all users that are part of a specific group, start in the /groups node to find their key:
ref.child("groups/techpioneers/members").observeSingleEvent(of: .value, with: { (snapshot) in
That gives you the keys of all members. Then loop over those keys and load the corresponding user.
This is essentially a client-side join operations. While you may initially think this is incredibly slow, it's actually not all that bad since Firebase pipelines the requests over a single connection. See http://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786.
Franks solution is spot on but an alternative is to use a Deep Query i.e. ordering by deep paths to query the users node for child->child nodes that match the criteria. Here's the format
let ref = self.ref.child("users")
let query = ref.queryOrdered(byChild: "groups/techpioneers").queryEqual(toValue: true)
query.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
print(snap)
}
})
the result is all of the users that are part of the techpioneers group
"alovelace": {
"name": "Ada Lovelace",
"groups": {
"techpioneers": true,
"womentechmakers": true
}
}
I've created a simple data structure in Firebase:
{
"authors": {
"John Lennon": [
"Life is what happens when you're busy making other plans.",
"Time you enjoy wasting, was not wasted."
],
"Steve Jobs": [
"Design is not just what it looks like and feels like. Design is how it works.",
"Stay hungry, stay foolish."
]
}
}
I want to randomly pick an Author, then choose a random quote from that author. I only have this code so far, which just returns all quotes from the user I manually specified.
func loadQuoteFromDatabase() {
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("authors/John Lennon/").observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot.value ?? "")
}) { (error) in
print (error.localizedDescription)
}
}
The return looks like this:
(
"Life is what happens when you're busy making other plans.",
"Time you enjoy wasting, was not wasted."
)
I'm not sure how to get an item out of the parentheses.
I'm an amateur at this sort of coding, so I haven't found this easy to solve. Any ideas how I can grab a random author, then a random quote of theirs?
Thanks.
I have the next JSON structure in Firebase:
{
"-KGX6kYg1NO6d9hn-8um" : {
"phase" : "A",
"timestamp" : "12-18-2015 19:43:37"
},
"-KGXOGSxa3vompZX9UO_" : {
"phase" : "B",
"timestamp" : "03-28-2016 15:28:21"
},
"-KMUvszD-vm3Nu02sofd" : {
"phase" : "A",
"timestamp" : "04-03-2014 03:57:56"
}
}
Is it possible to filter the objects by the timestamp key through a range of date?.. For example, I want to get the objects with timestamp from January 2015 to today date. If not possible, what's the better way to filter the objects by dates?... I'm developing an iOS app.
Thanks.
You can sort the snapshot data by timestamp and define a timestamp limit from which you want your data.
For example you want all data from a specific timestamp timestamp1, your reference handler should look like:
let refHandle = tableRef.queryOrderedByChild("timestamp").queryEndingAtValue("timestamp1").observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
for item in snapshot.children {
}
})
You can also apply number of records that you want by adding queryLimitedToLast or queryLimitedToFirst like :
let refHandle = tableRef.queryOrderedByChild("timestamp").queryEndingAtValue("some_time_stamp").queryLimitedToLast(kPostLimit + 1).observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
for item in snapshot.children {
}
})
Also, you want to have a look at the following post about common sql queries in Firebase.