Sum Embedded Document Fields in Mongo Aggregate Query - mongodb

Data looks like:
{
"_id":5345345345,
"city": "Detroit",
"people":[
{
"name":"Larry",
"value":1200
},
{
"name":"Steve",
"value":1100
},
{
"name":"Larry",
"value":1000
},
{
"name":"Joe",
"value":800
},
{
"name":"Larry",
"value":500
},
{
"name":"Joe",
"value":700
}
]
}
I have this in the middle of an aggregate query. What can I do from here to sum the value field and have unique entry for each person in "people"? i.e.
{
"_id":5345345345,
"city": "Detroit",
"people":[
{
"name":"Larry",
"value":2700
},
{
"name":"Steve",
"value":1100
},
{
"name":"Joe",
"value":1500
}
]
}
Also, cannot have query that returns empty document in case the "people" array is empty, e.g., cannot do:
{$unwind:{
path:people
}
},
{$group :
{
_id:"$people.name",
total_value:{"$sum":"people.value"}
}
}
which would work fine, if it didn't return an empty document when people array is empty.
Need to use $addToSet and $sum somehow? How to do this keeping value associated with name?

Use
{
$unwind: {
path : "$people",
preserveNullAndEmptyArrays : true // this line!
}
}

Related

Conditional array update MongoDB

I have a collection like this one:
{
"citizen":[
{
"country":"ITA",
"language":"Italian"
},
{
"country":"UK",
"language":"English"
},
{
"country":"CANADA",
"language":"French"
}]
}
I'm trying to update the collection but with a certain condition. I need to update remove an element of the array citizen if the value in the field country is longer than 3 characters.
I got that to remove an element i have to use $pull, to check the size of a string I have to use $strLenCP and $gt is greater than, but I'm struggling to put them together.
The result of the update should be:
{
"citizen":[
{
"country":"ITA",
"language":"Italian"
},
{
"country":"UK",
"language":"English"
}]
}
Any suggestions?
EDIT:
with the collection, as it is, the command:
db.getCollection('COLLECTION').update( { }, { $pull: {"citizen": {"country": /^[\s\S]{4,}$/}}}, { multi: true })
works perfectly.
I tried it on another collection as this one:
{
"cittadino":{
"citizen":[
{
"country":"ITA",
"language":"Italian"
},
{
"country":"UK",
"language":"English"
},
{
"country":"CANADA",
"language":"French"
}]
}
}
and it doesn't update anymore. What should i do?
You're on the right path:
db.getCollection('COLLECTION').update( { }, { $pull: {"citizen": {"country": /^[\s\S]{4,}$/}}}, { multi: true })
$pull operator takes a value or a condition. You need to use this to filter the array based on the "country" property
EDIT:
Use the dot-notation to access the nested document
db.getCollection('COLLECTION').update( { }, { $pull: {"cittadino.citizen": {"country": /^[\s\S]{4,}$/}}}, { multi: true })

how to filter the fields of a document within another document in mongodb?

My document is the following
{
"name":"Name1",
"status":"active",
"points":[
{
"lag":"final"
},
{
"lag":"final"
}
]
},
{
"name":"Name2",
"status":"active",
"points":[
{
"lag":"final"
},
{
"lag":""
}
]
}
I need to get all the documents that have some value in the lag field, for this example should get two document,
I tried with this query, but it only returns me when all points have full lag
{ "points.lag":{$not:{ $eq:"" }},status:{$in:['active']}}
Play
You need to use elemMatch to check whether atleast one element matches the condition.
db.collection.find({
"points": {
"$elemMatch": {
"lag": {
$ne: null
}
}
}
})

Trim string values of whitespace, from an array of sub-documents with string field

On all documents of my collection I want to perform a $trim operation to a specific field of an object that is in an array.
Example:
{
"_id" : ObjectId("53857680f7b2eb611e843a32"),
"company": Testcompany
"customer" :
"name": Testuser,
"addresses" : [
{
"_id" : ObjectId("5d6d2f96e3fdc8001077ac6c"),
"street" : "Teststreet. ",
"houseNr" : "133",
},
{
"_id" : ObjectId("5d6d2f96e3fdc8001077ac7b"),
"street" : " Simplestreet. ",
"houseNr" : "12",
}
],
}
In the example, I want to $trim all values of the field: "customer.addresses.street"
To answer the upcoming questions:
I know the article you mentioned (Removing white spaces (leading and trailing) from string value) but theres no example how to do it within an array.
My problem is, how to access the attributes within an array, heres the example of plain values:
[{ $set: { category: { $trim: { input: "$category" } } } }],
Yes, I want to update the values of all documents within the collection
One possible way to do:
db.YOUR_COLLECTION.find({}).forEach(
function(doc) {
db.Trim.update(
{ "_id":doc._id },
{
"$set": {
"customer.addresses":doc.customer.addresses.map(
function(child) {
return Object.assign(
child,
{ street: child.street.trim() }
)
}
)
}
}
)
}
)
Obs: Solution with Javascript Executed in MongoShell.
You can use $map and $trim in an updateMany aggregation pipeline like this :
db.YOUR_COLLECTION.updateMany({"customer.addresses":{$ne:null}},[
{
$set: {
"customer.addresses":
{
$map: {
input: "$customer.addresses",
as: "address",
in: { $trim: { input: "$$address" } }
}
}
}
}
])

Fillter array when publishing a mongdb collection

I'm trying to return a specific collection, however, I want to filter an array within the collection. I'm not sure if this is possible. In the example below I'm trying to return the collection with _id: 7ARk3dc2JA8g5pamA and filter out the array object for "candidateUserId": "2". I'm doing this in a Meteorjs application.
Eg: `Collection'
{
"_id": "7ARk3dc2JA8g5pamA",
"jobTitle": "Developer",
"candidateApplication": [
{
"candidateUserId": "1",
"applied": true
},
{
"candidateUserId": "2",
"applied": false
}
]
}
Path: Publish command
return Jobs.find({ _id: 7ARk3dc2JA8g5pamA }, {
$filter: {
input: candidateApplication,
cond: { candidateUserId: { $eq: 1 } }
}
});
Jobs.find({ _id: 7ARk3dc2JA8g5pamA }, { candidateApplication: { $elemMatch: { candidateUserId: 2 } } }
This should return the document with only the _id and the candidateUserId array, but that array will now only contain the object that you want.
{ "_id" : 7ARk3dc2JA8g5pamA, "candidateApplication" : [ { "candidateUserId": 2, "applied": false } ] }
You can then get to the data with candidateApplication[0].candidateUserId and candidateApplication[0].applied
As mentioned in the comments above, if there are more instances of that same candidateUserId, only the first will be returned.

How to find several items from the same array in Mongodb?

In the collection "friendship" I would like to find all Bart's friends that are 10. The $elemMatch query only returns the first matching element of the array : I only get Milhouse. How can I get Milhouse and Martin ?
{ name:'Bart',
age:10
friends:[
{ name:'Milhouse',
age:10
},
{ name:'Nelson',
age:11
},
{ name:'Martin',
age:10
}
]
},
{ name:'Lisa',
age:8
friends:[
...
]
}
Try to use aggregation framework for this task (especially $unwind operator):
db.friendship.aggregate([
{ $match: { name: "Bart" } },
{ $unwind: "$friends" },
{ $match: { "friends.age": 10 } }
]);