How to effectively fetch comments for posts with MongoDB/mongoose? - node.js

I have the following Post and Collection documents:
// posts
{
"_id" : ObjectId("56978d8cdbc511a81e7e2ea8"),
"body" : "Post body 1",
"created_at" : 1452772748737
},
{
"_id" : ObjectId("56978d3cdbc655b81e7e2e10"),
"body" : "Post body 2",
"created_at" : 1452772759731
}
// comments
{
"_post" : ObjectId("56978d8cdbc511a81e7e2ea8"),
"body" : "Comment 1"
},
{
"_post" : ObjectId("56978d3cdbc655b81e7e2e10"),
"body" : "Comment 2"
}
I need to query all posts with all comments to be the following result:
{
"_id" : ObjectId("56978d8cdbc511a81e7e2ea8"),
"body" : "Post body 1",
"created_at" : 1452772748737,
"comments": [{
"_post" : ObjectId("56978d8cdbc511a81e7e2ea8"),
"body" : "Comment 1"
}]
},
{
"_id" : ObjectId("56978d3cdbc655b81e7e2e10"),
"body" : "Post body 2",
"created_at" : 1452772759731,
"comments": [{
"_post" : ObjectId("56978d3cdbc655b81e7e2e10"),
"body" : "Comment 2"
}]
}
My schema for post and collections look the following:
// post
var PostSchema = mongoose.Schema({
},{
strict: "throw",
collection: "posts"
});
PostSchema.add({
created_at: {
type: Number,
'default': Date.now
}
});
PostSchema.add({
title: {
type: String
}
});
PostSchema.add({
body: {
type: String
}
});
// comment
var CommentSchema = mongoose.Schema({
},{
strict: "throw",
collection: "comments"
});
CommentSchema.add({
_post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
});
CommentSchema.add({
body: {
type: String
}
});
What is the effectively way to get the result above?

Try to do it through mongoose populate and aggregate. Sample codes as below.
var Post = mongoose.model('Post', PostSchema);
var Comment = mongoose.model('Comment', CommentSchema);
Comment.aggregate([
{$group: {_id: '$_post', comments: {$push: '$body'}}}
// ...
], function(err, result) {
if (err)
// error handling
Post.populate(result, {path: "_id"}, function(err, ret) {
if(err)
console.log(err);
else
console.log(ret);
});
});

Related

I get an error when trying to establish a $ lookup relationship in Mongodb and nodejs

I'm at the learning stage yet.
I am having trouble establishing a relationship in Mongodb.
error not found 404
I made all the operations in the collections with the data in both tables, and in the training I received. But somehow I couldn't run it successfully.
Models Books
const Book = require('../Models/Book');
router.post('/new', function(req, res, next) {
const book = new Book({
title: 'Hikayelerde ve çocuk',
userId: '5ed267005e5d568b58cd17f7',
published: true,
category: 'Hikaye',
comment: [
{
author: "Macit",
mail: "macit#macit.com",
subject:"Kitap fena değil",
message: "Bu kitap oldukça güzel ben çok beğendim ",
},
{
author: "Mutlu",
mail: "mutlu#mutlu.com",
subject:"Kitap fena değil",
message: "Bu kitap oldukça güzel ben çok beğendim ",
},
],
meta:
{
votes: 3,
favs: 3
},
});
book.save((err, data) => {
if (err)
console.log(err);
res.json(data);
});
});
// aggregate $lookup
router.get('aggregate-lookup', (req, res) => {
Book.aggregate([
{
$lookup: {
from: 'users',
localField: 'userId',
foreignField: '_id',
as: 'user'
}
}
], (err, result) => {
res.json(result);
});
});
module.exports = router;
Books Collection
/* 1 */
{
"_id" : ObjectId("5ed26a1ca997a08c2d6d59bf"),
"published" : true,
"title" : "Hikayelerde ve çocuk",
"userId" : ObjectId("5ed267005e5d568b58cd17f7"),
"category" : "Hikaye",
"comment" : [
{
"_id" : ObjectId("5ed26a1ca997a08c2d6d59c0"),
"author" : "Macit",
"mail" : "macit#macit.com",
"message" : "Bu kitap oldukça güzel ben çok beğendim "
},
{
"_id" : ObjectId("5ed26a1ca997a08c2d6d59c1"),
"author" : "Mutlu",
"mail" : "mutlu#mutlu.com",
"message" : "Bu kitap oldukça güzel ben çok beğendim "
}
],
"meta" : {
"votes" : 3,
"favs" : 3
},
"publishedAt" : ISODate("2020-05-30T14:13:48.440Z"),
"__v" : 0
}
Book
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BookSchema = new Schema({
title: String,
comment: [
{
author: String,
mail: String,
subjet: String,
message: String,
},
],
category: String,
meta: {
votes: Number,
favs: Number
},
published: {
type: Boolean,
default: true
},
publishedAt : {
type: Date,
// şuanki tarih
default: Date.now
},
userId: {
type: mongoose.Schema.Types.ObjectID
},
});
module.exports = mongoose.model('Book', BookSchema);
User Collection
/* 1 */
{
"_id" : ObjectId("5ed267005e5d568b58cd17f7"),
"published" : true,
"name" : "Macit Mutlu Sarı",
"age" : "34",
"about" : "Ankara doğumlu ve güzel sanatlar mezunu 18 yıllık deneyimi olan bir profesyonel",
"publishedAt" : ISODate("2020-05-30T14:00:32.322Z"),
"__v" : 0
}
Replace _Id with _id
$lookup: {
from: 'users',
localField: '_id', //Replace here
foreignField: 'userId',
as: 'user'
}

UnhandledPromiseRejectionWarning: MongoError: The argument to $each in $push must be an array but it was of type: object

I'm trying to add values to an existing document in my collection with nested arrays.
First Problem I have, is that I cant add new domain to my domains array
I don't understand what am I am doing wrong??
here is an Example of what i want in my db:
{
"_id" : ObjectId("5c76a093aac6fa3f140a5672"),
"KdNr" : "10004",
"Kundenname" : "Customer GmbH",
"__v" : 0,
"domains" : [ {
"domain" : "testB.de",
"in" : [ {
"content" : "New Graph B",
"graph" : { ... } } ],
"out" : [ {
"content" : "Another new Graph B",
"graph" : { ... } } ]
}, [ {
"domain" : "testA.de",
"in" : [ {
"content" : "New Graph A",
"graph" : { ... } } ],
"out" : [ {
"content" : "Another new Graph A",
"graph" : { ... } } ]
} ]
}
here is an Example of what i get (not what I want):
{
"_id" : ObjectId("5c76a093aac6fa3f140a5672"),
"KdNr" : "10004",
"Kundenname" : "Customer GmbH",
"__v" : 0,
"domains" : [ {
{ "_id" : ObjectId("5c7f86ad42d63141fc921d04") },
{ "_id" : ObjectId("5c655c828be0b2b295aa126f") }
] }
here is my Schema:
const mongoose = require('mongoose');
const graphSchema = mongoose.Schema({
graph: {
nodes: [{
id: { type: String, required: true, unique: true },
label: { type: String, required: true },
description: { type: String }
}],
links: [{
source: { type: String, required: true },
target: { type: String, required: true },
label: { type: String }
}]
}
});
const domainSchema = mongoose.Schema({
domain: {
name: { type: String, unique: true, required: true },
in: {
content: { type: String },
graphSchema
},
out: {
content: { type: String },
graphSchema
}
}
});
const diagramSchema = mongoose.Schema({
KdNr: { type: String, required: true, index: true, unique: true },
Kundenname: { type: String, required: true },
domains: [{
domainSchema
}]
});
module.exports = mongoose.model('Diagram', diagramSchema);
here is my domains-routes.js:
// core Modules
const express = require('express');
// own modules
const Diagram = require('../models/diagrams');
const router = express.Router();
// add Domain to Diagram
router.put('/:KdNr', (req, res, next) => {
console.log(req.body.domain);
const data = {
domain: req.body.domain,
in: [{
content : "New Graph",
graph : {}
}],
out: [{
content : "New Graph",
graph : {}
}]
}
Diagram.updateOne(
{ KdNr: req.params.KdNr },
{ $push: { domains: { $each: data } } } // Seems like here is some problem?
// { $push: data }
).then(result => {
if(result) {
res.status(200).json({ message: 'Diagram saved' });
} else {
res.status(404).json({ message: 'Diagram for customer: '
+ req.params.KdNr + 'not found!'})
}
})
})
module.exports = router;
// $each in $push is only allow to a array of value.so you should be change your data object to array of object
const data =[ {
domain: req.body.domain,
in: [{
content : "New Graph",
graph : {}
}],
out: [{
content : "New Graph",
graph : {}
}]
}]

Mongoose: updating a single sub-document issues

Schema:
var educationSchema = new Schema({
schoolName: String,
startDate: Number,
endDate: Number,
degree: String,
major: String,
grade: String
});
var UserSchema = new Schema({
firstName: String,
lastName: String,
education: [educationSchema]
});
Update code:
User.findOneAndUpdate(
{"_id": req.user.id, "education._id": req.body.id},
{
"$set": {
"education.$": req.body
}
},
function(err, edu) {
}
);
Now, if the user only edits the schoolName on the UI the following happens:
Pre-save State:
{
"_id" : ObjectId("5878fb4f51ec530358fea907"),
"firstName" : "John",
"lastName" : "Doe",
"education" : [
{
"schoolName" : "ABC",
"startDate" : 1998,
"endDate" : 2005,
"degree" : "Bachelor’s Degree",
"major" : "CS",
"grade" : "3.5",
"_id" : ObjectId("5878fbb951ec530358fea909")
}
]
}
Post-save State:
"education" : [
{
"schoolName" : "XYZ"
}
]
Is $set not the right operator to use?
Updating education.$ updates the sub-document. If you want to update only the schoolName you must use education.$.schoolName.
Change your update code to:
User.findOneAndUpdate(
{"_id": req.user.id, "education._id": req.body.id},
{
"$set": {
"education.$.schoolName": req.body
}
},
function(err, edu) {
}
);
EDIT: (update any field sent through req.body)
const update = {};
Object.getOwnPropertyNames(req.body).forEach(key => {
update['education.$.' + key] = req.body[key];
});
User.findOneAndUpdate(
{"_id": req.user.id, "education._id": req.body.id},
{
"$set": update
},
function(err, edu) {
}
);

How to update Reference Array in mongoose

I have a Group Collection, which is having a Reference array of Members. Two Objects are inter-connected like follow. When I am adding new members to the group the members field of Group object needed to be updated. How can I do this with a mongoose update operator.
var MemberSchema = new Schema({
name:{
type:String,
default:null
},
user_id:{
type : Schema.ObjectId,
ref : 'User',
default : null
}
});
var GroupSchema = new Schema({
name:{
type:String,
default:null
},
description:{
type:String,
default:null
},
members:[MemberSchema],
},{collection:"groups"});
Thank You in advance.
Update
I added a sample document of group.
{
"_id" : ObjectId("586a2e694467c41218b302c3"),
"members" : [
{
"_id" : ObjectId("586a2e694467c41218b302c6"),
"user_id" : ObjectId("58171d75e72bf516f92dcd4e"),
"name" : "Lakmal Kapukotuwa"
},
{
"_id" : ObjectId("586a2e694467c41218b302c5"),
"user_id" : ObjectId("5821807516325e127f59438e"),
"name" : "Prasad Perera"
},
{
"_id" : ObjectId("586a2e694467c41218b302c4"),
"user_id" : ObjectId("586263515356e908de6c899a"),
"name" : "Sadun Prasad"
}
],
"description" : "Des 1",
"name" : "My group",
"__v" : 0
}
If you are sending the new members as a list of objects with the following structure e.g.
membersListToAdd = [
{
"user_id": "58171d75e72bf516f92dcd4e",
"name": "foo"
},
{
"user_id": "5821807516325e127f59438e",
"name": "bar"
}
]
then use $push with $each modifier in an update as follows:
var query = { name: 'My Group' },
options = {},
callback = function (err, result) { console.log(result); };
Group.update(query, { $push: { members: { $each: membersListToAdd } } }, options, callback)
You are doing this wrong,
no need to have links in both collections and no need to nest models
try this instead
var Group = mongoose.model("Group", new Schema({
name: {
type:String
},
description: {
type:String
},
}));
Group.virtual("users", {
ref: "User",
localField: "_id",
foreignField: "groups"
});
var User = mongoose.model("User", new Schema({
name:{
type:String
},
groups: [{
type : Schema.ObjectId,
ref : 'Group'
}]
}));

Can't get mongoose 'populate' to work

I am stuck on populating a ref field in a simple mongoose (3.8.8) query.
It's probably a stupid issue but i can't actually sort it out..
var Schema = mongoose.Schema;
var ItemSchema = new Schema({
description: { type: String },
comments: [ { type:Schema.Types.ObjectId, ref:'Comment'} ],
created: { type: Date, default: Date.now }
});
var Item = mongoose.model('Item', ItemSchema);
var CommentSchema = new Schema({
text: { type: String },
item_id: { type:Schema.Types.ObjectId, ref:'Item'} ,
created: { type: Date, default: Date.now }
});
var Comment = mongoose.model('Comment', CommentSchema);
Item.find().populate('comments').exec(function(err,item){
console.log('Item ',item)
})
the data is:
/* item0 */
{
"_id" : ObjectId("53da00cc5ddd29442463e716"),
"description" : "item1",
"created" : ISODate("2014-07-31T08:39:40.475Z"),
"comments" : [],
"__v" : 0
}
/* item1 */
{
"_id" : ObjectId("53da00cc5ddd29442463e717"),
"description" : "item2",
"created" : ISODate("2014-07-31T08:39:40.478Z"),
"comments" : [],
"__v" : 0
}
/* comment0 */
{
"_id" : ObjectId("53da01e9ef4ecaa0231fdc8d"),
"item_id" : ObjectId("53da00cc5ddd29442463e716"),
"text" : "comment1",
"created" : ISODate("2014-07-31T08:44:25.768Z"),
"__v" : 0
}
It just won't work as expected, not populating the 'comments' array, tho in my comments collection the item_id is populated correctly.
What's the problem?
Thanks in advance

Resources