I am having some trouble with mongoDB/mongoose and node.js. I am used to SQL, and mongoDB is...hard! Here is my schema:
var mongoose = require('mongoose');
mongoose.Promise = global.Promise;
var itemSchema= mongoose.Schema({
item_info : {
user_id : Number,
location : String,
item_id : Number,
title : String
},
item_hist : {
user_id : Number,
location : String,
item_id : Number,
founddate : String
}
});
module.exports = mongoose.model('item', itemSchema);
And I can add a new item by doing this:
var item= require('./app/models/item');
var item= new item();
item.item_info.user_id = 12345;
item.item_info.location = 'This address';
item.item_info.item_id = 4444;
item.item_info.title = 'New item';
item.save(function(err)
{
if (err) throw err;
});
What I want to be able to do is say: "look for an item with item_info.item_id 5555. if it exists, do nothing. if it doesn't exist, then add it to the database." I've read through so much mongodb and mongoose documentation, but between using dot notation and accessing through nodejs instead of command line mongodb, I still can't figure out how to do this. SQL seemed so much easier!
Just use this -
var query = { user_id: 12345, location: "This address", item_id: 4444, title: "New item" },
options = { upsert: true };
Model.findOneAndUpdate(query.item_id, query, options, function(error, result) {
if (error) return;
// do something with the document
});
I have a problem with Creating with Multiple Parameter using Middleware validation,
here is my Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Order = mongoose.Schema({
orderCode : {type: String, default : null, unique: true},
price : {type : Number, default : 0},
createdAt : {type : Date, default : new Date()},
updatedAt : {type : Date, default : new Date()}
});
Order.pre("save", function(next, done){
var self = this;
var templateDate = new Date().toISOString().slice(0,10).replace(/-/g,"");
mongoose.models["Order"].find({}, function(err, orders){
if(err){
done(err)
} else {
var queue = orders.length + 1;
self.orderCode = "#"+templateDate+queue;
done();
next();
}
});
});
and here is my parameters and code,
[{"price":10000}, {"price":15000}]
models.Order.create(parameters, function(err){
...
});
without Using Middleware, I have created 2 data based on this parameter, but when I'm using Middleware, it just created only one but in the arguments it shows 2.
any help? thanks
I'm trying to import an Object into Mongo. But when I try to use a value of the Object as _id it fails. The error i.e: "[CastError: Cast to ObjectID failed for value "11563195" at path "_id"]" and later "[Error: document must have an _id before saving]"
What am I doing wrong ?
// Read and import the CSV file.
csv.fromPath(filePath, {
objectMode: true,
headers: keys
})
.on('data', function (data) {
setTimeout(function(){
var Obj = new models[fileName](data);
Obj._id = Obj.SOME_ID;
Obj.save(function (err, importedObj) {
if (err) {
console.log(err);
} else {
console.log('Import: OK');
}
});
}, 500);
})
Here is the used Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var SomeSchema = new Schema(
{
SOME_ID: String,
FIELD01: String,
FIELD02: String,
FIELD03: String,
FIELD04: String,
FIELD05: String,
FIELD06: String,
FIELD07: String,
FIELD08: String,
FIELD09: String,
FIELD10: String,
FIELD11: String,
FIELD12: String,
FIELD13: String
},
{
collection: 'SomeCollection'
});
module.exports = mongoose.model('SomeCollection', SomeSchema);
Many thanks for your time and help.
By default mongoose is validating that the _id field is a MongoId. If you want to store something other than a MongoId in the _id field you will need to give the _id field a different type.
var SomeSchema = new Schema({
_id: { type: String, required: true }
}
I am trying to create a hierarchy of categories in MongoDB for use with Node.js via Mongoose. I am using the Array of Ancestors approach (http://docs.mongodb.org/manual/tutorial/model-tree-structures-with-ancestors-array/) and have already saved the hierarchy in the database. Directly from Mongo an element looks like this:
{
"_id" : "Football",
"ancestors" : [
"Categories",
"Sports and fitness"
],
"parent" : "Sports and fitness"
}
I have created a model and controller for the categories, and are as of now having problems querying the database.
This is the code in model/Category.js:
var mongoose = require('mongoose');
var Category = mongoose.Schema({
_id: String
});
var categorySchema = mongoose.Schema({
ancestors: [Category],
parent: [Category]
});
// Initiate database connection
var db = mongoose.createConnection('mongodb://localhost/Categories');
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
console.log("openDB categories");
});
module.exports.category = db.model('Category', categorySchema);
This is the controller:
var categoryModel = require('../models/Category');
var Category = categoryModel.category;
exports.getAncestors = function(req, res) {
if (req.params.id == undefined){res.send("no id specified!"); return;}
Category.findOne({_id: 'Football'}, 'ancestors', function(err, ancestors){
if(err) console.log(err);
res.send(ancestors);
});
}
When running this code I get the following error message:
{ message: 'Cast to ObjectId failed for value "Football" at path "_id"',
name: 'CastError',
type: 'ObjectId',
value: 'Football',
path: '_id' }
I believe the problem may be in the mongoose schema, but all help is greatly appreciated.
Many thanks!
Mongoose tries to set an ObjectId by default. You can suppress this with the following:
var categorySchema = mongoose.Schema({
_id: String,
ancestors: [{type: String }],
parent: {type: String}
},{ _id: false });
var Category = mongoose.model( "Category", categorySchema );
And noting that there is only one schema for you layout.
So, I want to create a client side based paritioning schema, where I set the collection name as function(), my pseudo code is something like that:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
var ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
}, function CollectionName() {
return (this.user_id % 10000);
});
Is this in any way possible through moongose such that both read and writes will work as expected?
Hello you just need to declare schema model with your dinamically name, like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// our schema
function dynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
//no we export dynamicSchema function
module.exports = dynamicSchema;
so in your code anywhere you can do this:
var userAdress = require('address.js')(id_user);
var usrAdrs1 = new userAddress({...});
userAdrs1.save();
Now go to your mongo shell & list collections (use mydb then show collections), you will see a new collection for address with uid prefix. In this way mongoose will create a new one collection address for each different user uid.
Use the function to get the model dynamically.
/*
* Define Schemas as you used to
*/
const ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
},{
versionKey : false,
strict: false
});
/*
* Define the dynamic function
*/
const models = {};
const getModel = (collectionName) => {
if( !(collectionName in models) ){
models[collectionName] = connection.model(
collectionName, ConvForUserSchema, collectionName
);
}
return models[collectionName];
};
Then get the dynamic model using the function
const result = getModel("YourCollectionName").findOne({})
Collection name logic is hard coded all over the Moongose codebase such that client side partitioning is just not possible as things stands now.
My solution was to work directly with the mongo driver -
https://github.com/mongodb/node-mongodb-native
This proved great, the flexibility working with the driver directly allows for everything required and the Moongose overhead does not seem to add much in any case.
Implemented:
//Require Mongoose
const mongoose = require('mongoose');
const moment = require('moment');
//Define a schema
const Schema = mongoose.Schema;
const EntranceModelSchema = new Schema({
name: String,
birthday: Date,
gender: String,
phoneNumber: {type: String, require: true},
email: String,
address: String,
addressReference: String,
addressLatitude: {type: Number, require: true},
addressLongitude: {type: Number, require: true},
vehicleReference: String,
date: Date
});
//Export function to create "SomeModel" model class
module.exports = function(){
let dateSuffix = moment().format('MMMDoYYYY');
const collectionName = `Entrance${dateSuffix}`;
return mongoose.model(collectionName, EntranceModelSchema);
};
Gomosoft's solution works. It needs some amends but the idea works nicely.
The above solution works only the first time. If you are trying to send a second request to the same collection, it will throw an error for trying to overwrite the model that is already defined. So I had to tweak it as follows:
var Rating = require('./app/models/rating');
var myRating;
router.route('/ratings/:user_id')
.post(function(req,res){
var user_id = req.params.user_id;
if(myRating == undefined){
myRating = Rating(user_id);
}
...
rating.save(...);
});
Because I'm checking if myRating is undefined, it will create this reference only once. So no errors will occur.
Hope this helps.
I am adding to the answer by Javier Gomez, to give a solution to Exis Zang's "OverwriteModelError: Cannot overwrite xxx model once compiled" problem. The Schema model file can store an array of the dynamic models based on the Singleton pattern. If that model already exists return it, otherwise create it with new, store it and return it:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const Addresses = {}
// our schema
function DynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
// this function will store the model in the Addresses object
// on subsequent calls, if it exists, it will return it from the array
function getAddressModel(prefix) {
if (!Addresses[prefix]) {
Addresses[prefix] = new DynamicSchema(prefix)
}
return Addresses[prefix]
}
//now we export getAddressModel function
module.exports = getAddressModel;
To Create a dynamic collection follow the below steps,
const mongoose = require('mongoose');
function createCompanyDynamicSchema(prefix) {
let collectionName = prefix + '_company';
companySchema = new mongoose.Schema(
{
name: { type: String },
enabled: { type: Number, default: 1 },
},
{ timestamps: true },
{ versionKey: false },
{ strict: false }
);
collectionName = mongoose.model(collectionName, companySchema);
return collectionName;
}
module.exports = { createCompanyDynamicSchema };
To call this method from any file,
let companySchema = require('./schema');
_this.createCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let addr = new companyCollection({ first_name: 'test' });
addr.save();
});
};
To query from dynamically created collection,
_this.getCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let data = await companyCollection.model('IO_users').find();
console.log(data);
});
};