How to model a "checkin" style object in MongoDB? - mongodb

I am making a new application in MongoDB, and I have found that the Document-oriented modeling style fits all of my models quite well.
However, my one stumbling block is a "CheckIn" style action. Users can check in at a location, and I need to store the following for each check in:
User ID
Place ID
Date of checkin
Now normally I'd just store this under the User document as an embed, but I frequently will want to ask the following questions:
Where are all places a user has checked in?
What are all checkins that have happened at a certain place?
All checkins for a given user-place combo?
All checkins for a user or place in a specific time frame?
In a relational database this screams has-many through, but in Mongo that's not such an obvious relation. Should I just make Checkin a top-level object and take the performance hit of the join-style query? I might also need to add fields to the checkin object over time, so I want to keep the solution flexible.

Yes. If you embed checkins as an array within the user document, then the query "10 most recent checkins for a place" will be nearly impossible. Same if you embed in place, "10 most recent checkins for user" will be very hard. So make checkins its own collection.
If you index both userid and placeid in the checkins collection your queries should be fast enough. For example, to find user Jesse's most recent checkins, look up users by name to find Jesse's _id, and query checkins for that userid. It's just two queries. Same for a place's most recent checkins.
If you query the most recent checkins for a place and want the users' names, you can first query the checkins collection to get the list of userids, and use an $in query to get all the user documents. Again, it's just two queries, and both are fully indexed if you create the proper indexes.

Related

How to organize FireStore Collections and Documents based on app similar to BlaBlaCar rides

It's my first time working with FireStore. I'm working on a ridesharing app with Flutter that uses Firebase Auth where users can create trips and offer rides similarly to BlaBlaCar, where other users can send requests to join a ride. I’m having difficulty not only deciding the potential collections and paths to use, but also how to even structure it.
For simplicity at this stage, I want any user to be able to see all trips created, but when they go to their “My Rides” page, they will only see the rides that they’ve participated in. I would be grateful for any kind of feedback.
Here are the options I’ve considered:
Two collections, “Users” and “Trips”. The path would look something like this:
users/uid and trips/tripsId with a created_by field
One collection of “Users” and a sub-collection of “Trips". The path seems to make more sense to me, which would be users/uid/trips/tripId but then I don't know how other users could access all the rides on their home feed.
I'm inclined to go with the first option of two collections. Also very open to any other suggestions or help. Thanks.
I want any user to be able to see all trips created, but when they go
to their “My Rides” page, they will only see the rides that they’ve
participated in
I make the assumption that participating in a ride is either being the author or being a passenger of the ride.
I would go for 2 collections: one for users and one for trips. In a trip document you add two fields:
createdBy with the uid of the creator
participants: an Array where you store the author's uid and all the other participants uids (passengers)
This way you can easily query for:
All the rides
All the rides created by a user
All the rides for which a user is a participant, using arrayContains.
(Regarding the limit of 1 MiB for the maximum size for a document I guess this is not a problem because the number of passengers of a ride shouldn't be so huge that the Array fields size makes the document larger than 1 Mib!)
Note that the second approach with subcollections could also be used since you can query with collections group queries but, based on the elements in your question, I don't see any technical advantage.

Firestore - How to perform "NOT IN" like you would in SQL

I have a collection of "quizes" that users will participate in. When a user takes a quiz I create a document in "results" collection for with that userId and quizId. I want my app to pull all docs from "quizes" collection excluding the ones that the user taken. In SQL I would do "NOT IN" clause and accomplish that, but I have no idea how to best approach this in Firestore.
There's no equivalent query in Firestore. You would need to pull all the data and determine which docs are relevant on clientside.
Alternatively, you can create a list of all quizzes for each user and maintain this list. You could add and remove quizzes for each user as they become relevant/irrelevant to show them.

"Join" multiple Algolia indices?

Is it possible to "join" indices in Algolia to get a merged result?
For example:
If I have two indices : one for 'users', and one for 'events'. Users each have id and name attributes. Events each have date and userId attributes.
How would I go about searching for all users named "bob", and for each user also return the next 5 events associated with them?
Is it possible to "join" them like you would in a relational database? Or do I need to search for users, then iterate through the hits, searching for events for each user? What's the best solution for this type of query here?
Algolia is not designed as a relational database. To get to what you're trying to achieve, you have to transform all your records into "flat" objects (meaning, each object also includes all their linked dependencies).
In your case, what I would do is to add a new key to your user records, named events and have it be an array of events (just like you save them in the events table). This way, you got all the information needed in one call.
Hope that helps,

How to balance quickness and redundancy in MongoDB data structures?

I am creating a MongoDB database with a users collection (with UserFiles in it) and a posts collection. Each post has tags and sharedFrom fields in it. I eventually plan to have users' search results influenced by what tags they normally post about and from which other users they often share posts. Would it be better to:
make a field in the UserFile document of each user that lists the post IDs made by the user?
make a field in the UserFile that documents that lists all the tags they have used and other users that they have sharedFrom?
make the search function look up the searchers activity that then influences the search results?
something I haven't thought of?

MongoDB storing user-specific data on shared collection objects

I'm designing an application that processes RSS feeds using MongoDB. Currently my collections are as follows:
Entry
fields: content, feed_id, title, publish_date, url
Feed
fields: description, title, url
User
fields: email_address
subscriptions (embedded collection; fields: feed_id, tags)
A user can subscribe to feeds which are linked from the embedded subscription collection. From the subscriptions I can get a list of all the feeds a user should see and also the corresponding entries.
How should I store entry status information (isRead, isStarred, etc.) that is specific to a user? When a user views an entry I need to record isRead = 1. Two common queries I need to be able to perform are:
Find all entries for a specific feed where isRead = 0 or no status exists currently
For a specific user, mark all entries prior to a publish date with isRead = 1 (this could be hundreds or even thousands of records so it must be efficient)
Hmm, this is a tricky one!
It makes sense to me to store a record for entries that are unread, and delete them when they're read. I'm basing this on the assumption that there will be more read posts than unread for each individual user, so you might as well not have documents for all of those already-read entries sitting around in your DB forever. It also makes it easier to not have to worry about the 16MB document size limit if you're not having to drag around years of history with you everywhere.
For starred entries, I would simply add an array of Entry ObjectIds to User. No need to make these subscription-specific; it'll be much easier to pull a list of items a User has starred that way.
For unread entries, it's a little more complex. I'd still add it as an array, but to satisfy your requirement of being able to quickly mark as-read entries before a specific date, I would denormalize and save the publish-date alongside the Entry ObjectId, in a new 'UnreadEntry' document.
User
fields: email_address, starred_entries[]
subscriptions (embedded collection; fields: feed_id, tags, unread_entries[])
UnreadEntry
fields: id is Entry ObjectId, publish_date
You need to be conscious of the document limit, but 16MB is one hell of a lot of unread entries/feeds, so be realistic about whether that's a limit you really need to worry about. (If it is, it should be fairly straightforward to break out User.subscriptions to its own document.)
Both of your queries now become fairly easy to write:
All entries for a specific feed that are unread:
user.subscriptions.find(feedID).unread_entries
Mark all entries prior to a publish date read:
user.subscriptions.find(feedID).unread_entries.where(publish_date.lte => my_date).delete_all
And, of course, if you simply need to mark all entries in a feed as read, that's very easy:
user.subscriptions.find(feedID).unread_entries.delete_all