beatgammit
beatgammit

Reputation: 20205

Can I order CouchDB documents by common keys?

This is my first question here on stackoverflow, so I hope that I am following the correct protocol.

I am creating a basic browser game based on CouchDB and NodeJS. In my game, users can have multiple characters. I've decided to separate the players from their characters in the database because I rarely need to associate user account data (email, real name, etc) with their character data. Anyway, here's my problem:

I want to be able to create a view which returns all of the characters that are controlled by a player. There will be a property called "owner" in this document. I figure that the easiest way to do this is to make a view which returns a list of all of the characters categorized by owner.

Here's my code so far:

function(doc){ emit(doc.owner, doc); }

I'm trying to get a result like this (note, this is simplified; I know CouchDB includes other data in the output):

{
    "player1":{
        "character1":{
            "data":{}
        },
        "character2":{
            "data":{}
        }
    },
    "player2":{
        "character1":{
            "data":{}
        },
        "character2":{
            "data":{}
        }
    }
}

Do keys have to be unique? Will my code give me the desired result?

I'm not married to the idea of separating the data, and I could alternatively just put the characters under the user document and just create two views, one that would emit just the user account data and omit the character data and another to emit just the character data and omit the user data, but that seems a little awkward to me. I would rather keep the data separate because I may need to write other views specific to the character data, and it seems like it would be better organized if I separated the characters from the players in the database.

On a related note, is there any way to filter the responses by passing in a second parameter? I assume this negates the efficiency of a view and that I should probably use a temporary view in this instance. My problem with this is that I could potentially have a large amount of data being returned by this view (the entire database in fact), which could be very slow when transferred over HTTP, especially since I won't need most of the data.

Another possible solution is to store the _id of each character a user controls as a property in the user account database. This seems sensible enough to me, but if there is a different, or dare I say "better" option available, then I'd love to hear it.

What would be the best solution to this problem?

Thank you so much in advance for your help.

Upvotes: 1

Views: 306

Answers (1)

Victor Nicollet
Victor Nicollet

Reputation: 24577

You're on the right path. If you wish to do this with a Reduce, you can define this Map:

function(doc) { 
  var r = {}, c = {}; 
  c[doc._id] = doc;
  r[doc.owner] = c; 
  emit(doc.owner,r); 
}

And this Reduce:

function(k,v,red) {
  var r = {};
  for (var i in v) {
    for (var owner in v[i]) {
      if (!(owner in r)) r[owner] = {};
      for (var character in v[i][owner])
        r[owner][character] = v[i][owner][character];
    }
  }
  return r;
}

This should return directly what you asked for, but is hardly optimal: the view has to store a lot of data for both the Map and Reduce parts...

Instead, I would go for a Map without Reduce, like this:

function(doc) {
  emit(doc.owner,null);
}

This map, when queried with include_docs=true, would return lines that look like this:

[  
  { id : 'charid', key : 'playid', doc : { /* character document */ } },
  ...
]

The server load is lighter, and you can still regroup the characters by player by using the key in the result, if you really need to. The results are sorted by key, then by id.

You can query this view using startkey and endkey both equal to the player identifier to get all the characters a player has. If you only need the character identifiers, then add include_docs=false and the heavy doc part will not be sent.

Upvotes: 4

Related Questions