I am trying to update the value of an object using $ set to pass an Object into a nested array element, however the element tha is updated is the first element not the one I query. I am not sure what I am doing wrong.
Schema
const storySchema = new mongoose.Schema({
//some fields
stages: [{
//some fields
events: [{
author: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
body: {
type: String,
required: true,
trim: true
}
}]
}]
})
The update route in express
Story.findOneAndUpdate({ 'stages.events._id': req.params.eventId }, {
'$set': {
'stages.0.events.$': req.body.event
}
}, {
new: true,
runValidators: true
})
Following the #Veeram suggestion, I used arrayFilters in the solution.
Here is the solution.
Story.findOneAndUpdate({ 'stages.events._id': req.params.eventId }, {
'$set': {
'stages.0.events.$[event]': req.body.event
}
}, {
new: true,
runValidators: true,
arrayFilters: [{ 'event._id': mongoose.Schema.Types.ObjectId(req.params.eventId) }]
})
you can use this
Story.findOneAndUpdate({ 'stages.events._id': req.params.eventId }, {
'$set': {
'stages.$.events.$': req.body.event
}
}, {
new: true,
runValidators: true
})
Related
I have a Mongoose schema with an array lists of objects that consist of a reference to another collection and a nested array of numbers:
var Schema, exports, mongoose, schema;
mongoose = require("mongoose");
Schema = mongoose.Schema;
schema = new Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
lists: [{
list: {
type: Schema.ObjectId,
require: true,
ref: "List"
},
allocations: [{
type: Number,
required: true
}]
}],
createdAt: {
type: Date,
"default": Date.now
},
updatedAt: {
type: Date
}
});
exports = module.exports = mongoose.model("Portfolio", schema);
However, I cannot get populate to work as expected without getting a TypeError: Cannot read property 'ref' of undefined. I've tried populate('list') and populate('lists list') but I'm either not calling things correctly or my Schema isn't formed correctly. I don't have this problem if I simply reference the lists by themselves:
lists: [{
type: Schema.ObjectId,
require: true,
ref: "List"
}]
but I want to have the allocations array alongside each list. What do I need to do to get the behavior I want?
I found the answer: populate('lists.list') works. Thanks to this question: Mongoose populate within an object?
Actual answer:
Use this,
populate('lists.list')
Extra:
Here are lists are an array of objects (list).
In JS you can access this like that,
console.log(lists.list);
and MongoDB syntax is 99% similar to JS syntax.
so....................
lists: [{
list: {
type: Schema.ObjectId,
require: true,
ref: "List"
},
allocations: [{
type: Number,
required: true
}]
}],
=> Because it's an array of objects, you can do this -: Portfolio.populate('lists.list');
2.
lists: [{
type: Schema.ObjectId,
require: true,
ref: "List"
}]
=> Because it's an array, you just have to do this -: Portfolio.populate('lists');
if you have nested schema
YourSchema.find()
.populate({
path: 'map_data',
populate: {
path: 'location'
}
})
it's work for me
// Cart schema
var CartSchema = new mongooseSchema({
productDetails: [{
productId: {
type: mongoose.Schema.ObjectId,
required: true,
ref: 'Product'
},
productCount: Number,
}],
UserId: {
type: String,
default: '',
required: true,
trim: true,
},
shopId: {
type: String,
default: '',
required: true,
trim: true,
},
});
// add this .populate('productDetails.productId').
db.Cart.find({
UserId: userId,
shopId: shopId
}).populate('productDetails.productId')
.skip(pagination.skip)
.limit(pagination.limit)
.exec(function(error,
CartList) {
if (error) {
callback(error, null)
} else {
callback(null, CartList)
}
});
Populate didn't work in my case when nesting to array my Schema was
const chatSchema = mongoose.Schema({
chats: [{
type: Schema.Types.ObjectId,
ref: "ChatMsg"
}],
})
How I solved it
Chat.findOne({
customer: req.body.customer,
agency: req.body.agency
}, (err, chat) => {
if (err) {
res.status(422).send("Our fault");
}
chat.populate("chats").execPopulate(() => {
res.send(chat);
});
});
I am trying to display all the data with the "populate" statement. But I only get one "populate" but when I put many it doesn't work.
what I want to do is to bring me the data from the "User", "Customer" model as well.
This is my code
My Model:
import { model, Schema } from 'mongoose';
const RoomSchema = new Schema({
Users: [
{
agentId: {
type: Schema.Types.ObjectId,
ref: "User",
required: false,
},
customerId: {
type: Schema.Types.ObjectId,
ref: "Customer",
required: false,
},
typeId: Number, // 1 - agent, 2 - client
},
],
Messages: [
{
agentId: {
type: Schema.Types.ObjectId,
ref: "User",
required: false,
},
customerId: {
type: Schema.Types.ObjectId,
ref: "Customer",
required: false,
},
message: {
type: String,
required: true,
},
date: Date,
sender: Number,
},
],
FinishAt: Date,
FinishBy: String,
typeFinishBy: Number, // 1 - agent, 2 - client
}, {
timestamps: true,
versionKey: false
});
export default model('Room', RoomSchema);
this is the sentence I am using
import Room from "../models/Room.js";
async function getOnlyRoom(id) {
const foundRoom = await Room.findById(id)
.populate('Users.agentId')
.populate('Users.customerId')
.populate('Messages.agentId')
.populate('Messages.customerId')
.execPopulate();
return foundRoom
}
Image of Json Postman
only works with one
foundRoom.populate('Users.customerId')
Works with only populate
this is the error
image of error
Thank you very much for your help
I have found the solution to my problem
I was researching the solution on the mongoose website and did it with a single populate.
const populateRoom = await foundRoom
.populate([
{ path: 'Users.agentId', select: 'name' },
{ path: 'Users.customerId', select: 'name' },
{ path: 'Messages.agentId', select: 'name' },
{ path: 'Messages.customerId', select: 'name' }
])
return populateRoom;
here is my course schema
const CourseSchema = new Schema(
{
courseName: {
type: String,
required: true,
lowercase: true,
},
comments: [
[
{
user: {
type: Schema.Types.ObjectId,
ref: "Users",
required: true,
},
comment: {
type: String,
required: true,
},
createdAt: {
type: Date,
required: true,
},
},
],
],
},
{
timestamps: true,
}
);
const Course = mongoose.model("Course", CourseSchema);
I want to populate the user field. I've tried many stack overflow solutions but none of them works for me.
I populated the model like this but, doing so it only populates the first index the of every model.
courses = await Course.findOne({}).populate({
path: "comments.0.0.user",
});
You can populate another level deeper, here's what you need to do:
db.Course.findOne({}).populate({"path":"comments",populate:[{
path:"user",
model:"Users"
}]})
Another way of nested populating data:
db.Course.findOne({}).populate("comments.user")
I want to create an experience array in User model with new data, and the problem is that I don't get saved data in exec function so I can push new data in array on frontend. This is what I got so far.
router.post('/:username/experience', function(req, res) {
const username = req.params.username;
User.findOneAndUpdate(
username, {
$push: {
experience: req.body
}
}, {
safe: true,
upsert: true
})
.exec(function (err, data) {
console.log(data, "------>");
});
})
This is my schema for experience, which is called in User model like experience: [ExperienceSchema].
const ExperienceSchema = new Schema({
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
from: {
type: Date,
},
to: {
type: Date,
},
workingNow: {
type: Boolean,
default: false
},
description: {
type: String
}
}, {
usePushEach: true
})
Since findOneAndUpdate returns the original document (state before update) you need to add new: true to the options in order to get the updated document.
options:
{
safe: true,
upsert: true,
new: true
}
I am using sails v0.12 , I have different models in my MySql relational database but the area of concern lies in these 2 models which are
1. User
2. Appointment
I want to add apptCustomer and apptProvider data into appointment model
but the value is NULL in the database
My User model is :
module.exports = {
attributes: {
name: {
type: "string",
},
email: {
type: "email",
required: true,
unique: true
},
contact_no:{
type: "int",
maxLength: 15,
//required: true,
},
address: {
type:"longtext",
//required: true,
},
userId:{
type: "string",
primaryKey: true,
unique:true
},
gtoken:{
type: "string"
},
provider:{
type:"string"
},
cancel:{
type: "boolean",
// required: true
},
business_name:{
type:"string",
unique:true
},
business_category:{
type:"string"
},
roles:{ // Many to Many = User <-> User-role <-> Role
collection:'Role',
through:'userrole',
},
services:{
collection:'Service',
through:'serviceprovider', // Many to Many = User (provider) <-> Service-provider <-> Service
},
schedules:{ // One to One = User (provider) - Schedule
collection:'Schedule',
via:'owner',
},
providerAppointments:{ // One to Many = User(customer) - multiple Appointments
collection:'Appointment',
via:'appProvider',
},
customerAppointments:{
collection:'Appointment', // One to Many = User(provider) - multiple Appointments
via:'appCustomer'
}
}
};
And my Appointment Model is
module.exports = {
attributes: {
appointment_id:{
type:'int',
primaryKey: true,
autoIncrement:true
},
appointmentDate: {
type: 'datetime',
required: true,
},
start_time: {
type: 'string',
required: true,
},
end_time: {
type: 'string',
},
status: {
type: 'string',
enum: ['booked', 'canceled']
},
appProvider: {
model: 'user',
},
appCustomer:{
model: 'user',
},
serviceAppointment: {
model: 'service',
}
}
};
And my Model Methods are as follows
Appointment.findOrCreate({appointmentDate:req.body.values.appointmentDate, start_time:req.body.values.start_time, end_time:req.body.end_time, status: status},{appointmentDate:req.body.values.appointmentDate, start_time:req.body.values.start_time, end_time:req.body.end_time, status: status})
.exec(function apptCreated(err,appt){
if(err) { sails.log('err',err)}
Service.findOne({ service_id : req.body.values.selected_service})
.exec(function(err,service){
service.serviceAppointments.add(appt);
service.save(function(err,result){
if(err) { sails.log(err)}
})
}),
User.find({userId: req.body.businessId})
.populate('roles')
.exec(function(err,provider){
_.map( provider.roles, role => { // Problem lies here .. this method is not working
if(role.role_id==1){
provider.providerAppointments.add(appt);
provider.save(function(err, result){
if(err) { sails.log(err)}
sails.log('appointment added to provider')
})
}
})
}),
//Appointment adding to Customer
User.find({userId: req.body.customerId})
.populate('roles')
.exec(function(err,customer){
_.map( customer.roles, role => { // Problem lies here... this method is not working
if(role.role_id==2){
customer.customerAppointments.add(appt)
customer.save(function(err, result){
if(err) { sails.log(err)}
sails.log('appointment added to customer')
})
}
})
}),
// Adding contact to customer
User.update({userId: req.body.customerId},{ contact_no: req.body.values.contact_no}) // this method works fine
.exec(function userUpdated(err, user){
if(err) { return sails.log(err)}
sails.log('contact number updated',user);
})
})
As far as I can see, at the moment that you call provider.providerAppointments.add, provider.providerAppointments is still just a regular property - possibly an array of ids.
I think you need to add .populate('providerAppointments') to your User.find... if you do that, then provider.providerAppointments should have a .add method that works the way you expect.
Of course, if this is the source of error, I would have expected a pretty clear error message, like provider.providerAppointments.add is not a function or some such. But try adding the populate, see if it fixes your problem.