How to delete specific map field in an array? - ionic-framework

I created an firestore collection with users, and in this one I save favourites for an favourite list by an array, and the favourites get created as map fields of this array, and I want to know how I can remove an specific favourite (map field) of my array?
That is my function to add favourites to my array.
addFav(name, performer, category, room, abstract, begin, dis, color, btn, id`){
try{
this.afs.doc(`users/${this.user.getUID()}`).update({
favs: firestore.FieldValue.arrayUnion({
name,
performer,
category,
room,
abstract,
begin,
dis,
color,
btn
})
})
}
I need help to delete an specific favourite / map field of my array
deleteFav()
{
try{
this.afs.doc(`users/${this.user.getUID()}`).update({
favs: firestore.FieldValue.delete()
})
}
}
following pictures shows my firestore collections with the array and the map fields.
https://i.stack.imgur.com/1Y1Xj.png

You should use arrayRemove method like:
favs: firestore.FieldValue.arrayRemove("performer")
Check this one:
https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array

Related

how to write nested array in Firestore using flutter

Building a shopping app I added a cart item in my 'users' collection.
cart stored in
and after the user purchase those item I wanted to store those cart items in other collection 'orders'. but when I tried to store those cart items in my order collection firebase throws Invalid data. Nested arrays are not supported.
what is my mistake here?
Here is my Code
onPressed: (){
//orderItem(cartItem);
getData() async {
return await FirebaseFirestore.instance.collection('users').where('id', isEqualTo: FirebaseAuth.instance.currentUser.uid).get();
}
getData().then((val){
if(val.docs.length>0){
//print(val.docs[0].data()['cart']);
// Map map = val.docs[0].data()['cart'];
var list = [val.docs[0].data()['cart']];
// map.entries.forEach((e) {
// list.add();
// });
// List items = [val.docs[0].data()['cart'].toList()];
FirebaseFirestore.instance.collection('orders').doc().set({
"userId": FirebaseAuth.instance.currentUser.uid,
"userName": FirebaseAuth.instance.currentUser.displayName,
"orders": FieldValue.arrayUnion([[val.docs[0].data()['cart']]]),
"price": cartController.totalCartPrice.value
});
}
else{
print("Not Found");
}
});
},
I think the problem is trivial. You accidentally put list inside list.
"orders": FieldValue.arrayUnion([[val.docs[0].data()['cart']]]),
I think you can skip one pair of [ so you have this:
"orders": FieldValue.arrayUnion([val.docs[0].data()['cart']]),
Also... if val.docs[0].data()['cart'] is a list you'll probably need to skip another pair to this:
"orders": FieldValue.arrayUnion(val.docs[0].data()['cart']),
Arrays in a Firestore documents cannot contain arrays so you cannot have have something like:
[
[orderId: 1, orders: [...orders]],
[orderId: 2, orders: [...orders]],
//This is possible
{orderId: 3, orders: [...orders]}
]
If you try adding a document from the console itself, you'll notice the same.
The best you can do is store the orders as a string, [JSON.stringify(ordersObject)]. Then when you fetch the data back you can parse it into as JSON again..
Second option is restructuring your data, it would have ideal to have a separate collection for orders. Then you can use .where() queries to fetch orders of a specific user. However this'll be more expensive (but ideal) because you will be fetching 12 documents (12 reads) if user has 12 orders.

How to update more than one field in sub document?

{
movies: [
{
_id,
title,
rating
}
]
}
I want to update title or rating or both only if exists.
my query should be something like this
Movies.findOneAndUpdate('movies._id': movieId, {
movies.$.rating: rating,
movies.$.title: title
});
But it doesn't support multiple positional operator.
The possible solution I thought was to send entire movie object and replace it as
Movies.findOneAndUpdate('movies._id': movieId, {
movies.$: movie
});
I don't want to replace entire object or I don't want to send entire movie object from frontend.
Please guide me with an optimised solution.
Movies.findOneAndUpdate({'movies._id': movieId},{$set:{title:req.body.title}} {
movies.$: movie
});
You can do it with array filters https://jira.mongodb.org/browse/SERVER-1243
db.movies.update(
{},
{$set: {“movies.$[i].title”: "newTitle", “movies.$[i].rating”: 4}},
{arrayFilters: [{i._id: "movieId"}]}}
)

AngularFire2 query collection for documents that have a value within an array efficiently

I have the following model in an Angular 6 cli/TS/AngularFire thing I'm trying to build. I'm new to all of those things.
export class Book {
constructor(
public id: string,
public title: string,
public genres: any[]
) {}
}
And I want to be able to find all books that matcha genre stored in Firebase's Cloud Firestore using AngularFire2.
A standard query looks like this (documentation):
afs.collection('books', ref => ref.where('size', '==', 'large'))
Ideally, I want to make a call to Firebase that doesn't get all documents in the collection so it's more efficient (tell me if that's wrong thinking). For example, something like this.
afs.collection('books', ref => ref.where(book.genres.containsAny(array of user defined genres)));
I have a limited understanding of NoSQL data modeling, but would happily change the model if there's something more effective that will stay fast with 1000 or 30,000 or even 100,000 documents.
Right now I am doing this.
filterArray = ["Genetic Engineering", "Science Fiction"];
filteredBooks: Book[] = [];
ngOnInit() {
this.db.collection<Book>('books')
.valueChanges().subscribe(books => {
for (var i=0; i < books.length; i++) {
if (books[i].genres.some(v => this.filterArray.includes(v))) {
this.filteredBooks.push(books[i]);
}
}
});
}
This works to filter the documents, but is there a more efficient way both in terms of speed and scalability (get only the matching documents instead of all)?
You're right to limit the documents first. You don't want to pull 30K documents, THEN filter them. And you are on the right path, but your formatting wasn't quite right. You want to do something like this:
afs.collection<book>('books', ref => ref.where('genres', 'array-contains', genre)
I believe that as of right now, you cannot pass in an array like:
afs.collection<book>('books', ref => ref.where('genres', 'array-contains', genres) // note that genres is plural implying a list of genres
However, it still may be better to do a for-loop through the genres and pull the books list, once for each genre, and concatenate the lists afterward.
Now, you mentioned that you would also like a suggestion to store the data differently. I would recommend that you do NOT use an array for genres. Instead make it a map (basically, an object), like this:
author: string;
title: string;
...
genres: map
Then you can do this:
author: 'Herman Melville'
title: 'Moby Dick'
...
genres: {
'classics': true
'nautical': true
}
And then you can filter the collection like this:
afs.collection<book>('books', ref => ref.where('genres.classics', '==', true).where('genres.nautical', '==' true)
I hope this helps

Is it possible to populate nested references in Mongoose?

I'm new to MongoDB, trying to populate data from another collection into a response. The simplest example would be as follows:
const CartSchema = new Schema({
items: [{
product: { type: Schema.Types.ObjectId, ref: 'product' },
qty: Number
}],
...
});
I'm able to use .populate() when the relationships are at the root level, but in the above example, I have an array of items with their own properties, e.g. qty, plus an _id reference to a product. I would like to populate the product object into each car item, but can't seem to find any examples on what's the "right" way to do it.
Cart.findById(id)
.populate('products') // <-- something like this
.then(record => ... )
.catch(next);
I know that I could probably do a separate .find() on the products collection after locating the cart record and manually extend the initial object, but I was hoping there was a way to populate the data within the original query?
You can try this, it will work for you.
Cart.findById(id)
.populate('items.product')
.then(record => ... )
.catch(next);
.populate('items.product') will populate the product object of all the cart item present in the array.

Send more than one term to algolia search

I'm implementing algolia search in my site and i want to get a set of data matching any id's i send to the search, so i need to know how could i send more than one parameter to the search, so i can send a set of ids, something like this:
let client = algoliasearch(APP_ID, API_KEY),
index = client.initIndex(INDEX_NAME);
let term=["3223212","2423434"];
index.search(term, callback)
This is not working right now, have any idea? or even how could i achieve my goal using another algolia feautre like filtering for instance?
If you're trying to retrieve objects by their objectIDs (which you can manually set at creation time to match your database ids), you can simply use the getObjects method.
Extract from the documentation:
You can also retrieve a set of objects:
index.getObjects(['myObj1', 'myObj2'], function(err, content) {
console.log(content);
});
If you're trying to list all the records that belong to a group with a specific id, you can use a facet that will contain this id and filter on it.
Inside your record:
{
"group_id": "3223212",
// or
"group_ids": ["3223212", "2423434"]
}
Inside your index settings:
{
attributesForFaceting: [
'onlyFilter(group_id)'
]
}
At query time:
let ids = ["3223212", "2423434"];
let filters = ids.map(id => `group_id:${id}`).join(' OR ');
index.search('', { filters: filters }, callback);