I am inserting bulk records in mongodb. I am using the native DB drivers to do this, as the performance is much higher. At other points in my application, i am using mongoose. The problem I am having is that mongoose translates the date into a different format whereas mongodb native just inserts it as the number of seconds since 1970. So later queries in mongoose based off that date do not work.
Here's my mongoose schema:
var MySchema = new Schema({
name : { type: String, required: true },
updatedAt : Date
});
And my mongo db mass insert:
var newRec = {
name : entry.Name,
updatedAt : Date.now
};
newRecords.push(newRec);
MySchema.collection.insert(newRecords, function(err, newRecs) {
res.json(newRecs.ops);
});
This produces in the DB:
{
"_id": {
"$oid": "562818ecf24d540f0053a38d"
},
"name": "Cool Record",
"updatedAt": 12312423512
}
Whereas if it was run through Mongoose it would produce:
{
"_id": {
"$oid": "561fd90285b5e73f5626f74e"
},
"name": "Cool Record",
"updatedAt": {
"$date": "2015-10-20T20:01:17.553Z"
}
}
If going through mongoose, queries like this work well:
MySchemda.find({ updatedAt : { $gt: lastSynced }}).exec();
But do not work otherwise.
Date.now is a number representing milliseconds since 1970. While it conceptually represents a date, it isn't actually a Date:
var x = Date.now;
typeof x;
// "number"
You need to switch your schema to be:
var MySchema = new Schema({
name : { type: String, required: true },
updatedAt : Number
});
alternately, you can use:
var newRec = {
name: entry.Name,
updatedAt: new Date()
}
and keep your schema as it is.
You may use it like this:
Define the date type and the default value, then create a variable which can be defined as your schema use year : new Date this would help a lot to set the date.
var songSchema = new mongoose.Schema({
name : String,
year : {type : Date, default : Date.now},
singer : String,
});
var song = mongoose.model("song", songSchema);
var xyz= new song({
name : "abcd",
year :new Date, // to set the date first set new Date
singer : "aabbccdd",
});
You can use setDate , setMonth, setYear method to solve the issues.There are more methods defined under the object year .You can out further at the documentation of mongoose.
xyz.save(function(err, songs){
if(err)
winston.log("Something is wrong"+ " "+ err);
else {
songs.year.setDate(03);
songs.year.setMonth(03);
songs.year.setYear(2015);
winston.log(songs);
}
});
Related
My lat & lng numbers are being converted to strings. My section integers are still the correct data type of Number. How do I set up model so that I can get my lat & lng back out as Float rather than String?
I'm storing latLng data in my db. Right now I have my data type set to Number for lat & lng. When I check out my db I see this:
{
"_id" : ObjectId("563bd98a105249f325bb8a7e"),
"lat" : 41.8126189999999980,
"lng" : -87.8187850000000054,
"created" : ISODate("2015-11-05T22:34:50.511Z"),
"__v" : 0,
"section" : 0,
}
But when I get my data back out using express I get this:
{
"_id": "563bd98a105249f325bb8a7e",
"lat" : "41.8126189999999980",
"lng" : "-87.8187850000000054",
"__v": 0,
"section" : 0,
"created" : "2015-11-05T22:34:50.511Z",
}
My model:
var WaypointSchema = new Schema({
lat: {
type: Number
},
lng: {
type: Number
},
section: {
type: Number
}
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Waypoint', WaypointSchema);
Express controller:
exports.list = function(req, res) {
Waypoint.find().sort('-created').populate('user', 'displayName').exec(function(err, waypoints) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(waypoints);
}
});
};
While the mongoDB fully supports float type, the mongoose supports only type of Number which is integer. If you try to save to mongoDB float number using mongooses type of Number it will be converted to string.
To sort this out, you will need to load some plugin for mongoose which will extend its value types. There are some plugins which work best with currencies or dates, but in your case I would use https://www.npmjs.com/package/mongoose-double.
Your model after changes would look something like this:
var mongoose = require('mongoose')
require('mongoose-double')(mongoose);
var SchemaTypes = mongoose.Schema.Types;
var WaypointSchema = new Schema({
lat: {
type: SchemaTypes.Double
},
lng: {
type: SchemaTypes.Double
},
section: {
type: Number
}
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Waypoint', WaypointSchema);
Hope it helps.
As of the current version of mongoose (v5.12.6), It supports Decimal128 which can be used for this.
var mongoose = require('mongoose');<br>
var Schema = mongoose.Schema;<br>
var Waypoint = new Schema({<br>
lat: {<br>
type: SchemaTypes.Double<br>
},<br>
lng: {<br>
type: SchemaTypes.Double<br>
},<br>
section: {<br>
type: Number<br>
}<br>
point: {<br>
type: [Number],<br>
index: '2d'<br>
},<br>
}, {<br>
timestamps: true<br>
})<br>
event.index({<br>
Point: '2dsphere'<br>
});<br>
module.exports = mongoose.model('Waypoint', Waypoint);<br>
waypoint.save(point: [parseFloat(values.latitude), parseFloat(values.longitude)],)
I'm trying to update a document which has a specific ID with the current date/time but the below code is not resulting in the DB getting updated and no errors. Any help would be great, thanks.
Schema:
var MerchantShema = new Schema({
merchant_id: Number,
merchant_aw_id: Number,
merchant_name: String,
merchant_url: String,
merchant_image: String,
product_feed: String,
product_feed_updated: Date,
created_at: {type: Date, default: Date.now},
updated_at: {type: Date, default: Date.now}
});
Update Query:
updateMerchantLastProductUpdate: function (mID) {
now = new Date();
var query = { "merchant_aw_id" : mID };
Merchants.update(query, { "product_feed_updated": now }, function (err) {
if (err) return console.error(err);
})
}
Route
app.get('/queries', function (req, res) {
queries.updateMerchantLastProductUpdate("2926");
});
Example document
{
"_id": {
"$oid": "55997638e4b01f0391cb99aa"
},
"merchant_id": "0003",
"merchant_aw_id": "2926",
"merchant_name": "Multipower",
"merchant_url": "www.multipower.com/uk/",
"merchant_image": "",
"product_feed": "aw",
"product_feed_updated": "",
"created_at": "",
"updated_at": ""
}
The merchant_aw_id field in your mongoose schema is expecting a number so you need to parse the string for integer by using the parseInt() method in your query. You also need the $set update operator which replaces the value of a field with the specified value to update your document, together with the {multi: true} option which if set to true, updates multiple documents that meet the query criteria. If set to false, updates one document. The default value is false:
updateMerchantLastProductUpdate: function (mID) {
var now = new Date(),
query = { "merchant_aw_id" : parseInt(mID) },
update = {
"$set": { "product_feed_updated": now }
},
options = { "multi": true };
Merchants.update(query, update, options, function (err) {
if (err) return console.error(err);
})
}
My error was caused by my model have the ID I was looking for in the format Number but my data in mongoDB was a String
I have the following schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ShopSchema = new Schema({
name: Schema.Types.Mixed,
country: {
type: String,
default: ''
},
createdAt: {
type: Date,
default: Date.now
},
defaultLanguage: {
type: String
},
account: {type : Schema.ObjectId, ref : 'Account'},
});
mongoose.model('Shop', ShopSchema);
"name" field is multilingual. I mean, I will keep the multilingual data like
name: {
"en": "My Shop",
"es": "Mi Tienda"
}
My problem is, in a controller, I am using this code to update the shop:
var mongoose = require('mongoose')
var Shop = mongoose.model('Shop')
exports.update = function(req, res) {
Shop.findByIdAndUpdate(req.params.shopid, {
$set: {
name: req.body.name
}
}, function(err, shop) {
if (err) return res.json(err);
res.json(shop);
});
};
and it is obvious that new data overrides the old data. What I need is to extend the old data with the new one.
Is there any method to do that?
You should to use the method .markModified(). See the doc http://mongoosejs.com/docs/schematypes.html#mixed
Since it is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect and save those changes. To "tell" Mongoose that the value of a Mixed type has changed, call the .markModified(path) method of the document passing the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
Use "dot notation" for the specific element:
Shop.findByIdAndUpdate(req.params.shopid, {
"$set": {
"name.en": req.body.name
}
}, function(err, shop) {
if (err) return res.json(err);
res.json(shop);
});
});
That wil either only overwrite the "en" element if that is what you want to do or "create" a new element with the data you set it to. So if you used "de" and that did not exist there will be the other elements and a new "de" one with the value.
I store the date of birth (dob) within a user model and I would like to check if it is the users birthday today. How do I do query that?
I have started to try, but obviously failed
// User Model
var UserSchema = new Schema({
name: String,
email: { type: String, lowercase: true },
role: {
type: String,
default: 'user'
},
hashedPassword: String,
dob: { type: Date, default: new Date() },
joinedOn: { type: Date, default: new Date() },
leftOn: { type: Date },
position: { type: String },
provider: String,
salt: String,
google: {},
github: {}
});
// Birtdhays
exports.birthdays = function(req, res, next) {
var todayStart = moment().startOf('day');
var todayEnd = moment().endOf('day');
User
.find()
.where('dob').gt(todayStart).lt(todayEnd)
.limit(3)
.sort('dob')
.select('name dob')
.exec(function(err, users){
if(err) return res.send(500, err);
res.json(200, users);
});
};
Presuming that you have birthdays stored as a date object in your documents then they probably look something like this:
{
"name": "Neil",
"dob": ISODate("1971-09-22T00:00:00Z")
}
So it's not just the "day" but the full year as well from the originally selected day of birth. This probably seems like a logical way to store a date of birth and it is a useful date object for many purposes. But how to query on that? Any date derived from the current year is not going to match that value within a range and other users data on the same day can occur in different years.
There is some JavaScript date manipulation you can do in order to deal with this though, and also some functionality of MongoDB in the aggregation framework ( or alternately using JavaScript in mapReduce ), to get data out of the date that is useful for matching.
Firstly you can look at the $dayOfyear operator and code to get that from the current date to use as a match:
var now = new Date();
var start = new Date( now.getFullYear() + "-01-01" );
var diff = now - start;
var oneDay = 1000 * 60 * 60 * 24;
var dayOfYear = Math.floor(diff / oneDay);
User.aggregate(
[
{ "$project": {
"name": 1,
"dob": 1,
"dayOfYear": { "$dayOfYear": "$dob" }
}},
{ "$match": { "dayOfYear": dayOfYear }
],
function(err,users) {
// work here
}
)
Now that's all fine, or it seems. But of course what happens when there is a leap year? All days after February 28 are moved forward one. You could account for this in other ways, but how about just using the "day" and "month" to do the match on instead:
var now = new Date();
var day = now.getDate(); // funny method name but that's what it is
var month = now.getMonth() + 1; // numbered 0-11
User.aggregate(
[
{ "$project": {
"name": 1,
"dob": 1,
"day": "$dayOfMonth",
"month": "$month"
}},
{ "$match": {
"day": day,
"month": month
}}
],
function(err,users) {
// work here
}
)
And the aggregation operators for $dayOfMonth and $month help there.
So that's all better and you can query for the "birthday" now, but there is something still not really right. Whilst the query will work, it's clearly not really efficient. Since what you are doing here is running through all of the results in the collection and manipulating the existing dates in order to extract the parts to perform a match.
Ideally you don't want to do this, and have the "query" itself target the current "birthdays" in a simple stroke to avoid doing this manipulation to get a match. This is where modelling comes in, and where you should consider adding more data to your document where queries like this are common:
{
"name": "Neil",
"dob": ISODate("1971-09-22T00:00:00Z"),
"day": 22,
"month": 9
}
Then it's easy to query on this as both "day" and "month" fields can also be indexed to further improve performance and avoid scanning the whole collection for matches:
var now = new Date();
var day = now.getDate(); // funny method name but that's what it is
var month = now.getMonth() + 1; // numbered 0-11
User.find({ "day": day, "month": month },function(err,users) {
// work here
})
So those are the considerations. Either accept the manipulation with the aggregation framework ( or possibly mapReduce ), or where you are going to frequently use such a query and/or have many items in the collection then add additional fields to your document schema that can be used as a data point directly in the query itself.
I solved it in the following way:
Added the fields birthday and brithmonth to the user model and added a pre save hook to keep them updated on any changes to the birthday:
UserSchema
.pre('save', function(next) {
this.birthmonth = this.dob.getMonth() + 1;
this.birthday = this.dob.getDate()
next();
});
Thanks Neil for the inspiration!
I'm building a simple REST app on the Yeoman Express MVC generator with MongoDB.
This is my MongoDB/Mongoose model (updated with complete update.js model):
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UpdateSchema = new Schema({
title: String,
text: String,
authors: String,
url: String,
imageUrl: String,
dateCreated: { type: Date, default: Date.now },
reloadNeeded: { type: Boolean, default: true }
});
mongoose.model('Update', UpdateSchema);
This is what the data looks like in the Mongo client:
> db.updates.find();
{ "_id" : ObjectId("5476453f8920d05ecdef4eec"), "title" : "Hello World", "text" : "yoda yoda" }
{ "_id" : ObjectId("547653748920d05ecdef4eed"), "title" : "Hihi", "text" : "mookie" }
And this is the JSON output from my Express app:
[
{"_id":"5476453f8920d05ecdef4eec","title":"Hello World","text":"yoda yoda","reloadNeeded":true,"dateCreated":"2014-11-27T10:50:10.078Z"},
{"_id":"547653748920d05ecdef4eed","title":"Hihi","text":"mookie","reloadNeeded":true,"dateCreated":"2014-11-27T10:50:10.078Z"}
]
So, dateCreated and reloadNeeded are set at runtime - but I'd rather want them set (and persisted) when I create the documents. What's going on?
Update: seems like values are persisted if I create from Mongoose rather than the MongoDB shell.
Do you use mongoose for data model? If it so, default values will be created on document construction http://mongoosejs.com/docs/2.7.x/docs/defaults.html
Anyway I assume the reason is in defaults