$lookup on an array of objects matching by id - mongodb

So I have two collections, stock and store.
I'm trying to query the store by id.
my stock looks like this
{
_id: ObjectId('5ee93b1e9f96dd1257885748')
productId: 100,
inStore: [
{
_id: ObjectId('5ee7ad659f96dd1257885742'),
store: ObjectId('5ee906e59f96dd1257885745'),
quantity: 10
},
{
_id: ObjectId('5ee9085f9f96dd1257885746'),
store: ObjectId('5ee904549f96dd1257885744'),
quantity: 50
}
]
}
With store being a referenced object to a store
store: { type: Types.ObjectId, ref: 'Store' },
{
_id: ObjectId('5ee904549f96dd1257885744')
name: 'Manchester Store'
}
What's the correct way to query by all stock documents, that contain inStore.store._id?
I'm currently trying this (following How do I query referenced objects in MongoDB?):
const inventories = await InventoryModel()
.aggregate([
{ $unwind: "$inStore" },
{ $lookup: {
from: "Store",
localField: "inStore.store",
foreignField: "_id",
as: "inStore.store"
}},
{$match: {
"inStore.store._id": '5ee906e59f96dd1257885745'
}}
])
But I'm not getting any results? but I do notice that store is an empty array, when i remove the match condition. How do I properly use lookup in this scenario?
Thanks in advance

If you have the store id, you don't need to use the aggregation pipeline at all. Use a simple find with condition
'inStore._id' => ObjectId('...')
Your query condition uses a string, but your ids are ObjectIds. These are different values because their types are different and therefore you won't get any matches.

Related

How to update fields in a MongoDB collection if certain conditions met between two collections?

What am I doing?
So I am trying to update two fields in my MongoDB collection. The collection name is mydata and looks like this:
{
id: 123,
name: "John",
class: "A-100",
class_id: "", <-- Need to update this field,
class_type: "", <-- Need to update this field
}
What do I want to do?
I have another collection that is older, but it contains two fields that I need that I do not have in my current collection. But they both have the id field that corresponds. This is how it looks like the other collection:
{
id: 123,
name: "John",
class: "A-100",
class_id: 235, <-- Field That I need,
class_type: "Math" <-- Field That I need
}
What have I done so far?
I started an aggregate function that starts with a $lookup then $unwind then $match then $project. Looks like this:
db.mydata.aggregate([
{
$lookup: {
from: "old_collection",
localField: "id",
foreignField: "id",
as: "newData"
}
},
{
$unwind: "newData"
},
{
$match: {"class": "A-100"}
},
{
$project: {
_id: 0,
"id": "$newData.id",
"class_id": "$newData.class_id",
"class_type": "$newData.class_type"
}
},
Need help here to update mydata collection in the
fields that I pointed in the top
])
In summary
What I am trying to do is: If two objects from different collections have the same Id then pick the keys from the second object and update the keys in the first object.
Is there a way to do that in MongoDB?
Thanks.

Performing multiple queries and joining the result tables of those queries all on the mongodb server side before returning to client APP

In my database there are severy collections.
I need to perform a query on each of those collections.
And then join the output of those queries by a common id field.
As far as I know, I need to perform several queries and then join the outputs when the results are returned to the client.
Can this be done with one query call to MongoDB?
EDITED:
each of those collections will be sharded.
You can achieve the effect you want through the Aggregation feature of MongoDB.
Use $lookup to query on multiple collections.
Use $unwind to make field key.
Use $project to make your own output.
For example:
Script:
db.mainusers.aggregate([
{
$lookup: {
from: 'subusers',
localField: 'name',
foreignField: 'name',
as: 'subusers',
},
},
{
$unwind: '$subusers',
},
{
$project: {
_id: 1,
email: 1,
name: 1,
subuserInfo: '$subusers.info',
},
},
])
Result:
{
_id: 'U5967278ce90299ce10f545889b786ba7',
email: 'xxxx#xxxx.com',
name: 'Jay',
subuserInfo: 'Some subuser Info',
}

Filter by id in an array of object ids before populate mongoose

I have post schema like this
const postSchema = new mongoose.Schema({
title: { type: String, minlength: 2 },
description: { type: String },
categoryId: [{ type: mongoose.Schema.ObjectId, ref: "category" }],
});
I have to fetch random documents from the posts and populate all categories in it. What I have achieved so far is this
await Post.aggregate([
{
$lookup: {
from: "categories",
localField: "categoryId",
foreignField: "_id",
as: "categoryId",
},
},
]).sample(5)
Need to do
Suppose i have an id of a category in a variable catId, what I need to do is to filter out those posts which contains this id in their array of categoryId before getting those random posts. By filtering out I mean i need random posts which has catId in their array.
You can do it like below :
Post.aggregate([
/** Use match to Get docs where 'categoryId' has passed in value */
{
$match: {
categoryId: catId /** Input 'catId', make sure you convert input request of string to 'ObjectId()' to match with type in DB*/
}
},
{
$sample: { /** Picks some random 3 docs (Sometimes a single doc can be repeated) */
size: 3
}
},
{
$lookup: {
from: "categories",
localField: "categoryId",
foreignField: "_id",
as: "categoryId"
}
}
])
Test : MongoDB-Playground
Ref : $sample
Note : So $sample can get the job done, but it has few limitations read through the doc for more info. Use $explain to check if your query is performing well or not, In any case if $sample is not working for you, then you can actually use $skip & $limit to get this job done.
Let's say if you can auto generate some number at least 3 less than collections size for skip & skip those docs to eventually do limit of 3 on remaining docs. You can have a sort in between skip & limit - such way do test this for better uniqueness. Check this : skip-and-limit-in-aggregation-framework - Usually this used for pagination but might help you in least case.

Find documents matching ObjectIDs in a foreign array

I have a collection Users:
{
_id: "5cds8f8rfdshfd"
name: "Ted"
attending: [ObjectId("2cd9fjdkfsld")]
}
I have another collection Events:
{
_id: "2cd9fjdkfsld"
title: "Some Event Attended"
},
{
_id: "34dshfj29jg"
title: "Some Event NOT Attended"
}
I would like to return a list of all events being attended by a given user. However, I need to do this query from the Events collection as this is part of a larger query.
I have gone through the following questions:
$lookup on ObjectId's in an array - This question has the array as a local field; mine is foreign
MongoDB lookup when foreign field is an array of objects - The array is of objects themselves
MongoDB lookup when foreign field is an array
I have tried various ways of modifying the above answers to fit my situation but have been unsuccessful. The second answer from the third question gets me closest but I would like to filter out unmatching results rather than have them returned with a value of 0.
My desired output:
[
{
_id: "2cd9fjdkfsld"
title: "Some Event Attended"
},
]
One option would be like this:
db.getCollection('Events').aggregate({
$lookup: // join
{
from: "Users", // on Users collection
let: { eId: "$_id" }, // keep a local variable "eId" that points to the currently looked at event's "_id"
pipeline: [{
$match: { // filter where
"_id": ObjectId("5c6efc937ef75175b2b8e7a4"), // a specific user
$expr: { $in: [ "$$eId", "$attending" ] } // attends the event we're looking at
}
}],
as: "users" // push all matched users into the "users" array
}
}, {
$match: { // remove events that the user does not attend
"users": { $ne: [] }
}
})
You could obviously get rid of the users field by adding another projection if needed.

Mongoose aggregate pipeline not working as expected [duplicate]

This question already has an answer here:
Matching ObjectId to String for $graphLookup
(1 answer)
Closed 4 years ago.
I am trying to use the Mongoose aggregate pipeline to query my User collection and Company collection simultaneously.
Specifically, I am trying to return all users and return the user's associated companyName. Each user has a companyId attribute
that maps to a company _id field. Here are my sample user and company documents:
User:
{
_id: ObjectId("5b12ef5ba07ce1f8b212f07b"),
companyId: "12345"
first: "John",
last: "Doe",
}
Company:
{
_id: ObjectId("12345"),
companyName: "UPS"
}
Here is my query code:
User.aggregate([{
$match: {}
},
{
$lookup: {
localField: "companyId",
from: "company",
foreignField: "_id",
as: "companyInfo"
}
},
{
$unwind: "$companyInfo"
},
{
$project: {
first: 1,
last: 1,
"companyInfo.companyName": 1,
}
}
], (err, users) => {
console.log(users) // returns []
});
I THINK the reason is the companyId is stored as a string in my User collection, but it is an objectId in the company collection.
Can anyone confirm? And if it is the issue, I'm not sure how to resolve it... Can someone help? Thanks in advance!
Your assumption is correct.
When doing $lookup, local field and remote field must be of the same type.
There are no way to do a converter when lookup, afaik.
This is the related issue:
https://jira.mongodb.org/browse/SERVER-22781
The version 3.7.3 has it implemented
For now you might want to change your data type