julien_c
julien_c

Reputation: 5102

How to make Backbone's and MongoDB's ids work seamlessly?

My Backbone app is communicating with a REST API built on top of MongoDB, so my objects' "natural" ids are really MongoIDs. When serialized into JSON, they look like:

"_id":{"$id":"505099e998dee4db11000001"}

The Backbone documentation mentions that you can specify another name than id for your Backbone model's id attribute (using idAttribute), however, as MongoIDs' string representations are nested, just using idAttribute: '_id' doesn't help in consuming JSON directly.

Is there a way around this, other than rewriting attributes on the server side?

Update:

Here's what I'm doing server-side:

$m = new Mongo();
$posts = $m->mydatabase->posts->find();

$out = array();

foreach ($posts as $post) {
    $out[] = $post;
}

echo json_encode($out);

I know I could do something like $post['id'] = (string) $post['_id']; unset($post['_id']); but it is precisely what I'm looking to avoid!

Upvotes: 12

Views: 3030

Answers (5)

jordanb
jordanb

Reputation: 2055

Your PHP code is the REST API that Backbone will interact with, so it should handle the abstraction of Mongo's complex _id property to the simple id string you're looking for. That is definitely the API's job.

Backbone, or any other consumer of your API, should NOT know anything about Mongo or _id.

Why? Because if you were to switch from Mongo to some other Db where there is no _id, then your API is still good because all that was ever exposed was id, which is super generic. So you're original idea was actually the best idea (although you'd want to abstract that piece of code into something that can be reused for all models, not just posts).

Upvotes: 0

MICHAEL PERMANA
MICHAEL PERMANA

Reputation: 71

It's better to do the conversion in the browser so you move the computation off from the server. So you'd want to create a base MongoModel, and then you can extend from MongoModel instead of Backbone.Model.

MongoModel = Backbone.Model.extend({
    // this handle conversion between the mongo's _id and backbone id
    // this is best done on the client side to alleviate server load
    //_id : { "$oid" : ... } <--> id
    parse : function (response) {
        response.id = response._id.$oid;
        delete response._id;
        return response;
    },
    toJSON : function () {
        var
            json = Backbone.Model.prototype.toJSON.apply(this);
        json._id = {'$oid':json.id};
        delete json.id;
        return json;
    }
});

Upvotes: 5

Jason Prawn
Jason Prawn

Reputation: 1053

Looking at various suggested options I found the easiest would be to do what you want to avoid. (Using parse does work though)

$m = new Mongo();
$posts = $m->mydatabase->posts->find();

$out = array();

foreach ($posts as $post) {
    // populate an id field
    $post['id']=$post['_id']->{'$id'},
    $out[] = $post;
}

echo json_encode($out);

Keeping $post['_id'] if you want your model to send it back when syncing. I prefer correcting the API as more clients might want to connect to it.

Upvotes: 3

Dmitry
Dmitry

Reputation: 2138

In Ruby I'm able to do the following:

items.find.to_a.map do |item|
  frombsonid(item)
end.to_json

def frombsonid(obj) obj.merge({'_id' => obj['_id'].to_s}) end

and when you get the data back, you can convert the string to BSON::ObjectId

def tobsonid(id) BSON::ObjectId.fromstring(id) end

So the idea is to replace _id in each item with a string version of BSON::ObjectId

In backbone you can add

idAttribute: "_id", 

In PHP you will use different syntax and methods, but I believe it's quite possible to replicate this.

Upvotes: 0

jmk2142
jmk2142

Reputation: 8581

This sounds like a good example for parse().

Since your Mongo JSON ends up sending:

"_id":{"$id":"505099e998dee4db11000001"}

Why not use the Backbone parse to properly namespace the incoming data?

parse: function(response) {
    response.id = response._id['$id'];
    delete response._id;

    return response;
}

Or something like this. Similarly, since Backbone will be sending the id as JSON with 'id', your server might take that and "translate" it the other way.

If you want to use a different id attribute, _id, you would just replace the above parse with this code:

idAttribute: '_id',

parse: function(response) {
    response._id = response._id['$id'];

    return response;
}

Whatever works best for you. :-)

Upvotes: 15

Related Questions