i am not able to query the sub document in mongodb - node.js

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]}
}
}
}
}
}
])

Related

MongoDB get the array element count based on the condition

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.

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

Cant able to find subdocument counts based on condition

I have a schema in which it has some fields..
i am not able to find query for this, i tried $group but was not able to find results
collection: tasks
{
"_id" : ObjectId("5a475ee4b342fa03e71192bd"),
"title" : "Some Title",
"assignedUsers" : [
{
"_id" : ObjectId("5a47386ee4788102e530f60d"),
"name" : "Sam",
"status" : "Unconfirmed"
},
{
"_id" : ObjectId("5a473878e4788102e530f60f"),
"name" : "Ricky",
"status" : "Rejected"
}
{
"_id" : ObjectId("5a47388be4788102e530f611"),
"name" : "Niel",
"status" : "Unconfirmed"
},
{
"_id" : ObjectId("5a47388be4788102e530f611"),
"name" : "ABC",
"status" : "Unconfirmed"
},
{
"_id" : ObjectId("5a473892e4788102e530f612"),
"name" : "Rocky",
"status" : "Rejected"
}
]
}
Result should contain
Unconfirmed=3
Rejected=2
Thanks
Use below query,
db.coll3.aggregate([{
$unwind: '$assignedUsers'
}, {
$group: {
_id: '$assignedUsers.status',
'count': {
$sum: 1
}
}
}
])
If you want to query against a particular document make sure, you use a $match as first stage and then use the other 2 $unwind and $group.
You would get result as
{ "_id" : "Rejected", "count" : 2 }
{ "_id" : "Unconfirmed", "count" : 3 }
Hope this helps.

Searching value in 2 different fields mongodb + node.js

I am newbie. But I try to learn the most logical ways to write the queries.
Assume I have collection which is as;
{
"id" : NumberInt(1),
"school" : [
{
"name" : "george",
"code" : "01"
},
{
"name" : "michelangelo",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "elisabeth",
"code" : NumberInt(21)
}
]
}
{
"id" : NumberInt(2),
"school" : [
{
"name" : "leonarda da vinci",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "michelangelo",
"code" : NumberInt(25)
}
]
}
I want to list occurence of a key with their corresponding code values.
As an example key : michelangelo
To find the occurence of the key, I wrote two differen aggregation queries as;
db.test.aggregate([
{$unwind: "$school"},
{$match : {"school.name" : "michelangelo"}},
{$project: {_id: "$id", "key" : "$school.name", "code" : "$school.code"}}
])
and
db.test.aggregate([
{$unwind: "$enrolledStudents"},
{$match : {"enrolledStudents.userName" : "michelangelo"}},
{$project: {_id: "$id", "key" : "$enrolledStudents.userName", "code" : "$enrolledStudents.code"}}
])
the result of these 2 queries return what I want as;
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
One of them to search in enrolledStudents, the other one is searching in school field.
Can these 2 queries reduced into more logical query? Or is this the only way to do it?
ps: I am aware that database structure is not logical, but I tried to simulate.
edit
I try to write a query with find.
db.test.find({$or: [{"enrolledStudents.userName" : "michelangelo"} , {"school.name" : "michelangelo"}]}).pretty()
but this returns the whole documents as;
{
"id" : 1,
"school" : [
{
"name" : "george",
"code" : "01"
},
{
"name" : "michelangelo",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "elisabeth",
"code" : 21
}
]
}
{
"id" : 2,
"school" : [
{
"name" : "leonarda da vinci",
"code" : "01"
}
],
"enrolledStudents" : [
{
"userName" : "michelangelo",
"code" : 25
}
]
}
Mongo 3.4
$match - This stage will keep all the school array and enrolledStudents where there is atleast one embedded document matching both the query condition
$group - This stage will combine all the school and enrolledStudents array to 2d array for each _id in a group.
$project - This stage will $filter the merge array for matching query condition and $map the array to with new labels values array.
$unwind - This stage will flatten the array.
$addFields & $replaceRoot - This stages will add the id field and promote the values array to the top.
db.collection.aggregate([
{$match : {$or: [{"enrolledStudents.userName" : "michelangelo"} , {"school.name" : "michelangelo"}]}},
{$group: {_id: "$id", merge : {$push:{$setUnion:["$school", "$enrolledStudents"]}}}},
{$project: {
values: {
$map:
{
input: {
$filter: {
input: {"$arrayElemAt":["$merge",0]},
as: "onef",
cond: {
$or: [{
$eq: ["$$onef.userName", "michelangelo"]
}, {
$eq: ["$$onef.name", "michelangelo"]
}]
}
}
},
as: "onem",
in: {
key : { $ifNull: [ "$$onem.userName", "$$onem.name" ] },
code : "$$onem.code"}
}
}
}
},
{$unwind: "$values"},
{$addFields:{"values.id":"$_id"}},
{$replaceRoot: { newRoot:"$values"}}
])
Sample Response
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
Mongo <= 3.2
Replace last two stages of above aggregation with $project to format the response.
{$project: {"_id": 0 , id:"$_id", key:"$values.key", code:"$values.code"}}
Sample Response
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
You can use $redact instead of $group & match and add $project with $map to format the response.
$redact to go through a document level at a time and perform $$DESCEND and $$PRUNE on the matching criteria.
The only thing to note is usage of $ifNull in the first document level for id so that you can $$DESCEND to embedded document level for further processing.
db.collection.aggregate([
{
$redact: {
$cond: [{
$or: [{
$eq: ["$userName", "michelangelo"]
}, {
$eq: ["$name", "michelangelo"]
}, {
$ifNull: ["$id", false]
}]
}, "$$DESCEND", "$$PRUNE"]
}
},
{
$project: {
id:1,
values: {
$map:
{
input: {$setUnion:["$school", "$enrolledStudents"]},
as: "onem",
in: {
key : { $ifNull: [ "$$onem.userName", "$$onem.name" ] },
code : "$$onem.code"}
}
}
}
},
{$unwind: "$values"},
{$project: {_id:0,id:"$id", key:"$values.key", code:"$values.code"}}
])

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.

Resources