How to query based on related element's field value? - node.js

In my database, every article has its own 6-digit identifying number (in addition to id). Every comment belongs to one of the articles. My question is, how to query comments knowing only the article number, but not the article's id?
controllers/fetch-comments.js:
const Article = require('../models/article')
const User = require('../models/user');
const Comment = require('../models/comment');
module.exports = async function (req, res) {
try {
let { article_number, articleId } = req.body
let filter;
console.log(article_number)
/* Here is my failed attempt: */
if (article_number) filter = { 'article.number': article_number };
if (articleId) filter = { 'article': articleId };
let projection = null
let options = null
let comments = await Comment.find(filter, projection, options).populate('author', 'username fullname').lean()
return res.status(200).send(comments)
} catch (error) {
console.log("error on getting comments: " + error.message);
return res.status(500).send('Server side error');
}
}
models/article.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const articleSchema = new Schema({
author: { type: Schema.Types.ObjectId, ref: 'User', required: true },
title: { type: String, required: true },
content: { type: String, required: true },
coverPhotoUrl: { type: String, required: false },
number: { type: Number, required: true }, // number is public id for address bar
createdAt: { type: Date, required: true },
category: { type: String, required: false },
fake: { type: Boolean, required: false },
});
module.exports = mongoose.model('Article', articleSchema);
models/comment.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const commentSchema = new Schema({
author: { type: Schema.Types.ObjectId, ref: 'User', required: true },
content: { type: String, required: true },
createdAt: { type: Date, required: true },
article: { type: Schema.Types.ObjectId, ref: 'Article', required: true }
});
module.exports = mongoose.model('Comment', commentSchema);
Please do not suggest doing 2 queries intead of one, I already know how to do that.

You can use the aggregation framework and use a $match and $lookup operator to get the comment of a particular article number.
Following would be the code for the same:
db.Article.aggregate( [
{ $match : { number : article_number} },
{
$lookup:
{
from: "Comment",
localField: "_id",
foreignField: "article",
as: "article_comments"
}
}
] )
This will return an array of the matching comments.

Related

TypeError: Cannot read properties of null (reading 'idTechnical')

I was trying from some object get others info from it. Like from object A, get the objectId from object B, and printing the object B instead the object A. But I got this error message:
TypeError: Cannot read properties of null (reading 'idTechnical')
I'm using nodejs
My function:
const getFinalReport = async (req, res) => {
try{
const finalReport = await Final.findById(req.params.id);
const technicalReport = await Technical.findById(finalReport.idTechnical);
const finantialReport = await Finantial.findById(finalReport.idFinantial);
const writeReport = await Write.findById(finalReport.idWrite);
res.status(200).json(finalReport, {technicalReport, finantialReport, writeReport});
}catch(err){
console.error(err);
res.status(500).send({"msg": "Internal Error!"})
}}
My model:
const finalreportSchema = new mongoose.Schema({
idWrite: { type: mongoose.Schema.Types.ObjectId, ref: "reports", default:''},
idTechnical: { type: mongoose.Schema.Types.ObjectId, ref: "technincal", default: ''},
idFinantial: { type: mongoose.Schema.Types.ObjectId, ref: "finantial", default:''},
isConclude: { type: Boolean, default: false }
},
{
timestamps:true
});
const Report = mongoose.model('final-report', finalreportSchema);
module.exports = Report;
My techincal model:
const mongoose = require("mongoose");
const TechincalSchema = mongoose.Schema({
idInstallationNumber: { type: mongoose.Schema.Types.ObjectId, ref: "installations" },
months: { type: String, required: true },
previousBalance: { type: String, required: true },
actualBalance: { type: String, required: true },
injected: { type: String, required: true },
totalInjected: { type: String, required: true },
},
{
timestamps:true
});
const Technical = mongoose.model('technincal', TechincalSchema);
module.exports = Technical;
Same happen on the Finantial and Write Reports. Had someone had some clue?
Sorry for my english but, this is the first time asking, and also it's been a while that I write something in english (isn't my first language)

Can not populate array of objects

EDIT added mongoose.model.
I'm pretty new to mongodb and mongoose. I'm not able to populate books to authors in mongoose.
Can anyone help me with that? Here is my code. I removed unnecessary fields
const authorSchema = mongoose.Schema({
_id:{
type: String,
required: true,
unique: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
books: [{
type: String,
ref: "Book"
}]
}, {_id:false})
const Author = mongoose.model('Author', authorSchema)
module.exports = {Author}
And my books schema looks as following
const bookSchema = mongoose.Schema({
_id:{
type:String,
required:true,
unique:true,
trim: true
},
name: {
type: String,
required: true,
trim: true,
unique: true
},
description:{
type: String,
trim: true,
default: 'No description specified'
},
datePublished: {
type: Date,
trim: true,
default: '01/01/2001'
},
author:{
type: String,
ref: 'Author',
required: true
}
}, {_id:false})
const Book = mongoose.model('Book', bookSchema)
module.exports = {Book}
Here is the route to populate them together
AuthorRouter.get('/authors/:id', async(req, res)=>{
const authorID = req.params.id
try {
const author = await Author.findById(authorID)
try {
const populated = await author.populate({path: 'books.book', select: 'name description -_id'})
console.log(populated);
res.status(201).send(populated)
} catch (e) {
res.status(401).send(`${e}\n Unable to populate categories`)
}
} catch (error) {
res.status(401).send('Unable to find author')
}
})
Output is following with empty books array:
{
"_id": "123",
"name": "testuser",
"books": [],
"__v": 0
}
Populating an arrays of refs works the same way as a single ref. Just call the populate method on the query and an array of documents will be returned in place of the original _id's.
In your case that would be:
const populated = await author.populate({path: 'books', select: 'name description -_id'});
Hey i have modify your code , try to use this one
This is your author schema file
const Schema = mongoose.Schema;
const authorSchema = mongoose.Schema({
_id:{
type: String,
required: true,
unique: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
books: [{
type: Schema.Types.ObjectId,
ref: "bookSchema"
}]
}, {_id:false})
const Author = mongoose.model('Author', authorSchema)
module.exports = {Author}
And this is your route
AuthorRouter.get('/authors/:id', async(req, res)=>{
const authorID = req.params.id
try {
const author = await Author.findById(authorID)
try {
const populated = await author.find().populate('books');
console.log(populated);
res.status(201).send(populated)
} catch (e) {
res.status(401).send(`${e}\n Unable to populate categories`)
}
} catch (error) {
res.status(401).send('Unable to find author')
}
})

Mongoose(mongoDB) Linking multiple schema's

Im relatively new to MongoDB and Mongoose. Im much used to MySQL so in used to inner joining tables on calls. Ive read a lot that you can link two Mongoose Schemas to achieve the same outcome. How would like like the two schemas together to when I make a call to get a chore by id it'll return the chore and then for the assignedTo & createdBy have the user scheme data for the said userId?
Chore Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ChoreSchema = new Schema({
title: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
time: {
type: Number,
required: true
},
reaccurance: {
type: [{
type: String,
enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly']
}]
},
reward: {
type: Number,
required: true
},
retryDeduction: {
type: Number,
required: false
},
createdDate: {
type: Date,
default: Date.now
},
createdBy: {
type: String,
required: true
},
dueDate: {
type: Date,
required: true
},
status: {
type: [{
type: String,
enum: ['new', 'pending', 'rejected', 'completed', 'pastDue']
}],
default: ['new']
},
retryCount: {
type: Number,
default: 0,
required: false
},
rejectedReason: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
assignedTo: {
type: String,
required: false,
default: ""
}
});
let Chores = module.exports = mongoose.model('Chores', ChoreSchema);
module.exports.get = function (callback, limit) {
Chores.find(callback).limit(limit);
};
User Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
role: {
type: [{
type: String,
enum: ['Adult', 'Child']
}]
},
birthday: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
balance: {
type: Number,
required: true,
default: 0.00
}
});
let Users = module.exports = mongoose.model('Users', UserSchema);
module.exports.get = function (callback, limit) {
Users.find(callback).limit(limit);
};
Im trying to link ChoreSchema.createdBy & ChoreScheme.assignedTo by UserSchema._id
How I make the call in Node.js:
exports.index = function(req, res) {
Chore.get(function(err, chore) {
if (err)
res.send(err);
res.json({
message: 'Chore List',
data: chore
});
});
};
Mongoose has a more powerful alternative called populate(),
which lets you reference documents in other collections.
https://mongoosejs.com/docs/populate.html
Here is how you can link ChoreSchema.createdBy and ChoreScheme.assignedTo by UserSchema._id
var mongoose = require('mongoose');
const { Schema, Types } = mongoose;
var UserSchema = new Schema({
firstName: { type: String, required: true },
...
})
var ChoreSchema = new Schema({
title: { type: String, required: true },
...
//The ref option is what tells Mongoose which model to use during population
assignedTo: { type: Types.ObjectId, ref: 'Users' },
createdBy: { type: Types.ObjectId, ref: 'Users' },
})
let Chores = mongoose.model('Chores', ChoreSchema);
let Users = mongoose.model('Users', UserSchema);
Then in your express route handler you can populate assignedTo & createdBy like this
router.get('/chores/:id', function (req, res) {
const choreId = req.params.id;
Chores.find({ _id: choreId })
.populate('createdBy') // populate createdBy
.populate('assignedTo') // populate assignedTo
.exec(function (err, chore) {
if(err) {
return res.send(err)
}
res.json({ message: 'Chore List', data: chore });
});
})

It says items is undefined

The code is show below.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const user = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
resetToken: String,
resetExpiration: String,
products: [{type: mongoose.Types.ObjectId, required: true, ref: 'Shop'}],
cart: {
items: [
{
productId: {type: mongoose.Types.ObjectId, ref: 'Shop', required: true},
quantity: {type: Number, required: true},
}
]
},
});
user.methods.addToCart = (product) => {
const itemIndex = this.cart.items.findIndex(prod => {
return prod.productId.toString() === product._id.toString();
});
let newQuantity = 1;
const updatedCartItems = [...this.cart.items];
if(itemIndex >= 0) {
newQuantity = this.cart.items[itemIndex].quantity + 1;
updatedCartItems[itemIndex].quantity = newQuantity;
} else {
updatedCartItems.push({
productId: product,
quantity: newQuantity
});
}
const updatedCart = {
items: updatedCartItems
}
this.cart = updatedCart;
return this.save();
}
const model = mongoose.model('User', user);
module.exports = model;
I am trying to store product in the cart instance method as per above schema, but when i send product from my controller to addToCart it says items is undefined on this.cart.items. I haven't used instance method much in mongoose so, i don't know this issue is it with schema or general problem.
let me know if you need any other information.
It was a silly mistake, actually i was using arrow function. so it wasn't bind to schema.

getting nothing from lookup mongodb

I want to get all the posts with their author details from user model. I am using mongoDB lookup. But getting an empty array. I am matching author.uid from post to _id of user.
I want to get all the posts with their author details from user model. I am using mongoDB lookup. But getting an empty array. I am matching author.uid from post to _id of user.
//Post Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const postSchema = new Schema({
category : {
type: String
},
content: {
type: String
},
caption: {
type: String
},
tags: [{
type: String
}],
createdAt: {
type: Number,
required: true
},
author: {
uid:{
type: String,
required: true
},
name:{
type: String
}
},
likes:[{
type:String
}],
comments:[{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}]
});
module.exports = mongoose.model('Post', postSchema);
//User Model
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
_id: {
type: String,
required: true
},
name:{
type: String,
required: true
},
avatar:{
type:String
},
bio:{
type: String
},
followers:[
{
type: String
}
],
followings:[
{
type: String
}
],
posts:[{
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}]
});
module.exports = mongoose.model('User', userSchema);
//Node js
const express = require('express');
const router = express.Router();
const Post = require('../../models/Post');
const User = require('../../models/user');
router.get('/', (req, res) => {
Post.aggregate([
{
$lookup:
{
from: 'User',
localField: "author.uid",
foreignField: "_id",
as: "creator"
}
}
]).exec((err, result) => {
if (err) {
console.log("error" ,err)
}
if (result) {
console.log(JSON.stringify(result));
}
});
});
//Output
{"_id":"5b9c7f30d",
"author": {"uid":"y08RxtsHe","name":"Sujoy Saha"},
"tags": ["#lo"],
"likes":[], "comments[],
"category":"image","content":"jsdnvs","caption":"standing
\n#lol","createdAt":1536982759517,"__v":0,"creator":[]}
You can see, i am getting empty creator array. Please help me out.
mongoose.js pluralizes (adds 's' after your model name) when it creates a collection in MongoDb.
Can you try with from: 'users' in your $lookup clause?

Resources