How do I get this nested schema working in Mongoose? - node.js

So I'm trying to figure out how to how to save multiple commands in a command list but everything I've tried hasn't worked. This is how I have it set up so far but when it saves, it saves in the format of
"command_list" : [ { "action" : "goto,goto", "target" : "http://www.google.com,http://www.cnn.com" } ]
when I really want something like
"command_list" : [ "command" : { "action" : "goto", "target" : "http://www.google.com" },
"command" : { "action" : "goto", "target" : "http://www.cnn.com" } ]
where there are multiple commands. So far my app.js is storing the data like this
var configSample = new Configurations({
command_list_size: request.body.command_list_size,
command_list: [ {action: request.body.action, target: request.body.target}]
});
and the model looks like this
var mongoose = require("mongoose");
var command = mongoose.Schema({
action: String,
target: String
});
var configSchema = mongoose.Schema({
command_list_size: Number,
command_list: [command]
});
module.exports = mongoose.model('Configurations', configSchema);
So how do I get that nesting action going? Thanks!

It looks like you're not packing the data right when you send it to the server. If you use the following:
command_list: [ {action: request.body.action, target: request.body.target}]
it's going to grab all of the actions and lump them together and do the same with the targets. You'd be better off sending an array to your server with the documents already nested in them.
The other option would be to parse the data to pull out the elements once you receive it on your server, but I think it'd be easier to just package it right in the first place.
ADDITION:
If you wanted to split what you have, you could use the String.split() method and rebuild the object:
// not certain the chaining will work like this, but you get the idea. It works
// on the string values you'll receive
var actions = response.body.action.split(',');
var targets = response.body.target.split(',');
// the Underscore library provides some good tools to manipulate what we have
// combined the actions and targets arrays
var combinedData = _.zip(actions, targets);
// go through the combinedData array and create an object with the correct keys
var commandList = _.map(combinedData, function(value) {
return _.object(["action", "target"], value)
});
There may be a better way to create the new object, but this does the trick.
EDIT:
I created a question about trying to refactor the above code here.

Related

mongoose - field contained in string

I need to make the following query, I have a model in the db that holds a string. for example lets say that's the data:
[{ a : 'test/t' },
{ a : 'test/b' }]
Now I have the following string
var search = 'http://ttt.com/test/t'
I want to make a query which will find all the documents that 'a' property is contained inside the search variable, case insensetive.
All the exmaples I've seen talk about the opposite equation.
You can use the following query for the operation which uses the short for $where. Since this runs in Javascript, expect it to run slowly.
db.coll.find(function () {
var re = new RegExp(this.a.replace("/", "\/"))
return 'http://ttt.com/test/t'.match(re)
});

mongoose - have optional additional implicit fields

I have a schema with a field in which I can store anything : new Schema({settings : {}}).
I have a database with
I want to keep this ability to add data without adding new fields, but for some of the fields have default values if they are not present.
I can do the following :
new Schema({
settings : {
key : { type : String, default: "abc" }
// I want to be able to add data that contains more than just "key"
}
});
I just want to make sure that when requesting the data from this schema, I will still get all the data, and not just the keys explicitly defined ?
It seems to work, but I want to make sure that I can still :
read all the data
still write arbitrary data (ie. not necessarily defined in the schema)
Are there rules on mongo/mongoose that would prevent me from doing one of these two things (I'm very unsure for the writing part) ? If there is such a "feature", how can it be done ?
Note : I saw this question. Correct me if I am wrong, but the fields are not implicit (like in the first case with {}), and have to be defined (it's actually the opposite question).
Edit : I now saw also this question that addresses my concerns (even if the accepted solution sounds more like a workaround to me). But in my case I already have data stored so (1 - disable strict) would mean writing a lot of validation code to be safe (because a lot of keys, this is the biggest collection of the app), and (2 - mixed schemas) would require to migrate the data of this specific sub-element... In short : I would still welcome a solution to my particular problem.
I think you will want to build your own custom validation here rather than rely on the defauly schema type validation methods. Luckily, mongoose has a facility for this:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
settings: {}
});
var Test = mongoose.model( 'Test', testSchema, "test" );
testSchema.path('settings').validate(function(value) {
return Object.keys(value).indexOf("key") != -1;
},'Error "settings" must contain "key" as "settings.key"');
//var test = new Test({ settings: { "key": "something" } });
var test = new Test({ settings: { } });
test.save(function(err) {
try {
if (err) throw err;
console.log(test);
} catch (e) {
console.log(e);
}
});
So basically, I have set up the validate function there for the "settings" path in the schema to look for the presence of "key" with it's own object. Where that "key" does not exist, an exception is reported in the errors.
Like any such errors, it will be returned within the err object when you .save() the object, thus blocking the write. It can be then be acted on to handle the error however you want, with the message that was defined reported.
So that is a self contained "test" where you can alternately uncomment the valid data for the object and successfully save that object without errors being reported.
Alternately you can do a "pre save" to fill in some default data:
testSchema.pre("save",function(next) {
this.settings = (this.settings) ? this.settings : {};
if (Object.keys(this.settings).indexOf("key") == -1)
this.setting.key = "abc";
next();
});
Which fills in a default if it is not already there.
Try to use like this way
new Schema({
settings : {}
});
var modelObj = new myModel();
modelObj.settings.key = "keyval";
modelObj.settings.key1 = "keyval";
modelObj.settings.key2 = "keyval";
modelObj.settings.key3 = "keyval";
modelObj.save(function(err){
//handle
});

Mongoclient: how to set a value of an embedded document

Im using the MongoClient in nodejs. I want to update an specific embedded document value, but I don't know how to do that.
In the mongo shell it works like a charm With {$set{doc.doc : "test"}}
But when I'm trying to use it in exactly the same way in node, It gives me the error that the dot isn't undestandable.
I've tried it as a string, which doesn't work either.
Does somebody got a solution for this problem?
EDIT:
Json doc in the Mongodb:
{
name : test,
doc : {},
}
and I want to add the following key-value pair to the 'doc'-document
test:test
Relevant Code part (How i thought It should work)
db.collection("test").update({name:test},{$set:{doc.test:test}}, callback)
You can try setting up the update object in a variable using the array-syntax style, e.g:
var update = { $set: {} };
update["$set"]["doc"]["test"] = "test"; // -> same as update = {"$set": {"doc": {"test": "test" } } }
var query = {"name": "test"};
db.collection("test").update(query, update, callback);

Saving Schema-less Records with Mongoose?

So I've been trying to save CSP reports into Mongoose with a Mixed schema and have ran into a snag of sorts.
If I try to save anything using the "schema-less" way, it only saves the default _v and _id fields
ViolationSchema = new Schema({});
Violation = mongoose.model('CSPViolation', ViolationSchema);
... wait for POST ...
new Violation( req.body ).save( callback );
// { _id : <some_id>, _v : <some_hash> }
If I set a field in the schema to be Mixed and add a .markModified() to the field, it will save.
ViolationSchema = new Schema({ report : { type : Mixed } });
Violation = mongoose.model('CSPViolation', ViolationSchema);
... wait for POST ...
var v = new Violation( { report : req.body } );
v.markModified('report');
v.save( callback );
// report saved under v.report.<actual_report>
I thought about using native MongoDB-style collection.insert, however it doesn't look like the model has an insert method (nor the schema for that matter).
I suppose I could also go over each key in the report I'm saving and manually mark it as modified, but I'd like to avoid that just to store a report such as this.
Any ideas how I can blindly save a mixed schema type using Mongoose?
It looks like this can be done by setting { strict : false } on the schema. This ensures that Mongoose will save any fields that weren't declared in the original schema.
Normally this isn't something you would enable on 95% of your data, it just fits perfectly with what I'm trying to do currently.
Example
ViolationSchema = new Schema({ type: Mixed }, { strict : false });
Violation = mongoose.model('CSPViolation', ViolationSchema);
... wait for POST ...
new Violation( req.body ).save( callback );
// Saves with full data

Underscore.js _.extend function does not copy mongoose model properties when they are not defined in the schema?

Why can I not use underscore(_) extend to update mongoose models where the properties are not defined in the schema definition. Is there a way to get around this?
Node model:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var NodeSchema = new Schema({
label: {type : String, default : 'none'}
}, { strict: false })
mongoose.model('Node', NodeSchema)
Node Controller:
var node = new Node();
node = _.extend(node, {"EXTENDNOTinSchema":"TRUE"});
console.log("extend: " + node);
node.set("SETNOTinSchema","TRUE");
console.log("set: " + node);
Console Output:
extend: { __v: 0,
_id: 50bb05656880a68976000001,
label: 'none' }
set: { __v: 0,
_id: 50bb05656880a68976000001,
label: 'none'
SETNOTinSchema: TRUE}
This is happening because if something is not in the schema then Mongoose cannot use 'defineProperty', and this treats the assignment like any other.
So first off, just to be clear.
node = _.extend(node, {"EXTENDNOTinSchema":"TRUE"});
is identical to this:
node['EXTENDNOTinSchema'] = 'TRUE';
Which is entirely different from this, in the general case.
node.set("SETNOTinSchema","TRUE");
The trick is that Mongoose is smart, and using the defineProperty function I mentioned above, it can bind a function to get called for things like this:
node['INSCHEMA'] = 'something';
But for things that are not in the schema, it cannot do this, so the assignment works like a normal assignment.
The part that tripping you up I think is that console.log is doing some hidden magic. If you look at the docs, console.log will call the inspect method of an object that is passed to it. In the case of Mongoose, it's models do not store attributes directly on the model object, they are stored on an internal property. When you assign to a property being watched with defineProperty or call set, it stores the value on the internal object. When you log the model, inspect prints out the internal model contents, making it seem like the model values are stored right on the object.
So when you do
console.log(node);
what you are really seeing is
console.log(node.somehiddenproperty);
So the answer to your question is really, if you have a bunch of values that are not in the schema, you cannot use _.extend. Instead, just use set because it takes an object anyway.
node.set({"EXTENDNOTinSchema":"TRUE"});

Resources