I trying populate a mongoose schema.
i have 3 schema named clothBrandSchema, clothSchema,userSchema.
clothBrandSchema :
name: {
type: String,
required: true,
},
Origin: String,
}
const ClothBrand = new mongoose.model("ClothBrand", ClothBrandSchema);
second is:
clothSchema:
{
category: {
type: String,
enum: ["Ladies", "Mens","Kids","Boys","Girls"],
},
name: String,
brand:[{
type:mongoose.Schema.Types.ObjectId,
ref:"ClothBrand"
}] //many brands have same type of cloth so define array of brands name
}
const Cloth = new mongoose.model("Cloth", clothSchema);
third is:
userSchema:
{
name: {
type: String,
required: true
},
cloth: {
type:mongoose.Schema.Types.ObjectId,
ref:"Cloth"
}
}],
}
const User = new mongoose.model("User", userSchema);
so i want to when i get data of user then i got like this:
User:
{
"_id": "62df18bdb19a5a056a18c0c9",
"name": "ABC",
"cloth": {
"_id": "62df12525e2c53efd6afe2c5",
"category": "Boys",
"name": "Jeans",
"brand":[
{
"name": "xyz"
"Origin": "xyz"
},
{
"name": "xyz"
"Origin": "xyz"
}
...
] // somewhere not written an object id so ignore it
},
so is it possible then what is solution. how can i Write Query in my node js Code Base.
I got an answer in few hours from mongoose offical documentaion mongoose docs on populate
so the code is like that:
user.findOne({ name: 'ABC' }).
populate({
path: 'Cloth',
populate: { path: 'brand' }
})
.then(result => {
res.json(result)
})
Related
I am getting some data in an array of object like this :
{
"success": true,
"result": {
"docs": [
{
"_id": "60a602901a74f62935a4898f",
"user": "607030ba3c82e235443db610",
"weekNum": 19,
"__v": 0,
"createdAt": "2021-05-20T06:32:48.742Z",
"data": [
{
"activity": "6063f898232d3f2acca5d2ae",
"_id": "60a6063668f27715b0f08753",
"project": "60702d1f3c82e235443db5ff",
"task": "60702d3d3c82e235443db601",
"workingDate": "2021-05-10T18:30:00.000Z",
"dayVal": 1,
"description": ""
}
],
"managersComment": "leleleleelelel",
"status": "Submitted",
"updatedAt": "2021-05-20T06:48:22.163Z"
}
],
"paginator": {
"itemCount": 1,
"offset": 0,
"perPage": 10000,
"pageCount": 1,
"currentPage": 1,
"slNo": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prev": null,
"next": null
}
}
}
my schema for this collection in like this:
const timesheetSchema = new Schema({
managersComment: {
type: String
},
weekNum: {
type: Number
},
data:[{
project: {
type: Schema.ObjectId,
ref: projectModel
},
task: {
type: Schema.ObjectId,
ref: taskModel
},
activity: {
type: Schema.ObjectId,
default: null,
ref: activityModel
},
workingDate: {
type: Date
},
dayVal: {
type: Number
},
description: {
type: String
},
}],
user: { type: ObjectId, ref: userModel },
status: {
type: String,
enum: ['Saved', 'Submitted', 'Approved', 'Rejected', 'Reset']
},
}, { timestamps: true });
timesheetSchema.plugin(mongoosePaginate);
const timesheetModel = mongoose.model('timesheet', timesheetSchema);
my code for getting data is something like this:
try {
console.log('populateRequired --------------------------------------------------')
const populateArray = [
{ path: "task", select: "taskName" },
{ path: "project", select: "projectName" },
{ path: "activity", select: "title" },
];
const query = {
user: req.params.userId,
status: req.query.status,
};
const paginationParams = {
populate: populateArray,
customLabels: customLabels,
limit: req.query.limit,
};
console.log("USER QUERY ", query);
const userTimesheet = await getTimesheetDataByUserId(
query,
paginationParams
);
console.log(userTimesheet);
res.send({ success: true, result: userTimesheet });
} catch (err) {
console.log(err);
next(err);
}
But as shown in return data above i am not getting populate applied in data array. Please help not sure what to do.
According to the data you posted, I think that the issue is that you're not creating virtual fields to populate with your references. Your fields project, task and activity in each array element, or user, are meant to be ids referring to the corresponding models. But those ids alone will not implement the population, they are only the pointers that the population will need in order to be executed. To make that a little bit more clear, I would change those names to userId: { type: ObjectId, ref: userModel }.
After that, you will need to create the virtual fields:
timesheetSchema.virtual("user", {
ref: "userModel",
localField: "userId",
foreignField: "_id",
justOne: true,
});
Finally, if you want to have the virtual field timesheet.user populated each time you query your collection, you will have to add some middleware to your schema. For me, the most reasonable way to make this work is:
timesheetSchema.pre("find", function (next) {
this.populate("user");
next();
});
Just to have a complete solution: I think this will solve your problem for the timesheet.user field. But I don't think it will work in your data array. In fact, I'm not 100% sure the way you're defining it is really going to work: creating a timesheet with an array of imputations doesn't make too much sense to me. A more coherent approach would be creating a collection of all the imputations that looked like this:
const dataSchema = new Schema({
projectId: {
type: Schema.ObjectId,
ref: projectModel
},
taskId: {
type: Schema.ObjectId,
ref: taskModel
},
activityId: {
type: Schema.ObjectId,
default: null,
ref: activityModel
},
userId: {
type: ObjectId,
ref: userModel
},
workingDate: {
type: Date
},
dayVal: {
type: Number
},
description: {
type: String
},
});
With virtual fields like:
dataSchema.virtual("project", {
ref: projectModel,
localField: "projectId",
foreignField: "_id",
justOne: true
});
And so on. I would populate each field just like I showed you with the user example. Then, for the timesheet schema I would only reference userId, and populate data like this:
const timesheetSchema = new Schema({
managersComment: {
type: String
},
weekNum: {
type: Number
},
userId: {
type: ObjectId,
ref: dataModel
},
status: {
type: String,
enum: ['Saved', 'Submitted', 'Approved', 'Rejected', 'Reset']
},
}, { timestamps: true });
timesheetSchema.virtual("data", {
ref: dataModel,
localField: "userId",
foreignField: "userId"
});
timesheetSchema.virtual("user", {
ref: userModel,
localField: "userId",
foreignField: "_id",
justOne: true
});
This way you would have a collection with all the imputations for all the users, and you would be able to query and filter that collection for each userId, projectId or anything you would need. Having an array inside your timesheet collection would make this quite more difficult.
One simple solution I found on another SO post is like this:
const result = await timesheetModel.findOne(query).populate({
path: 'data.project data.activity data.task'
});
Hi so i have an array of objects that look like this
[{
"id": 0,
"name": "Del Vecchios | Italian Food | Asheville, NC",
"domain": "delvecchiositalian.com",
"email": "eat#delvecchiositalian.com",
"phone": "828-258-7222",
},
{
"id": 1,
"name": "DeRango's Pizza Palace | Italian Restaurant | Racine, WI",
"domain": "derangos.com",
"email": "info#derangospizzapalace.com",
"phone": "262-639-4112",
},
{
"id": 2,
"name": "Michigan's Premier Flooring Installation Services | D.E. McNabb",
"domain": "demcnabb.com",
"email": "no email",
"phone": "(248) 587-1500",
},
}]
And i want to store it in my mongo database but i dont know how to make the schema, my actual schema looks like this
const mongoose = require("mongoose");
const infoSchema = new mongoose.Schema(
{
info: {
type: String,
trim: true,
required: true,
maxlength: 3200
}
},
{ timestamps: true }
);
module.exports = mongoose.model("ScrapedInfo", infoSchema);
and this is the controller for saving the data
router.post('/stored', (req, res) => {
const info = new ScrapedInfo(req.body)
info.save((err) => {
if (err) {
console.log(err+"error")
}
else{
console.log('saved')
}
});
});
Dunno if i am making a mistake in the controller, seems fine to me, but i every time i run the button for the controller, i have the rror ValidationError: info Path info is required
When trying to update an array of objects, using mongoose, you will have to tell MongoDB that there are pending changes by using the markModified(path) method. after this you will have to call the save() method.
example
We have the passwordsArr part of the schema as an array of objects.
// passwordsArr modified
// mark the path as having changes to write to the DB
user.markModified('passwordsArr');
// now saves the document user.save(function (err, user) { if (err) return next(err);
Your schema should look something like this. The "Path info is required error" is caused by the fact that the key "info" doesn't exist in the Schema.
const mongoose = require('mongoose')
const Schema = mongoose.Schema
// Schema
const infoSchema = new Schema(
{
name: {
type: String,
required: true,
},
domain: {
type: String,
required: true
},
email: {
type: String,
required: true
},
phone: {
type: String,
required: true
}
},
{ timestamps: true }
)
// Model
module.exports = mongoose.model('Info',infoSchema)
If you want to save an array your "info" key should look like this:
info: {
type: Array,
trim: true,
required: true,
maxlength: 3200
}
Where the "type" is Array instead of String.
I am trying to create a query in my API endpoint that fetches all documents and sub documents. Currently I have only had luck fetching the ID's of each sub document instead of fetching all of the information within each document. Is it possible to extract the data from each sub document based on each ID using mongoose(along with the data in the parent document)?
I have followed the documentation for deep populating: https://mongoosejs.com/docs/populate.html#deep-populate, but have not had much luck.
This is my current code that "works" but only responds with the ID's of each sub document(and the User data which is fine).
//The get request
router.get("/allUsers", async (req, res) => {
const allUsers = await User.find()
.populate("Post")
.exec();
res.send(allUsers);
});
//User Schema
const userSchema = Schema({
name: { type: String, required: true },
date: { type: Date, default: Date.now },
posts: [{ type: Schema.Types.ObjectId, ref: "Post" }]
});
//Post Schema
const postSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: true }
date: { type: Date, default: Date.now }
});
What am I missing to extract ALL of the information from the sub documents instead of the ID alone?
As JohnnyHK stated you need to use the name of the field to populate.
If you are having empty array of posts, please check your users collection if you have a posts array with the post ids.
Here are the steps I could make it work:
1-) Inserted 2 posts like this to the posts collection.
{
"_id" : ObjectId("5dc6cd65067a61191839ff38"),
"name" : "name 3",
"description" : "description 3",
"date" : ISODate("2019-11-09T17:29:57.249+03:00")
}
{
"_id" : ObjectId("5dc6cd5d067a61191839ff37"),
"name" : "name 2",
"description" : "description 2",
"date" : ISODate("2019-11-09T17:29:49.070+03:00")
}
2-) Inserted a user with these 2 posts id.
{
"_id" : ObjectId("5dc6cf5b67da8a40cc519866"),
"posts" : [
ObjectId("5dc6cd5d067a61191839ff37"),
ObjectId("5dc6cd65067a61191839ff38")
],
"name" : "user 1",
"date" : ISODate("2019-11-09T17:38:19.807+03:00")
}
3-) And used the following route to get posts in user.
router.get("/allUsers", async (req, res) => {
const allUsers = await User.find().populate("posts");
res.send(allUsers);
});
The result is like this:
[
{
"posts": [
{
"_id": "5dc6cd5d067a61191839ff37",
"name": "name 2",
"description": "description 2",
"date": "2019-11-09T14:29:49.070Z",
"__v": 0
},
{
"_id": "5dc6cd65067a61191839ff38",
"name": "name 3",
"description": "description 3",
"date": "2019-11-09T14:29:57.249Z",
"__v": 0
}
],
"_id": "5dc6cf5b67da8a40cc519866",
"name": "user 1",
"date": "2019-11-09T14:38:19.807Z",
"__v": 0
}
]
By the way your user and post modeling is not ideal. Every time user posts the user document must be updated.
I would use the following schemas (parent referencing).
User model ( I removed the posts field, and added virtual feature)
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
date: { type: Date, default: Date.now }
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
userSchema.virtual("posts", {
ref: "Post",
foreignField: "userId",
localField: "_id"
})
const User = mongoose.model("User", userSchema);
module.exports = User;
Post model ( I added the userId)
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: true },
date: { type: Date, default: Date.now },
userId: { type: mongoose.Schema.ObjectId, ref: "User" }
});
const Post = mongoose.model("Post", postSchema);
module.exports = Post;
With this way user document is not affected when a user posts. And you can use the same /allUsers route to get users with their posts included.
Pass the name of the field to populate, not the name of the model:
const allUsers = await User.find()
.populate("posts")
.exec();
use populate inside other populate
.populate({
path: 'document',
populate: { path: 'sub document'}
})
I am trying to query for records in my database but I am getting an empty array. I don't know what I am doing wrong? I have a feeling the issue could be my schema, here it is:
my schema
const QuizSchema = new Schema({
question: {
type:String,
default:''
},
answers: [{
type: {
type: String,
default:''
},
content: {
type: String,
default:''
},
}]
});
module.exports=Quiz=mongoose.model('quizes',QuizSchema,'quiz');
Here is the mongoose query
const Quiz = require('../../models/Quiz');
router.get('/',(req,res)=>{
Quiz.find({},null)
.then(data=>res.json(data))
.catch(err=>console.log(err))
});
module.exports = router;
And here is a sample JSON data
{
"question": "What is a Mongoose ? ",
"answers": [
{ "type": "Smart","content": "Animal" }, {"type": "Subpar", "content": "Software"}, { "type": "Confused", "content": "Poem" }]
}
Would appreciate your assistance. Thanks in advance!
<query>.then doesn't return the data. You should use <query>.exec().then to access the data returned by the query. So change your code to something like:
Quiz.find({},null)
.exec()
.then(data=>res.json(data))
.catch(err=>console.log(err))
});
module.exports = router;
Source: Mongoose docs
Modify Schema like this: Inside the array, field name like 'type' is not accepted.
const QuizSchema = new Schema({
question: {
type:String,
default:''
},
answers: [{
ans_type: { // CHANGE HERE
type: String,
default:''
},
content: {
type: String,
default:''
},
}]
});
module.exports=Quiz=mongoose.model('quizes',QuizSchema,'quiz');
im trying to insert data in mongodb using express in the format as below which im not able to achieve.
I need to enter multiple product and serial no in the data field. please help!
[
{
"_id": "5cbabbd7545ac20f7c912e6a",
"refno1": "REF1",
"refno2": "REF2",
"prodregdate": "2019-04-09T00:00:00.000Z",
"data": [
{
"_id": "5cbabbd7545ac20f7c912e6b",
"product": "5cb86b45cfafaa1860e29b2a",
"serialno": "s123"
},
{ // this data im not able to enter how to do it
"_id": "5cbabbd7545ac20f7c912e6b",
"product": "5cb86b45cfafaa1860e29b2a",
"serialno": "s123"
},
],
"customer": {
"_id": "5c98bb0a42207b16d8fbd3cf",
"customername": "Raghav Update"
},
"customertype": {
"_id": "5c7a1a1d4913fa08ac75c027",
"customertype": "Government "
},
"__v": 0
}
]
// My Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ProductRegistrationSchema = new Schema({
//Product Details
_id: { type: mongoose.Schema.Types.ObjectId },
refno1: { type: String },
refno2: { type: String },
data: [{
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "product"
},
//DATES
//OEM
oemwarrantyfrom: { type: Date },
oemwarrantyto: { type: Date },
//SERVICE PROVIDER
warrantyfrom: { type: Date },
warrantyto: { type: Date },
serialno: { type: String },
}],
prodregdate: { type: Date },
//Details of Customer buying the product
customer: {
type: mongoose.Schema.Types.ObjectId,
ref: "customer"
},
customertype: {
type: mongoose.Schema.Types.ObjectId,
ref: "customertype"
},
department: {
type: mongoose.Schema.Types.ObjectId,
ref: "customersubdepartment"
},
remarks: { type: String },
entrydate: {
type: Date,
dafault: Date.now
}
module.exports = ProductRegistration = mongoose.model('productregistration', ProductRegistrationSchema);
// My Routes just for add
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Product = require("../../../models/Master/Products");
//importing the model of ProductRegistrationSchema
const ProdReg = require('../../../models/Entries/ProductRegistration');
//Creating a new ProductRegistration Data
router.post('/add', (req, res)=>{
const newProdReg = new ProdReg({
_id: new mongoose.Types.ObjectId(),
refno1: req.body.refno1,
refno2: req.body.refno2,
prodregdate: req.body.prodregdate,
data: {
product: req.body.productid,
oemwarrantyfrom: req.body.oemwarrantyfrom,
oemwarrantyto: req.body.oemwarrantyto,
warrantyfrom: req.body.warrantyfrom,
warrantyto: req.body.warrantyto,
serialno: req.body.serialno,
},
customer: req.body.customerid,
customertype: req.body.customertypeid,
department: req.body.customersubdepartmentid,
remarks: req.body.remarks
// deliverydate: req.body.deliverydate,
// address: req.body.address,
// assignedto: req.body.employeesid,
// warrantyprovider: req.body.serviceproviderid,
// oemwarrantyprovider: req.body.oemcompanyid,
// warrantystartdate: req.body.warrantystartdate,
// warrantyexpiredate: req.body.warrantyexpiredate,
});
newProdReg.save().then(prodreg => res.json(prodreg));
});
im not able to enter 2 product and serial no in the data field. One one is getting entered.
Firstly make your request JSON in the proper format if you want to insert two products which you are getting from request data.
For example, your request JSON should be in the flowing format:
{"refno1":"x", "refno2": "y", "prodregdate": "2019-04-19T18:30:00.000Z","data": [{"product": "product_1_object_id","oemwarrantyfrom":"2019-04-19T18:30:00.000Z", "oemwarrantyto": "2019-04-19T18:30:00.000Z","warrantyfrom":"2019-04-19T18:30:00.000Z", "warrantyto":"2019-04-19T18:30:00.000Z","serialno":"123" },{"product": "product_2_object_id","oemwarrantyfrom":"", "oemwarrantyto": "2019-04-19T18:30:00.000Z","warrantyfrom":"2019-04-19T18:30:00.000Z", "warrantyto":"2019-04-19T18:30:00.000Z","serialno":"456" }],"customersubdepartmentid":"departement_object_id","customerid":"customer_object_id","customertypeid":"customer_type_object_id","remarks":"anything"}
If you are using POSTMAN then you can try this JSON in the "raw" option.
Then in your code, it should be like below:
router.post('/add', (req, res)=>{
const newProdReg = new ProdReg({
_id: new mongoose.Types.ObjectId(),
refno1: req.body.refno1,
refno2: req.body.refno2,
prodregdate: req.body.prodregdate,
data: req.body.data, // This will be type array with two products details
customer: req.body.customerid,
customertype: req.body.customertypeid,
department: req.body.customersubdepartmentid,
remarks: req.body.remarks
});
newProdReg.save().then(prodreg => res.json(prodreg));
});
Please match your request param with JSON I took it from your schema.