i'm writing a backbone.js 'app', and would like to map the json output of graphite/carbon directly to some backbone models/collections.
in general, the json output is something like this:
[
{
'target': 'some.string.1',
'datapoints': [ [ val1, timestamp1 ], [ val2, timestamp2 ]... ]
},
{
'target': 'some.string.2',
'datapoints': [ [ val1, timestamp1 ], [ val2, timestamp2 ]... ]
},
...
]
i have defined a simple model and collection such that:
class Measurement extends Backbone.Model
defaults:
id: undefined
val: undefined
class Measurements extends Backbone.Collection
model: Measurement
initialize: (model, options) ->
if options.metric
#metric = options.metric
url: ->
'/?target=' + #metric
parse: (data,xhr) ->
if _.size(data) == 1
return _(data[0]['datapoints']).map( (d) ->
m = {}
m['id'] = new Date(0)
m['id'].setUTCSeconds( d[1] )
m['val'] = d[0]
m
)
undefined
as you can see, i overload the id to be the timestamp of each Measurement and all measurements for a specific 'metric' is stored under a Collection called Measurements.
i've also hardcoded it so it really only works for one Measurements Collection (ie the 'target' in the json).
my question concerns how best/elegant/flexible to implement the gathering of multiple Measurements (the collection) in a single call. ie graphite supports the use of wildcards for its 'targets' so that an ajax request to /?target=some.string.* would bring back all matching targets and datapoints (like in the json example). i would then present this to a View where i would render say cumulative data or plot all of the Measurements against time.
i was thinking of using another Collection (lets call it a Set) that would contain many Measurements. I would like to be able to do something like Set.fetch() to get all the matching Measurements from the server and have the Set create many Measurements Collections from the single ajax request.
does anyone have any suggestions of how to implement this? or even a better way of representing this model/collection layer?
whilst not directly answering your question - have you looked at graphene? - it's a javascript dashboard for graphite and it's using backbone.js. You might see how it was implemented there and borrow/steal some ideas.
As a side note - from my experience building a different beast (giraffe, sorry for the plug, but it's important to give context), there are some difficulties matching wildcard targets with graphite. The main reason is that you don't know how many targets are returned. This can be tricky when, for example, you want one of those targets to have a red colour on the graph, or want to add a target like an annotator (to draw as infinite to mark a time-based event). Just something to bear in mind when working with wildcards and multi-target requests with graphite.
Related
I am trying to create a backbone client side application. I am receiving a list of json objects from the server on startup that will be a list of the possible tables exposed from the server, with their structure. E.g. Customers, Orders, Invoices, Employees
I want to create the models, collections and views dynamically based on the data I receive from the server.
Only when I receive the json on load will I know what the models should be and what the relationships between the models should be.
E.g. Customers structure might be Id, CustomerName, Address, Contact Numbers.
Order Structure might be Id, CustomerId, OrderDate, Amount
etc
By building Models, collections, views, controllers dynamically, I could in theory on startup point at another server who might give me a totally different set of tables e.g. : Movies, Actors etc.. with their structures.
Also, if additional fields are added I don't have to change the client side code again. E.g. Customer table might include a new field called ContactPerson
Please assist me as all the examples I saw on backbone is all based on statically defining the models on the client side up front. So create a model and collections and views for Customers, Orders, Invoices, Employees etc. etc.
Best wishes,
Andy
As already mentioned in the comments, Backbone models are dynamic by nature. So this is perfectly valid for example:
// A example dataset, this could be returned as JSON from the server
var jsonDataA = [
{
name: "Foo",
title: "Bar"
},
{
name: "a",
title: "b"
}
],
// A different example dataset
jsonDataB = [
{
make: "X",
model: "Y"
},
{
make: "Z",
model: "ZZ"
}
],
MyModel = Backbone.Model.extend({
/* Empty Model definition */
}),
MyCollection = Backbone.Collection.extend({
model: MyModel
}),
collection = new MyCollection();
collection.reset(jsonDataA);
console.log(collection.models);
collection.reset(jsonDataB);
console.log(collections.models);
Here I have reused the same Collection and Model definition to store completely different datasets.
One part is the raw data, the other part is its relations. You need to transport the metadata also, which contains the types and their relations. Model attributes will be populated automatically.
From your metadata a simple object can be constructed, where the keys describe one entity, for example:
var entites = {};
entities["Customer"] = Backbone.Model.extend({
/* Model definition based on metadata */
});
var parametersFromServer = {name: "John Doe"};
var customer = new entities["Customer"](parametersFromServer);
For building relations I would recommend using BackboneRelational plugin.
Suppose I'm working with an API which returns JSON data, but which has a complex or variable structure. For example, a string-valued property may be a plain literal, or may be tagged with a language:
/* first pattern */
{ "id": 1,
"label": "a foo"
}
/* second pattern */
{ "id": 2,
"label": [ {"value": "a foo", "lang": "en"},
{"value": "un foo", "lang": "fr"}]
}
In my client-side code, I don't want to have view code worrying about whether a label is available in multiple-languages, and which one to pick, etc. Or I might want to hide the detailed JSON structure for other reasons. So, I might wrap the JSON value in an object with a suitable API:
/** Value object for foo instances sent from server */
var Foo = function( json ) {
this.json = json;
};
/** Return a suitable label for this foo object */
Foo.prototype.label = function() {
var i18n = ... ;
if (i18n.prefLang && _.isArray(this.json.label)) // ... etc etc
};
So this is all pretty normal value-object pattern, and it's helpful because it's more decoupled from the specific JSON structure, more testable, etc. OK good.
What I currently don't see a way around is how to use one of these value objects with Backbone and Marionette. Specifically, I'd like to use a Foo object as the basis for a Backbone Model, and bind it to a Marionette ItemView. However, as far as I can see, the values in a Model are taken directly from the JSON structure - I can't see a way to recognise that the objects are functions:
var modelFoo = new Backbone.Model( foo );
> undefined
modelFoo.get( "label" ).constructor
> function Function() { [native code] }
So my question is: what is a good way to decouple the attributes of a Backbone Model from the specifics of a given JSON structure, such as a complex API value? Can value objects, models and views be made to play nice?
Edit
Let me add one more example, as I think the example above focussing on i18n issues only conveys part of my concern. Simplifying somewhat, in my domain, I have waterbodies comprising rivers, lakes and inter-tidal zones. A waterbody has associated with it one or more sampling points, and each sampling point has a latest sample. This might come back from the data API on the server as something like:
{"id": "GB12345678",
"centre": {"lat": 1.2345, "long": "-2.3456"},
"type": "river",
"samplingPoints": [{"id": "sp98765",
"latestSample": {"date": "20130807",
"classification": "normal"}
}]
}
So in my view code, I could write expressions such as:
<%= waterbody.samplingPoints[0].latestSample.classification %>
or
<% if (waterbody.type === "river") { %>
but that would be horrible, and easily broken if the API format changes. Slightly better, I could abstract such manipulations out into template helper functions, but they are still hard to write tests for. What I'd like to do is have a value object class Waterbody, so that my view code can have something like:
<%= waterbody.latestClassification() %>
One of the main problems I'm finding with Marionette is the insistence on calling toJSON() on the models passed to views, but perhaps some of the computed property suggestions have a way of getting around that.
The cleanest solution IMO is to put the label accessor into the model instead of the VO:
var FooModel = Backbone.Model.extend({
getLabel : function(){
return this.getLocalized("label");
},
getLocalized : function(key){
//return correct value from "label" array
}
});
and let the views use FooModel#getLabel instead of FooModel#get("label")
--EDIT 1
This lib seems interesting for your use case as well: Backbone.Schema
It allows you to formally declare the type of your model's attributes, but also provides some syntax sugar for localized strings and allows you to create dynamic attributes (called 'computed properties'), composed from the values of other attributes.
--EDIT 2 (in response to the edited question)
IMO the VO returned from the server should be wrapped inside a model and this model is passed to the view. The model implements latestClassification, not the VO, this allows the view to directly call that method on the model.
A simple approach to this (possibly to simple for your implementation) would be to override the model's parse method to return suitable attributes:
var modelFoo = Backbone.Model.extend({
parse: function ( json ) {
var i18n = ... ;
if (i18n.prefLang && _.isArray(json.label)) {
// json.label = "complex structure"
}
return json;
}
});
That way only your model worries about how the data from the server is formatted without adding another layer of abstraction.
I want to grab the length of a collection to use in a random number generator. I want a view that shows one model of the collection, randomly generated. I'm using coffeescript, btw
So far I've tried stuff like
#collection.fetch
data:
id: Math.floor((Math.random()*#length)+1)
which won't work because the length isn't there until after it's fetched... I've tried a couple of other methods, such as grabbing after fetching, but length is always zero.
Anyone give me an idea of how to do this?
edit: javascript for those who can't read coffee
this.collection.fetch({
data: {
'id': Math.floor((Math.random() * length) + 1)
}
});
I had same task in the past. I used underscore _.sample method.
Please try _.sample(collection) it will return random model from collection or even better _.sample(collection, 4) for 4 random models.
According to the Backbone manual :
Backbone.Collection
Collections are ordered sets of models
So what you need in your application is actually a random model from your server database. According to your API, you need to get the count of your records in your server and then get a random model of one of the records. If you are the developer of your Serverside API there is a way to do that with one connection, otherwise you can do something like this :
class randomModel extends Backbone.Model
// Assuming 'GET' /api/model/100 will get record No. 100
urlRoot: '/api/model'
// ... in your document ready
$ () ->
model = null
// Assuming 'GET' /api/count, will return JSON string with your records count
$.getJSON '/api/count', (response) =>
model = new randomModel id: (Math.random()*response.count)+1
model.fetch()
console.log model
Pretty much that's what I would use in your case. Another method is to populate the whole collection and get the random model after it is populated ( you save one request ), by doing :
collection.fetch() // get's all models
collection.get (Math.random()*collection.length)+1
I am trying to set up cakephp to work with the very nice javascriptMVC (http://forum.javascriptmvc.com). JavaScriptMVC requires the JSON-Output in the following format:
[{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20'
},
{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10'
}]
Cake would generate a deeper nested array with a prepended Class Name. I found attempts to solve the problem but theyre not for cakephp 2.x. I know that I can simply generate a new array and json_encode() it via php, but it would be nicer to include a function like this https://gist.github.com/1874366 and another one to deflatten it.
Where would be the best place to put such functions? The AppController doesnt seem to work. Should i put it in beforeRender () or beforeFilter() of the controller? Or does someone maybe even know of an existing solution/plugin for this? This would be the best for me in my current Situation, as Im pretty much pressed for time.
Ok, I'm not 100% sure I understand what you are trying to do so here's a word to the wise just in case: Cake and JMVC are both comprehensive MVC frameworks. if you are attempting to combine them as a single cohesive platform to build your application, I strongly suggest you review your approach / platform / etc.
Also -- I'm not an expert by any means in jmvc, so I'm just going to pretend that processing the response from Cake in jmvc is completely out of the question, for some odd reason. For the record, think of Cake's responses like this:
{ "Model" :
[{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20'
},
{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10'
}]
}
Cake has had comprehensive REST service support, since at least Cake 1.2. The lib you are interested in is HttpSocket. As for json encoding and serving response, Request Handling covers, among other things, responding to all manners of requests, content types, decoding and encoding json, etc. Finally, the built-in Set utility will almost certainly cover whatever array manipulation you need in a line or two.
The functionality you are interested in is pretty basic and hasn't changed too much. I'd bet a lot of the (reasonably simple) solutions you have already found would probably still work, maybe with a little bit of tweaking.
For pretty much any basic service endpoint, you would probably create a controller (not AppController - that is application-wide, hence you can't invoke it directly) method, considering Cake routes the controller/action into your url:
Cake consuming services from a different app would look like this:
http://cakeproject/collect/getInfo
class CollectController extends AppController {
public function getInfo($array = null) {
App::uses('HttpSocket', 'Network/Http');
$http = new HttpSocket();
$http->get('http://jmvcproject/controller/action', $array);
// ...etc.
}
Cake providing services from the same controller / action to a different app would simply be:
public function getInfo($array = null) {
$results = $this->Collect->find('all', $array);
// ...fetch the results
}
Or you could just loop over that array with foreach($this->data as $data) { ... to drop the class name. But if your data will include associated models, etc, Set is probably the most versatile and resilient solution.
Anyway, HTH
Let's say I have the following document schema in a collection called 'users':
{
name: 'John',
items: [ {}, {}, {}, ... ]
}
The 'items' array contains objects in the following format:
{
item_id: "1234",
name: "some item"
}
Each user can have multiple items embedded in the 'items' array.
Now, I want to be able to fetch an item by an item_id for a given user.
For example, I want to get the item with id "1234" that belong to the user with name "John".
Can I do this with mongoDB? I'd like to utilize its powerful array indexing, but I'm not sure if you can run queries on embedded arrays and return objects from the array instead of the document that contains it.
I know I can fetch users that have a certain item using {users.items.item_id: "1234"}. But I want to fetch the actual item from the array, not the user.
Alternatively, is there maybe a better way to organize this data so that I can easily get what I want? I'm still fairly new to mongodb.
Thanks for any help or advice you can provide.
The question is old, but the response has changed since the time. With MongoDB >= 2.2, you can do :
db.users.find( { name: "John"}, { items: { $elemMatch: { item_id: "1234" } } })
You will have :
{
name: "John",
items:
[
{
item_id: "1234",
name: "some item"
}
]
}
See Documentation of $elemMatch
There are a couple of things to note about this:
1) I find that the hardest thing for folks learning MongoDB is UN-learning the relational thinking that they're used to. Your data model looks to be the right one.
2) Normally, what you do with MongoDB is return the entire document into the client program, and then search for the portion of the document that you want on the client side using your client programming language.
In your example, you'd fetch the entire 'user' document and then iterate through the 'items[]' array on the client side.
3) If you want to return just the 'items[]' array, you can do so by using the 'Field Selection' syntax. See http://www.mongodb.org/display/DOCS/Querying#Querying-FieldSelection for details. Unfortunately, it will return the entire 'items[]' array, and not just one element of the array.
4) There is an existing Jira ticket to add this functionality: it is https://jira.mongodb.org/browse/SERVER-828 SERVER-828. It looks like it's been added to the latest 2.1 (development) branch: that means it will be available for production use when release 2.2 ships.
If this is an embedded array, then you can't retrieve its elements directly. The retrieved document will have form of a user (root document), although not all fields may be filled (depending on your query).
If you want to retrieve just that element, then you have to store it as a separate document in a separate collection. It will have one additional field, user_id (can be part of _id). Then it's trivial to do what you want.
A sample document might look like this:
{
_id: {user_id: ObjectId, item_id: "1234"},
name: "some item"
}
Note that this structure ensures uniqueness of item_id per user (I'm not sure you want this or not).