Sequelize include with an OR statement on the join - javascript

I have a user entity with the following relationship:
User.associate = (models) => {
User.belongsToMany(User, {
as: "friends",
through: "user_friend",
onDelete: "CASCADE",
})
User.hasMany(models.Message, { as: "sentMessages", foreignKey: "senderId" })
User.hasMany(models.Message, {
as: "receivedMessages",
foreignKey: "recipientId",
})
}
and a messages entity with relationship:
Message.associate = (models) => {
Message.belongsTo(models.User, { foreignKey: "senderId" })
Message.belongsTo(models.User, { foreignKey: "recipientId" })
}
and I'm attempting to do something like: get me a user, include their friends, include their conversation history with each friend using the following:
let user = await User.findOne({
where: {
auth_key: auth_key,
},
include: {
association: "friends",
attributes: ["first_name", "last_name", "chat_name", "profile_picture"],
through: {
attributes: [],
},
include: [
{
required: false,
association: "sentMessages",
where: {
recipientId: 1,
},
},
{
required: false,
association: "receivedMessages",
where: {
senderId: 1,
},
},
],
},
})
which does kind of work but it seems really messy and I don't like having the messages split into sent and received, I would just like them grouped together. Currently the response looks like this:
{
"id": 1,
"username": "ajsmith",
"first_name": "Anthony",
"last_name": "Smith",
"chat_name": "Anthony.Smith",
"email": "a#gmail.com",
"createdAt": "2020-06-15T19:59:58.000Z",
"updatedAt": "2020-06-15T19:59:58.000Z",
"friends": [
{
"first_name": "Sarah",
"last_name": "Smith",
"chat_name": "Sarah.Smith",
"sentMessages": [
{
"id": 2,
"senderId": 2,
"recipientId": 1,
"type": "text",
"content": "Hi",
"createdAt": "2020-06-15T19:59:58.000Z",
"updatedAt": "2020-06-15T19:59:58.000Z"
}
],
"receivedMessages": [
{
"id": 1,
"senderId": 1,
"recipientId": 2,
"type": "text",
"content": "Hello",
"createdAt": "2020-06-15T19:59:58.000Z",
"updatedAt": "2020-06-15T19:59:58.000Z"
}
]
}
]
}
I'm not sure if my relationships are wrong, my querying is wrong or it's just not possible to do but over the last week I've searched the sequelize docs, stackoverflow and everything in between and haven't found a solution. It seems like if I could include an OR statement on the LEFT OUTER JOIN which is occurring, then I could have my messages include both sent and received where the senderId or recipientId is my user.

Related

Mongoose Populate Null Reference

I have two simple models defined in Mongoose, composed of two schema Client and City, I have the property city defined in Client as a ObjectId, ref: 'City', so far so good.
If I query for a client and want also to filter by the 'province' property of City, I do this:
const client = await Client
.find({ name: "Gérard" })
.populate([{
path: 'city',
model: City,
match: { province: 'BA' }
}]);
And the output is just fine:
{
"id": "627264e3ec261a883d42ead9",
"name": "Gérard",
"email": "gerard#depardieu.fr",
"date": "1948-12-27",
"active": true,
"city": {
"id": "627264e3ec261a883d42ead1",
"name": "Buenos Aires",
"province": "BA"
}
}
Howerver, if I input a province code of a nonexistent city:
const client = await Client
.find({ name: "Gérard" })
.populate([{
path: 'city',
model: City,
match: { province: 'CA' }
}]);
It would return me that:
{
"id": "627264e3ec261a883d42ead9",
"name": "Gérard",
"email": "gerard#depardieu.fr",
"date": "1948-12-27",
"active": true,
"city": null
}
I don't want in this particular scenario, any instance of Client to be returned, and I don't know how to avoid this behavior with Mongoose, a behavior I never had to worry about with Spring Data for instance.
Any tips for me?
Thanks in advance.
I solved it myself, I had to go a little lower level with Mongoose and use aggregates and lookups.
const client = await Client.aggregate([
{
$match: { name: "Gérard" }
},
{
$lookup: {
from: City.collection.name,
pipeline: [
{
$match: {
province: 'BA'
}
}
], as: "city"
}
},
{
$unwind: "$city"
},
{
$match: {
city: { $ne: [] }
}
}
]);
Expected result:
{
"id": "627264e3ec261a883d42ead9",
"name": "Gérard",
"email": "gerard#depardieu.fr",
"date": "1948-12-27",
"active": true,
"city": {
"id": "627264e3ec261a883d42ead1",
"name": "Buenos Aires",
"province": "BA"
}
}
Witch is ok, client name "Gérard" lives in "Buenos Aires", situated in province "BA".
On the other hand:
const client = await Client.aggregate([
{
$match: { name: "Gérard" }
},
{
$lookup: {
from: City.collection.name,
pipeline: [
{
$match: {
province: 'CA'
}
}
], as: "city"
}
},
{
$unwind: "$city"
},
{
$match: {
city: { $ne: [] }
}
}
]);
Returns nothing, once the city of "Buenos Aires" is not located in province "CA".
Notice here the last parameter (as an object) the array passed to Client.aggregate() receives:
{
$match: {
city: { $ne: [] }
}
}
This tells MongoDB that in order to return data city must be not equal to an empty array.
And with that the issue is solved.

How to pull from array of objects with array of objects in MongoDB?

I was trying to pull multiple objects from an array of objects, and I found this article
Using MongoDB $pull to delete documents within an Array
so here's my schema and how I did,but it does'nt work
{
"_id": "61fed9b89763c1c3b886b74f",
"TeamName": "Hogwarts",
"TeamImage": "Avatar ",
"createdAt": "2022-02-05T20:10:32.885Z",
"updatedAt": "2022-02-05T20:59:51.359Z",
"__v": 0,
"TeamMember": [
{
"Name": "Ronne",
"Email": "test4#gmail.com",
"_id": "61fee1807df64451141f08df"
},
{
"Name": "Kai",
"Email": "school021195#gmail.com",
"_id": "61fee1e3fffd0f55ed92caee"
},
{
"Name": "Selina",
"Email": "test#gmail.com",
"_id": "61fee1e3fffd0f55ed92caef"
},
{
"Name": "Jessica Wu",
"Email": "test1#gmail.com",
"_id": "61fee1e3fffd0f55ed92caf0"
},
{
"Name": "Hormione",
"Email": "test3#gmail.com",
"_id": "61fee1807df64451141f08de"
}
]
},
Delete method
const team = await Team.findOneAndUpdate(
{_id: TeamId},
{
$pull: { TeamMember: [ {Name: "Ronne" },{ Name : "Hormione"} ] },
// $pull: { TeamMember: {$in:[ {Name: "Ronne" },{ Name : "Hormione"} ]}},
},
{ new: true,multi: true}
);
I also try the $in method, but both don't work, did I miss something? or what's the right way to do multiple pulls using MongoDB?
BTW, I am using Mongoose for my schema, I don't know if it matters.
Try using this way
const team = await Team.findOneAndUpdate(
{_id: TeamId},
{
$pull: { TeamMember: {Name: {$in: ["Ronne","Hormione"] } } },
},
{ new: true,multi: true}
);
https://mongoplayground.net/p/Sqo83w8YvVU
DO NOT Include [ ] Brackets in $pull. It will not work
const team = await Team.findOneAndUpdate(
{_id: TeamId},
{
$pull: { TeamMember: {Name: "Ronne} },
},
{ new: true,multi: true}
);

Mongoose update subdocument by key

for the following collection:
id: Number
name:[
new Schema({
language: { type: String, enum: ['en-US', 'fr-CA'] },
text: String,
},{ _id: false }
);
],
isActive: Boolean
and sample data like the following:
{
id:1,
"name": [
{"language": "en-US", "text": "Book"},
{"language": "fr-CA", "text": "livre"}
],
isActive:true
// and so many other fields
},
{
id:2,
"name": [
{"language": "en-US", "text": "Pen"}
],
isActive:true
// and so many other fields
}
I would like to update the document by Id:1 and change the text only for french, I tried by:
const input={ "id":"1", "isActive": false}
const name= { "name": [{"language": "fr-CA", "text": "Nouvel article"}]}
await langs.findByIdAndUpdate(
{ _id: input.id },
{ ...input, $addToSet: { name } },
{ new: true, upsert: true }
);
but the result is: (added another french item)
{
id:1,
"name": [
{"language": "en-US", "text": "Book"},
{"language": "fr-CA", "text": "livre"},
{"language": "fr-CA", "text": "Nouvel article"}
],
isActive:false
},
This is based on Brit comment:
https://mongoplayground.net/p/atlw5ZKoYiI
Please advise.
As long as that attribute already exists on the document, you can do something like:
Collection.findOneAndUpdate({id: 1}, {
$set: {
"name.$[elem]": name
}
},
{
arrayFilters: [ { "elem.language": name.language } ],
upsert: true,
new: true
})

Trouble using mongoose 'Populate'

Good Morning,
So I seem to have an issue with populating my fields with Node.js and Mongoose. It is just turning up a blank array:
Result: (JSON)
[
{
"courseassignments": [],
"_id": "5db56ceb4cc2c92824ae2651",
"name": "Garibaldi",
"website": "www.garibaldi.org.uk",
"__v": 0
},
{
"courseassignments": [],
"_id": "5db56d074cc2c92824ae2652",
"name": "Kirk Hallam",
"website": "www.kirkhallamacademy.co.uk",
"__v": 0
}
]
Below is the code I am using the call the populate function, plus the models that I am using for each of the data. It is very weird.
*school.js (Model) *
// Require modules within our file:
const mongoose = require('mongoose')
const schoolSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
website: {
type: String,
required: true,
trim: true
},
logo: {
type: Buffer
},
courseassignments: [{
type: mongoose.Schema.Types.ObjectID,
ref: 'CourseAssignment'
}]
})
// Export the user to use within other files:
const school = mongoose.model('School', schoolSchema)
module.exports = school
courseassignment.js (Model)
// Require modules within our file:
const mongoose = require('mongoose')
const courseAssignmentSchema = new mongoose.Schema({
school: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'School'
},
course: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'Course'
}
})
// Export the user to use within other files:
const courseAssignment = mongoose.model('CourseAssignment', courseAssignmentSchema)
module.exports = courseAssignment
* Code to fetch data: (within app.js)*
router.get('/school', async (req, res) => {
const schools = await School.find({}).populate({ path: 'courseassignments' })
res.send(schools)
})
I would remove the courseassigment ref from the School model, and would take advantage of virtual populate.
So here are the steps:
school.js (school model - as you see I removed courseassignments ref, and added options for virtual features)
const mongoose = require('mongoose')
const schoolSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
website: {
type: String,
required: true,
trim: true
},
logo: {
type: Buffer
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
schoolSchema.virtual("courseassignments", {
ref: "CourseAssignment",
foreignField: "school",
localField: "_id"
})
const school = mongoose.model('School', schoolSchema)
module.exports = school
At this point when you hit the schools endpoint, your response will be like this.
( I only show one item to be short.)
[
{
"_id": "5db5a809cfc9951770d5078a",
"name": "school 1",
"website": "school 1 website",
"__v": 0,
"courseassignments": [
{
"_id": "5db5a892cfc9951770d50790",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a847cfc9951770d5078d",
"__v": 0
},
{
"_id": "5db5a89ccfc9951770d50791",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a851cfc9951770d5078e",
"__v": 0
},
{
"_id": "5db5a8a1cfc9951770d50792",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a858cfc9951770d5078f",
"__v": 0
}
],
"id": "5db5a809cfc9951770d5078a"
},
...
...
]
And if you also want to access to the Course name (which I think would be good),
courseassigment.js
const mongoose = require('mongoose')
const courseAssignmentSchema = new mongoose.Schema({
school: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'School'
},
course: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'Course'
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
courseAssignmentSchema.pre(/^find/, function (next) {
this.populate({
path: 'course'
});
next();
});
// an index may be required like this
//courseAssignmentSchema.index({ school: 1, course: 1 }, { unique: true });
const courseAssignment = mongoose.model('CourseAssignment', courseAssignmentSchema)
module.exports = courseAssignment
And with this the result will contain the course related fields like course name.
[[
{
"_id": "5db5a809cfc9951770d5078a",
"name": "school 1",
"website": "school 1 website",
"__v": 0,
"courseassignments": [
{
"_id": "5db5a892cfc9951770d50790",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a847cfc9951770d5078d",
"name": "course 1",
"__v": 0
},
"__v": 0,
"id": "5db5a892cfc9951770d50790"
},
{
"_id": "5db5a89ccfc9951770d50791",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a851cfc9951770d5078e",
"name": "course 2",
"__v": 0
},
"__v": 0,
"id": "5db5a89ccfc9951770d50791"
},
{
"_id": "5db5a8a1cfc9951770d50792",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a858cfc9951770d5078f",
"name": "course 3",
"__v": 0
},
"__v": 0,
"id": "5db5a8a1cfc9951770d50792"
}
],
"id": "5db5a809cfc9951770d5078a"
},
...
...
]
Docs:
https://mongoosejs.com/docs/tutorials/virtuals.html
https://mongoosejs.com/docs/populate.html#populate-virtuals

Mongoose Model populate callback is not called when there are references

I am trying to use Mongoose's Model.populate() to convert a user ID into a User, within a substructure of a document which has been fetched through an aggregation and an unwind. I'm guessing there's either something wrong with my schema or the perhaps unwind is breaking the connection to the sub-schema.
The problem is: when there is a valid reference, the populate callback simply isn't called. When there is no substructure the callback is called, with the original document unchanged.
Structure:
I have Articles which can have none or many ArticleRatings each by a User.
I'm using two references in the ArticleRating, to relate to the Article and the User who made the rating.
Process:
The process is actually exporting the articles to a (legacy) CSV format and flattening the structure, into duplicate article rows with their user-specific ratings. Unwind is perfect for this operation, and preserve nulls retains articles with no ratings.
Debugging:
I've tried stepping deep into the Model.populate code. It gets pretty complex with all the promises and callback wrappers, but I can see that the underlying populate call isn't calling the internal callbacks either. I'm not using the promise variant - but not 100% sure if I should be? (the Mongoose docs are a bit vague on the use-cases between callbacks and promises).
I've double checked my schema, tried explicitly adding the model to the populate call (which shouldn't be needed as it's in the schema). There are no errors or exceptions, it doesn't crash.
Stepping through the code in the Chrome debugger shows the model just as I'd expect: the first few Articles have a rating.userId with a valid ObjectId, but in those cases the populate callback simply doesn't get called. The next hundred Articles have no "rating" set, and the callback gets called reliably for all of them.
So I'm guessing something I'm doing wrong is leading Model.populate down a path where it's not erroring properly?
Note: I know I could rewrite the code to use aggregate $lookup or other embedding structures rather than the foreign reference, but I'm at the final piece of a feature jigsaw and would like to get this working as-is.
This is the simplified schema:
const ArticleRatingSchema = new Schema({
articleId: {type: Schema.Types.ObjectId, ref:'Article'},
userId: {type: Schema.Types.ObjectId, ref:'User'},
rating: String,
comment: String,
});
const ArticleSchema = new Schema({
title: String,
rating: ArticleRatingSchema,
});
And this is the lookup
// Find all articles relating to this project, and their ratings.
// Unwind does the duplicate per-user, and preserve keeps un-rated articles.
articleModel.aggregate([
{$match: {projectId: projectId}},
{$lookup:{from:'articleratings', localField:'_id', foreignField:'articleId', as:'rating' }},
{$unwind: {path:'$rating', preserveNullAndEmptyArrays:true}}
], (err, models) =>
{
if (!err) {
models.map((article) => {
articleModel.populate(article, {path:'rating.userId', model:'User'}, (err, article)=> {
// Process the article...
// this callback only gets called where there is NO rating in the article.
});
});
}
I realised it's because I'm processing the set in a synchronous map() loop, and the two cases must be different in that the non-matching populate is a synchronous callback whereas the matching replacements callback later.
If I console.log() in the callback, I find that the the four matching cases are processed last after the CSV has already been formatted and downloaded.
So the answer is: populate IS being called, but asynchronously.
I need to rework the map() loop to accomodate the usual async pattern.
I'm personally more than a bit baffled by where you think you are going with using $lookup in an aggregation pipeline and then wanting .populate() the results. Because asking to use .populate() essentially means that additional queries are being issued to the server in order to "emulate a join".
Therefore since $lookup actually does the "join on the server" then you really should be just using $lookup for this.
You can use .populate() and I'll show some code to show that it can be done. But it really is redundant here since you may as well just do all the work on the server.
So my best "approximation" of what you seem to have as a structure is:
articles
{
"_id" : ObjectId("5962104312246235cdcceb16"),
"title" : "New News",
"ratings" : [ ],
"__v" : 0
}
articleratings
{
"_id" : ObjectId("5962104312246235cdcceb17"),
"articleId" : ObjectId("5962104312246235cdcceb16"),
"userId" : ObjectId("5962104312246235cdcceb13"),
"rating" : "5",
"comment" : "Great!",
"__v" : 0
}
{
"_id" : ObjectId("5962104312246235cdcceb18"),
"articleId" : ObjectId("5962104312246235cdcceb16"),
"userId" : ObjectId("5962104312246235cdcceb14"),
"rating" : "3",
"comment" : "Okay I guess ;)",
"__v" : 0
}
{
"_id" : ObjectId("5962104312246235cdcceb19"),
"articleId" : ObjectId("5962104312246235cdcceb16"),
"userId" : ObjectId("5962104312246235cdcceb15"),
"rating" : "1",
"comment" : "Hated it :<",
"__v" : 0
}
users
{
"_id" : ObjectId("5962104312246235cdcceb13"),
"name" : "Bill",
"email" : "bill#example.com",
"__v" : 0
}
{
"_id" : ObjectId("5962104312246235cdcceb14"),
"name" : "Fred",
"email" : "fred#example.com",
"__v" : 0
}
{
"_id" : ObjectId("5962104312246235cdcceb15"),
"name" : "Ted",
"email" : "ted#example.com",
"__v" : 0
}
And then the aggregate statement:
Article.aggregate(
[
{ "$lookup": {
"from": ArticleRating.collection.name,
"localField": "_id",
"foreignField": "articleId",
"as": "ratings"
}},
{ "$unwind": "$ratings" },
{ "$lookup": {
"from": User.collection.name,
"localField": "ratings.userId",
"foreignField": "_id",
"as": "ratings.userId",
}},
{ "$unwind": "$ratings.userId" },
{ "$group": {
"_id": "$_id",
"title": { "$first": "$title" },
"ratings": { "$push": "$ratings" }
}}
],
(err,articles) => {
if (err) callback(err);
log(articles);
callback();
}
)
With the result:
{
"_id": "5962126f3ef2fb35efeefd94",
"title": "New News",
"ratings": [
{
"_id": "5962126f3ef2fb35efeefd95",
"articleId": "5962126f3ef2fb35efeefd94",
"userId": {
"_id": "5962126f3ef2fb35efeefd91",
"name": "Bill",
"email": "bill#example.com",
"__v": 0
},
"rating": "5",
"comment": "Great!",
"__v": 0
},
{
"_id": "5962126f3ef2fb35efeefd96",
"articleId": "5962126f3ef2fb35efeefd94",
"userId": {
"_id": "5962126f3ef2fb35efeefd92",
"name": "Fred",
"email": "fred#example.com",
"__v": 0
},
"rating": "3",
"comment": "Okay I guess ;)",
"__v": 0
},
{
"_id": "5962126f3ef2fb35efeefd97",
"articleId": "5962126f3ef2fb35efeefd94",
"userId": {
"_id": "5962126f3ef2fb35efeefd93",
"name": "Ted",
"email": "ted#example.com",
"__v": 0
},
"rating": "1",
"comment": "Hated it :<",
"__v": 0
}
]
}
Where it does not make sense to "populate" the references to the "articleId" on the "ratings" themselves. But we have indeed "populated" the "ratings" to the article, and the "user" for each rating.
Example Listing
Shows it both ways, using .populate() ( after $lookup ) like you are trying, and also just using the plain $lookup.
Methods use both "plain promises" and alternate with async.map:
const async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
mongoose.connect('mongodb://localhost/publication');
const userSchema = new Schema({
name: String,
email: String
});
const User = mongoose.model('User', userSchema);
const articleRatingSchema = new Schema({
articleId: {type: Schema.Types.ObjectId, ref:'Article'},
userId: {type: Schema.Types.ObjectId, ref:'User'},
rating: String,
comment: String,
});
const articleSchema = new Schema({
title: String,
ratings: [articleRatingSchema]
})
const Article = mongoose.model('Article', articleSchema);
const ArticleRating = mongoose.model('ArticleRating', articleRatingSchema);
function log(data) {
console.log(JSON.stringify(data,undefined,2))
}
const userData = [
{ name: 'Bill', rating: 5, comment: 'Great!' },
{ name: 'Fred', rating: 3, comment: 'Okay I guess ;)' },
{ name: 'Ted', rating: 1, comment: 'Hated it :<' }
];
async.series(
[
// Clean data
(callback) =>
async.each(mongoose.models,(model,callback) =>
model.remove({},callback),callback),
// Insert data
(callback) =>
async.waterfall(
[
// User and article
(callback) =>
async.parallel(
{
"users": (callback) =>
User.create(
["Bill", "Fred", "Ted"].map( name =>
({ name, email: `${name.toLowerCase()}#example.com` })
),
callback
),
"article": (callback) =>
Article.create({ title: "New News" },callback)
},
callback
),
// Article Ratings
(data,callback) =>
ArticleRating.create(
data.users.map( u => ({
articleId: data.article._id,
userId: u._id,
rating: userData.find( ud => ud.name === u.name ).rating,
comment: userData.find( ud => ud.name === u.name ).comment
})),
callback
)
],
callback
),
// $lookup and populate async.map
(callback) =>
Article.aggregate(
[
{ "$lookup": {
"from": ArticleRating.collection.name,
"localField": "_id",
"foreignField": "articleId",
"as": "ratings"
}}
],
(err,articles) => {
if (err) callback(err);
async.map(
articles.map( a => new Article(a) ),
(article,callback) =>
async.map(
article.ratings,
(rating,callback) =>
ArticleRating.populate(rating,{ path: 'userId' },callback),
(err,ratings) => {
if (err) callback(err);
article.ratings = ratings
callback(null,article)
}
),
(err,articles) => {
if (err) callback(err);
log(articles);
callback();
}
)
}
),
// $look and populate Promise
(callback) =>
Article.aggregate(
[
{ "$lookup": {
"from": ArticleRating.collection.name,
"localField": "_id",
"foreignField": "articleId",
"as": "ratings"
}}
]
)
.then(articles =>
Promise.all(
articles.map( a => new Article(a) ).map(article =>
new Promise((resolve,reject) => {
Promise.all(
article.ratings.map( rating =>
ArticleRating.populate(rating,{ path: 'userId' })
)
).then(ratings => {
article.ratings = ratings;
resolve(article);
}).catch(reject)
})
)
)
)
.then(articles => {
log(articles);
callback();
})
.catch(err => callback(err)),
// Plain $lookup
(callback) =>
Article.aggregate(
[
{ "$lookup": {
"from": ArticleRating.collection.name,
"localField": "_id",
"foreignField": "articleId",
"as": "ratings"
}},
{ "$unwind": "$ratings" },
{ "$lookup": {
"from": User.collection.name,
"localField": "ratings.userId",
"foreignField": "_id",
"as": "ratings.userId",
}},
{ "$unwind": "$ratings.userId" },
{ "$group": {
"_id": "$_id",
"title": { "$first": "$title" },
"ratings": { "$push": "$ratings" }
}}
],
(err,articles) => {
if (err) callback(err);
log(articles);
callback();
}
)
],
(err) => {
if (err) throw err;
mongoose.disconnect();
}
);
Full Output
Mongoose: users.remove({}, {})
Mongoose: articles.remove({}, {})
Mongoose: articleratings.remove({}, {})
Mongoose: users.insert({ name: 'Bill', email: 'bill#example.com', _id: ObjectId("596219ff6f73ed36d868ed40"), __v: 0 })
Mongoose: users.insert({ name: 'Fred', email: 'fred#example.com', _id: ObjectId("596219ff6f73ed36d868ed41"), __v: 0 })
Mongoose: users.insert({ name: 'Ted', email: 'ted#example.com', _id: ObjectId("596219ff6f73ed36d868ed42"), __v: 0 })
Mongoose: articles.insert({ title: 'New News', _id: ObjectId("596219ff6f73ed36d868ed43"), ratings: [], __v: 0 })
Mongoose: articleratings.insert({ articleId: ObjectId("596219ff6f73ed36d868ed43"), userId: ObjectId("596219ff6f73ed36d868ed40"), rating: '5', comment: 'Great!', _id: ObjectId("596219ff6f73ed36d868ed44"), __v: 0 })
Mongoose: articleratings.insert({ articleId: ObjectId("596219ff6f73ed36d868ed43"), userId: ObjectId("596219ff6f73ed36d868ed41"), rating: '3', comment: 'Okay I guess ;)', _id: ObjectId("596219ff6f73ed36d868ed45"), __v: 0 })
Mongoose: articleratings.insert({ articleId: ObjectId("596219ff6f73ed36d868ed43"), userId: ObjectId("596219ff6f73ed36d868ed42"), rating: '1', comment: 'Hated it :<', _id: ObjectId("596219ff6f73ed36d868ed46"), __v: 0 })
Mongoose: articles.aggregate([ { '$lookup': { from: 'articleratings', localField: '_id', foreignField: 'articleId', as: 'ratings' } } ], {})
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed40") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed41") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed42") ] } }, { fields: {} })
[
{
"_id": "596219ff6f73ed36d868ed43",
"title": "New News",
"__v": 0,
"ratings": [
{
"_id": "596219ff6f73ed36d868ed44",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed40",
"name": "Bill",
"email": "bill#example.com",
"__v": 0
},
"rating": "5",
"comment": "Great!",
"__v": 0
},
{
"_id": "596219ff6f73ed36d868ed45",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed41",
"name": "Fred",
"email": "fred#example.com",
"__v": 0
},
"rating": "3",
"comment": "Okay I guess ;)",
"__v": 0
},
{
"_id": "596219ff6f73ed36d868ed46",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed42",
"name": "Ted",
"email": "ted#example.com",
"__v": 0
},
"rating": "1",
"comment": "Hated it :<",
"__v": 0
}
]
}
]
Mongoose: articles.aggregate([ { '$lookup': { from: 'articleratings', localField: '_id', foreignField: 'articleId', as: 'ratings' } } ], {})
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed40") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed41") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed42") ] } }, { fields: {} })
[
{
"_id": "596219ff6f73ed36d868ed43",
"title": "New News",
"__v": 0,
"ratings": [
{
"_id": "596219ff6f73ed36d868ed44",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed40",
"name": "Bill",
"email": "bill#example.com",
"__v": 0
},
"rating": "5",
"comment": "Great!",
"__v": 0
},
{
"_id": "596219ff6f73ed36d868ed45",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed41",
"name": "Fred",
"email": "fred#example.com",
"__v": 0
},
"rating": "3",
"comment": "Okay I guess ;)",
"__v": 0
},
{
"_id": "596219ff6f73ed36d868ed46",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed42",
"name": "Ted",
"email": "ted#example.com",
"__v": 0
},
"rating": "1",
"comment": "Hated it :<",
"__v": 0
}
]
}
]
Mongoose: articles.aggregate([ { '$lookup': { from: 'articleratings', localField: '_id', foreignField: 'articleId', as: 'ratings' } }, { '$unwind': '$ratings' }, { '$lookup': { from: 'users', localField: 'ratings.userId', foreignField: '_id', as: 'ratings.userId' } }, { '$unwind': '$ratings.userId' }, { '$group': { _id: '$_id', title: { '$first': '$title' }, ratings: { '$push': '$ratings' } } } ], {})
[
{
"_id": "596219ff6f73ed36d868ed43",
"title": "New News",
"ratings": [
{
"_id": "596219ff6f73ed36d868ed44",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed40",
"name": "Bill",
"email": "bill#example.com",
"__v": 0
},
"rating": "5",
"comment": "Great!",
"__v": 0
},
{
"_id": "596219ff6f73ed36d868ed45",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed41",
"name": "Fred",
"email": "fred#example.com",
"__v": 0
},
"rating": "3",
"comment": "Okay I guess ;)",
"__v": 0
},
{
"_id": "596219ff6f73ed36d868ed46",
"articleId": "596219ff6f73ed36d868ed43",
"userId": {
"_id": "596219ff6f73ed36d868ed42",
"name": "Ted",
"email": "ted#example.com",
"__v": 0
},
"rating": "1",
"comment": "Hated it :<",
"__v": 0
}
]
}
]

Categories