MongoDB: Cannot use complex condition in subdocument [duplicate] - node.js

I have a strange problem and cannot figure out what the problem is. The Error-message doesn't help.
I'm sending an "alarm" to the server and want to save this alarm to my "device" which already exist in the database.
The alarm object I send to the server looks like this:
{
actionTaken: "none",
dateTime: "20152111191512",
difference: 4.88,
timestamp: 1448128894781
}
The Schema for the device is as follows:
var deviceSchema = new Schema({
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
alarms : [ {
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
} ]
});
I load the device from the database (deviceId is set):
Thermometer.findOne({
deviceId : deviceId
}, function(error, device){
//error handling
var now = (new Date().getTime());
var nowDateTime = (new Date()).toISOString().slice(0, 19).replace(/[-T\s:]/g, "");
var newAlarm = {
timestamp : now,
dateTime : nowDateTime, // yyyymmddhhmmss
difference : diff,
actionTaken : "none"
};
device.alarms.push(newAlarm); //EXCEPTION !
// device.save //doesn't get called
});
As you can see in the comment, I get an Exception/Error when I want to push the "newAlarm"-object to the alarms-array of my device.
The Error says:
Cast to string failed for value [object Object] at path alarms
Error-Object:
kind: "string",
message: "Cast to string failed for value "[object Object]" at path "alarms"",
name: "CaseError",
path: "alarms",
stack: undefined,
value: {actionTaken: "none", dateTime: "20152111191512", difference: 4.88, timestamp: 1448128894781}
Do you have an idea?
For me it doesn't make any sense. The array and its content (object) is specified in the Schema. Why is there a string cast error with the whole object as value?
What I use:
"express": "3.2.6",
"express-session":"1.7.6",
"hjs": "*",
"mongoose": "4.0.5",
"nodemailer": "1.4.0"
EDIT: I don't want to use nested Schemas. It is also possible to do it with arrays. I do it with arrays in some other Schemas.
EDIT 2:
I added an property lastAlarm and do
device.lastAlarm = alarm;
but after that, thermometer.lastAlarm is still undefined... but alarm is an object. So is it possible that the device object is locked some how?

Mongoose interprets the object in the Schema with key 'type' in your schema as type definition for that object.
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
}
So for this schema mongoose interprets deviceId as a String instead of Object and does not care about all other keys inside deviceId.
SOLUTION:
Add this option object to schema declaration { typeKey: '$type' }
var deviceSchema = new Schema(
{
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
alarms : [ {
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
} ]
},
{ typeKey: '$type' }
);
By adding this we are asking mongoose to use $type for interpreting the type of a key instead of the default keyword type
Mongoose Docs reference: https://mongoosejs.com/docs/guide.html#typeKey

I would declare alarm as its own schema and set the alarms property as an array of alarm aka subdocuments. This will allow you to add validation to the alarm schema, etc. NOTE: Subdocuments don't get saved until the parent is saved.
var alarmSchema = new Schema({
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
});
var deviceSchema = new Schema({
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
alarms : [alarmSchema]
});

Maybe it is too late, but here mongoose is assuming that deviceId is not an object and it is of type String
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
SIMPLE SOLUTION:
deviceId: {
type: {
type: String
},
index: {
unique: true,
dropDups: true
}
},

Wow, 5 years late but I recently faced this problem. All you need to do is specify the type in an object for nested routes. So your code would change to:
var deviceSchema = new Schema({
deviceId: {
type : { type: String },
index : {
unique : true,
dropDups : true
}
},
alarms : [ {
timestamp : {type: Number},
dateTime : { type: String }, //yyyymmddhhss
difference : {type: Number},
actionTaken : { type: String }, //"send sms"
} ]
});
So for nested objects, you'd need to use field: {type: String} and not field: String

var deviceSchema = new Schema({
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
},
alarms : {type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Alarm' }]}
});
var alarmSchema = new Schema({
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
});
I would recommend to make an own schema for the alarms. I think u cant define an array in a schema like you do.

Use inner schema to solve this,
var SchemaObject = require('node-schema-object');
// Create custom basic type
// Type can be extended with more properties when defined
var NotEmptyString = {type: String, minLength: 1};
// Create sub-schema for user's Company
var Company = new SchemaObject({
startDate: Date,
endDate: Date,
name: NotEmptyString
});
// Create User schema
var User = new SchemaObject({
// Basic user information using custom type
firstName: NotEmptyString,
lastName: NotEmptyString,
// "NotEmptyString" with only possible values as 'm' or 'f'
gender: {type: NotEmptyString, enum: ['m', 'f']},
// Index with sub-schema
company: Company,
// An array of Objects with an enforced type
workHistory: [Company],
// Create field which reflects other values but can't be directly modified
fullName: {type: String, readOnly: true, default: function() {
return (this.firstName + ' ' + this.lastName).trim();
}}
});
// Initialize a new instance of the User with a value
var user = new User({firstName: 'Scott', lastName: 'Hovestadt', gender: 'm'});
// Set company name
user.company.name = 'My Company';
// The date is automatically typecast from String
user.company.startDate = 'June 1, 2010';
// Add company to work history
user.workHistory.push({
name: 'Old Company',
startDate: '01/12/2005',
endDate: '01/20/2010'
});
console.log(user.toObject());
// Prints:
{ firstName: 'Scott',
lastName: 'Hovestadt',
gender: 'm',
company:
{ startDate: Tue Jun 01 2010 00:00:00 GMT-0700 (PDT),
endDate: undefined,
name: 'My Company' },
workHistory:
[ { startDate: Wed Jan 12 2005 00:00:00 GMT-0800 (PST),
endDate: Wed Jan 20 2010 00:00:00 GMT-0800 (PST),
name: 'Old Company' } ],
fullName: 'Scott Hovestadt' }

i was facing same issue in frontend when I was filling the login form then
like dispatch({email , password});
you can see above i wrapped up into curly braces , i just remove curly {} then it worked in my case.
If your facing this issue from frontend then try this

Related

mongoDB returning with $numberDecimal in the response of a query

Below is my mongoose schema
const mongoose = require('mongoose');
const AccountingCostsSchema = mongoose.Schema(
{
// other properties goes here
accountCosts: {
type: mongoose.Decimal128,
set: (v) =>
mongoose.Types.Decimal128.fromString(parseFloat(v).toFixed(4)),
required: true
}
},
{
collection: 'accountingCosts'
}
);
export = mongoose.model('AccountingCosts', AccountingCostsSchema);
Data in MongoDB
accountingCosts collection
Data in Text mode view
{
"_id" : ObjectId("4e1eb38c2bdea2fb81f5e771"),
"accountId" : ObjectId("4e8c1180d85de1704ce12110"),
"accountCosts" : NumberDecimal("200.00"),
}
Data in Text mode view
My query
db.getCollection('accountingCosts').find({'accountId': '4e8c1180d85de1704ce12110'})
Result from query
"accountCosts": {
"$numberDecimal": "123.00"
}
I tried writing a getter function on schema like i have a setter function. But it is not working
get: function(value) {
return value.toString();
}
My expected output is just a plain property with name and value like below
"accountCosts": "123.00"
I am sure you found a solution, but i will also write it here so who will land here would find a solution.
Your idea to using a getter was right, but maybe you forgot to enable it in the schema, so let' s see how using your code.
You have this schema:
var AccountingCostsSchema = new Schema({
accountId: String,
accountCosts: {
type: Schema.Types.Decimal128,
default: 0
}
});
So when you retrieve it you will get:
{
"_id" : "12345",
"accountId" : "123456",
"accountCosts" : {
"$numberDecimal": "123.00"
}
}
Taking in example that you would get accountCosts as number, so something like that:
{
"_id" : "12345",
"accountId" : "123456",
"accountCosts" : 123
}
We would need a getter as you said, so we will need to add a function of this getter in our model file, let' s say after your schema. This could be your getter function:
function getCosts(value) {
if (typeof value !== 'undefined') {
return parseFloat(value.toString());
}
return value;
};
Now, let' s declare this getter in our schema that will become:
var AccountingCostsSchema = new Schema({
accountId: String,
accountCosts: {
type: Schema.Types.Decimal128,
default: 0,
get: getCosts
}
});
Now mongoose will uderstand how you want that value is returned, but we have to specify that we want it uses the getter. So we have to enable it when we want to retieve the value in json format. We can simply add it as this:
var AccountingCostsSchema = new Schema({
accountId: String,
accountCosts: {
type: Schema.Types.Decimal128,
default: 0,
get: getCosts
}
}, {toJSON: {getters: true}});
And so, when you will retrieve it, you will get this:
{
"_id" : "12345",
"accountId" : "123456",
"accountCosts" : 123,
"id" : "12345"
}
But, whoa (this is not my batman glass!) what is that "id" : "12345"?
According to Mongoose documents:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
How we can avoid it? We can just add id: false in our schema that now will be:
var AccountingCostsSchema = new Schema({
accountId: String,
accountCosts: {
type: Schema.Types.Decimal128,
default: 0,
get: getCosts
},
id: false
}, {toJSON: {getters: true}});
And now you will not see it anymore.
Just a little last tip (no, i have not finished):
What happens if you have an array of objects in your schema like this?
var AccountingCostsSchema = new Schema({
accountId: String,
usersAndCosts: [{
username: String,
accountCosts: {
type: Schema.Types.Decimal128,
default: 0,
get: getCosts
}
],
id: false
}, {toJSON: {getters: true}});
Bad news now: you can not keep on using this schema. Good news now: you can fix it easly. You need to create a new schema for usersAndCosts and then reference it in the parent schema. Let' s see it:
var usersAndCostsSchema = new Schema({
username: String,
accountCosts: {
type: Schema.Types.Decimal128,
default: 0,
get: getCosts
},
id: false
}, {toJSON: {getters: true}});
var AccountingCostsSchema = new Schema({
accountId: String,
usersAndCosts: [usersAndCostsSchema],
id: false
}, {toJSON: {getters: true}});
The result will be the same, your accountCosts will be shown as number, no double ids.
Remember that of course this is to enable getters for toJSON but you could need to enable them for toObject too, as you could need to do the same for setters. But we here talked about getters for JSON.

Mongoose populate a field without ObjectId?

I have a schema:
var Schema = mongoose.Schema;
var TicketSchema = new Schema({
externalId: String,
name: String,
items: [{
externalId: String,
price: Number,
quantity: {
type: Number,
default: 1
},
comment: {
type: String,
default: '',
trim: true
},
entity: {
type: String,
ref: 'Entity'
}
}],
tableId: String
});
mongoose.model('Ticket', TicketSchema);
And I want to populate entity field with an unique field other than ObjectId.
How can I achieve that?
Though late answer. Please check Populate Virtuals for Mongoose 4.5.0
Click the link below
http://mongoosejs.com/docs/populate.html
And scroll down or search for Populate Virtuals you will see it does exactly what you want.
I found Views as one useful approach, though not sure it is the most efficient! For example, in movielens database, I wanted to refer 'movieId' in ratings collection to 'movieId' in the movies collection using 'movieId' as foreign key.
db.createView('rating-movie-view','ratings',[{$lookup:{from:"movies",localField:"movieId",foreignField:"movieId",as:"ratings_movie"}},{ $project:{'userId':1,'movieId':1,'rating':1,'timestamp':1,'ratings_movie.title':1,'ratings_movie.genres':1 } }])
New view "rating-movie-view" thus created has the required fields 'title and 'genres'.
db["rating-movie-view"].findOne()
{
"_id" : ObjectId("598747c28198f78eef1de7a3"),
"userId" : 1,
"movieId" : 1129,
"rating" : 2,
"timestamp" : 1260759185,
"ratings_movie" : [
{
"title" : "Escape from New York (1981)",
"genres" : "Action|Adventure|Sci-Fi|Thriller"
}
]
}
Hope this useful!
Those who are not familiar with movielens data here are the schema
var MovieSchema = new mongoose.Schema({
movieId: Number,
title: String,
genres: String,
});
var RatingSchema = new mongoose.Schema({
userid: Number,
movieId:Number,
rating: Number,
timestamp:Number,
});
//View schema
var RatingViewSchema = new mongoose.Schema({
userid: Number,
movieId:Number,
rating: Number,
timestamp:Number,
rating_movie:{title:String,genres:String}
});
const blogs = this.blogModel
.find(find)
.populate('blogCategory', 'name -_id');
Note -_id will exclude the object _id
I'm not sure if I understood your question correctly.
In Mongoose model, in case we do not specify a primary key, it automatically adds in an extra field called ObjectId and assigns a unique value for each object.
In case we need to specify our own key, we can do it by specifying the key property.
For example:
mongoose.model('Todo', {
todoID: {
type: String,
key: true
},
text: {
type: 'text'
},
done: {
type: Boolean,
default: false,
},
date: {
type: Date,
},
items: [{
entity: {
type: String,
ref: 'Entity'
}
}]
});
I hope, this is what you meant.
If you are asking about fetching objects based on Items -> entity's property,
Todo.find({'items.entity.type':required_type}, function(err, foundTodos){
// ---
});
Thanks,
Use crypto to hash something unique like the objectId , and then save it to your entities.
Var hash = crypto.createHmac('sha256', ticket.objectId).digest('hex');
Ticket.entities= hash;

node-mongo-native and mongo-shell return different results given the same query

Given the next code:
var daysToBeOld = 7;
var dateOlder = moment().subtract(daysToBeOld, 'days').toDate();
MongoClient.connect(mongouri, function(err, db) {
console.log('Filtering pending topics before: %s', dateOlder);
var conditions = {status: 'pending', updated: {$lt: dateOlder}};
console.log('Using the next filters: %j', conditions);
var topicsCol = db.collection('topics');
topicsCol.find(conditions).toArray(function (err, topics) {
if (err) {
console.log(err);
}
console.log("found %j topics", topics);
callback(err, topics);
});
});
I get the next console.log results. As you can see the result is an empty array:
Filtering pending topics before: Tue Feb 21 2017 15:13:35 GMT+0000 (UTC)
Using the next filters: {"status":"pending","updated":{"$lt":"2017-02-21T15:13:35.191Z"}}
found [] topics
If I execute the same query against mongo-shell it returns one document:
I.e:
> db.topics.find({"status":"pending","updated":{"$lt":"2017-02-21T15:13:35.191Z"}})
{ "_id" : "076bbbc0-e318-11e6-9375-e94b488c7ad8", "status" : "pending", "text" : "lalalalalla", "topicType" : "InformaciĆ³n", "member" : "NoUsuarioForus", "service" : "Limpieza", "idCard" : "", "receiveAnswer" : "1", "toc" : "1", "date" : ISODate("2016-08-31T16:36:45Z"), "sender" : { "name" : "alalalal", "email" : "alalalalala#lalalalal.com" }, "__v" : 0, "deleted" : false, "answered" : true, "firstAnswerTime" : 15614529, "updated" : "2016-02-01T17:28:34.868Z"
Why I'm getting no results in the query launched from node-mongo-native?
My node-mongo-native version is 2.2.24.
I've started using mongoose but switched to node-mongo-native to make this query because I was thinking that was an issue with mongoose. Btw I'm going to post my schema if it helps to clarify why it's not working:
topic.js:
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate');
var Schema = mongoose.Schema;
var uuid = require('node-uuid');
var xss = require('xss');
var TopicSchema = new Schema({
_id: {
type: String,
default: uuid.v1
},
status: {
type: String,
// open: When the message is created first time or not exists
// pending: The issue has been answered by some employee. Waiting for answer from the customer
// closed: The issue has been resolved
enum: ['open', 'closed', 'pending']
},
sender: {
name: String,
email: {
type: String,
required: true,
}
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
updated: {
type: Date
},
// If the topic is answered by an user different than creator it will be true
answered: {
type: Boolean,
default: false
},
firstAnswerTime: Number,
centerId: String,
topicType: String,
member: String,
service: String,
idCard: String,
receiveAnswer: String,
toc: String,
satisfaction: Number,
deleted: {
type: Boolean,
default: false
}
});
TopicSchema.plugin(mongoosePaginate);
TopicSchema.pre('save', function(next) {
this.updated = new Date();
this.text = xss(this.text);
next();
});
module.exports = mongoose.model('Topic', TopicSchema);
In the underlying collection, updated field is stored as a string and in your application you are querying it as a date, which won't yield anything but when you query it in mongo shell using a string it correctly returns the document.
Seeing that updated field is a string, try querying it as one i.e. changing the query variable to
var dateOlder = moment().subtract(daysToBeOld, 'days').toISOString();

Mongoose Schema Error: "Cast to string failed for value" when pushing object to empty array

I have a strange problem and cannot figure out what the problem is. The Error-message doesn't help.
I'm sending an "alarm" to the server and want to save this alarm to my "device" which already exist in the database.
The alarm object I send to the server looks like this:
{
actionTaken: "none",
dateTime: "20152111191512",
difference: 4.88,
timestamp: 1448128894781
}
The Schema for the device is as follows:
var deviceSchema = new Schema({
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
alarms : [ {
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
} ]
});
I load the device from the database (deviceId is set):
Thermometer.findOne({
deviceId : deviceId
}, function(error, device){
//error handling
var now = (new Date().getTime());
var nowDateTime = (new Date()).toISOString().slice(0, 19).replace(/[-T\s:]/g, "");
var newAlarm = {
timestamp : now,
dateTime : nowDateTime, // yyyymmddhhmmss
difference : diff,
actionTaken : "none"
};
device.alarms.push(newAlarm); //EXCEPTION !
// device.save //doesn't get called
});
As you can see in the comment, I get an Exception/Error when I want to push the "newAlarm"-object to the alarms-array of my device.
The Error says:
Cast to string failed for value [object Object] at path alarms
Error-Object:
kind: "string",
message: "Cast to string failed for value "[object Object]" at path "alarms"",
name: "CaseError",
path: "alarms",
stack: undefined,
value: {actionTaken: "none", dateTime: "20152111191512", difference: 4.88, timestamp: 1448128894781}
Do you have an idea?
For me it doesn't make any sense. The array and its content (object) is specified in the Schema. Why is there a string cast error with the whole object as value?
What I use:
"express": "3.2.6",
"express-session":"1.7.6",
"hjs": "*",
"mongoose": "4.0.5",
"nodemailer": "1.4.0"
EDIT: I don't want to use nested Schemas. It is also possible to do it with arrays. I do it with arrays in some other Schemas.
EDIT 2:
I added an property lastAlarm and do
device.lastAlarm = alarm;
but after that, thermometer.lastAlarm is still undefined... but alarm is an object. So is it possible that the device object is locked some how?
Mongoose interprets the object in the Schema with key 'type' in your schema as type definition for that object.
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
}
So for this schema mongoose interprets deviceId as a String instead of Object and does not care about all other keys inside deviceId.
SOLUTION:
Add this option object to schema declaration { typeKey: '$type' }
var deviceSchema = new Schema(
{
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
alarms : [ {
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
} ]
},
{ typeKey: '$type' }
);
By adding this we are asking mongoose to use $type for interpreting the type of a key instead of the default keyword type
Mongoose Docs reference: https://mongoosejs.com/docs/guide.html#typeKey
I would declare alarm as its own schema and set the alarms property as an array of alarm aka subdocuments. This will allow you to add validation to the alarm schema, etc. NOTE: Subdocuments don't get saved until the parent is saved.
var alarmSchema = new Schema({
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
});
var deviceSchema = new Schema({
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
alarms : [alarmSchema]
});
Maybe it is too late, but here mongoose is assuming that deviceId is not an object and it is of type String
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
}
},
SIMPLE SOLUTION:
deviceId: {
type: {
type: String
},
index: {
unique: true,
dropDups: true
}
},
Wow, 5 years late but I recently faced this problem. All you need to do is specify the type in an object for nested routes. So your code would change to:
var deviceSchema = new Schema({
deviceId: {
type : { type: String },
index : {
unique : true,
dropDups : true
}
},
alarms : [ {
timestamp : {type: Number},
dateTime : { type: String }, //yyyymmddhhss
difference : {type: Number},
actionTaken : { type: String }, //"send sms"
} ]
});
So for nested objects, you'd need to use field: {type: String} and not field: String
var deviceSchema = new Schema({
deviceId: {
type : String,
index : {
unique : true,
dropDups : true
},
alarms : {type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Alarm' }]}
});
var alarmSchema = new Schema({
timestamp : Number,
dateTime : String, //yyyymmddhhss
difference : Number,
actionTaken : String, //"send sms"
});
I would recommend to make an own schema for the alarms. I think u cant define an array in a schema like you do.
Use inner schema to solve this,
var SchemaObject = require('node-schema-object');
// Create custom basic type
// Type can be extended with more properties when defined
var NotEmptyString = {type: String, minLength: 1};
// Create sub-schema for user's Company
var Company = new SchemaObject({
startDate: Date,
endDate: Date,
name: NotEmptyString
});
// Create User schema
var User = new SchemaObject({
// Basic user information using custom type
firstName: NotEmptyString,
lastName: NotEmptyString,
// "NotEmptyString" with only possible values as 'm' or 'f'
gender: {type: NotEmptyString, enum: ['m', 'f']},
// Index with sub-schema
company: Company,
// An array of Objects with an enforced type
workHistory: [Company],
// Create field which reflects other values but can't be directly modified
fullName: {type: String, readOnly: true, default: function() {
return (this.firstName + ' ' + this.lastName).trim();
}}
});
// Initialize a new instance of the User with a value
var user = new User({firstName: 'Scott', lastName: 'Hovestadt', gender: 'm'});
// Set company name
user.company.name = 'My Company';
// The date is automatically typecast from String
user.company.startDate = 'June 1, 2010';
// Add company to work history
user.workHistory.push({
name: 'Old Company',
startDate: '01/12/2005',
endDate: '01/20/2010'
});
console.log(user.toObject());
// Prints:
{ firstName: 'Scott',
lastName: 'Hovestadt',
gender: 'm',
company:
{ startDate: Tue Jun 01 2010 00:00:00 GMT-0700 (PDT),
endDate: undefined,
name: 'My Company' },
workHistory:
[ { startDate: Wed Jan 12 2005 00:00:00 GMT-0800 (PST),
endDate: Wed Jan 20 2010 00:00:00 GMT-0800 (PST),
name: 'Old Company' } ],
fullName: 'Scott Hovestadt' }
i was facing same issue in frontend when I was filling the login form then
like dispatch({email , password});
you can see above i wrapped up into curly braces , i just remove curly {} then it worked in my case.
If your facing this issue from frontend then try this

Mongoose sub-schema shows '[object Object]' in document. [duplicate]

I have sub document as in metrics. But I don't think I am saving correctly, because each document doesn't have metrics data correctly..instead it shows metrics: [ '[object Object]', '[object Object]', '[object Object]' ] and the data is impossible to access. This is really difficult to figure out since Mongoose doesn't give errors for this kind of stuff. If anyone could please tell me what is wrong.
EDIT: To make things more confusing, in the browser it shows that metrics has 3 arrays:
Object {cpuResult: Object}
cpuResult: Object
__v: 0
_id: "53b781d9fb272c4f44d8d1d8"
avaiable: true
metrics: Array[3]
0: "[object Object]"
1: "[object Object]"
2: "[object Object]"
length: 3
Here is my schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CpuSchema = new Schema({
timeStamp : { type : Date, index: true },
avaiable : Boolean,
status : String,
metrics : [ { duration : String,
data : Number,
type : String,
unit : String
} ,
{ duration : String,
data : Number,
type : String,
unit : String
},
{ duration : String,
data : Number,
type : String,
unit : String
}
]
});
module.exports = CpuSchema;
Here is my save function:
function saveCpu(cpuResult) {
var cpu = new Cpu ({
timeStamp : cpuResult.timestamp,
avaiable : cpuResult.available,
status : cpuResult.status,
metrics : [ { duration : "15m",
data : cpuResult.metrics["15m"].data,
type: cpuResult.metrics["15m"].type,
unit: cpuResult.metrics["15m"].unit
},
{ duration : "5m",
data : cpuResult.metrics["5m"].data,
type: cpuResult.metrics["5m"].type,
unit: cpuResult.metrics["5m"].unit
},
{ duration : "1m",
data : cpuResult.metrics["1m"].data,
type: cpuResult.metrics["1m"].type,
unit: cpuResult.metrics["1m"].unit
}]
});
cpu.save(function (err, product, numberAffected) {
db_finish(err, product, numberAffected,
cpuResult, "cpuResult") });
Here is my data that gets inserted:
{ timestamp: 1404534588528,
available: true,
status: 'success',
metrics:
{ '15m': { data: 0.05, type: 'n', unit: 'unknown' },
'5m': { data: 0.01, type: 'n', unit: 'unknown' },
'1m': { data: 0, type: 'n', unit: 'unknown' } } }
When including a field named type in a sub-doc of a Mongoose schema, you need to define its type using an object or it looks to Mongoose like you're declaring the type of the parent field.
So change your schema to:
var CpuSchema = new Schema({
timeStamp : { type : Date, index: true },
avaiable : Boolean,
status : String,
metrics : [ { duration : String,
data : Number,
type : {type: String},
unit : String
} ,
{ duration : String,
data : Number,
type : {type: String},
unit : String
},
{ duration : String,
data : Number,
type : {type: String},
unit : String
}
]
});

Resources