Firebase database structure and query format options - ios

Basically, my app has posts based on location that can be upvoted/downvoted.
I need to construct a query that returns the top 15 upvoted posts that lie in a specified region, and were made in the last 24 hours. Coming from an sql background, I am finding this unnecessarily difficult to do with the JSON format and firebase's built in query by order and filtering options. I realize i will most likely have to a lot of client side filtering, so I am asking what the JSON structure and query format would be to minimize client side filtering. So far this is what my JSON format looks like:
"posts" : {
"-KPFIsDbf3WUljWvBwi-" : {
"latitude" : 33.64114800203589,
"longitude" : -116.4236003651668,
"time" : 1.47129994542173E12,
"uid" : "wjXpBBJMBVPvRVG48fFkerAw6TD3",
"upvotes" : 0
},
I tried using Geofire, but unfortunately that seems to only have the ability to query by region, and since I want to query by region and time posted (among other things), using Geofire will not scale well.

Here's what I would do in a similar setup, using a combination of GeoFire and Firebase queries:
Step 1 (saving): When saving a post, save it to the database first, using a random ID. Then, grab that ID, create a GeoFire location, and set your post ID as the location key.
Step 2 (querying): This will be a 2 step query
Step 2a: Get all GeoFire locations within specified region. Now, you can grab the keys of all locations from the snapshots and and attach a single data event handler on each.
Step 2b: Maintain a sorted array of post objects by number of upvotes. In the snapshot for single event listener, first check the timestamp to see if it is within the last 24 hours. Next, if the array has a count of less than 15, add your post in sorted position (using an appropriate parsing method). If it already has 15, you'll need to see if the minimum lowest element in the array has more upvotes. If it does not, you'll need to find a place in the array to replace. After you are done parsing through every snapshot from Geofire, you will have your array of posts from the last 24 hours, sorted by upvotes.

Related

Firestore query passing in an array of String - Firestore - Swift

I have an array of UIDs (String) and I want to perform a query using Firestore where I get all the documents in which the field "uid" is equal to any of the ones in the array provided.
Till now I tried doing:
ref.whereField("uid", in: uidArray)
But this doesn't work and gives me an error because the maximum number of elements in the array must be 10 and it surely surpasses that limit.
My question is: how can I achieve the same result with a workaround using Firestore?
Whenever I try to implement a query using this DB it seems it's built for making developers' lives harder.
Yes, according to the docs, the maximum number of elements in the array is 10. To be able to get over this limitation, you should perform successive calls. For example, if you need to check a field against 35 values, then you should perform 4 queries. Three queries with the 10 elements and the fourth with 5 elements. Lastly, you should combine the result on the client.

Ordering Firestore GeoHash query from closest to furthest?

Currently, I'm using parts of the GeoFirebase library along with Firestore to allow for geoquerying. When I set the geohash of a post, I do it as such if let geoHash = GFGeoHash(location: location.coordinate).geoHashValue {
However, to make the geohash querying less specific, I'm planning on truncating part of the geohash when I query; currently, the query looks similar to this
var geoQuerySpecific = GFGeoHashQuery()
let geoQueryHash = GFGeoHashQuery.queries(forLocation: (lastLocation?.coordinate)!, radius: (30)) as! Set<GFGeoHashQuery>
for query in geoQueryHash {
geoQuerySpecific = query
print("the key is this um \(geoQuerySpecific)")
}
print("the starting value is \(geoQuerySpecific.startValue) and the end value is \(geoQuerySpecific.endValue)")
let nearQuery = Firestore.firestore().collection("stuff").order(by: "g").whereField("g", isGreaterThanOrEqualTo: geoQuerySpecific.startValue).whereField("g", isLessThanOrEqualTo: geoQuerySpecific.endValue)
As you can see, this won't work correctly as there are multiple items in the geoQueryHash. I've thought about truncating the last four digits/letters from the geohash when I am setting it in firebase, however, that won't be specific enough. To get the closest posts, would it be best to set the geoHashes in the database as I currently am, then, when retrieving the stuff, make the start value the most specific geohash for the query and then make the end value the truncated version of the geohash, as to start by getting the closest posts and end with the broadest?
I can limit the Firestore query to 50, so then I can retrieve the 50 posts from closest to furthest... is my understand of geo hashing correct? Would this be feasible?
Conceputally, if there were a way to store geohashes as integers than I could make the firestore query start at the largest integer (i.e. most precise Geohash) and then work order the query by descending until it gets to the least precise intenser (broadest geoHash) and then limit it to 50.
GeoFirestore uses geohashes to be able to select documents that are within a range of geohashes, which is roughly the same as an area on the map. There is no way to retrieve those documents within a specified order from within that area with just a geoquery. If you want to have the documents sorted by distance, you will have to do that after the geoquery, in your application code.
If you want to learn more on why that is, have a look at a talk I gave a while ago: Querying Firebase and Firestore based on geographic location or distance. In it I explain how geohashes work, how they allow you to select documents in a certain geographic range, and why you can't do the more complex query on Firestore (or Firebase's original realtime database).

Firebase + Geofire complex queries based on properties

I'm producing a project with Firebase Realtime Database.
I have to post pictures with timestamp, latitude and longitude, and search for pictures in a radios from my position, ordered by timestamp and with the value of the timestamp 1 days from now.
Is Firebase Realtime Database + Geofire right for me?
I cannot figure out how to make queries.
Thank you!
The Realtime Database only supports one dimensional queries. So, if you did want to use
the RTDB, you'd need a two-stage query.
geoquery all the pictures in your given center/radius.
for each of these pictures, query all the data for that picture.
Once the data arrives for all the pictures, filter/sort client side.
RTDB structure would be something like:
pictures/{pic_id}: {
timestamp:,
name:
[,...]
},
geofire_pictures/{pic_id}: <geofire data>
With that said, I think RTDB would be a fine choice if you expect your
result set sizes to be managable. If you need to scale, I would suggest waiting for Firestore's geoquery functionality.

Query limited data in firebase

I am working on an application that retrieves the posts from firebase. The problem is I want to retrieve the first 20 posts and show in a table. When a user clicks the next button, it will retrieve the next 20 posts and show and so on.
I know firebase provides the following methods
queryLimitedToFirst
queryLimitedToLast
queryStartingAtValue
queryEndingAtValue
queryEqualToValue
But how do I use them in conjunction to retrieve the desired results like I want?
Here is the structure of the data I have:
You should order query by some property (I usually use timestamp). This organises your orders from latest to oldest. When you have them ordered you should limit them:
queryOrdered(byChild: "timestamp").queryLimited(toLast: UInt(limit))
As you can see we can limit results to whatever number we like. You can use this to read your posts each time for 20 results more.
So the first time you call it with a limit of 20, next time with a limit of 40, etc. Keep in mind that this will return posts that were already returned in the previous call.
Instead you could combine it with queryStartingAtValue and use your last post tiemstamp - this will "skip" your previous results and return only 20 posts you need.
This is just an idea but I think it should work like expected.

query firebase database by ordering two values in iOS swift

I have a database of posts in firebase, and each has a timestamp and a rating.
i know how to order by time OR by rating, but would it be possible to order by rating, then limit by time. ie. show highest rated posts over the last week?
ie.
Ref.queryOrderedByChild("rating").observeSingleEventOfType(.Value, withBlock: {...}
would order by rating, but how would i then limit the query by time, and not rating?
Unfortunately, Firebase does not support cross referencing nor server-side logic, so you'll probably have to query by the more specific value, pull the data down, then sort the rest client side.
Alternatively, you could also try to get creative with GeoFire (firebase's cross referencing solution for latitude/longitude location querying), though that'll the hackiest solution ever...

Resources