MongoDB: Map/Units Advise for Model Design in a Browser Game
I started studied MongoDB about 2~3 days ago, and I am feeling pretty happy about it :) However.. I do have some questions.. I already did some research, and they did not clarify much.. I am developing a browser game, and all databases were in MySQL, but I decided do migrate to MongoDB. But, well, to the questions.
1) In MySQL, I used to have a map table which had the fields *map_ndx*, column, row, terrain (terrain was a foreign reference to a terrain table). Basically, each *map_ndx* indicates a different map, and row and column represent their coordinates; terrain indicates like 'woods', 'desert', categorized on the other table. Now, on MongoDB, I have a map collection which has a field *map_ndx*, and embeds several tiles. Each tile have the fields col and row, and embeds a terrain. First of all, do you think this design is efficient for a map? I will do a lot of queries searching for "tile (x,y) on map z", for instance. Second, where do I place my indexes (such as row and column)? Do I place them in the tile collection, or in the map collection (using "index 'tile.row'", for example)? I ask this because on MySQL when I queried for "select * from maps where map_ndx=1 and row=1", I would have an answer instantly. On MongoDB, if I ask for "Map.where(:map_ndx => 1).first.tiles.where(:row=>1)" it takes almost a second to complete (obs.: that syntax there is for RoR, I am operating with mongoid)
2) In MySQL, I used to have a *global_开发者_如何学Pythonunits* table, which would have the description of several units, their attacks, defenses, costs, etc. And each player would have a *user_units*, which referenced that *global_units*, and informed the quantity of units the players had for this referenced *global_unit*. Now, on MongoDB, I considered embedding the *user_units* in the user collection, which seems the ideal. However, should I keep *user_units* only referencing *global_units*, or it would be best if *global_unit* was also embedded in each player's *user_units* collection? The problem is that I would have to access the units from *global_units* on other places, such as when the player is going to build them. Therefore, even If I had them embedded on the *user_units* collection, I would also need an exclusive non-embedded *global_units* collection, so I can access their information on other places. What is the best way out here?
I know these are very specific questions, but I would be glad if someone clarified those to me! Thanks in advance, Fernando.
The question you're asking here is the complex "embed vs. reference" question. And the first problem is that there is no single correct answer here, it's a series of trade-offs. Sometimes it's better to reference, sometimes it's better to embed.
There's an answer here that details one example.
One of the big fundamental questions here revolves around "top-level" objects. Here's my personal rule of thumb:
- If an object is only useful in context of its parent, then you typically embed.
- If an object is useful without its parent, then you typically reference.
Question #2, I think you've found your answers here:
user_units
is only useful in context of theuser
. If you're going to load theuser
, why not load theunits
at the same time.global_units
is useful without auser_unit
context, so you probably want to reference. Sure you have a "global lookup" of units, but how many do you have? A few hundred? A few thousand? Do they all fit in memory?
So a user
would have something like
{ _id: 'gates',
units: { footman: 10, cannon: 3, horse: 5 }
}
That seems fine, if a user needs a new footman, is it really a hassle to look up the stats? Of course, if you really want to customize, you can store stats on my footmen if they're different.
Question #1: is a little dicier.
How exactly do maps
relate to tiles
? Does a single map
contain many tiles
? Are you comfortable building a hash table of tiles
in a single map
?
Here's what I'm seeing:
{ _id: 'map1',
name: 'jungle',
height: 20,
width: 20
tiles: { "0_0": { type: 'jungle' },
"0_1": { type: 'jungle' },
...
"19_19": { type: 'beach' }
}
}
Notice how I'm not building an array of tiles, but an actual hashtable. I'm stuffing all tiles into one map and loading the whole thing into memory. Of course, this assumes that you want all tiles on a map.
I know your query looks at just a single row, but now it's a question of how often do you need a single row? Don't you generally need a square-like block of tiles?
精彩评论