How to query mongodb to fetch results based on values nested parameters? - mongodb

I am working with MongoDB for the first time.
I have a collection whose each document is roughly of the following form in MongoDB:
{
"name":[
{
"value":"abc",
"created_on":"2020-02-06 06:11:21.340611+00:00"
},
{
"value":"xyz",
"created_on":"2020-02-07 06:11:21.340611+00:00"
}
],
"score":[
{
"value":12,
"created_on":"2020-02-06 06:11:21.340611+00:00"
},
{
"value":13,
"created_on":"2020-02-07 06:11:21.340611+00:00"
}
]
}
How will I form a query so that I get the latest updated values of each field in the given document. I went through Query Embedded Documents, but I wasn't able to figure out how It is.
My expected output is:
{
"name": "xyz",
"score": "13"
}

If you always do push new/latest values to arrays name & score, then you can try below query, it would get last element from array as in general new/latest values will always be added as last element in an array :
db.collection.aggregate([
{ $addFields: { name: { $arrayElemAt: ['$name', -1] }, score: { $arrayElemAt: ['$score', -1] } } },
{ $addFields: { name: '$name.value', score: '$score.value' } }])
Test : MongoDB-Playground

Related

after aggregation how to check two fields are equal inside a document in mongodb

{
id: 1,
name: "sree",
userId: "001",
paymentData: {
user_Id: "001",
amount: 200
}
},
{
id: 1,
name: "sree",
userId: "001",
paymentData: {
user_Id: "002",
amount: 200
}
}
I got this result after unwind in aggregation any way to check user_Id equal to userId
Are you looking to only retrieve the results when they are equal (meaning you want to filter out documents where the values are not the same) or are you looking to add a field indicating whether the two are equal?
In either case, you append subsequent stage(s) to the aggregation pipeline to achieve your desired result. If you want to filter the documents, the new stage may be:
{
$match: {
$expr: {
$eq: [
"$userId",
"$paymentData.user_Id"
]
}
}
}
See how it works in this playground example.
If instead you want to add a field that compares the two values, then this stage may be what you are looking for:
{
$addFields: {
isEqual: {
$eq: [
"$userId",
"$paymentData.user_Id"
]
}
}
}
See how it works in this playground example.
You could also combine the two as in:
{
$addFields: {
isEqual: {
$eq: [
"$userId",
"$paymentData.user_Id"
]
}
}
},
{
$match: {
isEqual: true
}
}
Playground demonstration here

MongoDb 4.x Query with projection without empty entries using find

We are now using mongo-db to store data from tests. I am using Mongo-Shell
The document is structured like this:
{
static1:"abc",
static2:"xyz",
static3:"asd", [...],
nested:[
{
data1: "d1",
data2: "d2",
},
{
data1: "dx",
data4: "d4",
data5: "d5",
data6: "d6",
},
{
data1: "ds",
data8:"data8"
}, [...]
]
}
So the static-data is always in the same structure, but for every measurement the object can look different. It could be voltage with upper, lower and actual value. Or just comparison of a target value and an actual value. Highly dynamic.
Same data1-name represent same attributes.
Now e.g. I want to display only some static data and one (or several) attributes of a nested document.
I am using this query:
find({}, {_id:0, data1:1, "nested.data8":1}).pretty()
As expected, only the static-data1 is displayed, but the dynamic measurements has lots of empty objects in the shell-output
Example-Output:
{
"static1" : "123",
"nested" : [
{ },
{ },
{ }, [...] ,
{ "data8" : "OK" }
] }
The desired output would be:
{
"static1" : "123",
"nested" : [
{ "data8" : "d8" }
]
}
I also tried this query on the mongo shell:
aggregate( { $addFields: {"static":"$static1", "data8":"$nested.data8"} }, { $project:{"static1":1, "nested.data8":1} } ).pretty()
But the result is the same.
I hope there is a ways to get rid of the empty documents in the output.
Thanks
You can try $filter operator to filter result of nested array by checking condition not equal to empty object {}
db.collection.aggregate([
{
$project: {
_id: 0,
data1: 1,
"nested.data8": 1
}
},
{
$set: {
nested: {
$filter: {
input: "$nested",
cond: { $ne: ["$$this", {}] }
}
}
}
}
])
Playground

How to copy a value from an array to another field and then pop it?

Assume we have inserted the following object.
db.getCollection('things').insert(
{ plonks: [ "plonk1", "plonk2", "plonk3" ], _id: 1001 }
)
I can update the document and insert a new field like so.
db.getCollection('things').update(
{ _id: 1001 },
{ $set: { plonk: "beep" } }
)
I can also pop the latest addition like this.
db.getCollection('things').update(
{ _id: 1001 },
{ $pop: { plonks: 1 } }
)
Now, I'd like to connect those two operations somehow so that the popped element will be set as a value in the separate field. I want to end up in a document looking as follows.
{
_id: 1001,
plonk: "plonk3",
plonks: [ "plonk1", "plonk2" ]
}
There's nothing on fetching/storageing the popped value in the docs for $pop. I've seen an answer to a similar question, which didn't work out as they're copying the element onto itself, reducing the original array. I tried reading up on $aggregate as I sensed that let me provide a list of operations but I'm fumbling in darkness without any method of approach.
I also tried with a combination of $match and $set getting a bit success when copying over the whole array like this.
db.getCollection('things').aggregate([
{ $match: { _id: 1001 } },
{ $set: { plonk: "$plonks" } }
])
However, I haven't got the $sliceing to work.
db.getCollection('things').aggregate([
{ $match: { _id: 1001 } },
{ $set: { plonk: { plonks: { $slice: 1 } } } }
])
You can make use of the MongoDB Aggregation Pipeline support for update command feature.
Note: This will work only on MongoDB version >= 4.2
For plonk key, make use of $arrayElemAt to fetch the last element of the array
For plonks key, use $slice accumulator, where the position option is the size of plonks array - 1
db.getCollection('things').update({
"plonks": {"$exists": true}, // Find Conditions
},
[
{
"$set": {
"plonk": { // Fetch last element from `plonks` array
"$arrayElemAt": [
"$plonks",
-1
]
},
"plonks": {
"$slice": [ // Get elements from array till n-1
"$plonks",
{
"$subtract": [
{
"$size": "$plonks" // Fetches size of `plonks` array
},
1
]
},
],
},
}
},
])
Mongo Playground Sample Execution

MongoDB - count by field, and sort by count

I am new to MongoDB, and new to making more than super basic queries and i didn't succeed to create a query that does as follows:
I have such collection, each document represents one "use" of a benefit (e.g first row states the benefit "123" was used once):
[
{
"id" : "1111",
"benefit_id":"123"
},
{
"id":"2222",
"benefit_id":"456"
},
{
"id":"3333",
"benefit_id":"456"
},
{
"id":"4444",
"benefit_id":"789"
}
]
I need to create q query that output an array. at the top is the most top used benefit and how many times is was used.
for the above example the query should output:
[
{
"benefit_id":"456",
"cnt":2
},
{
"benefit_id":"123",
"cnt": 1
},
{
"benefit_id":"789",
"cnt":1
}
]
I have tried to work with the documentation and with $sortByCount but with no success.
$group
$group by benefit_id and get count using $sum
$sort by count descending order
db.collection.aggregate([
{
$group: {
_id: "$benefit_id",
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } }
])
Playground
$sortByCount
Same operation using $sortByCount operator
db.collection.aggregate([
{ $sortByCount: "$benefit_id" }
])
Playground

MongoDB aggregate - filter by subdocument

I have a mongodb collection with structure like that:
[
{
name: "name1",
instances: [{value:1, score:2}, {value:2, score:5}, {value:2.5, score:9}]
},
{
name: "name2",
instances: [{value:6, score:3}, {value:1, score:6}, {value:3.7, score:5.2}]
}
]
When I want to get all the data from a document, I use aggregate because I want each instance returned as a separate document:
db.myCollection.aggregate([{$match:{name:"name1"}}, {$unwind:"$instances"}, {$project:{name:1, value:"$instances.value", score:"$instances.score"}}])
And everything works like I want it to.
Now for my question: I want to filter the returned data by score or by value. For example, I want an array of all the subdocuments of name1 which have a value greater or equal to 2.
I tried to add to the $match object 'instances.value':{$gte:2}, but it didn't filter anything, and I still get all 3 documents for this query.
Any ideas?
After unwinding instances then again used $match as below
db.collectionName.aggregate({
"$match": {
"name": "name1"
}
}, {
"$unwind": "$instances"
}, {
"$match": {
"instances.value": {
"$gte": 2
}
}
}, {
$project: {
name: 1,
value: "$instances.value",
score: "$instances.score"
}
})
Or if you tried $match after project then used as below
db.collectionName.aggregate([{
$match: {
name: "name1"
}
}, {
$unwind: "$instances"
}, {
$project: {
name: 1,
value: "$instances.value",
score: "$instances.score"
}
}, {
"$match": {
"value": {
"$gte": 2
}
}
}])