can't sort in mongo/mongoose with populate - mongodb

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

how to get data in single array in mongodb?

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.

How to update array of objects to LowerCase in mongodb?

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

Join two collections by matching value present inside array of object using MongoDB

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

How to aggregate lookup sub array documents in mongoDB, and projecting the result?

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

MongoDB unwinding 2 embedded arrays separately

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