I can't seem to sort this mongo/mongoose query. The first one returns the exact results, just not sorted in the opposite direction.
const UserSchema = new Schema({
username : String,
current : { type: Schema.Types.ObjectId, ref: 'News' },
friends : [{ user: { type: Schema.Types.ObjectId, ref: 'User'}, status: String }],
profile : {...}
})
const NewsSchema = new Schema({
user : { type: Schema.Types.ObjectId, ref: 'User' },
owner : { type: Schema.Types.ObjectId, ref: 'User' },
date : Date,
desc : String
});
const News = mongoose.model('News', NewsSchema);
const User = mongoose.model('User', UserSchema);
I've tried
.populate({
path: 'friends.user',
model: 'User',
select: 'profile.firstname profile.lastname profile.avatar username current -_id',
populate: {
path: 'current',
model: 'News',
select: 'date owner desc',
populate: {
path: 'owner',
model: 'User',
select: 'profile.firstname profile.lastname profile.avatar username'
},
},
})
.sort({'current.date': -1})
as well as
.populate({
path: 'friends.user',
model: 'User',
select: 'profile.firstname profile.lastname profile.avatar username current -_id',
populate: {
path: 'current',
model: 'News',
select: 'date owner desc',
options: { sort: { date: -1 } }
},
})
and this one which gives an error
User.findOne({ _id: req.userId }, 'friends -_id')
.populate({
path: 'friends.user',
model: 'User',
select: 'profile.firstname profile.lastname profile.avatar username current -_id',
populate: {
path: 'current',
model: 'News',
select: 'date owner desc',
},
options: {
sort: { date: -1 }
}
})
{
"message": "MongooseError: Cannot populate with `sort` on path friends.user because it is a subproperty of a document array"
}
with the first and second one, it comes back as
{
"friends": [
{
"_id": "5b51fd7c3f4ec33546a0664f",
"user": {
"profile": {
...
},
"current": {
"date": "2018-07-20T15:19:00.968Z"
}
}
},
{
"_id": "5b51fdb53f4ec33546a06655",
"user": {
"profile": {
...
},
"current": {
"date": "2018-07-20T15:19:45.102Z"
}
}
}
]
}
It should be reversed as "date": "2018-07-20T15:19:45.102Z" is more recent.
Is there a better more efficient way of doing the same thing with aggregate and lookup?
Users collection
{
"_id" : ObjectId("5b51fd2a3f4ec33546a06648"),
"profile" : {
"firstname" : "user1",
"lastname" : "user1"
"avatar" : "user1.png"
}
"username" : "user1",
"friends" : [
{
"_id" : ObjectId("5b51fd7c3f4ec33546a0664f"),
"user" : ObjectId("5b51fd643f4ec33546a0664c")
},
{
"_id" : ObjectId("5b51fdb53f4ec33546a06655"),
"user" : ObjectId("5b51fd903f4ec33546a06652")
}
],
"__v" : 5,
"current" : ObjectId("5b51fd2a3f4ec33546a06649")
},
{
"_id" : ObjectId("5b51fd643f4ec33546a0664c"),
"profile" : {
"firstname" : "user2",
"lastname" : "user2"
"avatar" : "user2.png"
}
"friends" : [
{
"_id" : ObjectId("5b51fdcd3f4ec33546a06658"),
"user" : ObjectId("5b51fd2a3f4ec33546a06648")
}
],
"__v" : 5,
"current" : ObjectId("5b51fd643f4ec33546a0664d"),
"username" : "user2"
},
{
"_id" : ObjectId("5b51fd903f4ec33546a06652"),
"profile" : {
"firstname" : "user3",
"lastname" : "user3"
"avatar" : "user3.png"
},
"friends" : [
{
"_id" : ObjectId("5b51fdce3f4ec33546a0665a"),
"user" : ObjectId("5b51fd2a3f4ec33546a06648")
}
],
"__v" : 5,
"current" : ObjectId("5b51fd903f4ec33546a06653"),
"username" : "user3"
}
New collection
{
"_id" : ObjectId("5b51fd2a3f4ec33546a06649"),
"user" : ObjectId("5b51fd2a3f4ec33546a06648"),
"owner" : ObjectId("5b51fd2a3f4ec33546a06648"),
"date" : ISODate("2018-07-20T15:18:02.962Z"),
"desc" : "user1 gibberish",
"__v" : 0
}
{
"_id" : ObjectId("5b51fd643f4ec33546a0664d"),
"user" : ObjectId("5b51fd643f4ec33546a0664c"),
"owner" : ObjectId("5b51fd643f4ec33546a0664c"),
"date" : ISODate("2018-07-20T15:19:00.968Z"),
"desc" : "user2 gibberish",
"__v" : 0
}
{
"_id" : ObjectId("5b51fd903f4ec33546a06653"),
"user" : ObjectId("5b51fd903f4ec33546a06652"),
"owner" : ObjectId("5b51fd903f4ec33546a06652"),
"date" : ISODate("2018-07-20T15:19:44.102Z"),
"desc" : "user3 gibberish",
"__v" : 0
}
You can try below aggregation
User.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.userId) } },
{ "$unwind": "$friends" },
{ "$lookup": {
"from": User.collection.name,
"let": { "friendId": "$friends.user" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$friendId" ] } } },
{ "$lookup": {
"from": News.collection.name,
"let": { "current": "$current" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$current" ] } } },
],
"as": "current"
}},
{ "$unwind": "$current" }
],
"as": "friends.user"
}},
{ "$unwind": "$friends.user" },
{ "$sort": { "friends.user.current.date": 1 }},
{ "$group": {
"_id": "$_id",
"username": { "$first": "$username" },
"profile": { "$first": "$profile" },
"friends": { "$push": "$friends" }
}}
])
Related
I want to achieve my documents in a single array
My data is this:
"summary_data" : [
{
"_id" : ObjectId("60418b730ba57044ec0afddd"),
"category" : "Entertainment",
"name" : "Alia bhatt",
"profession" : "acting"
},
{
"_id" : ObjectId("60418bc838497d42a80879e3"),
"category" : "Entertainment",
"name" : "priyanka chopra",
"profession" : "acting"
},
{
"_id" : ObjectId("60418bef38497d42a80879eb"),
"category" : "Entertainment",
"name" : "radhika apte",
"profession" : "acting"
},
]
}
And i want data like this having min 10(limit) category data of each category.How can achieve this:
"summary_data" : [
{
"_id": ObjectId("60418b730ba57044ec0afddd"),
"data": [{
"Entertainment": [{
"_id": ObjectId("60418ce138497d42a8087a03"),
"category": "Entertainment",
"name": "Alia bhatt",
"profession": "acting"
}],
}]
i have tried many queries some of them are:
db.getCollection('experts').aggregate([
{
$match: {
$or: [
{ category: "Sports" },
{ category: "other" },
{ category: "Entertainment" }
]
}
},
{ $limit: 10 },
{
$group:
{
_id: null,
summary_data: {
$push: {
_id: "$_id",
category: "$category",
name: "$name",
profession: "$profession",
}
},
}
},
])
Please help me to build this query. Thanks in advance.
I need to update the role in team array to lowercase.
db.users.find().pretty().limit(1)
{
"_id" : ObjectId("5d9fd81d3d598088d2ea5dc9"),
"employed" : "USA-Atlanta",
"firstName" : "Rory",
"siteRole" : "super admin",
"status" : "active",
"team" : [
{
"name" : "SALES AND MARKETING",
"displayName" : "S&M",
"role" : "Manager"
}
]
}
Tried this code.I m getting it with normal fields.
db.users.find( {}, { 'role': 1 } ).forEach(function(doc) {
db.users.update(
{ _id: doc._id},
{ $set : { 'role' : doc.role.toLowerCase() } },
{ multi: true }
)
});
sample output
"team" : [
{
"name" : "SALES AND MARKETING",
"displayName" : "S&M",
"role" : "manager"
}
]
I think the below Aggregation query is what you are looking for
var count = 0;
db.users.aggregate([
{
"$match": {
"team.role": {$exists: true}
}
},
{
"$project": {
"_id": 1,
// "team": 1,
"teamModified": {
"$map": {
"input": "$team",
"as": "arrayElems",
"in": {
"$mergeObjects": [
"$$arrayElems",
{"role": {"$toLower": "$$arrayElems.role"}}
]
}
}
}
}
},
]).forEach(function(it) {
db.users.updateOne({
"_id": it["_id"]
}, {
"$set": {
"team": it["teamModified"]
}
})
printjson(++count);
})
printjson("DONE!!!")
Note: I haven't tested the script properly in my local, so do let me know if it didn't help you out
I want to join two collection using MongoDB but the local filed matching key is present inside array. I am explaining my two document below.
users:
{ "_id" : ObjectId("5ee8c77330e6a86c5e5ce69b"),
"IsDeleted" : false,
"Name" : "savitha",
"Number" : "9848868000",
"Email" : "savitha.k#edqart.com",
"Password" : "savitha1",
"RoleId" : ObjectId("5ee1d885de9b6a5ae2bae165"),
"RoleName" : "KIOSK",
"UserType" : "POS",
"IsActive" : true,
"SalesAgentName" : "",
"salesAgentEmail" : "",
"SalesAgentMobile" : "",
"SalesAgentAlternateMobile" : "",
"SalesAgentRole" : "",
"MerchantID" : "",
"MachineName" : "",
"MachineBank" : "",
"StoreDetails" : [
{
"StoreCode" : "DKAA",
"Counter" : 3,
"TerminalID" : "12345",
"CounterName" : "Counter3"
}
]
}
This is my primary collection and i want to join with below collection.
storeinfo:
{
"_id" : ObjectId("5e447571f034c748ab11bd15"),
"IsActive" : true,
"IsDeleted" : false,
"StoreCode" : "DKAA",
"StoreName" : "Deeksha Group",
"StoreDescription" : "Deeksha Store is a place where Parents can purchase all the school merchandise in one place at reasonable prices.",
"StoreBranch" : "Bengaluru",
}
Here I need to join both collection as per StoreDetails.StoreCode(users) = StoreCode(storeinfo) and then I want to add only StoreName(from storeinfo) with respective record in StoreDetails. I am explaining my query below.
db.getCollection('users').aggregate([
{
$match: {"_id" : ObjectId("5ee8c77330e6a86c5e5ce69b")}
},
{
$addFields: {
"StoreDetails": {
$ifNull : [ "$StoreDetails", [ ] ]
}
}
},
{
$lookup: {
"from": "storeinfo",
"localField": "StoreDetails.StoreCode",
"foreignField": "StoreCode",
"as": "StoreDetails.StoreCode"
}
}
])
But as per this query I am not getting the expected output. My expected output should like below.
{ "_id" : ObjectId("5ee8c77330e6a86c5e5ce69b"),
"IsDeleted" : false,
"Name" : "savitha",
"Number" : "9848868000",
"Email" : "savitha.k#edqart.com",
"Password" : "savitha1",
"RoleId" : ObjectId("5ee1d885de9b6a5ae2bae165"),
"RoleName" : "KIOSK",
"UserType" : "POS",
"IsActive" : true,
"SalesAgentName" : "",
"salesAgentEmail" : "",
"SalesAgentMobile" : "",
"SalesAgentAlternateMobile" : "",
"SalesAgentRole" : "",
"MerchantID" : "",
"MachineName" : "",
"MachineBank" : "",
"StoreDetails" : [
{
"StoreCode" : "DKAA",
"StoreName" : "Deeksha Group"
"Counter" : 3,
"TerminalID" : "12345",
"CounterName" : "Counter3"
}
]
}
Here I need only store Name will add into the respective object as per storecode. But as per my query I am getting all fields value from storeinfo. Can any body help me to resolve this issue.
Here goes the full query
db.users.aggregate([
{
$match: {
"_id": ObjectId("5ee8c77330e6a86c5e5ce69b")
}
},
{
$addFields: {
"StoreDetails": {
$ifNull: [
"$StoreDetails",
[]
]
}
}
},
{
$unwind: "$StoreDetails"
},
{
$lookup: {
"from": "storeinfo",
"localField": "StoreDetails.StoreCode",
"foreignField": "StoreCode",
"as": "StoreDetails.StoreCode"
}
},
{
$unwind: "$StoreDetails.StoreCode"
},
{
$project: {
"Email": 1,
"IsActive": 1,
"IsDeleted": 1,
"MachineBank": 1,
"MachineName": 1,
"MerchantID": 1,
"Name": 1,
"Number": 1,
"Password": 1,
"RoleId": 1,
"RoleName": 1,
"SalesAgentAlternateMobile": 1,
"SalesAgentMobile": 1,
"SalesAgentName": 1,
"SalesAgentRole": 1,
"UserType": 1,
"_id": 1,
"salesAgentEmail": 1,
"StoreDetails": {
"StoreCode": "$StoreDetails.StoreCode.StoreCode",
"Counter": 1,
"CounterName": 1,
"StoreName": "$StoreDetails.StoreCode.StoreName",
"TerminalID": 1
}
}
},
{
$group: {
_id: "$_id",
"Email": {
$first: "$Email"
},
"IsActive": {
$first: "$IsActive"
},
"IsDeleted": {
$first: "$IsDeleted"
},
"MachineBank": {
$first: "$MachineBank"
},
"MachineName": {
$first: "$MachineName"
},
"MerchantID": {
$first: "$MerchantID"
},
"Name": {
$first: "$Name"
},
"Number": {
$first: "$Number"
},
"Password": {
$first: "$Password"
},
"RoleId": {
$first: "$RoleId"
},
"RoleName": {
$first: "$RoleName"
},
"SalesAgentAlternateMobile": {
$first: "$SalesAgentAlternateMobile"
},
"SalesAgentMobile": {
$first: "$SalesAgentMobile"
},
"SalesAgentName": {
$first: "$SalesAgentName"
},
"SalesAgentRole": {
$first: "$SalesAgentRole"
},
"UserType": {
$first: "$UserType"
},
"salesAgentEmail": {
$first: "$salesAgentEmail"
},
"StoreDetails": {
$push: {
StoreCode: "$StoreDetails.StoreCode",
Counter: "$StoreDetails.Counter",
CounterName: "$StoreDetails.CounterName",
StoreName: "$StoreDetails.StoreName",
TerminalID: "$StoreDetails.TerminalID"
}
}
}
}
])
You can see the full working example here:
https://mongoplayground.net/p/6KqECy-o1U8
Thanks
I have 2 Collections
Posts
Users
Posts collection contains comments array which stores { userId, comment } object, along with other information.
users ollection contains user's information.
I want to return the complete result.
Ex:
{
"postId":"xvzeee",
"post": "Good Morning",
"likedBy":[
12342234,
23456534
]
"comments": [
{
"comment": "very good morning",
"userName": "Max"
},
{
"comment": "v. GM",
"userName": "Suraj"
}
]
}
My Approach to achieve the above result is.
db.wall.aggregate([
{ $lookup: { from: 'profiles', localField: 'likedBy', foreignField: 'profileId', as: 'likedBy' } },
{ $lookup: { from: 'profiles', localField: 'comments.commentedBy', foreignField: 'profileId', as: 'commentedUser' } },
{
$project:{
"likedBy.name": 1,
"likedBy.profileId": 1,
createdBy: 1,
createdAt: 1,
updatedAt: 1,
comments : [{
comment: "$comments.comment",
user: "$commentedUser.name"
}],
"commentedUser.name" : 1
}
}
])
The result is coming like below:
{
"_id" : ObjectId("5dfdbb129f644213c413eb18"),
"likedBy" : [
{
"profileId" : "96444206",
"name" : "Vinay3"
},
{
"profileId" : "400586806",
"name" : "Dev"
}
],
"createdBy" : "96444206",
"commentedUser" : [
{
"name" : "Vinay3"
},
{
"name" : "Dev"
}
],
/*Facing problem in comment array*/
"comments" : [
{
"comment" : [
"Super-awesome",
"FAB",
],
"user" : [
"Vinay3",
"Dev"
]
}
]
}
The comment should look like :
[{
...
"comments" : [
{
"comments" : [
{
comment :"Super-awesome",
user: "Vinay3",
profileId: "11111..."
},
{
comment: "FAB",
user: "Dev",
profileId: "2222..."
}
],
}
]
}]
The Posts Collection looks like this :
[{
"_id" : ObjectId("5dfdbb129f699913c413eb18"),
"post":"Good Morning",
"createdBy" : "96444206",
"postId" : "D9644s5h8m",
"likedBy" : [
"96444206",
"40058680"
],
"comments" : [
{
"commentId" : "COM9644",
"commentedBy" : "96444206",
"comment" : "Super-awesome"
},
{
"commentId" : "COM9644",
"commentedBy" : "96444206",
"comment" : "#FAB"
},
{
"commentId" : "COM00587",
"commentedBy" : "400586806",
"comment" : "marvelous"
}
],
"createdAt" : ISODate("2019-12-21T11:56:26.944+05:30"),
"updatedAt" : ISODate("2019-12-21T12:12:35.047+05:30"),
"__v" : 0
}, {...}, {...}]
User Profiles Collection
[{
"_id" : ObjectId("5dd4ff3abe53181160efa446"),
"accountStatus" : "CONFIRMED",
"profileId" : "400586806",
"name" : "Dev",
"email" : "dev#xyz.com",
"createdAt" : ISODate("2019-11-20T14:24:18.692+05:30"),
"updatedAt" : ISODate("2019-12-20T16:58:06.041+05:30"),
"__v" : 0
}, {...}, {...} ]
How to achieve this, any help will much be appreciated.
However, I'm able to give a solution to my problem like below.
If there any better solution than this will much be appreciated.
db.wall.aggregate([
{ $unwind: '$comments'},
{ $lookup: { from: 'profiles', localField: 'comments.commentedBy', foreignField: 'profileId', as: 'comments.user' } },
{ $unwind: '$comments.user' },
{
$group: {
_id:'$_id',
postId: { $first: "$postId"},
likedBy: { $first: "$likedBy"},
createdBy: { $first: '$createdBy'},
comments: { $push : '$comments' },
createdAt: { $first: "$createdAt" },
updatedAt: { $first: "$updatedAt" },
}
},{
$project: {
_id:1,
postId: 1,
"likedBy.name": 1,
"likedBy.profileId": 1,
createdBy: 1,
comments: {
$map : {
input: '$comments',
as: 'com',
in: {
commentId: "$$com.commentId",
comment: "$$com.comment",
name: "$$com.user.name"
}
}
},
createdAt: 1,
updatedAt: 1,
}
}
])
I want to add isActive => true || false field to the comments object.
now the challenge is how to filter only isActive => true comments ?
Here is my approach
db.posts.aggregate([
{
$unwind: '$likedBy',
},
{
$unwind: '$comments',
},
{
$lookup: {
from: 'users',
localField: 'likedBy',
foreignField: 'profileId',
as: 'likedByUser'
},
},
{
$unwind: '$likedByUser'
},
{
$lookup: {
from: 'users',
localField: 'comments.commentedBy',
foreignField: 'profileId',
as: 'commentByUser'
},
},
{
$unwind: '$commentByUser'
},
{
$group: {
"_id": '$postId',
"post": { $first: 'post'} ,
comments: {
$push: {
"commentId": "$comments.commentId",
"commentedBy": "$likedByUser.name",
"comment": "$comments.comment"
}
},
likes: {
$push: {
"likedByName": "$likedByUser.name",
"likeById": "$likedBy"
}
}
}
},
{
$project: {
"_id": 0,
"postId":'$_id',
"post": 1,
"comments": 1,
"likes": 1
}
}
])
I have a question regarding MongoDB aggregation query which is almost similar to $unwind 2 fields separately in mongodb query.
This is the document:
{
"_id" : "1",
"details" : {
"phonenumber" : [
"1",
"2"
],
"name" : [
"a",
"b"
]
}
}
And I am trying to frame a query which will return me the following result:
{ "_id" : "1", "phonenumber" : "1", "name" : null },
{ "_id" : "1", "phonenumber" : "2", "name" : null },
{ "_id" : "1", "phonenumber" : null, "name" : "a" },
{ "_id" : "1", "phonenumber" : null, "name" : "b" }
Could someone please help me with that?
Closest solution I could figure out is by following query:
db.document.aggregate( [ { $unwind: { path: "$details.name"} }, { $unwind: { path: "$details.phonenumber" } }, { $project: { _id: 1, name: "$details.name", phonenumber: "$details.phonenumber" } } ] )
And the output from above query is:
{ "_id" : "1", "phonenumber" : "1", "name" : "a" },
{ "_id" : "1", "phonenumber" : "1", "name" : "b" },
{ "_id" : "1", "phonenumber" : "2", "name" : "a" },
{ "_id" : "1", "phonenumber" : "2", "name" : "b" }
With MongoDB v3.4, one of the possible solution would be,
db.document.aggregate({
'$facet': {
'phonenumber': [{
'$unwind': '$details.phonenumber'
}, {
'$project': {
phonenumber: '$details.phonenumber',
name: null
}
}],
'name': [{
'$unwind': '$details.name'
}, {
'$project': {
name: '$details.name',
phonenumber: null
}
}]
}
}, {
'$project': {
'combined': {
'$setUnion': ['$phonenumber', '$name']
}
}
}, {
'$unwind': '$combined'
}, {
'$replaceRoot': {
'newRoot': '$combined'
}
})
facet allows us to include multiple aggregation pipelines within a single stage, which is available from version 3.4
Alternate solution for earlier versions of mongodb,
db.document.aggregate([{
$unwind: {
path: "$details.name"
}
}, {
$group: {
_id: "$_id",
nameArr: {
$push: {
name: "$details.name",
phonenumber: {
$ifNull: ["$description", null]
}
}
},
"details": {
$first: "$details"
}
}
}, {
$unwind: "$details.phonenumber"
}, {
$group: {
_id: "$_id",
phoneArr: {
$push: {
phonenumber: "$details.phonenumber",
name: {
$ifNull: ["$description", null]
}
}
},
"nameArr": {
$first: "$nameArr"
}
}
}, {
$project: {
_id: 1,
value: {
$setUnion: ["$nameArr", "$phoneArr"]
}
}
}, {
$unwind: "$value"
}, {
$project: {
name: "$value.name",
phonenumber: "$value.phonenumber"
}
}])