Is it impossible to use "$search in $lookup result" in MongoDB Atlas? - mongodb-atlas

i have a collection named 'crew'
{
_id:
name:
description:
person_id:
}
and collection named 'person'
{
_id:
name:
description:
}
My goal is to make recommend system by searching crew.name, crew.description and person.name.
first, i create index for each collection ( collection > search indexes ), and i use aggregate as follows.
db.crew.aggregate([
{
$lookup: {
from: 'person',
localField: 'person_id',
foreignField: '_id',
as: 'person_info'
}
}
, {
$unwind: {
path: '$person_info'
}
}
, {
$project: {
'crewId': '$_id',
'crewName': '$name',
'crewDescription': '$description',
'personName': '$person_info.name'
}
}
, {
$search: {
"text": {
"query": "SEARCH_WORD",
"path": ["crewName","crewDescription","personName"]
}
}
}
])
as a resutl I got a error message as follows.
MongoError: $_internalSearchBetaMongotRemote is only valid as the first stage in a pipeline.
Is it impossible to use "$search in $lookup result" in mongodb atalas?

Yes it is impossible to use $search with $lookup. As per documentation,
$search must be the first stage of any pipeline it appears in. $search cannot be used in:
a view definition
a $lookup subpipeline
a $facet pipeline stage

Related

$lookup with pipeline match and projection does not work for guid

I have two collections that I want to join with $lookup based on two id fields. Both fields are from type guid and looke like this in mongodb compass: 'Binary('cavTZa/U2kqfHtf08sI+Fg==', 3)'
This syntax in the compass aggregation pipeline builder gives the expected result:
{
from: 'clients',
localField: 'ClientId',
foreignField: '_id',
as: 'ClientData'
}
But i want to add some projection and tried to change it like this:
{
from: 'clients',
'let': {
id: '$_id.clients'
},
pipeline: [
{
$match: {
$expr: {
$eq: [
'$ClientId',
'$$id'
]
}
}
},
{
$project: {
Name: 1,
_id: 0
}
}
],
as: 'ClientData'
}
But the result here is that every client from collection 'clients' is added to every document in the starting table. I have to use MongoDB 3.6 so the new lookup syntax from >=5.0 is not available.
Any ideas for me? Does $eq work for binary stored guid data?
In the first example, you say that the local field is ClientId and the foreign field is _id. But that's not what you used in your second example.
This should work better:
{
from: 'clients',
'let': {
ClientId: '$ClientId'
},
pipeline: [
{
$match: {
$expr: {
$eq: [
'$$ClientId',
'$_id'
]
}
}
},
{
$project: {
Name: 1,
_id: 0
}
}
],
as: 'ClientData'
}

Is it possible to perform $lookup on the previous $lookup stage without unwinding?

I have 3 documents that are structured like this: (I add some annotation to highlight what data I am using for the different lookup stages)
Client {
_id,
companyId -> initial match
}
Project {
_id,
clientId, -> stage 1 lookup (client._id)
createdBy
}
User {
_id, -> stage 2 lookup (project.createdBy)
firstName,
lastName
}
I have an aggregate pipeline setup like this that returns an array of clients with an array of projects for each client.
Client.aggregate([
{
$match: {
companyId: mongoose.Types.ObjectId(req.user.companyId)
}
},
{
$lookup: {
from: 'projects',
localField: '_id',
foreignField: 'clientId',
as: 'projects'
}
},
])
.sort({ createdAt: -1 })
Now within the projects array is an object of the project key/values and I want to do another $lookup to get user information based off the createdBy field. I made a few attempts at solutions while reading through the aggregation pipeline docs and wasn't able to come to a working solution. One issue I'm having is that I'm not able to $unwind the first lookup as I need an array of projects. I think it would be possible if my projects were unwinded and looked something like this:
projects: {
"projectId": {
...project
}
}
But that's not currently how things are. Is there a way to accomplish this with lookup stages? My desired output is this:
clients: [
{
_id: 0,
projects: [
{
_id: 0,
createdBy: {
firstName: "John",
lastName: "Doe"
},
}
]
}
]
// ideal 2nd stage lookup
{
lookup: {
from 'users',
localField: 'projects.project.createdBy',
foreignField: '_id',
as: 'createdBy'
}
},
{ $unwind: '$createdBy' }

Expand Contained Mongo Document

This is for MongoDB
I have a Document say Employee as below:
{
"_id":"e01",
"name":"Andy",
"salary":"10000",
"address":"ad01id"
}
That is a simple Employee Document, with address being a foreign key to Address Document as below(for this above Employee)
{
"_id": "ad01id",
"pin":"999",
"Home":{
"street":"101",
"city":"someCity"
},
"Country":"someCountry"
}
I need a help to write a query which will fetch an Employee but will give me an output, which will have Employee's address field expanded with its entire document as
{
"_id":"e01",
"name":"Andy",
"salary":"10000",
"address":{
"pin":"999",
"Home":{
"street":"101",
"city":"someCity"
},
"Country":"someCountry"
}
}
I have tried $lookup and $merge but could not get the desired output - $merge hides the keys in the early document if the later document has the same key, which is the case in my real scenario. My actual documents have some keys with the same name. Such as name is there in both of my actual Documents.
Thanks in advance!
$lookup is needed to "join" the data from both collections and $unwind or $arrayElemAt can be used to convert an array being a result of $lookup into nested subdocument:
db.Employee.aggregate([
{
$lookup: {
from: "Address",
localField: "address",
foreignField: "_id",
as: "address"
}
},
{
$addFields: {
address: { $arrayElemAt: [ "$address", 0 ] }
}
},
{
$project: {
"address._id": 0
}
}
])
Mongo Playground
You can also use populate.
db.Employee.find(
{
},
)
.populate("address", "pin Home Country")
.exec(function(err, result) {
console.log(result)
});

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.

MongoDB: Conditional select from one collection based on another collection

I'm fairly new to MongoDB and need help doing a select, or perhaps some sort of left join, on one collection based on another collection's data.
I have two collections, animals and meals, and I want to get the animal(s) that has had it's last registered meal after a certain date (let's say 20171001) to determine if the animal is still active.
collection animals:
{
name: mr floof,
id: 12345,
lastMeal: abcdef
},
{
name: pluto,
id: 6789,
lastMeal: ghijkl
}
collection meals:
{
id: abcdef,
created: 20171008,
name: carrots
},
{
id: ghijkl,
created: 20170918,
name: lettuce
}
So the expected output of the query in this case would be:
{
name: mr floof,
id: 12345,
lastMeal: abcdef
}
As Mr Floof has had his last meal 20171008, i.e. after 20171001.
Hope I was clear enough, but if not, don't hesitate to ask.
You can try below aggregation query.
db.animals.aggregate([ [
{
"$lookup": {
"from": "meals",
"localField": "lastMeal",
"foreignField": "id",
"as": "last_meal"
}
},
{
"$unwind": "$last_meal"
},
{
"$match": {
"last_meal.created": {
"$gt": 20171001
}
}
}
])
More info here.
You can use $project with exclusion after $match stage to format the response to exclude joined fields. Something like { $project: {"last_meal":0} }
MongoDB supports joins with $lookup , In your case you can use query like:-
db.animals.aggregate([
{
$lookup:
{
from: "meals",
localField: "lastMeal",
foreignField: "id",
as: "last_meal"
}
},
{
$match: {
"created" : {
$gt: "date" //your date format
}
}
}
])
thanks !