updating array object in mongodb using Nodejs driver - node.js

Here is my saved document
"_id": ObjectId("5e26be38c13b7149d0a95111"),
"isApproved": false,
"vendorOrder": [
{
"_id": ObjectId("5e26be38c13b7149d0a95113"),
"publicationCode": "TOI",
"publicationName": "Times of India",
"editionName": "chennai city",
"productCode": "TCE1",
"subscriptionCopies": 70,
"tradeCopies": 40
},
{
"_id": ObjectId("5e26be38c13b7149d0a95112"),
"publicationCode": "ET",
"publicationName": "Economic Times",
"editionName": "chennai city",
"productCode": "ECE1",
"subscriptionCopies": 20,
"tradeCopies": 100
}
],
"frequency": "TH",
"orderCreatedBy": ObjectId("5e25550a3405363bc4bf86c1"),
"submittedTo": ObjectId("5e2555363405363bc4bf86c2"),
Here is my mongo shell query:
db.orders.updateOne(
{
"_id": ObjectId("5e26be38c13b7149d0a95111"),
"submittedTo":ObjectId("5e2555363405363bc4bf86c2"),
"vendorOrder.productCode": "TCE1"
},
{ $set:{"vendorOrder.$.tradeCopies":80 }
})
But, only one element is getting updated MOREOVER this query is not working in nodejs.
Nodejs query:
const { orderId, dealerId, productCode, tradeCopies } = req.body;
try {
const orders = await Order.updateOne(
{
_id: orderId,
submittedTo: dealerId,
vendorOrder.productCode: productCode
}, {
$set: { vendorOrder.$.tradeCopies: tradeCopies }
}
)
} catch(ex) {
console.log(ex);
}
Goal is to update the vendorOrder:[{tradeCopies}] of all the elements in one go unique code is Product Code i.e i want to upadate tradeCopies in vendorOrder where productCode, orderId, dealerId would come from req.body and tradeCopies should get updated against the productCodes.

To update all elements ("tradeCopies" field) in the array matching a condition on the array element field ("productCode") you must use the $arrayFilters update option. This can be done both in Mongo Shell as well as the NodeJS driver's API method call updateOne.
Mongo Shell version:
db.collection.updateOne(
{ _id: ObjectId("5e26be38c13b7149d0a95111"), submittedTo:ObjectId("5e2555363405363bc4bf86c2") },
{ $set: { "vendorOrder.$[elem].tradeCopies" : 80 } },
{
arrayFilters: [ { "elem.productCode": "TCE1" } ]
}
)
The NodeJS version:
col.updateOne( { _id: orderId, submittedTo: dealerId, },
{ $set: { "vendorOrder.$[elem].tradeCopies" : 80 } },
{ arrayFilters: [ { "elem.productCode": "TCE1" } ] },
function( err, result ) {
console.log(result);
}
);
[ Edit - Add ]
Using the async / await syntax the query will be like this (for details see MongoDB NodeJS driver update examples):
result = await coll.updateOne( { ... }, { $set: { ... } }, { arrayFilters: [ { ...} ] } );

Related

Update String inside object inside array mongodb

I have a document that looks something like this
{
"_id":{
"$oid":"id"
},
"side1":[
{
"username":"test1",
"id":"id1",
},
{
"username":"test2",
"id":"id2",
},
{
"username":"test3",
"id":"id3",
}
],
"side2":[
{
"username":"test4",
"id":"id4",
},
{
"username":"test5",
"id":"id5",
},
{
"username":"test6",
"id":"id6",
}
],
}
I want to be able to search and update one of the sides, for example, if I searched with username for side1 and that username would be there then I would be able to $set other fields for the object with this username. Something like: Search side1 username test1: $set result.id: "43242342" this would set the id of the object with the username of test1 to 43242342. I am not sure on how I would go about doing this, I have tried using $elemMatch but that didn't bring any results.
Test.findOne({ id: id },
{ side1: { $elemMatch: {username: username} } }, function (err, result) {
if (err) {
console.log(err);
} else {
console.log(result)
}
});
I'm not exactly sure how you want to update the document, but perhaps this is what you are looking for?
db.collection.update({
"_id": ObjectId("000000000000000000000001"),
"side1.username": "test1"
},
{
"$set": {
"side1.$.id": "43242342"
}
})
Try it on mongoplayground.net.
Example updated document:
[
{
"_id": ObjectId("000000000000000000000001"),
"side1": [
{
"id": "43242342",
"username": "test1"
},
{
"id": "id2",
"username": "test2"
},
{
"id": "id3",
"username": "test3"
}
],
"side2": [
{
"id": "id4",
"username": "test4"
},
{
"id": "id5",
"username": "test5"
},
{
"id": "id6",
"username": "test6"
}
]
}
]
Could you do something like this?
function addProperty(id, side, username, property, value) {
const query = {
_id: {
$oid: id,
},
};
const update = {
$push: {
[side]: {
username: username,
[property]: value,
},
},
};
const options = {
upsert: true,
};
db.collection("Test").updateOne(query, update, options);
}

How to use object as filter in findOneAndUpdate in mongodb

I want to use _id object as filter in findOneAndUpdate:
{
"_id": {
"playerId": "189583",
"tournamentId": "197831"
},
"playerName": "Afiridi",
"score": [],
"__v": 0
}
I tried this but it didn't work:
await Scorecard.findOneAndUpdate(
{
_id: {
playerId: playerId,
tournamentId: tournamentId
}
},
{
$addToSet: {
"score": {
_id: matchID
}
}
}
);
//playerId and tournamentId are coming from above code, not listed
I am using mongoose, but any kind of help is appreciated.
Try the dot-notation syntax:
await Scorecard.findOneAndUpdate(
{
"_id.playerId": playerId,
"_id.tournamentId": tournamentId
},
{
$addToSet: {
"score": {
_id: matchID
}
}
}
);

How to update the data in existing document in mongodb

I want to update the data with in existing document and not create a other json document in the database. But its not modifing log is showing { n: 0, nModified: 0, ok: 1 }..please help me thanks in advance
this is my code:
const userUpdatedPassword = await UserPreferred.updateOne({_id: userPreferredExists._id},
{
$push: {
languages: req.body.languages,
storeType: req.body.storeType,
intrestedIn: req.body.intrestedIn
}})
}
i'm passing this data in postman:
{
"languages":["korean,"Hindi"],
"storeType":["gas station"],
"intrestedIn":["sarees"]
}
I've a json document in my db.
{
"_id" : ObjectId("60e968581871a42ab43a2345"),
"languages" : [
"English"
],
"intrestedIn" : [
"clothing",
"foot wear",
"watches",
],
"storeType" : [
"department stores",
"Medical stores"
]
}
I want in this fromat:-
{
"_id" : ObjectId("60e968581871a42ab43a2345"),
"languages" : [
"English",
"korean,
"Hindi"
],
"intrestedIn" : [
"clothing",
"foot wear",
"watches",
"sarees"
],
"storeType" : [
"department stores",
"Medical stores",
"gas station"
]
}
You can use following query as your requirment, i have tried and tested at my local , One thing you have forgot to use closing quote language array for korean
const userUpdatedPassword = await UserPreferred.updateOne({_id: userPreferredExists._id},
{
$push: {
languages: {
$each: req.body.languages
},
storeType: {
$each: req.body.storeType
},
intrestedIn: {
$each: req.body.intrestedIn
}
}
})
But this code will write duplicates in the array, so if you don't want to push duplicates in the array you can use $addToSet in place of $push, like following
const userUpdatedPassword = await UserPreferred.updateOne({_id: userPreferredExists._id},
{
$addToSet: {
languages: {
$each: req.body.languages
},
storeType: {
$each: req.body.storeType
},
intrestedIn: {
$each: req.body.intrestedIn
}
}
})

Autocomplete filtered with mongodb

I would like to perform autocompletion on the name but filtered on a specific city with mongoose and nodejs.
I have a mongodb collection like this :
{
"_id" : ObjectId("6007fd9d984e2507ad452cf3"),
"name" : "John",
"city" : "A",
},
{
"_id" : ObjectId("6007ff6844d9e517e1ec0976"),
"name" : "Jack",
"city" : "B",
}
What i have done so far :
I have setup MongoDB Atlas with a Search Index (with the help of search doc)
And set up the autocomplete like that :
router.get('/search', async (request, response) => {
try {
let result = await Client.aggregate([
{
"$search": {
"autocomplete": {
"query": `${request.query.term}`,
"path": "name",
"fuzzy": {
"maxEdits": 2,
"prefixLength": 3,
},
},
},
},
{
$limit: 3
},
{
$project: {
"_id": 0,
}
}
]);
response.send(result);
} catch (e) {
response.status(500).send({message: e.message});
}
});
In front-end, with autocompleteJs :
const autoCompleteJS = new autoComplete({
data: {
src: async () => {
const query = document.querySelector("#autoComplete").value;
const source = await fetch(`${window.location.origin}/search?term=${query}`);
const data = await source.json();
return data;
},
key: ["name"],
},
trigger: {
event: ["input", "focus"],
},
searchEngine: "strict",
highlight: true,
});
So far it is working well. But I don't know how to make the autocomplete result filtered based on city. It seems that the documentation does not mention this. Do you have any leads.
Use the $where pipeline stage from the aggregation pipeline after performing your search to filter out unwanted documents. So for example,
Client.aggregate([
{
"$search": {
"autocomplete": {
"query": `${request.query.term}`,
"path": "name",
"fuzzy": {
"maxEdits": 2,
"prefixLength": 3,
},
},
},
},
{
$match: { city: 'city-name' }
},
{
$limit: 3
},
{
$project: {
"_id": 0,
}
}
]);
Use a compound operator like so which lets you have more control over your results in a performant fashion:
"$search": {
"compound" : {
"filter" : [{
"text" : { path: "city", query: "New York" }
}],
"must": [{
"autocomplete": {
"query": `${request.query.term}`,
"path": "name",
"fuzzy": {
"maxEdits": 2,
"prefixLength": 3,
},
},}
}] }
using filter will filter your results, without impacting the score. must will require that the name field will also match. Also take a look at should and mustNot in the compound docs for more options.

MongoDB $addToSet to deep nested array of object

Below is my data structure.
{
"_id" : "room1",
"members" : [
{
"_id" : "member1",
"name" : "Michael",
"payments" : [
{
"month": "2018/09"
"amount": "20"
}
]
},
]
}
I want to push below object to Michael's payments
{
"month": "2018/09",
"amount": "5000"
}
In this case, What I want to is overwrite object, because month: "2018/09" already exist. Like below :
{
"_id" : "room1",
"members" : [
{
"_id" : "member1",
"name" : "Michale",
"payments" : [
{
"month": "2018/09"
"amount": "5000"
}
]
},
]
}
And, In case when I want to push object that not exist same month in payments, I want to add this object to payments.
{
"month": "2018/10",
"amount": "2000"
}
So the expected result is
{
"_id" : "room1",
"members" : [
{
"_id" : "member1",
"payments" : [
{
"month": "2018/09"
"amount": "5000"
},
{
"month": "2018/10"
"amount": "2000"
}
]
},
]
}
I tried like below, but it's not working. My code generate duplicated new month object every time I tried. How can I do this properly?
Rooms.update(
{
_id: "room1",
"members._id": "member1",
"members.$.payments": {
$not: {
$elemMatch: {
month: req.body.month
}
}
}
},
{
$addToSet: {
"members.$.payments": {
month: req.body.month,
amount: req.body.value
}
}
},
{ multi: true }, function (err, result) {
console.log(result)
}
)
You can use below command to add without duplicity either in months or amount
Rooms.update(
{
_id: "room1",
"members._id": "member1"
},
{
$addToSet: {
"members.$.payments": {
month: req.body.month,
amount: req.body.value
}
}
},function (err, result) {
console.log(result)
}
)
So I heard I have to determine duplication myself, so below is my code... it's writing now.,,
So Finally this is my code
Clubs.findOne({
uid: req.params.club_id,
"members._id": mongoose.Types.ObjectId(req.params.member_uid)
}, function(err, club){
let member = club.members.filter(el => {
if(el._id.equals(req.params.member_uid)) return el
})
let duplicated = false;
member[0].payments.map(el => {
if(el.month === req.body.month) duplicated = true
})
if(duplicated){
Clubs.update(
{
uid: req.params.club_id,
"members._id": mongoose.Types.ObjectId(req.params.member_uid),
},
{
$set: {
["members.$.payments."+index+".amount"] : req.body.value
}
},
function (err, result, third) {
if (err) throw err
console.log('result')
console.log(result)
res.json({})
}
)
} else {
Clubs.update(
{
uid: req.params.club_id,
"members._id": mongoose.Types.ObjectId(req.params.member_uid),
},
{
$push: {
"members.$.payments" : {
month : req.body.month,
amount: req.body.value
}
}
},
function (err, result, third) {
if (err) throw err
console.log('result')
console.log(result)
res.json({})
}
)
}
})
Perhaps consider changing the structure of your nested array to an object? So change this
{
"payments": [{
"month": "2018/09"
"amount": "5000"
},
{
"month": "2018/10"
"amount": "2000"
}
]
}
to this:
{
"payments": {
"2018/09": "5000",
"2018/10": "2000"
}
}
Then you can do a simple update:
Rooms.update({
_id: "room1",
"members._id": "member1",
"members.payments": {
$exists: true
}
}, {
$set: {
"members.payments." + req.body.month: req.body.value
}
},
)

Resources