Mongoose.js getter that inflates subdocuments on Find()? - json

Is there an inverse of Mongoose.js validation that can inflate the subdocument when the parent is retrieved? I may have been looking at the docs so long I'm not recognizing an existing feature for what it is.
A beauty of MongoDB is that the query specifications (e.g. {likes: {$gt: 10, $le: 14}} are themselves Javascript objects, and until recently have been storing them in a MongoDB instance as subdocuments.
However, upgrading from MongoDB 2.4 to 2.6, these are no longer valid to store as such, and am now getting the error: The dollar ($) prefixed field '$or' ... is not valid for storage
Am thus in the situation in this Google Groups Discussion. The author there suggests flattening the document to a String. This situation can also occur if the subdocuments have legitimate Javascript attributes that have embedded dots (e.g. {"802.11g": ...})
That's easy enough to by specifying JSON.parse and JSON.stringify as the getter/setter in Mongoose.js:
var ProjectSchema = new Schema({
name: { type: String, required: false, default: "New project" },
spec: {type: mongoose.Schema.Types.Mixed, set: JSON.stringify, get: JSON.parse},
});
But the getter only gets called if I explicitly ask for the attribute value. The attribute is still a string underneath and gets passed as such:
Project.findById(req.params.projectId, function(err, project) {
console.log("......"+(typeof project.spec)) // project.spec is an object!
res.send(project); // project.spec is a String!
});
Obviously i can call model.spec = JSON.parse(model.spec) within each Model.find(...) call and for each flattened attribute but it'd be nice to do it at one central location.
https://groups.google.com/forum/?fromgroups=#!topic/mongoose-orm/8AV6aoJzdiQ

You can invoke your getter in res.send by adding the {toJSON: {getters: true}} option to the ProjectSchema definition. You'll probably want to enable that for the toObject option as well for cases like passing the doc to console.log.
var ProjectSchema = new Schema({
name: { type: String, required: false, default: "New project" },
spec: {type: mongoose.Schema.Types.Mixed, set: JSON.stringify, get: JSON.parse},
}, {
toJSON: {getters: true},
toObject: {getters: true}
});
Docs here.

Related

Monaco editor default json uri schema

I'm using monaco editor to edit JSON and I would like to set a custom diagnostic option.
I'm trying that https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-configure-json-defaults
// Configures two JSON schemas, with references.
var jsonCode = [
'{',
' "p1": "v3",',
' "p2": false',
"}"
].join('\n');
var modelUri = monaco.Uri.parse("a://b/foo.json"); // a made up unique URI for our model
var model = monaco.editor.createModel(jsonCode, "json", modelUri);
// configure the JSON language support with schemas and schema associations
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [{
uri: "http://myserver/foo-schema.json", // id of the first schema
fileMatch: [modelUri.toString()], // associate with our model
schema: {
type: "object",
properties: {
p1: {
enum: ["v1", "v2"]
},
p2: {
$ref: "http://myserver/bar-schema.json" // reference the second schema
}
}
}
}, {
uri: "http://myserver/bar-schema.json", // id of the second schema
schema: {
type: "object",
properties: {
q1: {
enum: ["x1", "x2"]
}
}
}
}]
});
monaco.editor.create(document.getElementById("container"), {
model: model
});
Where does uri: "http://myserver/foo-schema.json" come from ? I just want to use default JSON schema. Not my own.
Setting uri like this works :
uri: "http://localhost:4200/assets/monaco-editor/min/vs/language/json/jsonMode.js",
But is there a clean way to set this value ? Maybe uri value for JSON is available somewhere ? I searched through monaco.languages.json.jsonDefaults but I did not find anything.
"http://myserver/foo-schema.json" is an arbitrary value-- you can make it anything you want. It only matters if you are also using enableSchemaRequest-- in which case it should point to the location that you want the schema to be fetched from-- but you're not doing that, so that doesn't matter. In fact, everything related to this URI is irrelevant to what you are trying to do, if I'm understanding your intent correctly.
When you say "I just want to use default JSON Schema, Not my own", I think what you mean to say is that you just want to ensure that it is valid JSON, right? Because, there is no such thing as "default JSON Schema"-- by definition, it is defined by you-- but there is such a thing as a formal definition of what JSON is (JSON Schema, on the other hand, assumes that you are already starting with valid JSON, and allows you to then define a schema that your (valid) JSON must conform to).
Assuming you just want to ensure it is valid JSON (but you don't care that the json conform to some custom schema), setting the language to 'json' is all you need to do and your code can be as simple as:
var myBadJSONText = '{this is not : "JSON"}'
monaco.editor.create(document.getElementById('container'), {
language: 'json',
value: myBadJSONText
});
which running in the Monaco playground gives you:

deep access in plain objects with Immutable

Consider the following example:
const stickers =
new OrderedMap().set(1, {hero: "batman", name: "Bruce"});
stickers.getIn([1]); //=> { hero: 'batman', name: 'Bruce' }
stickers.getIn([1, "hero"]); //=> undefined
Why is the result of that second getIn undefined?
The docs on ImmutableJS state:
Plain JavaScript Object or Arrays may be nested within an Immutable.js Collection, and getIn() can access those values as well
Therefore, it follows that it makes no difference if value of the OrderedMap is a plain javascript object or an Immutable collection - however we can see that the bug goes away if we convert that plain object to an Immutable Collection first:
const stickers =
new OrderedMap().set(1, fromJS({hero: "batman", name: "Bruce"}));
stickers.getIn([1]); //=> { hero: 'batman', name: 'Bruce' }
stickers.getIn([1, "hero"]); //=> 'batman'
I believe this is because you're looking at the docs for a newer version of the library than you're using. I just tried out your code with version 4.0.0-rc.9 and it works as expected.
For example:
const stickers =
Immutable.OrderedMap().set(1, {
hero: "batman",
name: "Bruce"
});
console.log(stickers.getIn([1]));
console.log(stickers.getIn([1, "hero"])); //
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/4.0.0-rc.9/immutable.js"></script>

How to traverse this json object in Angular js?

I have this object received from the server:
Resource {$promise: Promise, $resolved: false, $get: function, $save: function, $query: function…}
$promise: Promise
$resolved: true__v:
_id: "54ed85a92908cc9c0cce7044"
about: "about"
amenities: Array[0]
length:
__proto__: Array[0]
created: "2015-02-25T08:19:53.790Z"
direction: ""
images: Array[2]
location: "lkj"
name: "resort3"
path: "uploads/projects/ASH_RESORT002"
profile_pic: "uploads/projects/ASH_RESORT002/images/2013-05-18-1452.jpg"
resort_id: "ASH_RESORT002"
room_count:
user: Object
__proto__: Resource
I am able to access the $promise and $resolved items in json using obj.$promise and $.resolved respectively. But I am unable to access the non dollar items like "images" by doing obj.images or obj.resort_id
BTW the same items are accesible in the view when used against ng-model. Something like ng-model="obj.resort_id" is successfully binding.
Please guide!
You aren't including the code that retrieves, or logs, this resource, so I'm taking a bit of a guess here. You may be running into a side-effect of console.log(). That function is asynchronous - it doesn't instantly log the "current" values of the object. However, object property access attempts ARE instant. It's possible your object looks 'resolved' when you console.log() it (because it IS, by the time that does its work) but accessing a property directly is looking at an object that is not yet resolved. You can confirm this by console.log()'ing the $resolved property.
The documentation for $resource contains the appropriate solution:
var User = $resource('/user/:userId', {userId:'#id'});
User.get({userId:123}).$promise.then(function(user) {
// access user.value here.
});
That is, you can't access User.* immediately after calling .get(). You need to "then" the promise and let it resolve.

Sending complex JSON with fetch, save, and delete on a model or collection

We have an internal API that was specifically built to be used with a new piece of software I'm building that runs on Backbone. The API has a single URL and takes JSON as input to determine what it needs to return. It essentially allows me to build custom queries with JSON that return exactly what I'm looking for.
Thing is this JSON can get pretty verbose and is often 3–4 levels deep, but sometimes may just be a few lines and just 1 level deep.
First question first: How do I send a string of JSON along with the ID when I do a fetch()? Do I have to set these parameters as the model or collection's defaults?
Here is an example of a really simple string to get a specific user's info
{
"which" : "object",
"object" : {
"type" : "customer",
"place" : "store",
"customerID" : "14"
}
}
As others have suggested it will likely be challenging to work with SOAP, but it shouldn't be impossible. Backbone models and collections communicate with the server through the sync operation; you should be able to customize that. I think something along these lines might get the ball rolling (for models):
Backbone.SoapyModel = Backbone.Model.extend({
sync: function(method, model, options) {
// force POST for all SOAP calls
method = 'create';
options = _.extend(options, {
// Setting the data property will send the model's state
// to the server. Add whatever complexity is needed here:
data: JSON.stringify({
"which" : "object",
"object" : model.toJSON()
}),
// Set the request's content type
contentType: 'application/json'
});
// Defer the rest to Backbone
return Backbone.sync.apply(this, [method, model, options]);
}
});
var SoapyModelImpl = Backbone.SoapyModel.extend({
url: '/test'
});
var soapTest = new SoapyModelImpl({
id: 42,
name: 'bob',
address: '12345 W Street Dr',
phone: '867 5304'
});
soapTest.fetch();

Extjs store reader fully qualified json property names

Using Extjs 3+ and server side is sending back the following JSON:
{"com.klistret.cmdb.ci.pojo.QueryResponse": {"com.klistret.cmdb.ci.pojo.successful":true,"com.klistret.cmdb.ci.pojo.count":1,"com.klistret.cmdb.ci.pojo.elements":{"com.klistret.cmdb.ci.pojo.id":123,"com.klistret.cmdb.ci.pojo.name":"Mars","com.klistret.cmdb.ci.pojo.fromTimeStamp":"2010-07-08T16:38:00.478+02:00","com.klistret.cmdb.ci.pojo.createTimeStamp":"2010-07-08T16:38:00.478+02:00","com.klistret.cmdb.ci.pojo.updateTimeStamp":"2010-10-25T15:02:09.446+02:00","com.klistret.cmdb.ci.pojo.type":{"com.klistret.cmdb.ci.pojo.id":1,"com.klistret.cmdb.ci.pojo.name":"{http:\/\/www.klistret.com\/cmdb\/ci\/element\/logical\/collection}Environment","com.klistret.cmdb.ci.pojo.fromTimeStamp":"2009-08-05T11:20:12.471+02:00","com.klistret.cmdb.ci.pojo.createTimeStamp":"2009-08-05T11:20:12.471+02:00","com.klistret.cmdb.ci.pojo.updateTimeStamp":"2009-08-05T11:20:12.471+02:00"},"com.klistret.cmdb.ci.pojo.configuration":{"#www.w3.org.2001.XMLSchema-instance.type":"com.klistret.cmdb.ci.element.logical.collection:Environment","#Watermark":"past","com.klistret.cmdb.ci.commons.Name":"Mars"}}}}
The reader is setup up as follows:
var reader = new CMDB.JsonReader(
{
totalProperty : 'com.klistret.cmdb.ci.pojo.count',
successProperty : 'com.klistret.cmdb.ci.pojo.successful',
idProperty : 'com.klistret.cmdb.ci.pojo.id',
root : 'com.klistret.cmdb.ci.pojo.elements'
},
[
{name: 'Id', mapping: 'com.klistret.cmdb.ci.pojo.id'},
{name: 'Name', mapping: 'com.klistret.cmdb.ci.pojo.name'}
]
);
The store as:
var ds = new Ext.data.Store({
proxy : new Ext.data.ScriptTagProxy({
url : 'http://sadbmatrix2:55167/CMDB/resteasy/element'
}),
reader : reader
});
The reader extends the Ext.data.JsonReader as explained by http://erichauser.net/2007/11/07/more-wcf-json-and-extjs/ to remove the "com.klistret.cmdb.ci.pojo.QueryResponse" start node in the JSON returned from the server.
The extended reader never gets called. Assuming the problem is due to has fully qualified property names in the JSON object returned (ie. "com.klistret.cmdb.ci.pojo.name" rather than just "name").
Anybody use gotten around this?
We worked it out (well, mostly Matthew did) in the comments:
ScriptTagProxy needs the server to wrap the JSON data in a function call so that your local code can get access to it.
Instead of the server emitting something like:
{here:'is data}
it needs to return
somefunc("{here:'is data'}");
That way, your client-side implementaiton of somefunc() is called and can process the returned data.