Doing a Count on Array of Objects - mongodb

If I have the payload:
{
"objs": [
{ "_id": "1234566", "some":"data" },
{ "_id": "1234566", "some":"data" },
{ "_id": "2345666", "some":"otherdata" },
{ "_id": "4566666", "some":"yetotherdata" },
]
}
What would be the best filter to get all objects with id: "1234566"?

To find all the documents having the an obj with _id as 1234566:
db.collection.find({"objs._id":"1234566"});
To filter the obj items, having the specified _id, for the document. Assuming your document has the _id attribute.
db.collection.aggregate([
{$unwind:"$objs"},
{$match:{"objs._id":"1234566"}},
{$group:{"_id":"_id","objs":{$push:{"id":"$objs._id","some":"$objs.some"}}}},
{$project:{"_id":0,"objs":1}}
])
You can change the _id in the $group stage, if you want to group based on some different field.

Related

How do I sort results based on a specific array item in MongoDB?

I have an array of documents that looks like this:
patient: {
conditions: [
{
columnToSortBy: "value",
type: "PRIMARY"
},
{
columnToSortBy: "anotherValue",
type: "SECONDARY"
},
]
}
I need to be able to $sort by columnToSortBy, but using the item in the array where type is equal to PRIMARY. PRIMARY is not guaranteed to be the first item in the array every time.
How do I set my $sort up to accommodate this? Is there something akin to:
// I know this is invalid. It's for illustration purposes
$sort: "columnToSortBy", {$where: {type: "PRIMARY"}}
Is it possible to sort a field, but only when another field matches a query? I do not want the secondary conditions to affect the sort in any way. I am sorting on that one specific element alone.
You need to use aggregation framework
db.collection.aggregate([
{
$unwind: "$patient.conditions" //reshape the data
},
{
"$sort": {
"patient.conditions.columnToSortBy": -1 //sort it
}
},
{
$group: {
"_id": "$_id",
"conditions": { //re group it
"$push": "$patient.conditions"
}
}
},
{
"$project": { //project it
"_id": 1,
"patient.conditions": "$conditions"
}
}
])
Playground

Robomongo query to return a list of ids

I want to query my database in Mongo and then be able to copy and paste the list of ids the query returns.
I know I can project the _id like
db.getCollection('mymodel').find({}}, { _id: 1 })
But I want to be able to copy and paste the result as an array of ids, is there a way to achieve this with Robomongo/Mongo?
Is this query you want?
Using aggregate add all _ids to a set:
db.collection.aggregate([
{
"$group": { "_id": null, "ids": { "$addToSet": "$_id" } }
},
{
"$project": { "_id": 0 }
}
])
And the ouput is similar to this, an array called ids with all id:
"ids": [
ObjectId("5a934e000102030405000000"),
ObjectId("5a934e000102030405000004"),
ObjectId("5a934e000102030405000001"),
ObjectId("5a934e000102030405000005"),
ObjectId("5a934e000102030405000003"),
ObjectId("5a934e000102030405000002")
]
You can use $match to filter the documents you want to get the id like this example.

Is there a way to give order field to the result of MongoDB aggregation?

Is there any way to give order or rankings to MongoDB aggregation results?
My result is:
{
"score":100
"name": "John"
},
{
"score":80
"name": "Jane"
},
{
"score":60
"name": "Lee"
}
My wanted result is:
{
"score":100
"name": "John",
"rank": 1
},
{
"score":80
"name": "Jane"
"rank": 2
},
{
"score":60
"name": "Lee"
"rank": 3
}
I know there is a operator called $includeArrayIndex but this only works with $unwind operator.
Is there any way to give rank without using $unwind?
Using $unwind requires grouping on my collection, and I'm afraid grouping pipeline would be too huge to process.
The other way is to use $map and add rank in document using its index, and don't use $unwind stage because it would be single field array you can directly access using its key name as mention in last line of code,
$group by null and make array of documents in root array,
$map to iterate loop of root array, get the index of current object from root array using $indexOfArray and increment that returned index number using $add because index start from 0, and that is how we are creating rank field, merge object with current element object and rank field using $mergeObjects
let result = await db.collection.aggregate([
{
$group: {
_id: null,
root: {
$push: "$$ROOT"
}
}
},
{
$project: {
_id: 0,
root: {
$map: {
input: "$root",
in: {
$mergeObjects: [
"$$this",
{
rank: { $add: [{ $indexOfArray: ["$root", "$$this"] }, 1] }
}
]
}
}
}
}
}
]);
// you can access result using root key
let finalResult = result[0]['root'];
Playground

Getting all unique field-array combinations from the entire collection in MongoDB

Lets say I have the following collection
{
_id:1,
item:"cat"
keywords:['A','B']
},
{
_id:2,
item:"cat"
keywords:['B','C']
},
{
_id:3,
item:"dog"
keywords:['C','D']
},
I would like to get the following results:
[{"cat", "A"}, {"cat", "B"}, {"cat", "C"}, {"dog", "C"}, {"dog", "D"}]
Basically creating the combinations between item and keywords and removing duplicates.
Is that possible?
Thanks
You have to use $unwind on array and then you can use $group (by constant value) to get all elements into one array and $addToSet will handle uniqueness of specified pairs:
db.col.aggregate([
{
$unwind: "$keywords"
},
{
$group: {
_id: null,
unique: { $addToSet: { item: "$item", keyword: "$keywords" } }
}
}
])
You can then use onother $unwind on unique field to get a list of documents instead of single document as a result.

MongoDB find a given field and get average value

I'd like to get the average in a collection for a given property value. What am I doing wrong?
[{name:'Bob',city:'Barcelona',trips: 1 },
{name:'Bruce',city:'Barcelona',trips: 5 },
{name:'Bruno',city:'València',trips: 2 },
{name:'Bart',city:'Barcelona',trips: 3 }]
db.x.aggregate([{$group:{city:'Barcelona', $avg:"$trips"}}]);
You need to filter the documents using the $match operator i.e. create a pipeline before the $group operator which will filter all the documents in the collection based on the given city value.
In the preceding $group operator pipeline, you can then use a null key (as denoted by the _id field) to group all the documents from the previous pipeline and get the accumulated average:
db.x.aggregate([
{ "$match": { "city": "Barcelona" } },
{ "$group": { "_id": null, "$avg": "$trips" } }
]);
Another approach (not as optimal as the above) would be to group all the documents in the collection by the city key and then filter afterwards:
db.x.aggregate([
{ "$group": { "_id": "$city", "$avg": "$trips" } },
{ "$match": { "_id": "Barcelona" } }
]);