some questions about Aggregate running mechanism - mongodb

Aggregate1:
db.collection.aggregate([
{
$lookup: {
...
}
},
{
$limit: 1
}
])
Aggregate2:
db.collection.aggregate([
{
$limit: 1
},
{
$lookup: {
...
}
}
])
Aggregate1 and Aggregate2 are different?
In Aggregate1,is the whole collection scanned firstly, then do $lookup?
If it is different,how to lookup with some query?just like this:
db.collection.aggregate([
{
$lookup: {
from: 'collection2',
localField: 'field',
foreignField: 'field',
as: 'newField',
// do some query when lookup
query: {'newField.xxx': 1}
}
}
])
I know, i can do this:
db.collection.aggregate([
{
$lookup: {
from: 'collection2',
localField: 'field',
foreignField: 'field',
as: 'newField'
}
},
{$unwind: '$newField'},
{$match: {'newField.xxx': 1}}
])
But I'm afraid that, like the example above, the entire collection will be scanned。
Look forward to your reply!
Now,i find this api:$graphLookup.restrictSearchWithMatch,but:
NOTE
You cannot use any aggregation expression in this filter.
For example, a query document such as
{ lastName: { $ne: "$lastName" } }
will not work in this context to find documents in which the lastName
value is different from the lastName value of the input document,
because "$lastName" will act as a string literal, not a field path.

Related

How to get a field from another collection and replace with null if not found?

I have following shcemas:
employeeSchema: {
_id: ObjectId
isActive: Boolean
}
careerSchema: {
employeeId: ObjectId // points to the _id from employeeSchema
isCurrentlyWorking: Boolean
position: String // designation
}
I want to find the employees that have field isActive: true. Also, the same employee may have more than one document in the Career collection. I want to find the document with isCurrentlyWorking: true and want that document's position field to be in the employee document's career field. I have tried the following:
const result = await Employee.aggregate([
{ $match: { isActive: true } },
{
from: "careers",
localField: "_id",
foreignField: "employeeId",
as: "career",
pipeline: [
{ $match: { isCurrentlyWorking: true} },
{ $project: { position: 1} }
]
}
])
The problem that I have faced with this is the field career is an empty array in the result. And because of that I cannot add { $unwind: "$career" } to the aggregate function. One more thing, some employees may not have any correspoding document in the Careers collection. In that case, I just want the result to have career: null.
If I understand you correctly you want to do something like:
db.employees.aggregate([
{$match: {isActive: true}},
{$lookup: {
from: "careers",
localField: "_id",
foreignField: "employeeId",
as: "career",
pipeline: [
{$match: {isCurrentlyWorking: true}},
{$project: {position: 1}}
]
}},
{$unwind: {
path: "$career",
preserveNullAndEmptyArrays: true
}},
{$set: {career: {$ifNull: ["$career", null]}}}
])
See how it works on the playground example

MongoDB $lookup - conditional value for localfield?

I have 2 collections I want to combine using lookup.
Collection1: _AddressSyncStatus
fields I wanna use from this collection: "address"
Collection2: EthTokenTransfers
Fields I want to use from this collection: "to_address", "from_address".
Now when I use mongo compass, the lookup filter expects a local field, in my case the local field of EthTokenTransfers to join the collections. My problem now is, that I want to lookup where address from _AddressSyncStatus is either EthTokenTransfers.from_address OR EthTokenTransfers.to_address.
Is this possible using aggregations?
{
from: '_AddressSyncStatus',
localField: 'string', //from_address OR to_address
foreignField: 'address',
as: 'synced_address'
}
One way to do it is using the $lookup pipeline with $or:
db.EthTokenTransfers.aggregate([
{
$lookup: {
from: "_AddressSyncStatus",
let: {
from_address: "$from_address",
to_address: "$to_address"
},
pipeline: [
{
$match: {
$expr: {$or: [{$eq: ["$_id", "$$from_address"]},
{$eq: ["$_id", "$$to_address"]}
]
}
}
}
],
as: "synced_address"
}
}
])
As you can see here.
But I think that for the rset of the work with this data, it will be more convenient like this:
db.EthTokenTransfers.aggregate([
{
$lookup: {
from: "_AddressSyncStatus",
localField: "from_address",
foreignField: "_id",
as: "synced_address_from"
}
},
{
$lookup: {
from: "_AddressSyncStatus",
localField: "to_address",
foreignField: "_id",
as: "synced_address_to"
}
}
])
As you can see here

MongoDB: aggregation $lookup with lossy data type

I have two collections:
cats
balls
"cats" collection has documents with key "ballId" of type string
"balls" collection has documents with key "_id" of type ObjectId
An $lookup inside an aggregation is able to retrieve results if the join is done on keys with the same data type. However in my case, "ballId" and "_id" are of different types. This code retrieves the cats but doesn't retrieve the related balls:
collection('cats').aggregate([
{ $match:{} },
{
$lookup: {
from: "balls",
localField: "ballId",
foreignField: "_id",
as: "balls"
}
}
]);
How can I use $lookup with lossy data type?
Use $lookup with pipeline stage.
Join both collections by converting balls' _id to string ($toString) and next compare both values as string ($eq).
db.cats.aggregate([
{
$match: {}
},
{
$lookup: {
from: "balls",
let: {
ballId: "$ballId"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
{
"$toString": "$_id"
},
"$$ballId"
]
},
}
}
],
as: "balls"
}
}
])
Sample Mongo Playground

Use $match on fields from two separate collections in an aggregate query mongodb

I have an aggregate query where I join 3 collections. I'd like to filter the search based on fields from two of those collections. The problem is, I'm only able to use $match on the initial collection that mongoose initialized with.
Here's the query:
var pipeline = [
{
$lookup: {
from: 'blurts',
localField: 'followee',
foreignField: 'author.id',
as: 'followerBlurts'
}
},
{
$unwind: '$followerBlurts'
},
{
$lookup: {
from: 'users',
localField: 'followee',
foreignField: '_id',
as: 'usertbl'
}
},
{
$unwind: '$usertbl'
},
{
$match: {
'follower': { $eq: req.user._id },
//'blurtDate': { $gte: qryDateFrom, $lte: qryDateTo }
}
},
{
$sample: { 'size': 42 }
},
{
$project: {
_id: '$followerBlurts._id',
name: '$usertbl.name',
smImg: '$usertbl.smImg',
text: '$followerBlurts.text',
vote: '$followerBlurts.vote',
blurtDate: '$followerBlurts.blurtDate',
blurtImg: '$followerBlurts.blurtImg'
}
}
];
keystone.list('Follow').model.aggregate(pipeline)
.sort({blurtDate: -1})
.cursor().exec()
.toArray(function(err, data) {
if (!err) {
res.json(data);
} else {
console.log('Error getting following blurts --> ' + err);
}
});
Within the pipeline, I can only use $match on the 'Follow' model. When I use $match on the 'Blurt' model, it simply ignores the condition (you can see where I tried to include it in the commented line under $match).
What's perplexing is that I can utilize this field in the .sort method, but not in the $match conditions.
Any help much appreciated.
You can use the mongo dot notation to access elements of the collection that is being looked up via $lookup.
https://docs.mongodb.com/manual/core/document/#dot-notation
So, in this case followerBlurts.blurtDate should give you the value you are looking for.

Grouping by filter of another collection

I'm using Meteor and MongoDB. I need to publish with aggregation (I'm using jcbernack:reactive-aggregate and ReactiveAggregate).
db.getCollection('Jobs').aggregate([
{
$lookup:
{
from: "JobMatches",
localField: "_id",
foreignField: "jobId",
as: "matches"
}
},
{ $project:
{
matches: {
'$filter': {
input: '$matches',
as: 'match',
cond: { '$and': [{$eq: ['$$match.userId', userId]}]}
}
}
}
},
{$match: { 'matches.score': { '$gte': 60 }},
{$sort: { "matches.score": -1 }},
{$limit: 6}
])
On the client I get only the data part (limit 6). So I will have to count the number of all the data on the server side. I can't use find().count() because in the find() call without aggregation I can't use a filter associated with other collection (like this { 'matches.score': { '$gte': 60 }). How can I calculate the data filtered in this way? There may be a need to use a $group in the pipeline?