How to update array elements in MongoDB instead of creating new one - arrays

I have problem with my code. I want to update document, which looks like this:
{
"_id": {
"$oid": "5bf2ad5a0d46b81798232cf9"
},
"manufacturer": "VW",
"model": "Golf ",
"VIN": "WVWZZZ1J212566691",
"doors": 3,
"class": "compact",
"reservations": [
{
"_id": {
"$oid": "5bf2ad5a0d46b81798232cfa"
},
"pick_up_date": {
"$date": "2014-10-13T09:13:00.000Z"
},
"drop_off_date": {
"$date": "2014-10-14T09:13:00.000Z"
},
"user_id": {
"$oid": "5bec00bdfb6fc005dcd5423b"
}
}
],
"createdAt": {
"$date": "2018-11-19T12:32:26.665Z"
},
"updatedAt": {
"$date": "2018-11-19T12:32:26.665Z"
},
"__v": 0
}
I want to add new reservation to array Reservations. My function:
const createReservationForCarId = ({ body , params }, res, next) =>
Carmodel.findById(params.id)
.then(notFound(res))
.then((carmodel) => carmodel ? Object.assign(carmodel, body).save() : null)
.then((carmodel) => carmodel ? carmodel.view(true) : null)
.then(success(res))
.catch(next)
But when I'm trying to update through:
router.put('/:id/reservation',
createReservationForCarId)
Body:
{
"reservations":[
{
"pick_up_date" : "October 13, 2017 11:13:00",
"drop_off_date": "October 14, 2017 11:13:00",
"user_id":"5bec00bdfb6fc005dcd5423b"
}
]
}
Mongo instead of update my old document is creating new one with only one reservation gave in above body.
What should I do to only update existing document, not creating new one?

I suggest you use $push operator of Mongoose which will append your object to the existing array and it will not create a new one.
Check out this link:-
https://github.com/Automattic/mongoose/issues/4338

You can try the findAndModify instead of findById in your code. Check out the syntax here

I haven't used mongoose but they have documentation for array manipulation.
See here:
https://mongoosejs.com/docs/api.html#mongoosearray_MongooseArray-push
Also here is the related mongo doc for array manipulation:
https://docs.mongodb.com/manual/reference/operator/update/push/

Related

Sorting an array of objects in MongoDB collection

I have a collection in a MongoDB with a document like below:
{
"_id": "63269e0f85bfd011e989d0f7",
"name": "Aravind Krishna",
"mobile": 7309454620,
"email": "akaravindk59#gmail.com",
"password": "$2b$12$0dOE/0wj6uX604h3DZpGxuO/L.fZg7KCm7mOGsNMkarSaeG2C/Wvq",
"orders": [
{
"paymentIntentId": "pi_3LjFDtSHVloG65Ul0exLkzsO",
"cart": [array],
"amount": 3007,
"created": 1663475717,
"_id": "6326a01344f26617fc1a65d6"
},
{
"paymentIntentId": "pi_3LjFFUSHVloG65Ul1FQHlZ9H",
"cart": [array],
"amount": 389,
"created": 1663475816,
"_id": "6326a07744f26617fc1a65d8"
}
],
"__v": 0
}
I wanted to get only the orders array sorted by the created property in both ascending and descending manner. As we can see here that orders field inside this document is an array of objects. Please give a solution if you have one. I tried the sortArray method but it is giving an error. Please help.
For some assistance the output should look something like this:
{
"_id": "63269e0f85bfd011e989d0f7",
"orders": [
{
"paymentIntentId": "pi_3LjFDtSHVloG65Ul0exLkzsO",
"cart": [array],
"amount": 3007,
"created": 1663475717,
"_id": "6326a01344f26617fc1a65d6"
},
{
"paymentIntentId": "pi_3LjFFUSHVloG65Ul1FQHlZ9H",
"cart": [array],
"amount": 389,
"created": 1663475816,
"_id": "6326a07744f26617fc1a65d8"
}
]
}
See, I got only the orders field but I want it sorted by the created property value both ascending and descending.
As you mentioned the best way to achieve this is to use $sortArray, I'm assuming the error you're getting is a version mismatch as this operator was only recently added at version 5.2.
The other way to achieve the same result as not as pleasant, you need to $unwind the array, $sort the results and then $group to restore structure, then it's easy to add the "other" order by using $reverseArray although I recommend instead of duplicating your data you just handle the "reverse" requirement in code, overall the pipeline will look like so:
db.collection.aggregate([
{
$unwind: "$orders"
},
{
$sort: {
"orders.created": 1
}
},
{
$group: {
_id: "$_id",
asc_orders: {
$push: "$orders"
}
}
},
{
$addFields: {
desc_orders: {
"$reverseArray": "$asc_orders"
}
}
}
])
Mongo Playground

How to update array inside MongoDB document

Can someone help me with a solution to update an array object inside the MongoDB document, I've tried a couple of methods but still it's to updating, here is my document that I want to update the array in the document.
{
"title": "Products",
"description": "test",
"image": "bdd8510d75f6e83ad308d5f306afccef_image.jpg",
"_created_at": "2021-06-07T20:51:08.316Z",
"ratingCount": 0,
"ratingTotal": 0,
"placeListSave": [
{
"objectId": "g70brr45pfi",
"name": "Kale",
"email": "null",
"strBrandLogo": "84de8865e3223d1ca61386355895aa04_image.jpg",
"storeNumber": "56",
"phone": "0815342119",
"createdAt": "2021-06-10T10:19:53.384Z",
"image": "ad1fb7602c2188223fd891a52373cb9d_image.jpg"
},
{
"objectId": "0qokn33p773",
"name": "Apple",
"email": null,
"strBrandLogo": null,
"storeNumber": "01",
"phone": "011 393 8600",
"createdAt": "2021-06-11T03:11:17.342Z",
"image": "8cfcbf2bcb5e3b4ea8ade44d3825bb52_image.jpg"
}
]
}
So I only want to update the apple object and change the data, I've tried the following code but doesn't seem to work.
`
var db = client.db("test");
try {
db.collection("ShoppingCentres").updateOne({
"title": req.body.product,
"placeListSave.objectId": req.body.id,
}, {
$set: {
"placeListSave.$.email": req.body.email,
"placeListSave.$.storeNumber": req.body.storeNumber,
"placeListSave.$.phone": req.body.phone,
"placeListSave.name": req.body.name,
},
});
res.json("client");
} catch (e) {
console.log("verify", e);
}
});`
arrayFilters seems suitable here:
db.collection.update({
"title": "Products",
"placeListSave.objectId": "0qokn33p773",
},
{
$set: {
"placeListSave.$[x].email": "test#some.email",
"placeListSave.$[x].storeNumber": "test",
"placeListSave.$[x].phone": "test",
"placeListSave.$[x].name": "test"
}
},
{
arrayFilters: [
{
"x.objectId": "0qokn33p773"
}
]
})
explained:
Add array filter called "x" with the objectId for the element that you need to update and use this filter in the $set stage to update the necessary elements.
Hint: To speed up the update you will need to add index on title field or compound index on title+placeListSave.objectId
playground

CastError: Cast to ObjectId failed for value 'xxx' at path "_id"

I have an array in request as:
{
"acceptedBookings": [
{
"id": "e1f66d7a852986709f665c3",
"Date": "2020-02-04T05:03:25.332Z"
}
]
}
I want to update the "date" for every "id". However If I search as
await Booking.findById( acceptedBookings[0].id ) or
await Booking.findOne({_id : acceptedBookings[0].id})
It does not give a response
You're accessing wrong member, what you want is:
let's assume your map is something like
const acceptedBookings = {
"accepted": [{
"id": "e1f66d7a852986709f665c3",
"Date": "2020-02-04T05:03:25.332Z"
},
{
"id": "i123",
"Date": "2020-02-04T05:03:25.332Z"
},
{
"id": "i123",
"Date": "2020-02-04T05:03:25.332Z"
}
]
};
console.log(acceptedBookings.accepted[0].id); // e1f66d7a852986709f665c3
console.log(acceptedBookings.accepted[1].id); // i123
await Booking.findById( acceptedBookings.accepted[0].id ) //should work fine
Remember the object you've created that is not an array it's map/object with a key: value pair
thus get the right array/element first then access it's members

Pouchdb view returning empty result

Good morning,
I'm currently working with Couchdb and Pouchdb and I'm having a problem with one query on Pouchdb side.
I have a database with different documents setup like this:
{
"_id": "fd87b66087503d760fa501fa49029f94",
"_rev": "1-e2be19d447c98d624c2c8492eaf0a3f4",
"type": "product",
"name": "Blanc de Morgex et de la Salle Brut Extreme 2014",
"category": "Wine",
"subcategory": null,
"zone": "Italy",
"nation": "Valle d'Aosta",
"province": "Morgex, AO",
"cellar": "Cave Mont Blanc",
"price": 30,
"structure": null,
"year": 2014,
"mescita": null,
"tag": null
}
The query I wrote should return the available years of products that match some filters. This is the query, with reduce : _count:
function (doc) {
if(doc.category && doc.type == 'product' && doc.year != null) {
emit(doc.year , 1);
}
}
If I try it with Postman adding the group = true parameter everything works and the result is something like:
{
"rows": [
{
"key": 2004,
"value": 2
},
{
"key": 2006,
"value": 2
},
{
"key": 2008,
"value": 2
}
]
}
The problem is when i run this view with Pouchdb with the following code which return a JSON with an empty array:
wine_db.query('wine_list/years', {reduce: '_count', key : "Bollicine", group : true, group_level: 2}).then(function(doc) {
years_list = doc;
console.log('getting year list');
console.log(doc);
}).catch(function(err){
console.log(err);
});
I've tried to play a little with the parameters of the function and even changing the function to return just a list of all the years, but nope.
I can't find the problem neither a different solution so I'm open to every suggestion you can have.
Another solution (group result)
Working on the indications and on the solution suggested by #user3405291 I finally found a way to group the results by year.
Since the emit function return a complex key ['CATEGORY', YEAR] I can use the startkey and endkey parameters to query the result just for a section of the index returned keeping this way the reduce function enable to group the result.
In the end the view function is:
function (doc) {
if(doc.category && doc.type == 'product' && doc.year) {
emit([doc.category, doc.year], doc.year );
}
}
And the Pouchdb query:
wine_db.query('wine_list/years',
{
startkey : ['CATEGORY'],
endkey : ['CATEGORY', {}],
group: true
}
).then(function (doc) {
years_list = doc;
console.log(years_list);
}).catch(function (err) {
console.log(err);
});
The result, where value is the total number of elements with that index:
{
"rows": [
{
"key": [
"Bollicine",
2004
],
"value": 2
},
{
"key": [
"Bollicine",
2006
],
"value": 2
},
{
"key": [
"Bollicine",
2008
],
"value": 2
}
]
}
In your view map function you emit the year as the key/index:
emit(doc.year , 1);
Now, I'm not sure why your are doing your query with a key like {key : "Bollicine"}:
wine_db.query('wine_list/years', {key : "Bollicine"})
.then(res=>{console.log(res)})
Of course you would get an empty response, because your view is actually indexing your docs according to year. I think you might want to do a query with a key like: {key : "2014"}
UPDATE
Based on your comments, I feel like you need to find docs based on both year and category. I'm not sure if I understand what you want, but this may help you: change your view map function like this:
function (doc) {
if(doc.category && doc.type == 'product' && doc.year) {
emit([doc.year, doc.category] , 1);
}
}
The above view will index your docs according to both year and category. You then query your view like this:
wine_db.query('wine_list/years', {key : ['2014', 'Bollicine']})
.then(res=>{console.log(res)})
The above query will give you all the docs with year field equal to 2014 and category field equal to Bollicine.
Second Update
your code works, but I just get the result for the year 2014. What I'm trying to accomplish is to get all the available years given a specific category
One solution is this:
function (doc) {
if(doc.category && doc.type == 'product' && doc.year) {
emit(doc.category, doc.year);
}
}
The above view will index your docs according to category as key and will return the year as value. Therefore you can query like this:
wine_db.query('wine_list/years', {key : 'Bollicine'})
.then(res=>{console.log(res)})
You should get a response like this, by which you have all the available years for Bollicine category:
{
"total_rows": 400,
"offset": 0,
"rows": [
{
"key": "Bollicine",
"value": "2014"
},
{
"key": "Bollicine",
"value": "2015"
},
{
"key": "Bollicine",
"value": "2018"
}
]
}

Add new object inside array of objects, inside array of objects in mongodb

Considering the below bad model, as I am totally new to this.
{
"uid": "some-id",
"database": {
"name": "nameOfDatabase",
"collection": [
{
"name": "nameOfCollection",
"fields": {
"0": "field_1",
"1": "field_2"
}
},
{
"name": "nameOfAnotherCollection",
"fields": {
"0": "field_1"
}
}
]
}
}
I have the collection name (i.e database.collection.name) and I have a few fields to add to it or delete from it (there are some already existing ones under database.collection.fields, I want to add new ones or delete exiting ones).
In short how do I update/delete "fields", when I have the database name and the collection name.
I cannot figure out how to use positional operator $ in this context.
Using mongoose update as
Model.update(conditions, updates, options, callback);
I don't know what are correct conditions and correct updates parameters.
So far I have unsuccessfully used the below for model.update
conditions = {
"uid": req.body.uid,
"database.name": "test",
"database.collection":{ $elemMatch:{"name":req.body.collection.name}}
};
updates = {
$set: {
"fields": req.body.collection.fields
}
};
---------------------------------------------------------
conditions = {
"uid": req.body.uid,
"database.name": "test",
"database.collection.$.name":req.body.collection.name
};
updates = {
$addToSet: {
"fields": req.body.collection.fields
}
};
I tried a lot more but none did work, as I am totally new.
I am getting confused between $push, $set, $addToSet, what to use what not to?, how to?
The original schema is supposed to be as show below, but running queries on it is getting harder n harder.
{
"uid": "some-id",
"database": [
{ //array of database objects
"name": "nameOfDatabase",
"collection": [ //array of collection objects inside respective databases
{
"name": "nameOfCollection",
"fields": { //fields inside a this particular collection
"0": "field_1",
"1": "field_2"
}
}
]
}
]
}

Resources