Retrieve records in mongoDB using bidirectional query - mongodb

I have two collections - Tickets and Users. Where a user can have one to many tickets. The ticket collection is defined as follows
Ticket = {_id, ownerId, profile: {name}}
The ownerId is used to find all tickets that belong to a specific person. I need to write a query that gets me all users with no tickets.
How can i write this query without having to loop through all users, checking if the userID shows up in any Tickets?
Would a bidirectional storage cause me any performance problems ? For example, if i were to change my users collection and add an array of tickets: [ticketID, ticketID2, ...]?

I'd go with the array of tickets being stored in users. As far as I know, Mongo doesn't really have a way to query one collection based on the (lack of) elements in another collection. With the array, though, you can simply do db.users.find({tickets:[]}).

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.

Query documents in one collection that aren't referenced in another collection with Firestore

I have a firestore DB where I'm storing polls in one collection and responses to polls in another collection. I want to get a document from the poll collection that isn't referenced in the responses collection for a particular user.
The naive approach would be to get all of the poll documents and all of the responses filtered by user ID then filter the polls on the client side. The problem is that there may be quite a few polls and responses so those queries would have to pull down a lot of data.
So my question is, is there a way to structure my data so that I can query for polls that haven't been completed by a user without having to pull down the collections in their entirety? Or more generally, is there some pattern to use when you need to query for documents in one collection that aren't referenced by another?
The documents in each of the collections look something like this:
Polls:
{
question: string;
answers: Answer[];
}
Responses:
{
userId: string;
pollId: string;
answerId: string;
}
Anyhelp would be much appreciated!
Queries in Firestore can only return documents from one collection (or from all collections with the same name) and can only contain conditions on the data that they actually return.
Since there's no way to filter based on a condition in some other documents, you'll need to include the information that you want to filter on in the polls documents.
For example, you could include a completionCount field in each poll document, that you initially set to 0, and then update only every poll completion. With that in place, the query becomes a simple query on the completionCount field of the polls collection.
For a specific user I'd actually add all polls to their profile document, and remove them from there. Duplicating data is usually the easiest (and sometimes only) way to implement use-cases such as this.
If you're worried about having to add each new poll to each new user profile when it is created, you can also query all polls on their creation timestamp when you next load a user profile and perform that sync at that moment.
load user profile,
check when they were last active,
query for new polls,
add them to user profile.

Flutter & Firebase: Is it possible to get specific field of for each document? (Not whole field then filter it) [duplicate]

I'm currently working on an application where users can create groups and invite others in it.
I would like people in the same group to be able to see their first and last names.
To do that, I have a collection named Users where each of the users have a document contains all their personnal data, like first and last names, phone, position , ...
I have also another collection named Groups, where all of my groups are stored, with their name, and an array contaning the ID of the members.
When an user open the app, a first request is done for request his groups (he recieve the groups names and the arrays of members). After, if he want to know the user in a certain group, another request is done for search only the first and last name of all the members.
So, I imagine that there is a query that will return me only the fields that I would like to retrieve, and that there is a rule allowing a potential hacker to be refused access to the entire user document except if the user is the owner of the document.
// For retrieving my user's groups
Stream<List<Group>?> get organizations {
return firestore
.collection('Groups')
.where('members', arrayContains: this.uid)
.snapshots()
.map(_groupsFromSnapshot);
}
// For retrieving names of the members of a group
Stream<List<Member>?> getMembers(Group group){
return firestore
.collection('Users')
// and i dont know what to do here ...
}
With the Client SDKs and the Flutter plugin it is not possible to get only a subset of the fields of a Document. When you fetch a Document you get it with all its fields.
If you want to get only a subset of the fields of a document, you can implements the two following approaches:
Denormalize your data: You create another collection which contains documents that only contain the fields you want to expose. You need to synchronize the two collections (the Users collection, which is the "master", and the new collection): for that it's quite common to use a Cloud Function. Note also that it's a good idea to use the same documentID for the linked documents in the two collections.
Use the Firestore REST API to fetch the data: With the REST API you can use a DocumentMask when you fetch one document with the get method or a Projection when you query a Collection. The DocumentMask or the Projection will "restrict a get operation on a document to a subset of its fields". You can use the http package for calling the API from your Flutter app.
HOWEVER, the second approach is not valid if you want to protect the other users data: a malicious user could call the Firestore REST API with the same request but without a DocumentMask or a Projection. In other words, this approach is interesting if you just want to minimize the network traffic, not if you want to keep secret certain fields of a document.
So, for your specific use case, you need to go for the first solution.

MongoDB - many-to-many/few-to-few relationship schema design, without lookup performance issues

We are developing an app that basically works similarly to Instagram, but with groups. Users can invite other users to groups. The members in a group share posts containing photos, media etc. A user has a feed with all the posts that have been posted recently in the groups, with paging. The users can also access posts from within the groups.
The feed query is slow due to the nature of our current schema design. In practice, request time will increase as the amount of data increases, which is not very scalable now when we see more users on the platform.
Question 1 How should we model our data, in the best possible way, using MongoDB to make our feed query faster and more scaleable?
Our current backend is built using Parse Server with MongoDB and Node.js
A simplified version of our schema is as follows:
Class/Document
- Attribute
Users
- id
Group
- id
- members (array of user pointer objects)- basically an array of ids
- admins (array of user pointer objects) - basically an array of ids
- createdBy (pointer to user object) - basically an id of the owner of the group
Posts
- id
- groups (array of group pointer objects) - basically an array of ids
So right now, when we want to get the posts in a user's feed we have to go through the following process:
Go through all groups, checking the group's members/admins arrays for the user, and get all groups for the user
Go through all posts, checking the post’s groups array to see if post should be in the feed.
Possible solution to Question 1 (MongoDB - Many-to-many relationship?)
This would mean:
Have a group array (pointers to group objects) in the User class, that points to groups where the user is admin/member or owner. Have, like before, the member/admin arrays and createdBy in the Group class.
Have a post array (pointers to post objects) in the Group class. Have, like before, the groups array in the Post class.
This would translate into the following schema:
Class/Document
- Attribute
Users
- id
- groups (array of group pointer objects, where the user is either admin/member or owner) - basically an array of ids
Group
- id
- members (array of user pointer objects)- basically an array of ids
- admins (array of user pointer objects) - basically an array of ids
- createdBy (pointerObject to user) - basically an id of the owner of the group
- posts (array of post pointer objects) - basically an array of ids
Posts
- id
- groups (array of group pointer objects) - basically an array of ids
Meaning, you would have two-way query ability. Downside is that you have to be careful to keep these arrays consistent with one another.
Question 2 Is it sensible to use MongoDB in the long run for our type of application or should we aim to migrate to, for example,
PostgreSQL? (I’m a bit biased as I’ve been working more with RDMS-
than NoSQL databases)
We will also continue extend our statistics interface for showing different activities and their stats in the groups and we are predicting that we will have more many-to-many situations, or few-to-few relationships. We have started to experiment, adding PostgreSQL to the mix. Mirroring the data in MongoDB with PostgreSQL and use SQL for some of the above queries that didn’t scale well with our current schema design in MongoDB. We are also thinking of gradually moving over to PostgreSQL. However, this might be a premature decision, if we can make everything work well with MongoDB. As we will continue to extend our statistics interface, is it better to move over to using a RDMS like PostgreSQL?
All help is appreciated.
Have a great day!

"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,