MongoDB get the array element count based on the condition - node.js

For the given structure I need to find out the count of the likes array based on the unique slug value
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"name" : ["Name"],
"likes" : ["emp1"],
"slug": 'slugabcd'
}
{
"_id" : ObjectId("4e8ae86d08101908e1000002"),
"name" : ["Another ", "Name"],
"likes" : ["emp1","emp2","emp4"],
"slug": 'slugxyz'
}
{
"_id" : ObjectId("4e8ae86d08101908e1000002"),
"name" : ["Another ", "Name"],
"slug": 'slugpqr'
}
Here is my code but not working
db.blog.aggregate({slug:"slugxyz"},{$project:{NumberOfItemsInArray:{$size:"likes"}}}).count();
How can we achieve this?

Without using any query stage:
db.blog.aggregate({$project:{slug : "$slug", numberOfLikes:{$size:"$likes"}}})

Try the following:
const pipeline = [
{
$group: {
_id: "$slug",
likesCount: {$sum: {$size: "$likes"}}
}
]
Here we are grouping by the unique "slug" value and counting the size of "likes" array field.

Related

How to fetch document with filtered nested document using Mongo query?

Here is my organization collection.
[{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2e"),
"name" : "ClassA",
"orgMembers" : [
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2f"),
"userId" : "Ben",
},
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2f"),
"userId" : "Anton",
}
],
},
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2e"),
"name" : "ClassA",
"orgMembers" : [
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2f"),
"userId" : "Ben",
}
],
}]
Each document has properties like _id, name, orgMembers which represent document information.
And orgMembers is the Array of Members (_id, userId) who belongs to organization.
In this collection, I want to fetch the organizations which includes orgMember with Anton as userId and as well orgMembers of fetched organization document should only contain Anton as a orgMember.
Expected Result is likewise
[{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2e"),
"name" : "ClassA",
"orgMembers" : [
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2f"),
"userId" : "Anton",
}
],
}]
Here ClassA organization has two orgMembers but need to be filtered matching with userId.
I have tried with
documentModel.find({ 'orgMembers.userId': 'Anton' })
But within this query, I get the result like this.
[{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2e"),
"name" : "ClassA",
"orgMembers" : [
// should be omitted
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2f"),
"userId" : "Ben",
},
// should be contained
{
"_id" : ObjectId("5fd5fc1b9f117029b5233b2f"),
"userId" : "Anton",
}
],
}]
For expected result, orgMember with userId: Ben should be omitted.
How can I get my expected result with mongo query?
I believe this will be worked on your side
db.collection.find({
"orgMembers.userId": "Anton"
},
{
orgMembers: {
"$elemMatch": {
userId: "Anton"
}
}
})
not sure if i quite got your requirement:
but try this if it works
db.collection.find({
"orgMembers.userId": {
$regex: "Anton",
$options: "i"
}
},
{
name: 1,
"orgMembers.$": 1
})
this is to return only the userId you are looking for in orgMembers.if there are more orgmembers they will not be part of output

MongoDB Aggregate on Nested Object

Any idea why this isn't returing the average (returns null)?
Pattern.aggregate([
{ $match: {
name: 'asdfaddf'
}},
{ $unwind: "$ratings" },
{ $group : { _id: "test", ratings : { $avg : "$ratings.rating" } } }
My document looksl ike this
"ratings" : [
{
"rating" : "5",
"userID" : "5a73ef842aed0f399ff4ee40",
"_id" : ObjectId("5a74893230eed03f1ebf3c5f")
},
{
"rating" : "3",
"userID" : "awefawefawfawef",
"_id" : ObjectId("5a74893230aad03f1ebf3c5e")
}
],
Looks like using non-numeric values, as state in the mongodb documentation these will be ignored:
$avg ignores non-numeric values, including missing values. If all of the operands for the average are non-numeric, $avg returns null since the average of zero values is undefined.
https://docs.mongodb.com/manual/reference/operator/aggregation/avg/#non-numeric-or-missing-values

i am not able to query the sub document in mongodb

i am not able to query results in this query,
i want result based on detail.type (like fetch record where detail.type="one") and fetch only first 10 records in detail.numbers array
{
"_id" : ObjectId("5a27b609e101b6092b4ebf91"),
"city" : "Mumbai",
"detail" : [
{
"type" : "One",
"name" : "Some name",
"_id" : ObjectId("5a27b609e101b6092b4ebf92"),
"numbers" : [
"72598xxx78",
"81301xxx88",
"83302xxx30",
"84309xxx43",
"85309xxx77",
"86309xxx61",
"87270xxx88",
"85272xxx36",
"88272xxx23",
"85276xxx01"
]
},
{
"name" : "Some name",
"type" : "two",
"_id" : ObjectId("5a28e954d4f5a30527d92a32"),
"contact" : [
"72598xxx78",
"81301xxx88",
"83302xxx30",
"84309xxx43",
"85309xxx77",
"86309xxx61",
"87270xxx88",
"85272xxx36",
"88272xxx23",
"85276xxx01"
]
},
]
}
MongoDB facilitates querying over array elements using $elemMatch operator.
According to description as mentioned into above question as a solution to it please try executing following MongoDB query to fetch required data from MongoDB collection.
db.collection.find({
detail: {
$elemMatch: {
type: 'One'
}
}
}, {
_id: 1,
city: 1,
'detail.$': 1
})
db.collection.aggregate([
{
$project:{
detail:{
$map:{
input:{$filter:{input:"$detail",as:"d",cond:{$eq:["$$d.type","One"]}}},
as:"d",
in:{
"type" : "$$d.type",
"name" : "$$d.name",
"numbers":{$slice:["$$d.numbers",10]}
}
}
}
}
}
])

Find sub-documents using $in with MongoDB

My task is to find individual authors(comments.user_id) comment on the article (_id)
{
"_id" : ObjectId("56479d9c8510369a4ecea3a9"),
"comments" : [
{
"text" : "222",
"user_id" : ObjectId("563f2db0e2bf6c431b297d45"),
},
{
"text" : "333",
"user_id" : ObjectId("563f2db0e2bf6c431b297d45"),
},
{
"text" : "444",
"user_id" : ObjectId("563f2db0e2bf6c431b297d45"),
},
{
"text" : "55555",
"user_id" : ObjectId("563e3337e2bf6c431b297d41"),
},
{
"text" : "00000",
"user_id" : ObjectId("563f7c0a8db7963420cd5732"),
},
{
"text" : "00001",
"user_id" : ObjectId("563f7c0a8db7963420cd5732"),
}
]
}
My query looks as follows
db.getCollection('messages').find({
'_id': ObjectId("56479d9c8510369a4ecea3a9"),
'comments.user_id': {$in : [
ObjectId("563e3337e2bf6c431b297d41"),
ObjectId("563f7c0a8db7963420cd5732")
]}
})
It returns all comments. Please help to understand why it happens.
Expected Result
{
"_id" : ObjectId("56479d9c8510369a4ecea3a9"),
"comments" : [
{
"text" : "55555",
"user_id" : ObjectId("563e3337e2bf6c431b297d41"),
},
{
"text" : "00000",
"user_id" : ObjectId("563f7c0a8db7963420cd5732"),
},
{
"text" : "00001",
"user_id" : ObjectId("563f7c0a8db7963420cd5732"),
}
]
}
update query (hopelessness)
db.getCollection('messages').find(
{'_id': ObjectId("56479d9c8510369a4ecea3a9")},
{'comments.user_id': {$in: ["563f2db0e2bf6c431b297d45", "563e3337e2bf6c431b297d41"]}},
{'comments.user_id': {$elemMatch: {$in: ["563f2db0e2bf6c431b297d45", "563e3337e2bf6c431b297d41"]}}}
)
db.getCollection('messages').find(
{'_id': ObjectId("56479d9c8510369a4ecea3a9")},
{comments: {$elemMatch: {'user_id': {$in : [ObjectId("563f2db0e2bf6c431b297d45"), ObjectId("563f7c0a8db7963420cd5732")]}}}}
)
I return only 1 record, and I have all the records from these authors
As you've seen, the $ and $elemMatch projection operators only include the first matching element.
To include multiple, filtered array elements in your projection of the comment array, your can use aggregate with the $redact operator instead of find:
db.getCollection('test').aggregate([
{$match: {
'_id': ObjectId("56479d9c8510369a4ecea3a9"),
'comments.user_id': {$in : [
ObjectId("563e3337e2bf6c431b297d41"),
ObjectId("563f7c0a8db7963420cd5732")
]},
}},
{$redact: {
$cond: {
if: {
$or: [
{$eq: ['$user_id', ObjectId("563e3337e2bf6c431b297d41")]},
{$eq: ['$user_id', ObjectId("563f7c0a8db7963420cd5732")]},
{$not: '$user_id'}
]
},
then: '$$DESCEND',
else: '$$PRUNE'
}
}}
])
$redact iterates over each doc like a tree, keeping or trimming the fields of each doc as it's $cond expression dictates.
It gets a bit tricky to wrap your head around $redact, but it's basically saying that if the level's user_id field matches either of the two ObjectIds in your $in, or it's not present (i.e. as it is in the top level of the doc), include the data, otherwise remove it.

Get documents that contains specific attributes in array

I have a document collection with recipes that looks like this:
{
"title" : "Pie",
"url" : "pie.png",
"people" : "4",
"ingredients" : [
{
"amount" : "150",
"measure" : "g",
"name" : "Butter"
},
{
"amount" : "200",
"measure" : "g",
"name" : "Flour"
}
],
"_id" : ObjectId("55acf33223ae282719bdc9b7")
}
Im trying to create a query that retrieves all the documents that contains multiple fields, like "butter" and "flour".
I have managed to retrieve documents that contains one field, like the query below:
db.recipe.find(
{"ingredients.name": "Butter"},
{"_id": 1, "ingredients": {"$elemMatch": {"name": "Butter"}}},
callback
);
I’ve tried using
{
$all: [
{ $elemMatch: { name: "Butter" }},
{ $elemMatch:{ name: "Flour"}}
]
}
but I cant get it to work. Any help appreciated!
How about this:
db.recipe.find({
"$and": [
{ "ingredients.name": "Butter" },
{ "ingredients.name": "Flour" }
]
})
EDIT(thanks to #BlakesSeven):
The shorter way to write the above is using the $all operator.
db.recipe.find({ "ingredients.name": { "$all": ["Butter", "Flour"] } }

Resources