how to replace only one object from array in mongoDB - database

i have following bson data in mongoDB
array :
[
{
partyName : "p1",
poNumber : "789",
},
{
partyName : "p2",
poNumber : "700",
},
{
partyName : "p3",
poNumber : "889",
}
]
i want to replace object where partyName is "p2" with new object. i tried this
const user1 = await User.findOneAndUpdate({"array.partyName" :"p2"},{$set:{array:newObject}})
it replace the object "p2" but it delete the other objects ( p1 and p3 ).i want to keep p1,p3 and updated p2 objects. please help me to overcome this problem

Work with $ positional operator.
await User.findOneAndUpdate(
{ "array.partyName" :"p2" },
{ $set: { "array.$": newObject } }
)
Demo # Mongo Playground

Related

how to find unique values from array of object in mongoDB

i have following bson data in mongoDB
{name : "c1"
arr : [
{
partyName : "p1",
poNumber : "789",
},
{
partyName : "p1",
poNumber : "700",
},
{
partyName : "p3",
poNumber : "889",
}
]
},
{name : "c2"
arr : [
{
partyName : "p1",
poNumber : "789",
},
{
partyName : "p2",
poNumber : "700",
},
{
partyName : "p3",
poNumber : "889",
}
]
}
i want all unique values of partyName of name: "c1" object like [p1,p3]. i tried this
const unique = await User.distinct({name :"c1","createPurchaseOrder.partyName"})
(note :- User is my schema name )
but it gives error, i tried to search on web but cant find solution please help me with this
One option is using $reduce:
db.collection.aggregate([
{$match: {name: "c1"}},
{$project: {
res: {
$reduce: {
input: "$arr",
initialValue: [],
in: {$setUnion: ["$$value", ["$$this.partyName"]]}
}
}
}
}
])
See how it works on the playground example
Query1
match for the document with name=c1
path to take the array with the partyName and union with the empty array to remove the duplicates
union => order can be lost in patyNames
Playmongo
aggregate(
[{"$match": {"name": {"$eq": "c1"}}},
{"$project":
{"_id": 0, "parties-unique": {"$setUnion": ["$arr.partyName", []]}}}])
Query2
the above is fast and simple but loses the order of the names
if you want to keep the original order, you can use this instead
reduce and add on array only if element doesn't already exists
Playmongo
aggregate(
[{"$match": {"name": {"$eq": "c1"}}},
{"$set":
{"parties-unique":
{"$reduce":
{"input": "$arr.partyName",
"initialValue": [],
"in":
{"$cond":
[{"$in": ["$$this", "$$value"]}, "$$value",
{"$concatArrays": ["$$value", ["$$this"]]}]}}}}}])

how to get object from array by value inside desired object by mongoose

i have following bson data in mongoDB
{
[
{
partyName : "p1",
poNumber : "789",
},
{
partyName : "p2",
poNumber : "700",
},
{
partyName : "p3",
poNumber : "889",
}
]
}
i want object from partyName for example if partyName is p1 then i want this
{
partyName : "p1",
poNumber : "789",
}
i searched on the internet but not find relative result, all i found that query.elemMatch may help but i don't know how to use it please help me with the solution ( i want to use only mongoose )
await EntityName.findOne(
{ mainEntityName: { $elemMatch: { partyName: "p1" } } })
You can try this. Fill the EntityName and mainEntityName parts as your schema's names.
! Instead of "p1", add your parameter came from query.
To reach all elements which matched with your filter ; use find()
await EntityName.find(
{ mainEntityName: { $elemMatch: { partyName: "p1" } } })

how to get objects from array by value inside desired object by mongoose

I have following bson data in mongoDB
{
name : "company 1",
createPurchaseOrder :[
{
partyName : "p1",
poNumber : "789",
},
{
partyName : "p2",
poNumber : "700",
},
{
partyName : "p3",
poNumber : "889",
},
{
partyName : "p1",
poNumber : "800",
},
{
partyName : "p1",
poNumber : "200",
},
]
}
I want objects from partyName for example if partyName is p1 then I want this
[
{
partyName: 'p1',
poNumber: '789',
},
{
partyName: 'p1',
poNumber: '800',
},
{
partyName: 'p1',
poNumber: '200',
},
];
I tried this
const user1 = await User.findOne({name : "company 1"},{createPurchaseOrder:{$elemMatch:{partyName:"p1"}}})
its gives me only first matched object, then I tried this
const user1 = await User.find({name : "company 1"},{createPurchaseOrder:{$elemMatch:{"partyName.$":"p1"}}})
it returned an empty array. please help me with this I want solution which gives me all objects which have matched value
You need to use the aggregation pipeline for this, the find operator is very limited with what structure transformation it can do and for example this use case can't be achieved using it.
Just use $filter instead:
db.collection.aggregate([
{
$match: {
name: "company 1"
}
},
{
$addFields: {
createPurchaseOrder: {
$filter: {
input: {
$ifNull: [
"$createPurchaseOrder",
[]
]
},
cond: {
$eq: [
"$$this.partyName",
"p1"
]
}
}
}
}
},
{
$unwind: "$createPurchaseOrder"
},
{
$replaceRoot: {
newRoot: "$createPurchaseOrder"
}
}
])
Mongo Playground

How to update an array and pull a nested element from same array

With the following document:
{
"_id" : "123",
"firstArray" : [
{
"_id" : "456",
"status" : "open",
"nestedArray" : [
{
"_id" : "100",
"quantity" : 10
},
{
"_id" : "101",
"quantity" : 10
},
{
"_id" : "102",
"quantity" : 10
}
},
{
"_id" : "789",
"status" : "open",
"nestedArray" : [
{
"_id" : "200",
"quantity" : 10
},
{
"_id" : "201",
"quantity" : 10
},
{
"_id" : "202",
"quantity" : 10
}
}
]
}
How can I update the quantity by 20 of the nested ID 101 element and pull the one with the ID 201 from the same MongoDB query ?
I am trying to do that in Java with $set and $pull operator and I'm stuck with the following error:
[BulkWriteError{index=0, code=40, message='Update created a conflict
at 'firstArray.0.nestedArray'', details={}}]
MongoDB doesn’t allow multiple operations on the same property in the same update call. This means that the two operations must happen in two individual queries.
The first solution is you can write 2 seperate queries for both the operations.
The second solution is you can try update with aggregation pipeline, starting from MongoDB 4.2,
$map to iterate loop of firstArray
$filter to iterate loop of nestedArray and remove _id: "201" record
$map to iterate loop of above filtered nestedArray
$cond check condition if _id: "101" then return new quantity otherwise return current
$mergeObjects to merge current object with updated properties
db.collection.update(
{ "firstArray.nestedArray._id": "101" },
[{
$set: {
firstArray: {
$map: {
input: "$firstArray",
in: {
$mergeObjects: [
"$$this",
{
nestedArray: {
$map: {
input: {
$filter: {
input: "$$this.nestedArray",
cond: { $ne: ["$$this._id", "201"] }
}
},
in: {
_id: "$$this._id",
quantity: {
$cond: [
{ $eq: ["$$this._id", "101"] },
20,
"$$this.quantity"
]
}
}
}
}
}
]
}
}
}
}
}
])
Playground

Query Mongodb Sum of firsts element of an array of objects

I would like to write a query for summing each field payment of the first object inside an array, for each element of my database.
The schema is the following:
var schema = new Schema({
plate : String,
category : String,
brand : String,
model : String,
sign : String,
tax : [{
date : { type: Date, default: Date.now },
payment : { type: Number, default: 0 },
}],
});
I wrote the following function for my query:
function(callback){
Machine.aggregate(
[
{$unwind: "$tax"},
{$group : {
_id : null ,
tot : { $sum: "$tax.payment"}
}}
]
,callback);
}
But in this way I retrieve the sum of all the payments inside the array tax. My goal is to take only the first, so I tried with $tax.0.payment and using arrayElemAt : [$tax,0] but all my trials gave a tot = 0.
The idea here is pick out the first element of each of payment array field via $arrayElemAt with projection and then group-sum the field $group $sum.
Query:
db.collection.aggregate([
{
$project: {
firstPayment: {
$arrayElemAt: [
"$tax",
0
]
}
}
},
{
$group: {
_id: null,
PaymentSum: {
$sum: "$firstPayment.payment"
}
}
}
]);
Demo O/P:
[
{
"PaymentSum": 11,
"_id": null
}
]
Machine.aggregate({$unwind:
{path: "$tax"}
},
{$group:{
_id: "$_id",
payment: {$first: "$tax.payment"}
}},
{$group: {
_id: null,
total: {$sum: "$payment"}
}}
)
Explanation:
First I used $unwind on tax, then in the first $group stage I grouped them according to _id,
that way I will get the first payment information from unwinded tax array.
Then I used $sum to add them in the second $group stage.
I tested with this data:
Machine collection docs:
{
"_id" : ObjectId("5dbf09a4d7912bcbc61ee9e4"),
"tax" : [
{
"payment" : 10
},
{
"payment" : 20
}
]
},
{
"_id" : ObjectId("5dbf09aad7912bcbc61ee9e5"),
"tax" : [
{
"payment" : 30
},
{
"payment" : 40
}
]
},
{
"_id" : ObjectId("5dbf09afd7912bcbc61ee9e6"),
"tax" : [
{
"payment" : 50
},
{
"payment" : 60
}
]
}
The result I got is:
{ "_id" : null, "tot" : 90 }
I hope this fulfills your requirements.
Add $arrayElemAt in your aggregate like this..
Machine.aggregate(
[
{$unwind: "$tax"},
{$group : {
_id : null ,
tot : { $sum: { $arrayElemAt : [ "$tax.payment", 0 ]}
}}
]
,callback);

Resources