How can I find a value in the collection "inputs" based on this schema:
var inputJournalSchema = new Schema({
createdAt: {type: Date, default: Date.now},
inputs: [{ key: String, value: String}]
});
Basically, I'd like to check whether there is an element with a specifific key and if so, at which index position.
But how do I do that?
Once an object has been created with the schema:
object.inputs[0].key;
Here is a simple way to find a specific key, assuming objects are stored in an array. If the objects are not stored in array the loop is not needed.
var findThis = 1;
var objects = [{}, {}, {}, {}];
objects.forEach(function (object) {
object.inputs.forEach(function (input, index) {
if (input.key == findThis) {
// logic for once found here
console.log(index);
}
});
});
This code could also be wrapped in a function that returns the index using the key as an argument:
function findIndex (objects, key) {
objects.forEach(function (object) {
object.inputs.forEach(function (input, index) {
if (input.key == key) {
// logic for once found here
return index;
}
});
});
};
Related
I have a schema nested inside another(main) schema. I'd like to increase a Number field in the first schema, however it is an array. So I'd need to access that specific object in that array, and increase a field inside it which is a number. I think what I am looking for is the $inc operator, however I couldn't seem to get it to work.
My schema's:
const chainSchema = new mongoose.Schema({
chainName: String,
streak: Number,
});
const userSchema = new mongoose.Schema({
email: String,
password: String,
googleId: String,
pomodoroStreak: Number,
chains: [chainSchema],
});
Post route:
app.post("/chainDisplay", function (req, res) {
const clickedChain = req.body.secret;
const clickedButton = req.body.submit;
if (clickedButton === "increase") {
Chain.findOneAndUpdate(
{ chainName: clickedChain },
{ $inc: { streak: 1 } },
function (err, foundChain) {
if (!err) {
res.redirect("/chain");
}
}
);
} else if (clickedButton === "decrease") {
Chain.findOneAndUpdate(
{ chainName: clickedChain },
{ $inc: { streak: -1 } },
function (err, foundChain) {
if (!err) {
res.redirect("/chain");
}
}
);
}
});
I obtain the chainName and trying to use that as a parameter to find that specific object, and increase or decrease the streak by 1. Thank you for your help in advance.
I've tried using the $inc operator alongside mongoose's findOneandUpdate method. I am wondering if I should be updating the User, rather than the Chain itself. I was expecting to increase or decrease the "streak" key by 1 when the relevant button is clicked.
Thank you for taking out time to read this.
I have two models positions and users. I'm trying to add 'users' to the Array of 'Recruiters' as seen below in positions Model. When I make the put request, Everything goes well but my amended array which includes the new userids fail to save and give the following error.
'Cast to [ObjectId] failed for value "[3]" at path "recruiters"'
PositionsModel.js
const positionsSchema = new Schema(
{
title: {
type: String,
required: [true, 'Position title is required'],
},
description: {
type: String
},
recruiters: [{
type: Schema.Types.ObjectId,
ref: "users"
}]
},
{ timestamps: true }
);
usersModel.js
const usersSchema = new Schema(
{
name: {
type: String,
required: [true, "Name must be provided"],
},
email: {
type: String
},
password: {
type: String,
}
},
{ timestamps: true }
);
Controller.js (Problem Here)
I'm making a put request to add recruiter to the Array in Positions Model and sending two parameters. Id (this is the position id) and Recruiter (this is the userId)
exports.addRemove = async (req, res, next) => {
try {
const {id, recruiter} = req.params;
//Get Current Position Details
const position = await positionsModel.findById(id)
// Update Position
const newList = position.recruiters.push(recruiter) //this works, It adds the id to array
const newData = {
recruiters: newList
}
//At this point if you console log position.recruiters. You will see the newly added item in the array
const uptPosition = await positionsModel
.findByIdAndUpdate(id, newData, {
new: true,
runValidators: true,
})
.exec(); // this fails with error
if(!uptPosition) {
return res.status(400).json("Position failed to update");
}
//Success Response
return res.status(200).json('Position Updated');
} catch (err) {
console.log({ Error: err.message });
return;
}
Current List of Recruiter Id's in the array
The Current Recruiter Array already has two userIds. The third one gets added successfully to the newList variable, but it doesn't get saved in the database. You can see the error below as it points to the third element that was just added in the controller
'Cast to [ObjectId] failed for value "[3]" at path "recruiters"'
The push() method adds one or more elements to the end of an array and returns the new length of the array.
You did:
const newList = position.recruiters.push(recruiter);
Then newList will be the new length of recruiters array(in your case is 3). You can fix this by changing your code to:
position.recruiters.push(recruiter);
position.markModified('recruiters'); // mark recruiters as having pending change
position.save();
I currently have a problem with updating data in MongoDB via mongoose. I have a nested Document of the following structure
const someSchema:Schema = new mongoose.Schema({
Title: String,
Subdocuments: [{
SomeValue: String
Position: {
X: {type: Number, default: 0},
Y: {type: Number, default: 0},
Z: {type: Number, default: 0}
}
}]
});
Now my problem is that I am updating this with findOneAndUpdateById. I have previously set the position to values other than the default. I want to update leaving the position as is by making my request without the Position as my frontend should never update it (another application does).
However the following call
const updateById = async (Id: string, NewDoc: DocClass) => {
let doc: DocClass | null = await DocumentModel.findOneAndUpdate(
{ _id: Id },
{ $set: NewDoc },
{ new: true, runValidators: true });
if (!doc) {
throw createError.documentNotFound(
{ msg: `The Document you tried to update (Id: ${Id}) does not exist` }
);
}
return doc;
}
Now this works fine if I don't send a Title for the value in the root of the schema (also if i turn on default values for that Title) but if I leave out the Position in the Subdocument it gets reset to the default values X:0, Y:0, Z:0.
Any ideas how I could fix this and don't set the default values on update?
Why don't you find the document by id, update the new values, then save it?
const updateById = async (Id: string, NewDoc: Training) => {
const doc: Training | null = await TrainingModel.findById({ _id: Id });
if (!doc) {
throw createError.documentNotFound(
{ msg: `The Document you tried to update (Id: ${Id}) does not exist` }
);
}
doc.title = NewDoc.title;
doc.subdocument.someValue = NewDoc.subdocument.someValue
await doc.save();
return doc;
}
check out the link on how to update a document with Mongoose
https://mongoosejs.com/docs/documents.html#updating
Ok after I gave this some thought over the weekend I got to the conclusion that the behaviour of mongodb was correct.
Why?
I am passing a document and a query to the database. MongoDb then searches Documents with that query. It will update all Fields for which a value was supplied. If for Title I set a new string, the Title will get replaced with that one, a number with that one and so on. Now for my Subdocument I am passing an array. And as there is no query, the correct behavioud is that that field will get set to the array. So the subdocuments are not updated but indeed initialized. Which will correctly cause the default values to be set. If I just want to update the subdocuments this is not the correct way
How to do it right
For me the ideal way is to seperate the logic and create a seperate endpoint to update the subdocuments with their own query. So to update all given subdocuments the function would look something like this
const updateSubdocumentsById= async ({ Id, Subdocuments}: { Id: string; Subdocuments: Subdocument[]; }): Promise<Subdocument[]> => {
let updatedSubdocuments:Subdocument[] = [];
for (let doc of Subdocuments){
// Create the setter
let set = {};
for (let key of Object.keys(doc)){
set[`Subdocument.$.${key}`] = doc[key];
}
// Update the subdocument
let updatedDocument: Document| null = await DocumentModel.findOneAndUpdate(
{"_id": Id, "Subdocuments._id": doc._id},
{
"$set" : set
},
{ new : true}
);
// Aggregate and return the updated Subdocuments
if(updatedDocument){
let updatedSubdocument:Subdocument = updatedTraining.Subdocuments.filter((a: Subdocument) => a._id.toString() === doc._id)[0];
if(updatedSubdocument) updatedSubdocuments.push(updatedSubdocument);
}
}
return updatedSubdocuments;
}
Been struggling with this myself all evening. Just worked out a really simple solution that as far as I can see works perfectly.
const venue = await Venue.findById(_id)
venue.name = name
venue.venueContact = venueContact
venue.address.line1 = line1 || venue.address.line1
venue.address.line2 = line2 || venue.address.line2
venue.address.city = city || venue.address.city
venue.address.county = county || venue.address.county
venue.address.postCode = postCode || venue.address.postCode
venue.address.country = country || venue.address.country
venue.save()
res.send(venue)
The result of this is any keys that don't receive a new value will just be replaced by the original values.
I have been working on a project and creating an rest api with the use of nodejs and mongodb. I am trying to auto increment a field that increases by 1 when a new entry is there in the database. I am using mongoose auto increment library for that. I am able to increment a field when it is in the schema. However it fails when it increment a field that is present in the array and the array is included in the schema.
Here is my code:
const healthinfo = mongoose.Schema({
cur_madications:[{
sno:{type:String},
medicinename:{ type: String, require: true },
dosage:{ type: String, require: true },
}],
healthinfoid:{type:String},
medicalhistory: { type: String, require: true },
product: { type: String },
clientid:{type:String}
});
var HealthInfo = module.exports = mongoose.model('HealthInfo', healthinfo);
autoIncrement.initialize(mongoose.connection)
healthinfo.plugin(autoIncrement.plugin, {
model: 'healthinfo',
field: 'sno',
startAt: 1,
incrementBy: 1
});
module.exports.addhinfo = function (data, callback) {
data.save(callback);
}
module.exports.getAllhinfo = function (callback) {
HealthInfo.find(callback);
}
module.exports.update = function (updateobj, callback) {
HealthInfo.update({ _id: updateobj._id},{ $set: updateobj }, callback);
}
module.exports.deletehinfo = function (delobj, callback) {
HealthInfo.deleteOne({ _id: delobj }, callback);
}
// module.exports.getById = function (ids, callback){
// HealthInfo.find({'clientid': { $in: ids}}, callback);
// }
module.exports.getById = function (clientid, callback){
HealthInfo.find({'clientid' : clientid}, callback);
}
I am trying to increment the field sno that is present in cur_medication array. But due to some reason I am not getting the desired result. Any help would be appreciated.
Change the type of sno from string to number
sno:{type:Number}
Hope this solves your problem
I would like to create a schema where I can set a specific name for a user, for example a display name that many people can have, while also allowing people to add them based on that name. My solution is to save that name into a model with two fields, one of which is the name itself, the second one having an id that represents the number of times that name is being used currently.
var DisplayName = new Schema({
account: {
type: ObjectId,
ref: 'Account',
},
name: {
type: String,
trim: true,
required: true,
},
number: {
type: Number,
required: true,
}
});
is what I have, but I would like to be able to just say
var displayName = new DisplayName({
name: 'name',
account: req.user.id
});
displayName.save();
and it would automatically set the field for me?
EDIT: I realize there wasn't enough detail in the original question:
I'd like to use mongoose's built-in post-save handlers to generate this field for me on creation. It shouldn't be updated after its created, only set the first time.
You can use a function as shown below to do that thing.
function saveDisplayName(schema) {
var query = DisplayName.find({name: schema.name});
query.then(function(results) {
var userNumber = 1;
if (results.length < 1) {
return userNumber;
}
for (var i = 0; i <= results.length - 1; i++) {
var result = results[i];
if (userNumber <= result.number) {
userNumber = result.number + 1;
}
if (i == results.length - 1) {
return userNumber;
}
}
}).then(function(userNumber) {
schema.number = userNumber;
var displayName = new DisplayName(schema);
return displayName.save();
}).then(function(data) {
console.log('Data saved', data)
}).then(null, console.log);
}