I have two mongo collections within my database named user and order. Within the user collection, there is an array of object references to orders. (the code snippet is a reduced down version of my schema for each collection).
User ({
user_id
username
email
firstname
surname
...
orders : [{type: Schema.Types.ObjectId, ref: "ordersmodel"}]
})
Order ({
order_id
current_status
date_ordered
...
})
What I am looking to do is to access the order information for a specific user when passed a user_id. This was my thinking so far:
User.aggregate([
{
$lookup: {
from: 'order',
localfield: 'orders',
foreignField: 'order_id',
as: 'order'
}},
{
$unwind: '$order'
},
{$project: {
_id: 0,
order_id: '$order.order_id',
status: '$order.status'
}}
]).toArray();
and am not sure what to do next in order to return the orders for a specific user.
when you use ref in schema you can using populate like this:
let result = await User.findById(user._id).populate("orders").lean()
console.log(result )
Related
So, I have two collections named Records and Collections.
In records.model.js, I have a field which refers to Collections like this:
const Records = new mongoose.Schema(
{
collection: { type: mongoose.Schema.Types.ObjectId, ref: "Collection" },
},
);
While in the Collections, I have a field like this:
const Collection = new mongoose.Schema(
{
name: { type: String, required: true },
},
);
What I am trying to do is to perform a search in the Records with the collection name that I am getting from the req.query.
So far, I have tried:
let { text } = req.query;
const pipeline = [
{
$lookup: {
from: Collection.collection.name,
localField: "_id",
foreignField: "_id",
pipeline: [
{
$match: {
name: text
},
},
],
as: "collectionInfo",
},
}]
What I've tried is working but I want to get the results even with the substring of the name because currently, it needs the exact name for me to get the results.
If I have a query of abc, I want to get the Records with the text of abcdefg under the Collection not typing the exact words before I get the expected result. How can I do this?
In my Task schema I have fields like this:
{
name: String
},
{
user: ObjectID
}
I need to sort tasks. If I want to do it by name field it's easy:
await Tasks.find().sort({name: 1})
That works. But the problem is when I want to sort by user.name. To get fields from User I can populate them, so:
await Tasks.find().populate('user', 'name').sort({'user.name': 1})
And it doesn't work. I cannot sort by fields added by populate function. I've been searching a lot in documentation and in other users' questions. I've found that I can pass sorting option to populate function but it doesn't work also. I guess it sort fields in populated field.
When I've tried to use aggregate with lookup like this:
Tasks.aggregate([{ $lookup: {
{
from: 'User',
localField: 'user',
foreignField: '_id',
as: 'someField'
}}}])
it returns someField: []
Can somebody help me? Thanks a lot!
In aggregate query, you should reference your collection with it's real name, NOT with the model name. So, instead of from: 'User', it should be from: 'users':
Tasks.aggregate([
{
$lookup: {
from: 'users',
localField: 'user',
foreignField: '_id',
as: 'user'
}
},
{
$set: {
user: { $first: '$user' }
}
},
{
$sort: {
'user.name': 1
}
}
])
I have a query that I wish to perform something similar as:
Archive does not exists AND
Owner Email in schema is in Query OR
Owner Email in Populated's schema is in Query
Below is what I have tried as of my understanding
let docs = await Document.find({ archive: { $exists: false }})
.and([{ owner_email: { $regex: localQuery } }])
.or()
.populate('owner_id', null, {
email: { $regex: localQuery },
});
So what I wish to do is, I have two schema, the user and the documents, User sometimes is shared along [as a librarian], then I wish to return, both, which matches the populated email or the actual owner's email.
As mongoose's populate() method does not really "join" collections and rather makes another query to the database to populate after the find() operation, you can switch to an aggregation pipeline and use $lookup in order to match the email in the referenced field. So assuming your models look like:
const Document = mongoose.model('Document', {
name: String,
archive: String,
owner_email: String,
owner: {type: Schema.Types.ObjectId, ref: 'Person'}
});
const Person = mongoose.model('Person', {
firstName: String,
lastName: String,
email: String
});
Then, you can do:
const result = await Document.aggregate([
{
$lookup: {
from: Person.collection.name,
localField: "owner",
foreignField: "_id",
as: "referencedOwner"
}
},
{
$match: {
archive: {$exists: false},
$or: [
{"referencedOwner.email": {$regex: localQuery}},
{"owner_email": {$regex: localQuery}}]
}
}
]);
Here's a working example on mongoplayground: https://mongoplayground.net/p/NqAvKIgujbm
Users Schema:
{
username: "milkeypony",
_id: "_mongodbID",
id: "random_30_characters_string"
...
}
Blog Schema
{
title: "_title",
_id: "_mongodbID",
author: "random_30_characters_string"
...
}
The Blogs.author is the same ID as with in Users.id
And what I'm trying to do is when I use Blogs.findOne() to fetch some blog post, Mongoose will also help me fetch some user data.
And I already successfully done this with raw Mongo shell command
db.blogs.aggregate([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "id",
as: "author"
}
}
])
And I try the mongoose populate method, but it didn't work out for me
make sure Blogs schema like have
author:{
type:Schema.Types.ObjectId,
ref: 'Users'
}
and populate like below
Blogs.findAll({})
.populate({
path:author
})
.exec((err, blogs)=>{
console.log(err,blogs);
}))
more info check offical doc
Is it possible to populate a mongoose model with a field of a reference model that isn't the _id ... e.g. a username.
so something like
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : { type: String, field: "username", ref: 'Story' }
});
This is supported since Mongoose 4.5, and is called virtuals population.
You have to define your foreign keys relationships after your schemas definitions and before creating models, like this:
// Schema definitions
BookSchema = new mongoose.Schema({
...,
title: String,
authorId: Number,
...
},
// schema options: Don't forget this option
// if you declare foreign keys for this schema afterwards.
{
toObject: {virtuals:true},
// use if your results might be retrieved as JSON
// see http://stackoverflow.com/q/13133911/488666
//toJSON: {virtuals:true}
});
PersonSchema = new mongoose.Schema({id: Number, ...});
// Foreign keys definitions
BookSchema.virtual('author', {
ref: 'Person',
localField: 'authorId',
foreignField: 'id',
justOne: true // for many-to-1 relationships
});
// Models creation
var Book = mongoose.model('Book', BookSchema);
var Person = mongoose.model('Person', PersonSchema);
// Querying
Book.find({...})
// if you use select() be sure to include the foreign key field !
.select({.... authorId ....})
// use the 'virtual population' name
.populate('author')
.exec(function(err, books) {...})
It seems they enforce to use _id, and maybe we can customize it in the future.
Here is the issue on Github https://github.com/LearnBoost/mongoose/issues/2562
This is an example of using the $lookup aggregate to populate a model called Invite with the respective User based on the corresponding email field:
Invite.aggregate(
{ $match: {interview: req.params.interview}},
{ $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} }
).exec( function (err, invites) {
if (err) {
next(err);
}
res.json(invites);
}
);
It's probably quite similar to what you're trying to do.
To add to Frosty's answer, if you're looking to refer to an array of documents of another collection you would make changes like so.
BookSchema = new mongoose.Schema(
{
title: String,
authorId: [Number],
},
// schema options: Don't forget this option
// if you declare foreign keys for this schema afterwards.
{
toObject: { virtuals: true },
// use if your results might be retrieved as JSON
// see http://stackoverflow.com/q/13133911/488666
toJSON: {virtuals:true}
});
PersonSchema = new mongoose.Schema({ id: Number, name: String });
BookSchema.virtual("author", {
ref: "Person",
localField: ["authorId"],
foreignField: ["id"],
// justOne: true, // Needs to be commented out in this scenario,
});
You may use the populate() API.
The API is more flexible, you don't have to specify ref and field in the Schema.
http://mongoosejs.com/docs/api.html#document_Document-populate
http://mongoosejs.com/docs/api.html#model_Model.populate
You can mix and match with find().