Querying in Firebase by child of child - swift

I have a structure of objects in Firebase looking like this:
-KBP27k4iOTT2m873xSE
categories
Geography: true
Oceania: true
correctanswer: "Yaren (de facto)"
languages: "English"
question: "Nauru"
questiontype: "Text"
wronganswer1: "Majuro"
wronganswer2: "Mata-Utu"
wronganswer3: "Suva"
I'm trying to find objects by categories, so for instance I want all objects which has the category set to "Oceania".
I'm using Swift and I can't really seem to grasp the concept of how to query the data.
My query right now looks like this:
ref.queryEqualToValue("", childKey: "categories").queryOrderedByChild("Oceania")
Where ref is the reference to Firebase in that specific path.
However whatever I've tried I keep getting ALL data returned instead of the objects with category Oceania only.
My data is structured like this: baseurl/questions/
As you can see in the object example one question can have multiple categories added, so from what I've understood it's best to have a reference to the categories inside your objects.
I could change my structure to baseurl/questions/oceania/uniqueids/, but then I would get multiple entries covering the same data, but with different uniqueid, because the question would be present under both the categories oceania and geography.
By using the structure baseurl/questions/oceania/ and baseurl/questions/geography I could also just add unique ids under oceania and geography that points to a specific unique id inside baseurl/questions/uniqueids instead, but that would mean I'd have to keep track of a lot of references. Making a relations table so to speak.
I wonder if that's the way to go or? Should I restructure my data? The app isn't in production yet, so it's possible to restructure the data completely with no bigger consequences, other than I'd have to rewrite my code, that pushes data to Firebase.
Let me know, if all of this doesn't make sense and sorry for the wall of text :-)

Adding some additional code to Tim's answer for future reference.
Just use a deep query. The parent object key is not what is queried so it's 'ignored'. It doesn't matter whether it's a key generated by autoId or a dinosaur name - the query is on the child objects and the parent (key) is returned in snapshot.key.
Based on your Firebase structure, this will retrieve each child nodes where Oceania is true, one at a time:
let questionsRef = Firebase(url:"https://baseurl/questions")
questionsRef.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)
.observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot)
})
Edit: A question came up about loading all of the values at once (.value) instead of one at at time (.childAdded)
let questionsRef = Firebase(url:"https://baseurl/questions")
questionsRef.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)
.observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot)
})
Results in (my Firebase structure is a little different but you get the idea) uid_1 did not have Oceania = true so it was omitted from the query
results.
Snap (users) {
"uid_0" = {
categories = {
Oceania = 1;
};
email = "dude#thing.com";
"first_name" = Bill;
};
"uid_2" = {
categories = {
Oceania = 1;
};
"first_name" = Peter;
};
}

I think this should work:
ref.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)

Related

Strapi - How to GET data sorted based on relation property?

I have an Articles table and it has a relation with the Authors table. Each author has a name property. I want to make a GET request for the articles and receive the articles sorted based on their author's name.
When I use _sort=author in the url parameters, I get the articles sorted based on the author object's ID.
When I use _sort=author.name in the url parameters, I get the articles in a seemingly random order but definitely not based on author.name.
How can I get the data sorted based on author.name?
I use mongoDB as my database.
That is the default behavior of _sort. However, you can simply accomplish this by overriding the find method in api/article/services/article.js to sort by Author's name like so:
module.exports = {
async find(params, populate) {
let result = await strapi.query('article').find(params, populate);
if (params._sort == 'author')
return result.sort((a, b) => (a.author.name > b.author.name) ? 1 : -1);
return result;
},
}
Check the documentation for customizing services to get more info: https://strapi.io/documentation/v3.x/concepts/services.html#concept

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.

Realm Swift - How to remove an item at a specific index position?

I am storing a simple list of id's as GUIDs in Realm, but would like the ability to delete an object at a particular index position.
So for example, I want to remove 04b8d81b9e614f1ebb6de41cb0e64432 at index position 1, how can this be achieved? Do I need to add a primary key, or is there a way to remove the item directly using the given index position?
Results<RecipeIds> <0x7fa844451800> (
[0] RecipeIds {
id = a1e28a5eef144922880945b5fcca6399;
},
[1] RecipeIds {
id = 04b8d81b9e614f1ebb6de41cb0e64432;
},
[2] RecipeIds {
id = cd0eead0dcc6403493c4f110667c34ad;
}
)
It seems like this should be a straightforward ask, but I can't find any documentation on it. Even a pointer in the right direction would do.
Results are auto-updating and you cannot directly modify them. You need to update/add/delete objects in your Realm to effect the state of your Results instance.
So you can simply grab the element you need from your Results instance, delete it from Realm and it will be removed from the Results as well.
Assuming the Results instance shown in your question is stored in a variable called recipes, you can do something like the following:
let recipeToDelete = recipes.filter("id == %#","04b8d81b9e614f1ebb6de41cb0e64432")
try! realm.write {
realm.delete(recipeToDelete)
}

CS193P Smashtag Popularity Extra Task #3 - Get only new tweets using "IN" keyword

I'm working on Stanford CS193p's SmashTag Popularity Mentions assignment (asst. #5) and I've got everything working well. I'm working on Extra Task #3:
Loading up a lot of data by querying for an existing instance in the database, then inserting if not found over and over again, one by one (like we did in lecture), can be pretty poor performing. Enhance your application to make this more efficient by checking for the existence of a pile of things you want to be in the database in one query (then only creating the ones that don’t exist). The predicate operator IN might be of value here.
I've managed to do something like this, but not using the IN predicate operator! This is inside the perform block of my updateDatabase(with newTweets:) method: (I've called my core data entity CDTweet
if let databaseTweets = try? globalContext!.fetch(NSFetchRequest<CDTweet>(entityName: "CDTweet")) {
let databaseIDs = databaseTweets.map { $0.id! }
for twitterInfo in newTweets where !databaseIDs.contains(twitterInfo.id) {
_ = CDTweet.createCDTweet(with: twitterInfo, into: globalContext!)
}
}
As you can see, I get all the tweets in the database, get their IDs, and then only create a new tweet for internet-fetched-Twitter.Tweets whose IDs are not in the array of database tweets.
This appears to function properly (i.e., create only the new tweets), but I am very curious how the instructor imagined it would work with the IN predicate operator. Has anyone done this and can lend some insight?
Note: A number of solutions I've seen have a core data entity for the Search Term (usually called Query). I don't have this, only entities for Tweet and Mention, and I have everything working fine.
You need something like this (i assume searchIDs is an array of values you are looking for):
var searchIDs = // ... an array of IDs you are searching for
var fetchRequest = NSFetchRequest<CDTweet>(entityName: "CDTweet")
let predicate = NSPredicate(format: "id IN %#", searchIDs)
fetchRequest.predicate = predicate
databaseTweets = try? globalContext!.fetch(fetchRequest) {
// here you should only get all entries with IDs in the newTweets array
}
Details about predicates can be found here, about predicate syntax especially here

Firebase: query and delete keys in a range

If I have some data in the Firebase real-time database like this
root--.
|--stuff--.
|--1
|--2
|--3
|--4
|--5
|--6
|--7
where all those numbers are keys that contain more data, and I want to delete all the keys less than or equal to 4, how do I do that without downloading the entire "stuff" branch?
With swift I think I can query the keys in that range by
let ref = FIRDatabase.database().reference().child("stuff")
let query = ref.queryEnding(atValue: 4)
but I don't know how to retrieve the key names themselves so that I can delete them sort of like this pseudocode
for key in queryResults {
let ref = FIRDatabase.database().reference().child("stuff/\(key)")
ref.setValue(nil)
}
(In reality I'm dealing with timestamp keys and I want to delete data branches that have gotten too old.)
Not sure how to do it in swift, but you can do it with orderByKey.
.database().reference()
.child("stuff")
.orderByKey()
.startAt(STARTING_TIMESTAMP)
.endAt(ENDING_TIMESTAMP)
And then loop over the ids of the corresponding result.
This will download all information for those children though. If you don't want that you'll have to store it somewhere else to easily delete it.
Sample Swift 3 / Firebase 2.x code to get the keys from a snapshot
for child in (snapshot?.children)! {
let snap = child as! FDataSnapshot
print(snap.key)
}