MongoDB aggregate - lookup if the only minimum number of documents matches - mongodb

I Have a collection called "cv" and another collection called "sections"
In the collection "section" I have a field called "ownerCV" which refers to the CV ID
I want to get the CVs which have a minimum of 4 sections
I use
([
{
$lookup: {
from: UserSection.tableName,
localField: '_id',
foreignField: 'ownerCV',
as: 'sections'
}
}, {
$project: {
sectionsCount: {
$size: '$sections'
}
}
}, {
$match: {
'sectionsCount': {
'$gte': 4
}
}
}
])
.skip(count * page)
.limit(count)
.toArray();
But it takes a big time.
So is there another way i can do that in a fast time?

Related

MongoDB lookup with match not efficient in large amount of data

I have a two collection that systems and systemsToUsers. It is refer the systems _id as a foreign key system_id in a systemToUsers table. I need system data which all are _id not presents in the sytemToUser table.
I am using the below query but it is taking a long time (5 seconds) for 100 000 data. But need to optimize the query reduce the execution time
db.Systems.aggregate([
{ $lookup: {
from: 'systemsToUsers',
localField: '_id', foreignField: 'system_id',
as: 'systemswithusers' } },
{ $match: { $expr: { $eq: [ { $size: '$systemswithusers' }, 0 ] } } }
]);
Try below $match and make sure every column in aggregate have index.
db.Systems.aggregate([
{
$lookup: {
from: "systemsToUsers",
localField: "_id",
foreignField: "system_id",
as: "systemswithusers"
}
},
{
$match: {
systemswithusers: []
}
}
])
mongoplayground

MongoDB $lookup creates array

So I am trying to join two collections together:
Collections are:
shows
episodes
I am using the $lookup value inside the shows collection.
[{$lookup: {
from: 'episode',
localField: 'url',
foreignField: 'show_b',
as: 'match_docs'
}}]
However I am getting all of the episodes from each show inside the match_docs in theory that is fine, however I need to be able to limit it to the latest episode limit:1 for each show ordered by pubDate
If anyone knows how I could limit the match_docs to only lookup once that would be great
I have also tried
{
from: 'episode',
localField: 'url',
foreignField: 'show_b',
pipeline: [
{$sort:{id:1}},{$limit:1},
],
as: 'match_docs'
}
With no success.
That would be easy with the second syntax of $lookup:
[
{
$lookup: {
from: 'episodes', # make sure your collection name is correct
let: {url: '$url'},
pipeline: [
{
$match: {
$expr: {
$eq: ['$show_b', '$$url']
}
}
},
{
$sort: {
pubDate: -1
}
},
{
$limit: 1
}
],
as: 'match_docs'
}
}
]

MongoDB Compass: How can I perform a SQL join / Mongoose populate?

I have a collection called Post, which has an ID field called makeupProduct. This ID field is a foreign key to my MakeupProduct collection.
In MongoDB Compass, I'm trying to find all Posts
where productUrl === null and
populate the Makeup record along with it
Is this possible?
I have the 1st filter down, but don't know how to write the rest.
db.Post.aggregate([
{
$match: {
productUrl: null
}
},
{
$lookup:
{
from: "MakeupProduct",
localField: "makeupProduct",
foreignField: "_id",
as: "makeupProduct"
}
},
{
$set: {
makeupProduct: {
$arrayElemAt: ["$makeupProduct", 0]
}
}
}
])

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.

Mongo Aggregate Objects with $lookup using non matching values

I've got an Object Mission referring to another object Position with a key _p_position.
Mission objects look like:
{
_id: "ijjn97678",
_p_position: "Position$qwerty123",
...
}
Position objects look like:
{
_id: "qwerty123",
...
}
I don't know if it is Mongo or Parse convention but as one can see a Position$ is added on relational position attribute in missions.
I'd like to aggregate both into a single Object to get a results similar to the following:
{
_id: "ijjn97678",
_p_position: "Position$qwerty123",
positions: [
{
_id: "qwerty123"
}
]
}
using:
missions.aggregate([
{
$lookup: {
as: "position",
from: "Position",
foreignField: "_id",
localField: "_p_position",
},
},
])
But I need to remove Position$ from _p_position. Is there a way I can compute "_p_position" before it is used to find a matching Position's id ?
PS: I only have reading rights on DB
You can use $addFields to add another field which will be then passed to $lookup stage. To get the part that's following the dollar sign you need: $indexOfBytes and $substr operators. Additionally dollar sign itself is a special character in Aggregation Framework (represents a field reference) so you need $literal to force it to be considered as regular field
db.missions.aggregate([
{
$addFields: {
value: {
$let: {
vars: { index: { $indexOfBytes: [ "$_p_position", { $literal: "$" } ] } },
in: { $substr: [ "$_p_position", { $add: [ "$$index", 1 ] } , { $strLenBytes: "$_p_position" } ] }
}
}
}
},
{
$lookup: {
from: "Position",
localField: "value",
foreignField: "_id",
as: "position"
}
}
])