updating a nested field in mongodb not working - node.js

I have a model which has a field with nested fields, when I try to update the field it doesn't work. it still remail=ns the same. Below is the code with further explanations.
i have a model below
const mongoose = require('mongoose')
const placeSchema = new mongoose.Schema({
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
typeof: {
type: String,
required: true,
enum: ["studio", "hall",
"house", "mansion",
"field", "room",
"restaurant", "school",
"church", "beach", "warehouse",
"others"
]
},
location: {
type: String,
required: true,
trim: true
},
time: {
alwaysopen: {
type: Boolean,
required: true,
default: false
},
open: {
type: String
},
close: {
type: String
}
},
idealfor: [{
type: String,
required: false,
enum: ["reception", "production",
"meeting", "performance",
"dinner", "wedding",
"party", "conference"
]
}],
rooms: {
type: Number,
required: false,
trim: true
},
toilet: {
type: Number,
required: false,
trim: true
},
price: {
type: Number,
required: true,
trim: true
},
amenities: [{
type: String,
required: false,
enum: ["electricity", "a/c",
"wifi", "sound system",
"private entrance",
"kitchen", "large table", "tv",
"green screen", "Stage",
"changing room", "makeup room",
"lounge", "soundproof"
]
}],
accessibility: [{
type: String,
required: false,
enum: ["wheelchair", "Elevator",
"on-site parking", "parking near by",
"Stairs"
]
}],
isOpen: {
type: Boolean,
required: false,
default: true
},
phonenumber: {
type: String,
required: true,
trim: true
},
}, {
timestamps: true
})
the problem is that when i try to update the time.alwaysopen field from false to true, id doesn't work.
below is the update route
// Edit a place
router.patch('/api/place/:id', isVerified, async (req, res) => {
const updates = Object.keys(req.body)
const allowedUpdates = ["title", "description", "maxguest", "size", "isOpen", "rules", "rooms", "toilet", "price", "phonenumber", "location", "typeof", "time", "time.alwaysopen", "time.open", "time.close"]
const isValidOperation = updates.every((update) => allowedUpdates.includes(update))
if (!isValidOperation) {
return res.status(404).send({ error: 'Invalid updates!' })
}
try {
const place = await Place.findOne({ _id: req.params.id, owner: req.user._id })
if (!place) {
return res.status(404).send({ "message": "place does not exist" })
}
updates.forEach((update) => place[update] = req.body[update])
await place.save()
res.status(201).send({place, "message": "successfully updated" })
} catch (e) {
res.status(400).send({ "message": "failed to update" })
}
})
and below is the patch json request i make
{
"time.alwaysopen": false
}
after sending the patch request, i still get the vaue of the time.alwaysopen to be true, but updating the field is fine.
below is the response i get
{
"place": {
"time": {
"alwaysopen": true
},
"idealfor": [
"production",
"wedding"
],
"amenities": [
"electricity",
"a/c",
"Stage"
],
"accessibility": [],
"unavailabledate": [],
"isOpen": true,
"img": true,
"_id": "604028e717314039402c2c10",
"typeof": "studio",
"location": "good news church, olufemi street, surulere, Lagos, Nigeria",
"rooms": 2,
"toilet": 2,
"price": 1000,
"phonenumber": "+2348125580085",
"owner": "603e6b494e0df045745066a7",
"createdAt": "2021-03-04T00:25:11.413Z",
"updatedAt": "2021-03-04T01:19:14.867Z",
"__v": 0,
"description": "the famous hotel",
"title": "Royal habiscus edited"
},
"message": "successfully updated"
}
Please, what could be the problem?

Related

Create MongoDB new collection with json inside the schema

I try to create new collection with mongodb.
I wrote the MongoDB shcema, Nodejs+express code and everything work fine until I send parameters with Json inside them.
I was created this schema (and also more code under this schema) but its not work and I got error:
const partySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String, required: true },
datetime: { type: Date, required: true },
location: {
scraping_location: { type: String, required: true },
display_location: { type: String, required: false },
city: { type: String, required: false },
adress: { type: String, required: false },
area: { type: String, required: false }
},
picture: { type: String, required: true },
url: { type: String, required: true },
affiliate_url: { type: String, required: true },
music_by: {type: [], required: false },
music_type: {type: [], required: false },
min_age_for_men: {type: Number, required: true },
min_age_for_women: {type: Number, required: true },
status_check: {type: Boolean, default: false },
tags: {
concept: { type: String, required: false },
free_text: { type: [], required: false }
},
producer_id: { type: String, required: false },
});
and the code that I wrote with nodejs+express is this:
const { title, datetime, location, picture, url, affiliate_url, music_by, music_type, min_age_for_men, min_age_for_women, tags, producer_id } = req.body;
const party = new Party({
_id: new mongoose.Types.ObjectId(),
title,
datetime,
location: { scarping_location: location.location_scarping },
picture,
url,
affiliate_url,
music_by,
music_type,
min_age_for_men,
min_age_for_women,
tags,
producer_id
});
party.save().then(() => {
res.status(200).json({
message: 'Create a new user'
})
}).catch(error => {
res.status(500).json({
error
})
});
when I send reqeust to the api with this body:
{
"title": "test",
"datetime": "2002-07-15T10:00:00+0300",
"location": {
"location_scarping": "new york"
},
"picture": "test.jpg",
"url": "https://google.com",
"affiliate_url": "https://google.com",
"min_age_for_men": 18,
"min_age_for_women": 16
}
I got this error:
{
"error": {
"errors": {
"location.scraping_location": {
"name": "ValidatorError",
"message": "Path `location.scraping_location` is required.",
"properties": {
"message": "Path `location.scraping_location` is required.",
"type": "required",
"path": "location.scraping_location"
},
"kind": "required",
"path": "location.scraping_location"
}
},
"_message": "Party validation failed",
"name": "ValidationError",
"message": "Party validation failed: location.scraping_location: Path `location.scraping_location` is required."
}
}
why its happend?
Looks like this is just a typo, from your schema:
scraping_location: { type: String, required: true }
You're trying to insert: location_scarping not location_scraping
"location_scarping": "new york"
Hopefully that fixes it.

MongoDB: How to aggregate and $group then filter specific date

Employee Schema
const employeeSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid');
}
},
},
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error("Password can not contain a word 'password'.");
}
},
},
birthdate: {
type: Date,
required: true,
},
cellphone: {
type: String,
required: true,
trim: true,
},
gender: {
type: String,
enum: ['남성', '여성'],
required: true,
},
hourly_wage: {
type: Number,
trim: true,
default: 0,
},
timeClocks: [
{
type: new mongoose.Schema({
start_time: {
type: Date,
required: true,
},
end_time: {
type: Date,
},
wage: {
type: Number,
required: true,
},
total: {
type: Number,
},
totalWorkTime: {
type: Number
}
}),
},
],
role: {
type: String,
enum: ['staff'],
default: 'staff',
},
stores: [
{
location: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Location',
},
},
],
status: {
//현재 재직상태
type: String,
enum: ['재직자', '퇴직자'],
default: '재직자',
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
},
{
timestamps: true,
}
);
What I have done so far
const employees = shifts.map((d) => d.owner._id);
//timeclock
const temp = await Employee.aggregate([
{
$match: {
_id: { $in: employees },
},
},
{
$sort: { 'timeClocks.start_time': 1 },
},
{
$unwind: { path: '$timeClocks', preserveNullAndEmptyArrays: true },
},
{
$group: {
_id: '$_id',
name: { $first: '$name' },
timeClock: {
$push: '$timeClocks',
},
},
},
]);
My result
{
"shifts": [
{
"_id": "60e05b188be53900280bcdf2",
"date": "2021-07-09T00:00:00.000Z",
"day": "Fri",
"start": "2021-07-09T09:41:00.000Z",
"end": "2021-07-09T21:42:00.000Z",
"owner": {
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd"
},
"location": "60cd99b1b4ddcc00285b0df3",
"__v": 0
}
],
"timeClock": [
{
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd",
"timeClock": [
{
"_id": "60def63d19648a00286f0539",
"start_time": "2021-05-04T02:19:00.000Z",
"end_time": "2021-05-04T14:42:00.000Z",
"wage": 8720,
"total": 107735,
"totalWorkTime": 743
},
{
"_id": "60def63f19648a00286f053d",
"start_time": "2021-05-02T08:12:00.000Z",
"end_time": "2021-05-02T22:24:00.000Z",
"wage": 8720,
"total": 123540,
"totalWorkTime": 852
},
{
"_id": "60def64119648a00286f0541",
"start_time": "2021-05-10T20:14:00.000Z",
"end_time": "2021-05-10T22:17:00.000Z",
"wage": 8720,
"total": 17835,
"totalWorkTime": 123
},
}
]
Expected Result(2021-05-10)
{
"shifts": [
{
"_id": "60e05b188be53900280bcdf2",
"date": "2021-07-09T00:00:00.000Z",
"day": "Fri",
"start": "2021-07-09T09:41:00.000Z",
"end": "2021-07-09T21:42:00.000Z",
"owner": {
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd"
},
"location": "60cd99b1b4ddcc00285b0df3",
"__v": 0
}
],
"timeClock": [
{
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd",
"timeClock": {
"_id": "60def64119648a00286f0541",
"start_time": "2021-05-10T20:14:00.000Z",
"end_time": "2021-05-10T22:17:00.000Z",
"wage": 8720,
"total": 17835,
"totalWorkTime": 123
},
}
]
I am receiving the 'date string' example('URL/2021-05-10') via params and trying to query all employees that have the same date timeClocks.
also trying to send back everything I queried without different dates from timeClocks.
How can I filter out non-same dates?
You have string 2021-05-10 now you need a $match stage before your group so you can filter out timeClock. Something like:
{ $match: { 'timeClocks.start_time': new Date('2021-05-10') } }
Modify the match stage to your requirements like maybe add $gte or $lte or something like that.

removing object id from an array not working in mongoose

I am trying to remove logged-in users objectId from an array field called interest but it doesnt work.
model
const projectSchema = new mongoose.Schema({
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
isShowcase: {
type: Boolean,
required: false,
default: false
},
isCollab: {
type: Boolean,
required: false,
default: false
},
title: {
type: String,
required: true,
trim: true
},
description: {
type: String,
required: true,
trim: true
},
thumbnail: {
type: String,
required: true,
trim: true
},
media: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Media',
required: false,
}],
skillsNeeded: [{
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'Skill'
}],
contributors: [{
user: {
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'User'
},
role: {
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'Skill'
},
required: false,
}],
isOpen: {
type: Boolean,
required: false,
default: false
},
interest: [{
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'User'
}]
}, {
timestamps: true
})
the route to remove interest
// delete an interest on a collaboration
router.delete('/api/project/:id/apply', auth, async (req, res) => {
const project = await Project.findOne({ _id: req.params.id, isOpen: true })
if (!project) {
res.status(500).send({ "message": "you cannot show interest at the moment" })
}
try {
project.interest = project.interest.filter((objid) => {
return objid != req.user._id
})
await project.save()
console.log(project);
return res.status(204).send(project)
} catch (e) {
res.status(500).send()
}
})
below is the project I am trying to remove a users object id from the interest
{
"isShowcase": false,
"isCollab": true,
"media": [],
"skillsNeeded": [
"5f9fc49a0282a127e4aca0fa",
"5faa596e9d54a92ab805a7a9"
],
"isOpen": true,
"interest": [
"5fa35ac783fe7042fcbbf12a"
],
"_id": "5fb98a36cc3b62aa788adbe0",
"title": "American gods",
"description": "it a story about a war between new and old gods",
"thumbnail": "https://images-na.ssl-images-amazon.com/images/S/pv-target-images/fc3ef790bcbc143d2b5b76b454b7cdcc68a19d60f629e328121713590294a895._RI_V_TTW_.jpg",
"owner": "5fa4f0af4a7bf5471c41e225",
"contributors": [],
"createdAt": "2020-11-21T21:44:22.818Z",
"updatedAt": "2020-11-22T08:22:02.377Z",
"__v": 1
}
when I run the delete request to that route, I get status 204 but the project is not returned, and when I check back on the project, I still see the project with the user's objectID still in the array.
the logged-in users object
{
"skills": [],
"isEmailConfirmed": false,
"isAdmin": false,
"isActive": true,
"hasSpecialPrevilege": false,
"_id": "5fa35ac783fe7042fcbbf12a",
"fullname": "olayinka odedeyi",
"username": "yinkus",
"email": "olayinkaodedeyi#gmail.com",
"createdAt": "2020-11-05T01:52:07.378Z",
"updatedAt": "2020-11-22T10:37:08.307Z",
"__v": 4,
"bio": "a great girl"
}
Please, what could be preventing the logged-in user id from getting removed when the delete route to do that is run?
The problem lies in how you are comparing the two objectids in filter function. You need to use .equals() method available on objectId instance.
project.interest = project.interest.filter((objid) => {
return !objid.equals(req.user._id)
})

How to merge/partial update on sub document which is an array

const projectSchema = mongoose.Schema({
title: { type: String, required: true, trim: true },
projectId: { type: String, required: true, trim: true, unique: true },
company: { type: mongoose.Schema.Types.ObjectId, ref: 'Company', required: true },
description: { type: String, trim: true },
remark: { type: String, trim: true },
active: { type: Boolean, required: true },
participants: [{
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
active: { type: Boolean, required: true },
timeSpent: [{
date: { type: Date, required: true },
hour: { type: Number, required: true }
}]
}]
});
I've searched through a lot of SO questions, but none of them seems to be able to solve my problem. Sometimes I need to update one of the field from one participant, I want to update it in a way that when
//this data to apply update on the first element of the participants array
const partialParticipantUpdate = {
active: false
}
can be easily update to the sub document without rewriting all the other property. Basically like findByIdAndUpdate not findByIdAndReplace. I've tried aggregation but its only for reading data not writing data. I've also tried
let p = await Project.update({
"_id": mongoose.Types.ObjectId("5ee22f90e550c32194fb7a91"),
"participants._id": mongoose.Types.ObjectId("5ee4b5bd4fdcdb0c8813f5f5")
}, {
"$set": { "participants.0": partialParticipantUpdate } })
console.log(p);
However some of the participants property is removed after the update
{
"_id": { "$oid": "5ee22f90e550c32194fb7a91" },
"title": "Geo Chart Example",
"projectId": "myId",
"company": { "$oid": "5ee22ca46c7fc4358ced20a1" },
"description": "asd",
"remark": "re",
"active": true,
"participants": [{
"_id": { "$oid": "5ee4f37f4d8c234cc02d405a" },
"active": false,
"timeSpent": []
},
{
"_id": { "$oid": "5ee4c8a955ed7f23445a3cbc" },
"user": { "$oid": "5ee0bdc318998236706b5e5a" },
"active": true,
"timeSpent": []
}
],
"__v": 0
}
The update did change the first participant's "active" property from true to false, but it also lost some of the property like "user" and "timeSpent" array will get emptied as well
Finally I found the solution
const partialParticipantUpdate = {
active: false
}
let p = await Project.findById("5ee22f90e550c32194fb7a91");
let x = p.participants.id("5ee7224be4e4386084b87d3c");
x.set(partialParticipantUpdate);
let response = await p.save({ validateBeforeSave: false });
console.log(response);
The only downside is that I have to communicate with mongoose twice

Collections association with nested in Mongoose

I had a problem with the association of collections.
I spent 2 days and still did not solve the problem, it's new for me.
My models:
// Schema opened cases
const openedSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
required: [true, 'user is required'],
index: true
},
weapon: {
type: Schema.Types.ObjectId,
ref: 'cases.weapons',
required: [true, 'weapon is required'],
index: true
},
sellPrice: {
type: Number,
default: null
},
status: {
type: Number,
default: 0
}
}, {
timestamps: true
});
const opened = mongoose.model('opened', openedSchema);
// list cases
const casesSchema = new Schema({
name: {
type: String,
unique: true,
required: [true, 'name is required']
},
price: {
type: Number,
required: [true, 'price is required']
},
weapons: [ {
weapon: {
type: Schema.Types.ObjectId,
ref: 'weapon',
index: true
}
} ]
}, {
timestamps: false
});
const cases = mongoose.model('cases', casesSchema);
// list weapons
const weaponSchema = new Schema({
name: {
type: String,
unique: true,
required: [true, 'name is required']
},
price: {
type: Number,
required: [true, 'price is required']
},
autoship: {
count: Number,
status: Boolean,
price: Number
}
}, {
timestamps: false
});
const weapon = mongoose.model('weapon', weaponSchema);
That's what documents look like
// cases
{
"_id": {
"$oid": "59653bcfa9ac622e1913e10c"
},
"name": "test case #1",
"price": 256,
"weapons": [
{
"weapon": {
"$oid": "59653bcfa9ac622e1913e10b"
},
"_id": {
"$oid": "59653bcfa9ac622e1913e10d"
}
},
{
"_id": {
"$oid": "59653d3279aeda2fda9fb490"
},
"weapon": {
"$oid": "59653c5d069f562eb0ba4ef3"
}
},
{
"_id": {
"$oid": "59653d38ba04de2fdddc459f"
},
"weapon": {
"$oid": "59653c893a772e2ef7b65a29"
}
}
],
"__v": 0
}
// opened
{
"_id": {
"$oid": "5965d134c8c95972a1a498f5"
},
"updatedAt": {
"$date": "2017-07-12T07:35:16.419Z"
},
"createdAt": {
"$date": "2017-07-12T07:35:16.419Z"
},
"user": {
"$oid": "5965d0d6ea9db872360db98b"
},
"weapon": {
"$oid": "59653bcfa9ac622e1913e10d"
},
"status": 0,
"sellPrice": null,
"__v": 0
}
// weapon
{
"_id": {
"$oid": "59653bcfa9ac622e1913e10b"
},
"name": "AWP | Fever Dream",
"price": 300,
"autoship": {
"status": true,
"price": 167,
"count": 5
},
"__v": 0
}
I need to get a list of open cases with weapons data.
opened -> cases -> weapon
So, I do this:
opened.find()
.populate('cases.weapons')
.then(_opened => {
console.log(_opened);
})
.catch(err => {
logger.error(err);
});
But populate does not work.
Unless I am mistaken, there is no relationship between openedSchema and casesSchema.
It is not opened -> cases -> weapon but opened -> weapon as openedSchema has no field called cases -- which means cases will never be populated.
Based on your schema definition, it should be opened.find().populate('weapon').

Resources