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.
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}
);
currently, I am struggling with how the MongoDB document system works. I want to fetch array elements with an auto-generated id but how to fetch that specific data that I don't know.
my current schema is
const ItemPricesSchema = new mongoose.Schema({
_id : {
type: String
},
ItemsPrices: {
type: [{
barcode : {
type: String
},
itemName : {
type: String
},
price : {
type: String
}
}]
}
});
current data is stored in this way
{
"_id": "sha#c.c",
"ItemsPrices": [
{
"barcode": "345345",
"itemName": "maggie",
"price": "45",
"_id": "620a971e11120abbde5f4c3a"
},
{
"barcode": "356345",
"itemName": "monster",
"price": "70",
"_id": "620a971e11120abbde5f4c3b"
}
],
"__v": 0
}
what I want to achieve is that I want to find array elements through ids
if I want a specific array element with id "620a971e11120abbde5f4c3b" what should I do??
I have tried $unwind , $in, $match...
the result should be like
{
"_id": "sha#c.c",
"ItemsPrices": [
{
"barcode": "356345",
"itemName": "monster",
"price": "70",
"_id": "620a971e11120abbde5f4c3b"
}
],
"__v": 0
}
what I tried is like this from the answer
router.get('/filter/:id', async (req, res) => {
try {
const item = await ItemPricesSchema.aggregate([
{$project: {
"ItemsPrices": {
$filter: {
input: "$ItemsPrices",
as: "item",
cond: {
$eq: [
"$$item._id",
"620a8dd1c88ae3eb88a8107a"
]
}
}
}
}
}
])
res.json(item);
console.log(item);
} catch (error) {
res.status(500).json({message: error.message});
}
})
and returns something like this (Empty arrays)
[
{
"_id": "xvz#zyx.z",
"ItemsPrices": []
},
{
"_id": "zxc#xc.czx",
"ItemsPrices: []
},
{
"_id": "asd#asd.asd",
"ItemsPrices": []
},
{
"_id": "qwe#qwe.qwe",
"ItemsPrices": []
}
]
but If I search for price $$item.price
cond: {
$eq: [
"$$item.price",
"30"
]
}
it returns the perfect output
[
{
"_id": "xvz#zyx.z",
"ItemsPrices": []
},
{
"_id": "zxc#xc.czx",
"ItemsPrices: []
},
{
"_id": "asd#asd.asd",
"ItemsPrices": []
},
{
"_id": "qwe#qwe.qwe",
"ItemsPrices": [
{
"barcode":"234456345",
"price":"30",
"itemName":"monster",
"_id":"620a8dd1c88ae3eb88a8107a"
}
]
}
]
You can do an aggregation with $project and apply $filter on the array part. In mongoose you can apply the aggregation query in a more or less similar way https://mongoosejs.com/docs/api/aggregate.html
db.collection.aggregate([
{
$project: {
"ItemsPrices": {
$filter: {
input: "$ItemsPrices",
as: "item",
cond: {
$eq: [
"$$item._id",
mongoose.Types.ObjectId("620a971e11120abbde5f4c3b")
]
}
}
},
"__v": 1 //when projecting 1 means in the final result this field appears
}
}
])
more examples
demo
Option 1:
Use $filter in an aggregation query as explained by cmgchess
Option 2:
If you only want one object from array you can use $elemMatch like this:
db.collection.find({
"ItemsPrices._id": "620a971e11120abbde5f4c3b"
},
{
"ItemsPrices": {
"$elemMatch": {
"_id": "620a971e11120abbde5f4c3b"
}
}
})
Example here
But take care, using $elemMatch only the first element is returned. Check this other example where there are two objects with the desired _id but only returns one.
As said before, if you only one (or only exists one) maybe you can use find and $elemMatch to avoid a filter by the entire array. But if can be multiple values use $filter.
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
}
]
}
]