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.
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 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!!
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.
I'm using firebase to collect data, and i'm trying to retrieve the data in a usable format for an iPhone app, and i can't quite get it out properly. I'm writing the app in Swift.
The data is grouped by a date string then the with a random key and then the data. Eg:
{
"20160304" : {
"-KC-aOwSWpt4dlYmjJE4" : {
"coordinates" : "-37.7811465912404, 145.005993055861",
"event" : "Test event",
"time" : "2016-03-04 07:48:43 +0000"
}, etc...
I'm so far grabbing the data like this:
ref.queryOrderedByKey().observeEventType(.ChildAdded, withBlock: {
snapshot in
//print(snapshot.key) // date
print(snapshot.value)
})
And it returns something like this to the console:
{
"-KD8O0gL7gDGu_hRyFzQ" = {
coordinates = "-37.7540958861003, 145.001224694195";
event = "Test event";
time = "2016-03-18 11:02:32 +0000";
}; etc...
Does anyone know how i can get down to the next level, past the random keys, to the meaningful data? I had trouble before with this for javascript, but it's confusing me using swift.
I'd like to be able to grab the detailed data (bottom level) for a defined date (top level).
Try this code
let jsonLocations = snapshot.valueInExportFormat() as! NSDictionary
let keys = jsonLocations.allKeys
for key in keys {
let json = jsonLocations[key] as! [String: AnyObject]
self.sections.append(Location(JSONObject: json))
}
I usually try to stick to methods of FDatasnapshot as long as possible, which leads to:
ref.queryOrderedByKey().observeEventType(.ChildAdded, withBlock: { snapshot in
for child in snapshot.children {
print(child.key); // -KC-aOwSWpt4dlYmjJE4
print(child.childSnapshotForPath("event").value)
}
});