Add snapshot items to array without looping or forEach - google-cloud-firestore

Using Firestore, I can assign items to an array with the following:
this.Svc.getCategories().valueChanges().subscribe(categories => {
this.categories = categories;
})
However, this listens for changes on the items and would add them to the array on a change.
Using
this.Svc.getCategories().get().subscribe(categories => {
categories.forEach(category => {
this.categories.push(category.data())
});
})
allows me to add the items to an array without listening for changes.
However, this method required me to either use forEach or a loop to push the data into the array.
Is there a way to get a one-time snapshot of the data and assign the data to an array without looping and using push()?

Check out the Firestore docs for reading data once https://firebase.google.com/docs/firestore/query-data/get-data

Related

How to check if data matches field in array in flutter

I have a Firebase Firestore configuration as show below:
How would I check if eventParticipants's contains a uid matching the current user's uid? The below code used to work when eventParticipants used to be an array of user id's, but since creating more detailed array objects, the code seems to not work:
data["eventParticipants"]
.contains({
"uid": FirebaseAuth
.instance.currentUser!.uid
.toString()
})
Usually the above code would return a bool, and I would use the result in a ternary operator to load a widget, however, I am unable to rework the logic with the new array structure. Subsequently, how would I remove an array object if it's uid field matches the current user's id?
FirebaseFirestore
.instance
.collection(
"events")
.doc(document.id)
.set(
{
"eventParticipants":
FieldValue
.arrayRemove([
{
"uid": FirebaseAuth
.instance
.currentUser
?.uid
}
])
},
SetOptions(
merge: true),
);
Any pointers would be appreciated!
The arrayRemove (and arrayUnion and arrayContains) operators expect you to pass the complete, exact contents of the item in the array. In your case it looks for an item in the array with a single field uid with the value you pass.
So unless you know the values of all properties of the array item that you want to remove, you'll have to:
Read the document with the array in it.
Manipulate the array in your application code.
Write the entire array back to the database.
Also see:
Firestore, how to structure a "likedBy" query
Firestore conditional array query
Firestore array-contains-any is not working properly

Read key value from array using document id (Flutter)

Very new to flutter. I want to retrieve arrays of notifications using a specific document id and display [date, message, type] in my App. Tried to google but can't seem to find any solution.
Try this with this you will get specific document's data.
firebaseFirestore.collection('Customer').doc('paste-your-document-id').get();
you'll get whole data of Notification data then use it accordingly.
Whole Notification data will print.
FirebaseFirestore.instance.collection("Customer").doc(document_id).get().then((value) {
Map<dynamic, dynamic> map = value.data();
for(var element in map.values) {
element.asMap().forEach((index, firebaseValue) {
print(index);
print('${firebaseValue['Date']}');
print('${firebaseValue['Message']}');
print('${firebaseValue['Type']}');
});
}
}
);

Flutter Firestore stream list docs to list model more efficient method

I have a list of documents in firestore. When the user adds a new document I would like to update the document list as models. I have this working but it seems like a bad method.
I get the stream like this:
Stream buildingsStream = FirebaseFirestore.instance.collection('buildings').snapshots();
Then I create an empty list of BuildingModel. I do a forEach on the stream, add convert each document in the stream to BuildingModel and add it to the list. Finally, I update the value of the List of building model with the new list. Like this:
Stream buildingsStream = _firestoreApi.buildingsStream;
buildingsStream.listen((result) {
List<BuildingModel> buildings = [];
result.docs.forEach((document) => buildings.add(BuildingModel.fromJson(document.data())));
_buildings.value = buildings;
});
This is obviously not the best way to do it. Every time I add a single value, it does a for loop through the whole stream and updates with a new list.
How can I do this better?
Thanks

DynamoDB - How to upsert nested objects with updateItem

Hi I am newbie to dynamoDB. Below is the schema of the dynamo table
{
"user_id":1, // partition key
"dob":"1991-09-12", // sort key
"movies_watched":{
"1":{
"movie_name":"twilight",
"movie_released_year":"1990",
"movie_genre":"action"
},
"2":{
"movie_name":"harry potter",
"movie_released_year":"1996",
"movie_genre":"action"
},
"3":{
"movie_name":"lalaland",
"movie_released_year":"1998",
"movie_genre":"action"
},
"4":{
"movie_name":"serendipity",
"movie_released_year":"1999",
"movie_genre":"action"
}
}
..... 6 more attributes
}
I want to insert a new item if the item(that user id with dob) did not exist, otherwise add the movies to existing movies_watched map by checking if the movie is not already available the movies_watched map .
Currently, I am trying to use update(params) method.
Below is my approach:
function getInsertQuery (item) {
const exp = {
UpdateExpression: 'set',
ExpressionAttributeNames: {},
ExpressionAttributeValues: {}
}
Object.entries(item).forEach(([key, item]) => {
if (key !== 'user_id' && key !== 'dob' && key !== 'movies_watched') {
exp.UpdateExpression += ` #${key} = :${key},`
exp.ExpressionAttributeNames[`#${key}`] = key
exp.ExpressionAttributeValues[`:${key}`] = item
}
})
let i = 0
Object.entries(item. movies_watched).forEach(([key, item]) => {
exp.UpdateExpression += ` movies_watched.#uniqueID${i} = :uniqueID${i},`
exp.ExpressionAttributeNames[`#uniqueID${i}`] = key
exp.ExpressionAttributeValues[`:uniqueID${i}`] = item
i++
})
exp.UpdateExpression = exp.UpdateExpression.slice(0, -1)
return exp
}
The above method just creates update expression with expression names and values for all top level attributes as well as nested attributes (with document path).
It works well if the item is already available by updating movies_watched map. But throws exception if the item is not available and while inserting. Below is exception:
The document path provided in the update expression is invalid for update
However, I am still not sure how to check for duplicate movies in movies_watched map
Could someone guide me in right direction, any help is highly appreciated!
Thanks in advance
There is no way to do this, given your model, without reading an item from DDB before an update (at that point the process is trivial). If you don't want to impose this additional read capacity on your table for update, then you would need to re-design your data model:
You can change movies_watched to be a Set and hold references to movies. Caveat is that Set can contain only Numbers or Strings, thus you would have movie id or name or keep the data but as JSON Strings in your Set and then parse it back into JSON on read. With SET you can perform ADD operation on the movies_watched attribute. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.ADD
You can go with single table design approach and have these movies watched as separate items with (PK:userId and SK:movie_id). To get a user you would perform a query and specify only PK=userId -> you will get a collection where one item is your user record and others are movies_watched. If you are new to DynamoDB and are learning the ropes, then I would suggest go with this approach. https://www.alexdebrie.com/posts/dynamodb-single-table/

Firestore query on collection

I have list of Collection IDs. Is there any way I can query and get all the documents under these collection ids? without iterating to each id in the list.
Thank you for your time
According to your last comment, I understand that you want to get all documents within a single collection and not to query multiple collections, which is not possbile for the moment in Firestore.
If you have a list of ids, then simply iterate over it and create for each id in the list the corresponding DocumentReference and then add all those references to a List<DocumentReference>. After that, iterate over the new list and for each reference create a Task and then add all those Tasks objects to List<Task<DocumentSnapshot>>.
In the end, just pass the list of Tasks to Tasks's whenAllSuccess() method:
Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//Do what you need to do with your list
for (Object object : list) {
YourObject yb = ((DocumentSnapshot) object).toObject(YourObject.class);
Log.d("TAG", yb.getPropertyName);
}
}
});
In code it looks like my answer from this post:
Android Firestore convert array of document references to List<Pojo>