Flutter Firebase How to get random document - firebase

I am trying to get some random posts from Firebase. But i am unable to get random document id.
Is there any way to retrieve data from Firebase like this :-
getRandomData() async {
QuerySnapshot snapshot = await posts
.document(random.id)
.collection('userPosts')
.getDocuments();}
i am trying to say that. Now i am able to get documentID normally not in italics.so now how can i get random documentID from Firebase.

List documentIds in a list first.
var list = ['documentId1','documentId2','documentId3'];
var element = getRandomElement(list);
Then query the documentSnapshot

You can first get all documents in you collection.
Try this code:
async getMarker() {
const snapshot = await firebase.firestore().collection('userPosts').get();
const documents = [];
snapshot.forEach(doc => {
documents[doc.id] = doc.data();
});
return documents;
}
Next, from return documents you can create a list of documents id and get random numbers (document id) from this list.

The main problem here that's going to prevent you from moving forward is the fact that you don't actually have any documents nested immediately under "posts". Notice that the names of the documents are in italics. That means there isn't actually a document here at all. The reason why they are show, however, is because you have a subcollection "userPosts" nested under the path where that document ID exists.
Since you don't have any documents at all under "posts", the usual strategies to find a random document won't work at all. You're going to have to actually populate some data there to select from, or find another way to select from the data in the subcollections.

firestore()
.collection('SOURCE')
.doc(props?.route?.params?.data?.id)
.collection('userPosts')
.get()
.then(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
console.log('User ID: ', documentSnapshot.id, documentSnapshot.data());
})
})

Related

Flutter Firestore - How to get data from a Document Reference in a Document Field?

I'm building a Self-learning app with differente questions types. Right now, one of the questions have a field containing a list of DocumentReferences:
In Flutter, I have the following code:
Query<Map<String, dynamic>> questionsRef = firestore
.collection('questions')
.where('lesson_id', isEqualTo: lessonId);
await questionsRef.get().then((snapshot) {
snapshot.docs.forEach((document) {
var questionTemp;
switch (document.data()['question_type']) {
....
case 'cards':
questionTemp = CardsQuestionModel.fromJson(document.data());
break;
....
}
questionTemp.id = document.id;
questions.add(questionTemp);
});
});
Now, with "questionTemp" I can access all the fields (lesson_id,options,question_type, etc..), but when it comes to the "cards" field, how Can I access the data from that document reference?
Is there a way to tell firestore.instance to get the data from those references automatically? Or do I need to make a new call for each one? and, if so, how can I do that?
Thank you for your support in advance!
Is there a way to tell firestore.instance to get the data from those
references automatically? Or do I need to make a new call for each
one?
No there isn't any way to get these documents automatically. You need to build, for each array element, the corresponding DocumentReference and fetch the document.
To build the reference, use the doc() method
DocumentReference docRef = FirebaseFirestore.instance.doc("cards/WzU...");
and then use the get() method on this DocumentReference.
docRef
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document exists on the database');
}
});
Concretely, you can loop over the cards Array and pass all the Futures returned by the get() method to the wait() method which "waits for multiple futures to complete and collects their results". See this SO answer for more details and also note that "the value of the returned future will be a list of all the values that were produced in the order that the futures are provided by iterating futures."

Flutter Firebase Pagination - Nested Query

I am relatively new to the Flutter world and need some help in efficiently querying a nested Firebase query with pagination in Flutter.
I am building an app where I have a few thousand of user selected documents in collectionA (selected from >100k documents from collectionB) and need to loop through a chunk of 10 documents (whereIn limit of 10) queried via stream. My current implementation (snippet below) takes a list of document ID's "d" and chunks in a list of 10 via quiver/iterables. The drawback of the methodology is that it reads thousands of user documents upfront before displaying it in the app. I would like to use a pagination and control the reads.
Would you suggest a solution (with a code snippet) on how to use the pagination through thousands of user selected documents?
Future<List<Article>> fbWhereGT10Article(
String collection, String field, List<dynamic> d) async {
final chunks = partition(d, 10);
// print(chunks);
final querySnapshots = await Future.wait(chunks.map(
(chunk) {
Query itemsQuery = FirebaseFirestore.instance
.collection(collection)
.where(field, whereIn: chunk);
// .orderBy('timestamp', descending: true);
return itemsQuery.get();
},
).toList());
return querySnapshots == null
? []
: await Stream.fromIterable(querySnapshots)
.flatMap((qs) =>
Stream.fromIterable(qs.docs).map((e) => Article.fromFirestore(e)))
.toList();
}

how to retrieve unknown sub collection from document in flutter

I have the following code with the objective of retring all the documents like this: Collection('A').document(currentUser), this should give me the document YRZ1**** as you may see in the first image. Now what I want to do is get all the documents in the t22o9*** subcollection, as showned on the second image, how can I do that?
var docPath=Firestore.instance.document('Messages/$currentUser');
docPath.snapshots().listen((event) {
print('Hello 12 ${event.data}');
});
In order to get data from subcollection you can do the following:
var db = Firestore.instance;
//This is a reference to the YRZ1**** doc
var messagesDoc = db.collection('Messages').document('Messages/$currentUser');
//collectionId is the t22o9*** id, you need to have this set previosly in your code
var docList = [];
messagesDoc.collection(collectionId)
.getDocuments()
.then((QuerySnapshot snapshot) {
snapshot.documents.forEach((doc) => docList.add(doc)'));
});
With this code, docList will be a list of the documents located at your subcollection t22o9*** of the YRZ1*** document.
Also you can check this link with some examples on how to get data from firestore using flutter.
EDIT:
As per what you have clarified on the comments, getting the subcollection data is a bit more complicated with subcollections that were created dynamically, as you can see on this link, which I assume is the case you have. The most feasible solution in your case would be to create a list of ids of subcollections in your parent document, so for example your YRZ1*** document would have this structure
document
id: YRZ1***
subCollectionIds : [subCollectionId1, subCollectionId2,...]
subCollection1
subCollection2
...
subCollectionN
Having this structure you could use this code to retrieve the list of subcollections and the first document of each subcollection, ordered by whatever field you'd like:
var db = Firestore.instance;
var listFirstDocSub = []
//This is a reference to the YRZ1**** doc
db.collection('Messages')
.document('Messages/$currentUser')
.getDocument()
.then((DocumentSnapshot doc) {
var listSubCollections = doc["subCollectionIds"];
listSubCollections.forEach((id) {
db.collection('Messages')
.document('Messages/$currentUser')
.collection(id)
.orderBy("WHATEVER_FIELD_YOU_DECIDE")
.limit(1)
.getDocuments()
.then((DocumentSnapshot snapshot) {
listFirstDocSub.add(snapshot);
})
});
});
NOTE: With this approach, everytime you add a new subcollection, you will also need to add it's id to the subCollectionIds list.
Hope This helps.

Firestore/Cloud functions: Finding a document in array of document references that match criteria

Using Firebase Cloud Functions I'd like to search for documents that contain a certain other document in an array of document references. My structure looks as follows;
Users
name
email
cars
ref to cars/car1 for example
ref to cars/car2 for example
Cars
registration
make
model
There are multiple users and multiple cars. I need to search for users that have a certain 'car' in their car array.
I'm trying to write this in a Cloud Function and have the following;
admin.firestore()
.collection('users')
.where('cars', 'array-contains', registration)
.get().then(doc => {
console.log("TESTING: found the user " + doc.data().email)
return
}).catch(error => {
console.error(error);
});
I know this is currently just searching for the registration string in the array. Is there anyway to search for a specific document reference. I'm using Node.js.
Working code to get all the documents that have a document reference in an array;
// Notify the owner of the car
admin.firestore()
.collection('users')
.where('cars', 'array-contains', carRef)
.get().then(snapshot => {
snapshot.forEach(doc => {
console.log("TESTING found the user " + doc.data().email);
const message = {
notification: {
body: 'Your vehicle (' + carReg + ') recieved a report. Tap here to see!',
},
token: doc.data().cloudMessagingToken
};
sendMessage(message);
});
return
}).catch(error => {
console.error("Error finding a user that has the car in their garage");
console.error(error);
});
If you want to query using reference type fields, you will need to provide a DocumentReference type object to the query. If you pass a DocumentReference to a car, the query should work. For example:
const ref = admin.firestore().collection('Cars').doc(id)
where id is the id of the document.
However, you can't search using values of fields inside the referenced document. Firestore queries only work against data in a single collection at a time. With the way you have your data organized right now, it's not possible to make a single query for all users who have references to cars with a specific registration string field. For that query, you would need to also store an array of registration strings for each user that you could query with array-contains.
Yes, this involves duplication of data, and it's called "denormalization". This is very common in nosql type databases to enable the queries you need.

Firebase Firestore query with related document references

I'm trying to model "memberships" with Firestore. The idea is that there are companies, users and then memberships.
The memberships collection stores a reference to a company and to a user, as well as a role as a string, e..g admin or editor.
How would I query to get the users with a certain role for a company?
This is what I currently have with some basic logging.
const currentCompanyID = 'someid';
return database
.collection('memberships')
.where('company', '==', database.doc(`companies/${currentCompanyID}`))
.where('role', '==', 'admin')
.get()
.then(snap => {
snap.forEach(function(doc) {
console.log(doc.id, ' => ', doc.data());
const data = doc.data();
console.log(data.user.get());
});
})
.catch(error => {
console.error('Error fetching documents: ', error);
});
data.user.get() returns a promise to the user, but I'd have to do that for every user which seems inefficient?
What would be the best way to approach this?
Your code is close to what you want, but there are two issues:
Your where() clause can't compare a field with a document reference, because Firestore is a classic denormalized datastore. There aren't ways to strongly guarantee that one document refers to another. You'll need to store document IDs and maintain consistency yourself. (Example below).
Queries actually return a QuerySnapshot, which includes all the docs that result from a query. So you're not getting one document at a time — you'll get all the ones that match. (See code below)
So a corrected version that fits the spirit of what you want:
const currentCompanyID = '8675309';
const querySnapshot = await database
.collection('memberships')
.where('companyId', '==', currentCompanyID)
.where('role', '==', 'admin')
.get(); // <-- this promise, when awaited, pulls all matching docs
await Promise.all(querySnapshot.map(async snap => {
const data = doc.data();
const user = await database
.collection('users')
.doc(data.userId)
.get();
console.log(doc.id, ' => ', data);
console.log(user);
});
There isn't a faster way on the client side to fetch all the users that your query refers to at once -- it's part of the trouble of trying to use a denormalized store for queries that feel much more like classic relational database queries.
If this ends up being a query you run often (i.e. get users with a certain role within a specific company), you could consider storing membership information as part of the user doc instead. That way, you could query the users collection and get all the matching users in one shot.

Resources