MongoDB: Use $lookup aggregation in countDocuments() - mongodb

I am trying to make a pagination and would like to use the mongoDB's countDocuments() method to return the total number of teams who's leader belongs to DC organization.
teams collection:
{
_id: 1,
name: 'avengers',
leader_id: 'L1'
},
{
_id: 2,
name: 'justice league',
leader_id: 'L2'
},
{
_id: 3,
name: 'suicide squad',
leader_id: 'L3'
}
leaders collection:
{
_id: 'L1',
name: 'ironman',
organization: 'MCU'
},
{
_id: 'L2',
name: 'superman',
organization: 'DC'
},
{
_id: 'L3',
name: 'harley quinn',
organization: 'DC'
}
My question is, can we use the $lookup aggregation as the query to match my output?

No, countDocuments does not take aggregation operators in its argument. You can use the $count stage to get the count of documents in the pipeline.

Related

Group By Variable in Mongo Aggregate

I have a mongo aggregation which currently ends in this project:
{
$project: {
_id: 1,
name: 1,
subject_id: '$subject.subject',
groups: '$groups'
}
}
It creates this array of tests:
0:
_id: 1
groups: (27)
name: "Year 1 Maths Paper 1"
subject_id: "111"
1:
_id:2
groups: (27)
name: "Year 1 Maths Paper 2"
subject_id: "111"
However, I want to group the data by the subject_id. So each subject will be an array with tests related to it inside. Does anyone know how to do this?
Demo - https://mongoplayground.net/p/-cST7kqFYxS
Use $group to groupby subject_id and $push names to tests array.
db.collection.aggregate([
{
$group: {
_id: "$subject_id",
tests: { $push: "$name" }
}
}
])

Merge records if one field matches, but not if another field has values in both records MongoDb aggregation

I have some records like :
{
id: 1,
phone: "+15555555555",
name: "Acme CO.",
vendorcode: "ACMEC"
},
{
id: 2,
phone: "+15555555555",
name: "Acme corporation company",
vendorcode: "ACMECOMPANY"
},
{
id: 3,
phone: "+15555555555",
name: "Acme Incorporated",
vendorcode: null
}
I want to merge records:
IF phone field matches, merge the records. (can overwrite values with the values of the next record being merged).
But if there are vendorcode non-null values in multiple records, create an arrray of values. So "vendorcode" in the new record would be an array.
I would like the output of the above collection to be something like:
{
phone: "+15555555555",
name: "Acme Co.",
vendorcode: ["ACMEC","ACMECOMPANY"]
}
in a new collection.
How to write an aggregation for this in mongodb?
$group by phone, select first name, phone
$ifNull will return vendorcode if its not null
$addToSet to make array of unique vendorcode
$project to remove _id field
$out to write query result in new collection, this will create a new collection and write this query result
db.collection.aggregate([
{
$group: {
_id: "$phone",
phone: { $first: "$phone" },
name: { $first: "$name" },
vendorcode: {
$addToSet: { $ifNull: ["$vendorcode", "$$REMOVE"] }
}
}
},
{ $project: { _id: 0 } },
{ $out: "newCollectionName" }
])
Playground

How to query multiple collections in mongodb (without using $lookup)?

I would like to create a single query that gets data from three different collections from providing a single query parameter. I have seen methods that use $lookup but I do not want to use that as I cannot use it on sharded collections.
Here is an example to explain further.
I have three collections: user, chatroom and chatMessage.
user collection:
{
_id: ObjectId('456'),
username: 'John',
contacts: [
{
_id: ObjectId('AB12'),
name: 'Mary',
idOfContact: ObjectId('123'),
},
{
_id: ObjectId('AB34'),
name: 'Jane',
_idOfContact: ObjectId('234'),
},
{
_id: ObjectId('AB56'),
name: 'Peter',
_idOfContact: ObjectId('345'),
}
],
}
chatroom collection:
{
_id: ObjectId('AB34'),
usersInThisChatRoom: [
ObjectId("456"),
ObjectId("123"),
ObjectId("234"),
]
}
chatMessage collection:
[
{
_id: ObjectId("M01"),
chatRoomObjectId: _id: ObjectId('AB34'),
senderObjectId: ObjectId('456'),
message: 'Hello humans!',
date: ISODate("2019-09-03T07:24:28.742Z"),
},
...(other messages)
]
What I would like to be returned
[
{
chatRoomObjectId: ObjectId('AB34'),
usersInThisChatRoom: [
{
contactName: 'John',
contactUserId: ObjectId('456'),
},
contactName: 'Mary',
contactUserId: ObjectId('123'),
},
contactName: 'Jane',
contactUserId: ObjectId('234'),
}
]
chatMessages: [
{
_id: ObjectId("M01"),
senderObjectId: ObjectId('456'),
message: 'Hello humans!',
date: ISODate("2019-09-03T07:24:28.742Z"),
},
...(other messages)
]
},
...(other documents)
]
Is there a way to get my desired results by making a single query using the user._id and will that be performance friendly?
Or, do I have to make several queries, one after another, to achieve what I want?
According to this answer, you cannot perform a single query across multiple collections (aside from the $lookup aggregation pipeline function.
This means that You either use the $lookup aggregation pipeline or you make several queries to the DB.

mongodb get distinct values with category

Suppose there is the following collection
People:
{
_id: 1,
name: 'john',
last_name: 'blah1',
job: 'lifeguard'
}
{
_id: 2,
name: 'john',
last_name: 'blah2',
job: 'lifeguard'
}
{
_id: 3,
name: 'alex',
last_name: 'blah3',
job: 'lifeguard'
}
{
_id: 4,
name: 'alex',
last_name: 'blah4',
job: 'lifeguard'
}
{
_id: 5,
name: 'alex',
last_name: 'blah5',
job: 'gardener'
}
I need to get the distict jobs with an array of distict names:
Trying to get the following result:
[
{
value: 'lifeguard',
names: [
'john',
'alex'
],
},
{
value: 'gardener',
names: [
'alex'
],
},
]
I understand how to get the unique jobs
db.people.find().distinct('jobs')
However i did not figure out how to do a distinct query with multiple properties.
Better to use the aggregation framework where you have a pipeline that has a $group stage to group the documents by the job key and then construct the names distinct array within the group by the accumulator $addToSet.
Consider the following aggregate operation:
db.people.aggregate([
{
"$group": {
"_id": "$job",
"names": { "$addToSet": "$name" }
}
}
])
#chridam did help me find the right answer, in the real world my object was more like
{
_id: 1,
name: ['john', 'bah1', 'blah2', 'blah3'],
last_name: 'blah1',
job: 'lifeguard'
}
so i had to $unwind the names and aggregate $group just like in #chridam's answer.
model.aggregate([
{$unwind: "$name"},
{
$group: {
_id:"$name",
jobs: {
$addToSet: "$job"
}
}
}
]

MongoDB aggregation framework approach to a multi-doc query

I am looking into the best way to organize filtering. I have the following document format:
{
_id: "info",
ids: ["id1", "id2", "id3"]
}
{
_id: "id1",
value: 5
}
{
_id: "id2",
value: 1
}
{
_id: "id3",
value: 5
}
I need to make the following query: get all documents by id from doc "info" and then filter them out by value 5. So, that result would be something like:
{
_id: "id1",
value: 5
}
{
_id: "id3",
value: 5
}
I suppose I need to do unwind on ids, but how do I then select all documents that match those values? Or maybe I should just use $in operator somehow to grab all documents and after that do filtering?
Any help is aprpeciated. Thanks.
If it is only MongoDB shell/script, I would do it like this:
db.ids.find({ _id: { $in: db.ids.findOne({ _id: "info" }).ids }, value: 5 })
You also have worse versions using:
or the eval command:
db.runCommand({
eval: function(value) {
var ids = db.ids.findOne({ _id: "info" }).ids;
return db.ids.find({ _id: { $in: ids }, value: value }).toArray();
},
args: [5]
})
or the $where operator (low performance because you execute one find for each candidate result with value 5):
db.ids.find({
value: 5,
$where: "db.ids.findOne({ _id: 'info', ids: this._id })"
})
But if you are trying to run the queries through a MongoDb driver, the story might be different.