mongoose populate after another populate - arrays

I have 3 mongo collections.
resource
access
user
the user has a relationship with access and access has a relationship with resource. I want when populate a user all join be done and I see the full resource that user has access them.
I tried this code but was n't expected response
Users.findById(req.params.userId)
.populate({path:'accessID',model:'accesses'})
.populate({path:'accessID.accessLists',model:'resources'})
and this
Users.findById(req.params.userId)
.populate({path:'accessID',model:'accesses'})
.then
(
(user)=>
{
Users.populate(user,{path})
}
)
here is my defections
Resource:
var ResourceSchema =new Schema
({
resource:String
method:
{ type:String,
enum:["GET","PUT","POST","DELETE"]
}
})
Access:
var AccessSchema =new Schema
({
name:String
access_List:
[
{ type: Schema.Types.ObjectId, ref:'resources'}
]
})
User:
var UserSchema = new Schema
({
name:String,
email:String,
accessID:
{ type: Schema.Types.ObjectId, required: true, ref: 'accesses' }
)}
my output like this
{"accessID":
{
"_id": "5cef82d1d2f7e14d3013ac8c",
"accessLists":
[
"5cee8e093defe92b88c1626b",
"5cee8e093defe92b88c1626c",
"5cee8e093defe92b88c1626d",
],
"name": "Admin",
},
"name": "ali"
"email": "ali17#yahoo.com"
}
but I want my output like this:
{
"name": "ali",
"email": "ali17#yahoo.com"
"accessID":
{
"_id": "5cef82d1d2f7e14d3013ac8c",
"accessLists":
[
{
"_id": "5cee8e093defe92b88c1626b",
"resource": "groups",
"method": "POST",
},
{
"_id": "5cee8e093defe92b88c1626c",
"resource": "groups",
"method": "DELETE",
},
{
"_id": "5cee8e093defe92b88c1626d",
"resource": "groups",
"method": "GET",
}
]
"name": "Admin",
}
}

User.findById(req.params.userId)
.populate({path:'accessID',model:'accesses'})
.populate({ path: 'accessID', populate: { path: 'access_List',model:'resources' }})
.exec(function (err, response) {
})
I have tried and its working.
More Help: Example
also, I have tried with aggregation :
db.users.aggregate([
{$match : { _id : ObjectId("5cf00388eae5400e9439b2df")}},
{
$lookup: {
from:"access",
localField:"accessID",
foreignField: "_id",
as:"accessID"
}
},
{ "$unwind": '$accessID'},
{ "$unwind": '$accessID.access_List'},
{
$lookup: {
from:"resources",
localField:"accessID.access_List",
foreignField: "_id",
as:"access_List"
}
},
{
$group : {
_id : {
"_id" : '$_id', "name" : '$name', "email" : '$email',
"accessID" : {"_id" : '$accessID._id', "name": '$accessID.name' }
},
access_List : { $push : { "$arrayElemAt": [ "$access_List", 0 ] } }
}
},
{ $project : {
_id : '$_id._id',
name : '$_id.name',
email : '$_id.email',
accessID : {
_id : '$_id.accessID._id',
name: '$_id.accessID.name',
access_List : '$access_List'
}
}
}
]).pretty()

Related

Nested Query on Array in MongoDB collection

I have below User collection and I want find/update query to update nested array elements.
{
_id:"000-0000-0001",
Roles :{
0000-0000-0011: //EngagementId
["0000-0000-0111", "0000-0000-0112", "0000-0000-3333"],//RoleId
"0000-0000-0012" :
["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002",
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112", "0000-0000-3333"]
}
}
Requirement: I want to pull RoleId 0000-0000-3333 if the array have combination of 0000-0000-3333 and 0000-0000-0112
Below is expected result :
{
_id:"000-0000-0001",
Roles :{
"0000-0000-0011" : ["0000-0000-0111", "0000-0000-0112"],
"0000-0000-0012" : ["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002"
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"]
}
}
Note : Find/update will work on Key:value or if it is nested then key.key:value, but in above example we have key.value.[values]:$pull(value) and that's the challaenge.
The data model necessitates a complicated update with a pipeline.
Here' one way to do it.
db.collection.update({
"Roles": {"$exists": true}
},
[
{
"$set": {
"Roles": {
"$arrayToObject": {
"$map": {
"input": {"$objectToArray": "$Roles"},
"as": "roleKV",
"in": {
"k": "$$roleKV.k",
"v": {
"$cond": [
{
"$and": [
{"$in": ["0000-0000-0112", "$$roleKV.v"]},
{"$in": ["0000-0000-3333", "$$roleKV.v"]}
]
},
{
"$filter": {
"input": "$$roleKV.v",
"cond": {"$ne": ["$$this", "0000-0000-3333"]}
}
},
"$$roleKV.v"
]
}
}
}
}
}
}
}
],
{"multi": true}
)
Try it on mongoplayground.net.

how to get objects from array by value inside desired object by mongoose

I have following bson data in mongoDB
{
name : "company 1",
createPurchaseOrder :[
{
partyName : "p1",
poNumber : "789",
},
{
partyName : "p2",
poNumber : "700",
},
{
partyName : "p3",
poNumber : "889",
},
{
partyName : "p1",
poNumber : "800",
},
{
partyName : "p1",
poNumber : "200",
},
]
}
I want objects from partyName for example if partyName is p1 then I want this
[
{
partyName: 'p1',
poNumber: '789',
},
{
partyName: 'p1',
poNumber: '800',
},
{
partyName: 'p1',
poNumber: '200',
},
];
I tried this
const user1 = await User.findOne({name : "company 1"},{createPurchaseOrder:{$elemMatch:{partyName:"p1"}}})
its gives me only first matched object, then I tried this
const user1 = await User.find({name : "company 1"},{createPurchaseOrder:{$elemMatch:{"partyName.$":"p1"}}})
it returned an empty array. please help me with this I want solution which gives me all objects which have matched value
You need to use the aggregation pipeline for this, the find operator is very limited with what structure transformation it can do and for example this use case can't be achieved using it.
Just use $filter instead:
db.collection.aggregate([
{
$match: {
name: "company 1"
}
},
{
$addFields: {
createPurchaseOrder: {
$filter: {
input: {
$ifNull: [
"$createPurchaseOrder",
[]
]
},
cond: {
$eq: [
"$$this.partyName",
"p1"
]
}
}
}
}
},
{
$unwind: "$createPurchaseOrder"
},
{
$replaceRoot: {
newRoot: "$createPurchaseOrder"
}
}
])
Mongo Playground

How can I find subdocument using Mongoose?

I have defined a model like this.
const ShotcountSchema = new Schema({
shotCountId : {
type : ObjectId,
required : true,
ref : 'member-info'
},
userId : {
type : String,
required : true,
unique : true
},
shot : [{
shotId : {
type : String,
required : true,
unique : true
},
clubType : {
type : String,
required : true
}
createdAt : {
type : Date,
default : Date.now
}
}]
});
If you perform a query to find subdocuments based on clubType as follows, only the results of the entire document are output.
For example, if I write the following code and check the result, I get the full result.
const shotCount = await ShotcountSchema.aggregate([
{
$match : { shotCountId : user[0]._id }
},
{
$match : { 'shot.clubType' : 'driver' }
}
]);
console.log(shotCount[0]); // Full result output
I would like to filter the subdocuments via clubType or createdAt to explore. So, I want these results to be printed.
{
_id: new ObjectId("61d67f0a74ec8620f34c57ed"),
shot: [
{
shotId: 'undefinedMKSf*Tf#!qHxWpz1hPzUBTz%',
clubType: 'driver',
shotCount: 20,
_id: new ObjectId("61d67f0a74ec8620f34c57ef"),
createdAt: 2022-01-06T05:32:58.391Z
}
]
}
How should I write the code?
db.collection.aggregate([
{
"$match": {
_id: ObjectId("61d67f0a74ec8620f34c57ed"),
"shot.clubType": "driver",
shot: {
$elemMatch: {
$and: [
{
"createdAt": {
$gte: ISODate("2022-01-07T05:32:58.391Z")
}
},
{
"createdAt": {
$lte: ISODate("2022-01-09T05:32:58.391Z")
}
}
]
}
}
}
},
{
"$set": {
shot: {
"$filter": {
"input": "$shot",
"as": "s",
"cond": {
$and: [
{
"$eq": [
"$$s.clubType",
"driver"
]
},
{
"$gte": [
"$$s.createdAt",
ISODate("2022-01-07T05:32:58.391Z")
]
},
{
"$lte": [
"$$s.createdAt",
ISODate("2022-01-09T05:32:58.391Z")
]
}
]
}
}
}
}
}
])
mongoplayground

How to compare the elements of an array from the same object in MongoDB?

I've a document collection with the below data
[{
_id:ObjectId(),
status:"created",
users:[{
email:"xyz#gmail.com",
role:"admin"
},
{
email:"abc#gmail.com",
role:"admin"
},
{
email:"abc#gmail.com",
role:"user"
}]
},
{
_id:ObjectId(),
status:"created",
users:[{
email:"pqr#gmail.com",
role:"admin"
},
{
email:"abc#gmail.com",
role:"admin"
},
{
email:"pqr#gmail.com",
role:"user"
}]
}]
I want to fetch the count of documents which have the user with both "admin" and "user" roles and the status is "created". Now I'm trying to match the email id and also the role.
db.documents.aggregate([
{"$match":{
"status":"created",
"$expr":{
"$eq":[
{"$size":{"$setIntersection":["$users.email_id","$users.email_id"]}},
0
]
}
}},
])
Is there any way to compare the elements of the array from the same object?
I want to fetch the count of documents which have the user with both
"admin" and "user" roles and the status is "created". Now I'm trying
to match the email id and also the role.
You can use an aggregation or a find query as follows:
var MATCH_EMAIL = "abc#gmail.com"
db.collection.aggregate( [
{
$match: {
$and: [ { status: "created" } , { "users": { $elemMatch: { role: "admin", email: MATCH_EMAIL } } } ],
"users": { $elemMatch: { role: "user", email: MATCH_EMAIL } }
}
},
{
$count: "Count"
}
] )
// -- or --
db.collection.find( {
$and: [ { status: "created" } , { "users": { $elemMatch: { role: "admin", email: MATCH_EMAIL } } } ],
"users": { $elemMatch: { role: "user", email: MATCH_EMAIL } }
} ).count()
Note the query filter is same in both cases. The aggregation returns a result document like { "Count" : 1 }, but the find query returns just the count value, like 1.
[ EDIT ADD ]
Updated code to have the same email for "user" and "admin" riles.
db.collection.aggregate( [
{
$match: {
$and: [ { status: "created" } , { "users.role": "admin" } ],
"users.role": "user"
}
},
{
$unwind: "$users"
},
{
$group: {
_id: { _id: "$_id", email: "$users.email" },
roles: { $push: "$users.role" }
}
},
{
$match: {
$expr: { $gt: [ { $size: "$roles" }, 1 ] }
}
},
{
$count: "Count with user+admin roles and same email"
}
] )

Nodejs Mongoose select a entire document but only with selected items in a property array

I'm new to nodejs and currently i'm developing a Web API using nodejs, express and mongoose. Can anyone help me on following mongoose query.
var EventSchema = new Schema({
event_title: {
type: String,
required: true
},
service_order: [{
_id:{
type: String
},
service : {
type: Schema.Types.ObjectId,
ref: "Service"
}
}]
});
I want to select a entire Event document but only with selected service_order items in that array
ex :-
this is a entire Event document
{
_id: "sample_id",
name: 'some name',
"service_order": [
{
"_id": "1"
"service": {
"_id": "59c005524d9c141fe0d95f15"
},
{
"_id": "2"
"service": {
"_id": "59c005524d9c141fe0d95f18"
},
{
"_id": "3"
"service": {
"_id": "59c005524d9c141fe0d95f18"
},
{
"_id": "4"
"service": {
"_id": "59c005524d9c141fe0d95f18"
}
],
}
But I want to execute a single mongoose query which can give this as the output
{
_id: "sample_id",
name: 'some name',
"service_order": [
{
"_id": "1"
"service": {
"_id": "59c005524d9c141fe0d95f15"
},
{
"_id": "2"
"service": {
"_id": "59c005524d9c141fe0d95f18"
}
],
}
Initially I know the Event id ("sample_id") and the ids of service_orders that i want to select ( ["1","2"] ).
You can use mongodb aggregate to achieve that
don't forget to replace sample_id and ["1" , "2"] with your dynamic data
Check the code below:
db.event.aggregate([
{
$match: { _id: "sample_id" }
},
{
$unwind: { path: "$service_order" }
},
{
$match : { 'service_order._id' : { $in : [ "1" , "2"]} }
},
{
$group : {
_id: { _id: "$_id", "name": "$name"},
service_order: { $addToSet: "$service_order" },
}
},
{
$project : {
_id : "$_id._id",
name : "$_id.name",
service_order : 1
}
}
])

Resources