Firestore collections intersection? - firebase

I am implementing the typical followers/following/friends + feed system. For each user in my database, I have 3 subcollections: "followers", "following" and "friends". Currently, I am trying to implement a feed following these three business rules:
Feeds are order by date.
User A can be in the friends list of User B while user B is not in User A friends list.
You can see the posts of those users you follow who also follow you or/and have you scheduled on their friends list.
In my case, what I have thought to do is to perform multiple reads in a Cloud Function, because it seems not really easy to achieve the third rule... but now I wonder if it would be possible to make a query of this style (similar):
currentUserFollowingRef = db.collection(...).doc(userId).collection("following")
otherUserFollowingRef = db.collection(...).doc(otherUserId).collection("following")
otherUserFriendsRef = db.collection(...).doc(otherUserId).collection("friends")
// Some kind of intersection using queries (Pseudo)
U1 = currentUserFollowingRef.where(firebase.firestore.FieldPath.documentId(), "in", otherUserFollowingRef)
.limit(10).get();
// Other intersection
U2 = currentUserFollowingRef.where(firebase.firestore.FieldPath.documentId(), "in", otherUserFriendsRef)
.limit(10).get();
// Union of two sets using pure js
UFinal = U1 union U2 (using js)
/* Last step is to retrieve the last post of each user ordered by date using collection group queries */
Someone knows how to achieve this doing something similar (retrieving docs which
also are in other collection)?
Any consideration that should be taken into account?
Thank you.

What you're trying to do isn't supported by Firestore. A query can only consider documents in a single collection at a time. It's not possible to "join" with documents in other collections. You will have to do exactly what you said in the question - "perform multiple reads in a Cloud Function" and merge the results of those queries in your code.
Either that, or duplicate data into a new collection that effectively "pre-joins" the data from other collections for a single query. But that might be a lot of work for this case.

Related

How to get document with certain fields from firebase to flutter?

I need a flutter query for getting data from firebase;
The query should go to User collection and search every document with UserType = 1 field. There is a "classes" collection in each document and each of "classes" have other documents such as "math", "music" etc. I need to get the document with the field classCode == myInput
QuerySnapshot query1 = await _fireStore.collection("Users").where(["userType"], isEqualTo: 1).get();
I don't know how to move from here.
You have two main options for implement this.
The first is pretty close to how you describe in your question:
Find the user docs for your condition,
For each of those search the classes subcollection for the classCode.
The second approach makes use of a collection group query to:
Find all classes docs (across all the subcollections) with the searched user type.
Possibly then look up the users for those.
The best approach depends on mostly on what results you need. If you results are user documents, the first approach is usually pretty easy. If the results are classes then I'd definitely try the second approach first.

how to query nested maps inside an array field in firebase

I have structured my data in Firebase such that I have a collection of 'users' and each user has various fields as well as a 'skillsIndustry' Array field which contain Maps items (that have fields 'experience' and 'industry').
Now I would like to query my collection of users so that I can ask for all users that have worked in 'Airlines' with experience code of >= 1
Is this possible at all? Or do I have to create sub collections and then use Collection Group Queries?
Many thanks
No, this is not possible. You can use array-contains to query for the entire object, as explained in this SO answer, but not to query with > or <.
You could create a subcollection, but you can probably find a solution based on the duplication of the data in the same document, which is a common approach in the NoSQL world.
For example, you could have some extra fields named airlinesExperience, shippingExperience, etc. Then you use the skillsIndustry Array field when you want to display all the skills and years of experience, but you use the xxxExperience fields when you want to query.
Another possibility would be to use a map of key/value pairs like
key = "Airlines" / value = 1
key = "Shipping" / value = 3
Then you can query with:
firebase
.firestore()
.collection('users')
.where('skillsIndustry.Shipping', '>', 2)

Firebase database check if element exists in a ListField in Flutter

I have a real-time database on firebase which consists of ListFields. Among these fields, one field, participants is a list of strings and two usernames. I want to make a query to firebase database such that it will return the documents in which a particular username is present in the participants list.
The structure of my document is as follows :
I want to make a query such that Firebase returns all the documents in which the participants list consists aniruddh. I am using Flutter with the flutterfire plugins.
Your current data structure makes it easy to find the participants for a conversation. It does however not make it easy to find the conversations for a user.
One alternative data structure that makes this easier is to store the participants in this format:
imgUrls: {},
participants: {
"aniruddh": true,
"trubluvin": true
}
Now you can technically query for the the conversations of a user with something like:
db.child("conversations").orderByChild("participants/aniruddh").equalTo(true)
But this won't scale very well, as you'll need to define an index for each user.
The proper solution is to add a second data structure, known as an inverted index, that allows the look up of conversations for a user. In your case that could look like this:
userConversations: {
"aniruddh": {
"-LxzV5LzP9TH7L6BvV7": true
},
"trubluvin": {
"-LxzV5LzP9TH7L6BvV7": true
}
}
Now you can look up the conversations that a user is part of with a simple read operation. You could expand this data structure to contain more information on each conversation, such as the information you want to display in your list view.
Also see my answer heres:
Firebase query if child of child contains a value (for more explanation on why the queries won't work in your current structure, and why they won't scale in the first structure in my answer).
Best way to manage Chat channels in Firebase (for an alternative way of naming the chat rooms).

Firebase get data compare two fields

I'm trying to get all the documents that have my phone number as the fromNumber or the toNumber. My call right now is:
database.collection('documents').where('fromNumber','==',myPhoneNumber).get().then();
Instead of making 2 calls, one to check the fromNumber and the second one to check the toNumber, how can I check both at the same time and in the same .get()?
Btw: tested this code:
database.collection('documents')
.where('fromNumber','==',myPhoneNumber)
.where('toNumber','==',myPhoneNumber).get()
.then();
But it checks if both are true, it's an AND instead of the OR I'm looking for.
According to the official documentation, there a are some query limitations, when it comes to Firestore:
Cloud Firestore does not support the following types of queries:
Logical OR queries. In this case, you should create a separate query for each OR condition and merge the query results in your app.
As suggested in the documentation, you should create two separate queries and merge the result cliend side.

Firebase - Structuring Data For Efficient Indexing

I've read almost everywhere about structuring one's Firebase Database for efficient querying, but I am still a little confused between two alternatives that I have.
For example, let's say I want to get all of a user's "maxBenchPressSessions" from the past 7 days or so.
I'm stuck between picking between these two structures:
In the first array, I use the user's id as an attribute to index on whether true or false. In the second, I use userId as the attribute NAME whose value would be the user's id.
Is one faster than the other, or would they be indexed a relatively same manner? I kind of new to database design, so I want to make sure that I'm following correct practices.
PROGRESS
I have come up with a solution that will both flatten my database AND allow me to add a ListenerForSingleValueEvent using orderBy ONLY once, but only when I want to check if a user has a session saved for a specific day.
I can have each maxBenchPressSession object have a key in the format of userId_dateString. However, if I want to get all the user's sessions from the last 7 days, I don't know how to do it in one query.
Any ideas?
I recommend to watch the video. It is told about the structuring of the data very well.
References to the playlist on the firebase 3
Firebase 3.0: Data Modelling
Firebase 3.0: Node Client
As I understand the principle firebase to use it effectively. Should be as small as possible to query the data and it does not matter how many requests.
But you will approach such a request. We'll have to add another field to the database "negativeDate".
This field allows you to get the last seven entries. Here's a video -
https://www.youtube.com/watch?v=nMR_JPfL4qg&feature=youtu.be&t=4m36s
.limitToLast(7) - 7 entries
.orderByChild('negativeDate') - sort by date
Example of a request:
const ref = firebase.database().ref('maxBenchPressSession');
ref.orderByChild('negativeDate').limitToLast(7).on('value', function(snap){ })
Then add the user, and it puts all of its sessions.
const ref = firebase.database().ref('maxBenchPressSession/' + userId);
ref.orderByChild('negativeDate').limitToLast(7).on('value', function(snap){ })

Resources