Mongo complex aggregate (condition based on group count) - mongodb

Id need a mongo aggregate that given the sample data:
{
'employeeNumber': '1',
'companyId': '1',
'role': 'D',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
{
'employeeNumber': '1',
'companyId': '1',
'role': 'S',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
{
'employeeNumber': '1',
'companyId': '2',
'role': 'D',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
{
'employeeNumber': '2',
'companyId': '1',
'role': 'D',
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
queries for a given companyId (e.g. companyId = 1, using match stage probably) and would return something like:
{
'employeeNumber': '1',
'companyId': '1',
'role': 'D','S'
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
notice that
{
'employeeNumber': '1',
'companyId': '2',
'role': 'D'
'dateHired':ISODate("2013-11-26T00:00:00.0Z")
...
}
is not returned.
Ideally it would return the whole object as the collection has 10/12 fields.

By using aggregation you will not get exact expected output but you can get output like following:
{ "role" : [ "D" ], "employeeNumber" : "2" }
{ "role" : [ "S" ], "employeeNumber" : "3" }
{ "role" : [ "D", "S" ], "employeeNumber" : "1" }
And the query will be like:
db.collection.aggregate({
$group: {
_id: "$employeeNumber",
"role": {
"$push": "$role"
}
}
}, {
$project: {
"employeeNumber": "$_id",
"role": 1,
"_id": 0
}
})
Edit After question edit:
db.collection.aggregate({
$group: {
_id: {
employeeNumber: "$employeeNumber",
"companyId": "$companyId"
},
"role": {
"$push": "$role"
},
"dateHired": {
$last: "$dateHired"
}
}
}, {
$project: {
"employeesNumber": "$_id.employeeNumber",
"comapnyId": "$_id.companyId",
"role": 1,
"dateHired": 1,
"_id": 0
}
})

Related

How to duplicate fields in document in MongoDB?

I am trying to duplicate some fields inside documents.
In the case we have fields in object, I want to duplicate them outside and vice versa.
My MongoDB version is 4.2.12.
Here is my collection :
{
'id': '1',
'object': {
'a': 'OK',
'b': ''
}
},
{
'id': '2',
'a': '',
'b': 'OK',
}
Here is what I want as a result :
{
'id': '1',
'a': 'OK',
'b': '',
'object': {
'a': 'OK',
'b': ''
}
},
{
'id': '2',
'a': '',
'b': 'OK',
'object': {
'a': '',
'b': 'OK'
}
}
Your help will be highly appreciated.
$addFields to add new fields
$cond with $ifNull to check if the field already exists or not
db.collection.aggregate([
{
"$addFields": {
"a": {
"$cond": {
"if": { "$ifNull": [ "$a", null ] },
"then": "$a",
"else": "$object.a"
}
},
"b": {
"$cond": {
"if": { "$ifNull": [ "$b", null ] },
"then": "$b",
"else": "$object.b"
}
},
"object": {
"$cond": {
"if": { "$ifNull": [ "$object", null ] },
"then": "$object",
"else": { a: "$a", b: "$b"}
}
}
}
}
])
Working example

mongoose aggregate with array of string

I have a user collection like the below format:
[
{
"_id": ObjectId("5f76ac1c337b875f3aff8cad"),
"units": [ '1', '2', '3' ],
"status": "true",
"name": "xxx"
},
{
"_id": ObjectId("5f76ac1c337b875f3aff8cad"),
"units": [ '2', '3', '4' ],
"status": "true",
"name": "yyy"
},
{
"_id": ObjectId("5f76ac1c337b875f3aff8cad"),
"units": ['4', '1', '5' ],
"status": "true",
"name": "zzz"
}
]
I am trying to find the query like below
input: ['2', '3'] , should return all 3 records.
I have list of units like [ '2'] , should return first 2 records.
I am using aggregate query with facet, because required total count of records.
const id_lists = ['1', '2'];
User.aggregate(
{"$match": { "status" : "true" , "units": { "$in": id_lists } },
{
'$facet': {
metadata: [{ $count: "total" }],
users: [{ $skip: 0 }, { $limit: 10 }]
}
})
Also I am adding support for sorting, select fields, search the text(name field) and pagination. etc

mongodb: how to extract field instead of getting an object with aggregate + $lookup + $project

I have two contracts.
// UserInfo
{ _id: '1', name: 'Alice' }
{ _id: '2', name: 'Bob' }
{ _id: '3', name: 'Charlie' }
// Application
{ _id: '00', userId: '1', jobId: 'gg' }
{ _id: '01', userId: '1', jobId: 'fb' }
{ _id: '02', userId: '2', jobId: 'ms' }
I want to get:
{ name: 'Alice', jobIds: [ 'gg', 'fb' ] }
{ name: 'Bob', jobIds: [ 'ms' ] }
{ name: 'Charlie', jobIds: [] }
Note that Charlie doesn't have applications.
My current code:
UserInfo.aggregate([
{
$lookup: {
from: 'applications',
let: { userId: '$_id' },
pipeline: [
{
$match: {
$expr: { $eq: ['$userId', '$$userId'] }
}
},
{
$project: { jobId: 1, _id: 0 }
}
],
as: 'jobIds'
}
}
])
I got:
{ _id: '1', name: 'Alice', jobIds: [ { jobId: 'gg' }, { jobId: 'fb' } ] }
{ _id: '2', name: 'Bob', jobIds: [ { jobId: 'ms' } ] }
{ _id: '3', name: 'Charlie', jobIds: [] }
How to get jobIds: [ 'gg', 'fb' ] instead of jobIds: [ { jobId: 'gg' }, { jobId: 'fb' } ]?
Haven't found proper way in Aggregation Pipeline Stages.
Use $addFields stage at the end of the pipeline.
UserInfo.aggregate([
{ "$lookup": {
"from": "applications",
"let": { "userId": '$_id' },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$userId", "$$userId"] }}},
{ "$project": { "jobId": 1, "_id": 0 }}
],
"as": "jobIds"
}},
{ "$addFields": { "jobIds": "$jobIds.jobId" }}
])

MongoDB Aggregation : Group on common field of two arrays

Below is a sample document:
{
'uid': 1,
'sent': [
{
'mid': 100,
'date': 20171210,
},
{
'mid': 101,
'date': 20171210,
}
],
'open': [
{
'mid': 100,
'date': 20171220,
},
{
'mid': 101,
'date': 20171220,
}
]
}
I want to group on 'uid' and nested 'mid' fields.
My desired output is :
{
'uid': 1,
'mid': 100,
'sent': [ 20171210 ],
'open': [ 20171220 ]
}
{
'uid': 1,
'mid': 101,
'sent': [ 20171210 ],
'open': [ 20171220 ]
}
Is there any efficient way of Aggregation which can give me above result?
You can $unwind the one array, then use $filter to keep only the matching entries in the second array. Then $unwind the second array and $group.
db.temp.aggregate(
[
{
$unwind: {
'path': '$sent',
}
},
{
$project: {
'uid': 1,
'sent': 1,
'open': { $filter: {
input: '$open',
as: 'this',
cond: { $eq: [ '$sent.mid', '$$this.mid' ] }
} }
}
},
{
$unwind: {
'path': '$open',
}
},
{
$group: {
'_id': { 'uid': '$uid', 'mid': '$sent.mid' },
'sent': { '$push': '$sent.date' },
'open': { '$push': '$open.date' }
}
},
{
$project: {
'_id': 0,
'uid': '$_id.uid',
'mid': '$_id.mid',
'sent': 1,
'open': 1
}
},
]
);

Count Like of Comment

collection Status:
[
{
_id: '1',
StatusName: '....',
Comments: [
{
id_: '2',
CommentName: '.....',
Likes: ['user1', 'user2', 'user3']
},
{
id_: '3',
CommentName: '.....',
Likes: ['user2', 'user3']
}
]
},
{
_id: '3',
StatusName: '....',
Comments: [
{
id_: '4',
CommentName: '.....',
Likes: ['user1', 'user2', 'user3', 'user4']
},
{
id_: '5',
CommentName: '.....',
Likes: ['user1', 'user3']
}
]
}
]
I want count Like of Comment _id = '2' and Status _id: '1'
One way is to use aggregation framework :
db.status.aggregate(
[
{$match : {_id : "1", }},
{$unwind: "$Comments"},
{$match: {"Comments.id_": "2"}},
{$unwind: "$Comments.Likes"},
{$group: {_id: {"statusId":"$_id","CommentId":"$Comments.id_"},
"likes": {$sum:1}}
}
] )