I am using mongoose with Mongodb v3.4.3
Below is my image model code
const mongoose = require("mongoose");
const CoordinateSchema = require("./coordinate");
const ImageSchema = new mongoose.Schema({
image_filename: {
type: String,
required: true
},
image_url: {
type: String,
required: true
},
coordinates: [CoordinateSchema],
});
Below is my CoordinateSchema code
const mongoose = require("mongoose");
const CoordinateSchema = new mongoose.Schema({
coordinates : {
type: Array,
default: [],
}
});
module.exports = CoordinateSchema;
Below is my api js code running on express,
router.post('/receiveCoordinates.json', (req, res, next) => {
Image.findOneAndUpdate({image_filename:req.body.file_name}).then((image) => {
})
});
How to finish this code so I can store coordinates data in Image model.
Thanks.
UPDATE
To update the coordinates inside of findOneAndUpdate, you simply check that the returned document isn't undefined (which would mean your image wasn't found). Modify your api.js code like so:
router.post('/receiveCoordinates.json', (req, res, next) => {
Image.findOneAndUpdate({image_filename:req.body.file_name}).then((image) => {
if (!image) return Promise.reject(); //Image not found, reject the promise
image.where({_id: parent.children.id(_id)}).update({coordinates: req.body.coordinates}) //Needs to be an array
.then((coords) => {
if (!coords) return Promise.reject();
//If you reach this point, everything went as expected
});
}).catch(() => {
console.log('Error occurred');
);
});
Here's my guess why it isn't working.
In ImageSchema, you are sub-nesting an array of CoordinateSchema. But CoordinateSchema is a document which already contains an array.
This is probably not what you're looking for. If you're using mongoose version 4.2.0 or higher, you can nest CoordinateSchema inside of ImageSchema as a single document. Re-write your ImageSchema like this:
// ...
const ImageSchema = new mongoose.Schema({
// ...
coordinates: CoordinateSchema,
});
If this didn't work or doesn't resolve your issue, please let me know so we can work together to find a solution.
Related
I have created a sigle app with a Schema and a Model to create a Collection and insert some Documents.
I have my todoModel.js file:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const todoSchema = new Schema({
username: String,
todo: String,
isDone: Boolean,
hasAttachment: Boolean
});
const Todos = mongoose.model("Todo", todoSchema);
module.exports = Todos;
Then I have created a setUpController.js file with a sample of my Documents. Then I create a Model and I pass my sample of Documents and my Schema. I create a response to send tje result in JSON.
Everything good here, as I get the result in json when accessing to the route.
Here is the code:
Todos.create(sampleTodos, (err, results) => {
if (!err) {
console.log("setupTodos sample CREATED!")
res.send(results);
}
else {
console.log(`Could not create the setupTodos Database sample, err: ${err}`);
}
});
My problem is that this Documents don´t get saved in the collection !! When I access to the database, nothing is there.
This is my app.js file:
mongoose.connect("mongodb://localhost:27017/nodeTodo")
.then(connection => {
app.listen(port);
})
.catch(err => {
console.log(`Could not establish Connection with err: ${err}`);
});
Could anyone help me please ?
Thank you
Try creating an instance and making the respective function call of that instance. In your case, save the document after creating an instance and it works like a charm.
const newTodos = new Todos({
username: "username",
todo: "todos",
isDone: false,
hasAttachment: flase
});
const createdTodo = newTodos.save((err, todo) => {
if(err) {
throw(err);
}
else {
//do your staff
}
})
after the collection is created you can use the function inserMany to insert also a single document the function receives an array of objects and automatically saves it to the given collection
example:
Pet = new mongoose.model("pet",schemas.petSchema)
Pet.insetMany([
{
//your document
}])
it will save only one hardcoded document
I hope it was helpful
I am practicing my express.js skills by building a relational API and am struggling to populate keys in a schema.
I am building it so I have a list of properties, and those properties have units. The units have a propertyId key.
This is currently returning an empty array, whereas if i remove the populate({}) it returns an array of ObjectIds.
I've read a number of posts and some people solved this by using .populate({path: 'path', model: Model}); but this doesn't seem to be doing the trick. I think it might be the way I am adding a propertyId to the unit but I'm not sure. Can anyone see where I am going wrong? Any help will be massively appreciated.
Here are the schemas.
Property:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PropertySchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
units: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'unit'
}
]
});
module.exports = Property = mongoose.model('property', PropertySchema);
Unit:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UnitSchema = new Schema({
title: {
type: String,
required: true
},
propertyId: {
type: Schema.Types.ObjectId,
ref: 'property'
}
});
module.exports = Unit = mongoose.model('unit', UnitSchema);
I am then creating the unit like this:
-- api/properties/:id/units --
router.post('/:id/units', async (req, res) => {
// Get fields from req.body
const { title } = req.body;
// Get current property
const property = await Property.findById(req.params.id);
try {
// Throw error if no property
if (!property) {
return res.status(400).json({ msg: 'Property not found' });
}
// Create new unit
const newUnit = new Unit({
title,
propertyId: req.params.id
});
// Add new unit to property's units array
property.units.unshift(newUnit);
// Save property
await property.save();
// Return successful response
return res.status(200).json(property);
} catch (error) {
console.error(error.message);
return res.status(500).send('Server error');
}
});
And trying to populate in the GET request
-- /api/properties/:id/units --
const Unit = require('../../models/Unit');
router.get('/:id/units', async (req, res) => {
const property = await Property.findOne({ _id: req.params.id }).populate({path: 'units', model: Unit});
const propertyUnits = property.units;
return res.status(200).json(propertyUnits);
});
If i remove the .populate({path: 'units', model: Unit});, I get a list of unit id's like this:
[
"5ff7256cda2f5bfc1d2b9108",
"5ff72507acf9b6fb89f0fa4e",
"5ff724e41393c7fb5a667dc8",
"5ff721f35c73daf6d0cb5eff",
"5ff721eb5c73daf6d0cb5efe",
"5ff7215332d302f5ffa67413"
]
I don't know, why you don't try it like this:
await Property.findOne({ _id: req.params.id }).populate('units')
I've been try that code above and it's working.
Note: Make sure to check your req.params.id is not null or undefined and make sure the data you find is not empty in your mongodb.
Updated: I've been try your code and it's working fine.
The issue was caused by inconsistent naming and not saving the new created unit as well as the updated property.
I double checked all my schema exports and references and noticed I was using UpperCase in some instances and LowerCase in others, and saved the newUnit as well as the updated property in the POST request and it worked.
The initial problem was solved, thanks!
Now, whenever I insert the document it comes out like this:
{
_id: "5e3b64b6655fc51454548421",
todos: [ ],
title: "title"
}
It should look like this since in the schema the "title" property is above the "todos" property.
{
_id: "5e3b64b6655fc51454548421",
title: "title",
todos: [ ]
}
JavaScript
//project schema
const schema = mongoose.Schema({
title: String,
todos: []
});
const Project = mongoose.model('Project', schema);
//add a project
app.post('/addProject', (req, res) => {
const collection = db.collection('projects')
const proj = new Project({title: req.body.title});
collection.insertOne(proj, function(err, results) {
if (err){
console.log(err);
res.send('');
return
}
res.send(results.ops[0]) //retruns the new document
})
})
You're renaming your key in below line:
const proj = new Project({title: req.body.projTitle});
Your schema expects projTitle, try to change it to:
const proj = new Project({projTitle: req.body.projTitle});
Mongoose schema defines the structure of your document. Whenever you try to save additional field that's not defined in your schema it will be simply ignored by mongoose.
You are entering wrong key while creating new Project object. Change it to the correct key as per your schema and you'll be good to go.
//project schema
const schema = mongoose.Schema({
projTitle: String,
todos: []
});
const Project = mongoose.model('Project', schema);
//add a project
app.post('/addProject', (req, res) => {
const collection = db.collection('projects')
**const proj = new Project({projTitle: req.body.projTitle});** // changes are here
collection.insertOne(proj, function(err, results) {
if (err){
console.log(err);
res.send('');
return
}
res.send(results.ops[0]) //retruns the new document
})
})
Please try to look at the code below.
const collection = db.collection('projects')
const proj = new Project({projTitle: req.body.projTitle}); //make changes here
I'm not really sure what happens here, I set up my Schema properly, and the .save function itself works, however it only generates an object with id and versionkey (_id and __v) as properties.
Here's my code for the model (in the file todo.model.js):
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Todo = new Schema({
desc: String,
progress: Number
});
module.exports = mongoose.model('Todo', Todo);
And here's the function:
let Todo = require('./todo.model');
...
...
router.route('/add').post(function(req, res) {
let todo = new Todo(req.body);
todo.save()
.then(todo => {
res.status(200).json({'todo': 'todo added successfully'});
})
.catch(err => {
res.status(400).send('adding new todo failed');
});
});
The routing etc. works, and a HTTP Post request with the correct json also returns "todo added successfully", but when i use get, i only get the id and versionKey of the object back.
{
"_id": "5ccd94f4a98874a847e54b00",
"__v": 0
}
What's going wrong here?
Hey so I'm pretty new to Javascript and Node but I'm running into an issue that's been bothering me for a while.
I have a User model and an Image model, I'm using Multer to upload an array of images and trying to loop through this array, create a new Image model for each, then unshift that Image into my User's photos. I have Multer set up to successfully fills req.files. Here's the code.
router.post("/users/:user/photos/upload", middle.isLoggedIn, upload.array("photos", 4), function(req, res) {
User.findById(req.params.user, function(err, foundUser) {
for(var i = 0, len = req.files.length; i < len; i++) {
Image.create(req.files[i], function(err, newImage) {
if(err) {
return console.log(err.message);
}
newImage.human = foundUser;
newImage.save();
console.log(newImage);
foundUser.photos.unshift(newImage);
foundUser.save();
});
}
console.log(foundUser);
});
});
console.log(foundUser); seems to execute and print before console.log(newImage);
User Model
var mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = new mongoose.Schema({
username: String,
password: String,
firstName: String,
lastName: String,
city: String,
photos: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Image"
}
]
});
HumanSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Image Model
var mongoose = require("mongoose");
var ImageSchema = new mongoose.Schema({
fieldname: String,
originalname: String,
mimetype: String,
filename: String,
destination: String,
size: Number,
path: String,
human: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Human"
}
}
});
module.exports = mongoose.model("Image", ImageSchema);
This is my first stackoverflow question so let me know if I didn't post enough code.
I think it have something to do with Image.create() being asynchronous, and I'm still trying to learn more about this and promises, but I still don't fully understand how it's relevant in my code.
Use Mongoose's promise support.
Promise.all allows you to resolve an array of promises.
async/await control flow for Promises.
I'm not sure your code as it is structured will work without a serial loop due the async code that can execute in any order. I'm not sure that triggering multiple foundUser.save()s on the same object at different times will work out very well. Holding database objects in memory for a long time can open up more concurrency data issues too.
The Bluebird promise library includes some additional helpers like Promise.each which will serially complete promises before the next starts which may be of use here.
const Promise = require('bluebird')
router.post("/users/:user/photos/upload", middle.isLoggedIn, upload.array("photos", 4), async function(req, res, next) {
try {
let foundUser = await User.findById(req.params.user)
await Promise.each(req.files, async file => {
let newImage = await Image.create(file)
newImage.human = foundUser;
await newImage.save()
console.log(newImage)
foundUser.photos.unshift(newImage)
await foundUser.save()
}
console.log(foundUser)
}
catch (err) {
next(err)
}
})
Other methods like .map and .reduce help make standard array/loop type operations with Promises easier to do.
Atomic Updates
In regards to the concurrency issue, any updates you can do in MongoDB that are "atomic" are a good thing. So instead of selecting something, modifying it in JS, then saving it back, you send the update to Mongo and let the db server deal with it. No matter what order you send the updates to the database, they will always be updating the latest copy of data.
In this case the array unshift can be completed without the initial select by using findByIdAndUpdate and $push (it can be made to push at position 0, there is no $unshift in mongo).
If you add a method to your User model for the adding the photo:
addPhoto(user_id, newImage){
return User.findByIdAndUpdate(
user_id,
{ $push: { photos: { $each: [newImage], $position: 0 } } } }
{ safe: true, new: true }
)
}
So the code would look like
const Promise = require('bluebird')
router.post("/users/:user/photos/upload", middle.isLoggedIn, upload.array("photos", 4), async function(req, res, next) {
try {
let foundUser = await User.findById(req.params.user)
if (!foundUser) throw new Error(`No user found: $user`)
let results = await Promise.map(req.files, async file => {
let newImage = await Image.create(file)
newImage.human = foundUser
await newImage.save()
console.log(newImage)
let user_update = await User.addPhoto(req.params.user, newImage)
console.log(user_update)
}
}
catch (err) {
next(err)
}
})