One to many query with Firebase and Swift 3 - swift

I have an app, that has Topics and categories, one category may belong to one or more topics, i'm trying to filter the data to only show category that has a certain topic id marked as true, this is the structure:
Here is the code i come up with ( which was working on Swift 2.3 ):
self.ref = FIRDatabase.database().reference(fromURL: FIREBASE_URL).child("categories")
let query = ref?.queryOrdered(byChild: "topics/idt2").queryEqual(toValue: true)
query!.observe(.value, with: { (snapshot) in
//This should bring back both categories, Soccer and Moon
print("Inside query \(snapshot.value)") // Prints null
})
Any ideas?

When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
You will need to handle this list in your callback block:
query!.observe(.value, with: { (snapshot) in
for child in snapshot.children {
print(child.key)
}
})
Also see:
Firebase remove snapshot children swift
Grabbing information out of a query with Firebase and Swift
Retrieving Data using Firebase Swift
Firebase complex query clauses
Firebase snapshot.key not returning actual key?

Related

Firebase BarcodeScanner get parent/other nodes of a specific child

In my app I have certain items in my firebase database like this:
Categories
Category1
Item1
id: item1
name: Item 1 name
barcode: 473088034839
item2
id: item2
name: Item 2 name
barcode: 564084724885
These items are in a collectionview. I have another view where I'm using a barcodeScanner to scan the barcode of the products in my database. The barcode scanner works great and I'm able to print the barcode of scanned products to the console, and it matches the codes in "barcode" in my database.
The problem is I'm trying to get the name if the item I'm scanning. With the following code I'm able to find/match the barcode I'm scanning with the code in my database:
let someBarcode = Database.database().reference().child("Categories").queryOrdered(byChild: "barcode").queryEqual(toValue: code)
print(someBarcode)
code is the string of numbers I get from scanning a barcode. After finding the correct barcode in my database, how can I then retrieve the current Items id and/or name?
Firebase Realtime Database queries work on a flat list. They can order/filter on a property at a fixed path under each direct child under the location you query on.
So in your scenario you could query across all categories on a fixed property of each category, or you can query a specific category for items with a specific property value. But you can't search across all categories for the ones that contain an item with a certain property value.
The solution is to create an data structure that allows the specific query, typically either by flattening the list you already have, or by adding an additional structure to allow the lookup.
For more on this, see:
Firebase Query Double Nested
Firebase query if child of child contains a value
With Franks help I managed to create a new node in my firebase DB with the barcode codes:
Barcodes
item1 : code1
item2 : code2
item3 : code3
Then I used the following function to
func scanner(_ controller: BarcodeScannerViewController, didCaptureCode code: String, type: String) {
// get product id from Barcodes
Database.database().reference().child("barcodes")
.observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else {
self.showNotFoundMessage()
return
}
for (key, value) in dict {
let valueString = "\(value)"
if valueString == code {
self.getProduct(id: key)
return
}
}
self.showNotFoundMessage()
}
}

Retriving data from database

First time asking a question here, so sorry if I do it wrong.
Anyways. I'm using Firebase Database to store "Results" in my Quiz app. When data is store it looks like this
Results
-LjQ34gs7QoL1GMufiMsaddclose
Score: xx
UserName: xx
-LjQ3NeCoDGob8wnhstH
Score: xx
UserName: xx
I would like to access score and username from it and display it in a HighScore tableview. Problem is - I can get the "Results" node, but because of the id of the results (ie LjQ34gs7QoL1GMufiMsaddclose) I don't know how to access the score and username.
I got the data snapshot​, but not sure how to "bypass" the id to get to score and username.
Hope I made it at least a bit clear, what the problem is.
let ref = Database.database().reference().child("Results")
ref.observe(.value) { (DataSnapshot) in
print(DataSnapshot.value as Any)
}
You current code gets you a single snapshot with the results of all users. You'll need to loop over the child snapshots to get the result of each user, and then look up their specific properties with childSnapshot(byName:):
let ref = Database.database().reference().child("Results")
ref.observe(.value) { (snapshot) in
for case let userSnapshot as DataSnapshot in snapshot.children {
print(userSnapshot.childSnapshot(forPath: "Score").value)
}
}
Also see:
How to get all child data from firebase without knowing the path
Iterate through nested snapshot children in Firebase using Swift
How do I loop through and get all the keys of the nested nodes in firebase?
Retrieving Data using Firebase Swift
And probably some more from this list.

Query and filter by child when child is a list

I'm having some trouble figuring out how to filter and query when my data is a list. In my current database model, I have conversations and then under each UID are the participants of that conversation. I want to be able to look up all conversations a specific user is a part of.
Im not sure how I would write my code when there is no real "child"
let query = Constants.refs.databaseConvo.queryOrdered(byChild: "").queryEqual(toValue: username)
query.observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children {
print(childSnapshot)
}
})
Due to the limitations of Firebase queries, you will need to create a new collection in your database that keeps track of all of the conversations that a user is a part of. It should be structured something like this:
usersConversations {
userID1 {
conversationID1: timestamp (or what ever value you would like, I would recommend a timestamp they joined so you can query the latest conversations, etc.)
conversationID2: timestamp
conversationID3: timestamp
userID2 {
conversationID1: timestamp
conversationID2: timestamp
} ... and so on
}
You will need to add and delete to this collection whenever a user joins or leaves a conversation as well as your existing conversations collection.
You can then get all of the conversations a user with the uid userID is a part of by doing something like this:
databaseRef.child("usersConversations").child(userID).observeSingleEvent(of: .value) (snapshot) in {
if snapshot.exists() {
// each snapshot child's key will be a conversationID that they are a part of
} else {
// the user is part of no conversations
}
}

How to know which index a child was added to an ordered FirebaseQuery

Using Firebase and Swift SDK
I just started with Firebase and wanted to display a list of Conversations ordered by last_update. The following query works fine :
let query = Database.database().reference().child("chat").child("channels").queryOrdered(byChild: "last_update")
query.observe(DataEventType.childAdded) { (snapshot: DataSnapshot) in
if let value = snapshot.value as? [String: Any?] {
log.debug("added channel: \(snapshot.key) : \(value)")
//add object to array, insertRow in tableview
}
}
The first time my view is loaded, each item arrives in the correct order specified by the query, so the display is ok. But if I create a new channel, it does appear at the end of the tableview, because I just add it at the end of the array and just call insertRow on my table view. My question is : is there any mecanism that give us the new inserted position of the DataSnapshot ?
Same question for DataEventType.childMoved : we get to know that a snapshot has moved, but how to know where it has moved ??
I finally end up using FirebaseUI, especially the submodule FirebaseDatabaseUI (see the github repo here)
They provide a FUITableViewDataSource, you just create a query and pass it to it, it will handle everything like sorting etc. I also used FUIArray, which is simply an array backed by a query, with a delegate for added / deleted / moved / update events (and proper indexes).

Query latest entries using Firebase in Swift

I'm new using Firebase and I can't find how to do what sounds really simple to do : list all the latest entries of my database.
Here is a screenshot of what my database looks like :
So, I'm trying to list the latest entries like that :
// picturesRef = FIRDatabase.database().reference().child("pictures")
let _ = self.picturesRef.queryOrdered(byChild: "createdTime").queryLimited(toLast: 50).observe(.value, with: { snapshot in
// Stocking the result into picturesArr array
for elem in picturesArr {
print(elem.createdTime)
}
})
And right now, when I'm displaying the createdTime value of each item, I have something like :
1484738582.0
1484000086.0
1484738279.0
1484734358.0
1484625525.0
1484728677.0
Which doesn't seem to be ordered from the oldest entry to the newest one...
Also, when I replace "createdTime" in the query by "fieldThatDoesntExist", I have the exact same result.
Anyone would know where did I do something wrong in the code ? Thanks in advance !
The query returns the items in the correct order. But most likely (the relevant code seems to be missing from your question) you're losing that order when you convert the snapshot to a dictionary (which is unordered by definition).
To keep the items in the correct order, iterate over snapshot.children:
let picturesRef = FIRDatabase.database().reference().child("pictures")
let _ = picturesRef
.queryOrdered(byChild: "createdTime")
.queryLimited(toLast: 50)
.observe(.value, with: { snapshot in
for child in snapshot.children {
print(child.key)
print(child.child("createdTime").value
}
})
Also see:
Firebase snapshot.key not returning actual key?
Firebase access keys in queryOrderBy
Retrieving Data using Firebase Swift
Iterate over snapshot children in Swift (Firebase) (I usually try to avoid allObjects, but it's fine too)
Actualy, after sorting the array returned by the query with this :
picturesArr.sort(by: {$0.createdTime > $1.createdTime})
I've figured out that the query returns the entries that I'm looking for but not sorted.
It looks a bit wierd to me, maybe someone knows why or even better, how to get the result already sorted ?