将数据库作为多级关联数组寻址

时间:2014-05-30 20:42:59

标签: database associative-array

我正在编写一个网页游戏,将完整的游戏状态视为单个任意级别的哈希非常方便。

游戏状态更新的几个例子:

// Defining a mission card that could be issued to a player
$game['consumables']['missions']['id04536']['name'] = "Build a Galleon for Blackbeard";
$game['consumables']['missions']['id04536']['requirements']['lumber'] = 20;
$game['consumables']['missions']['id04536']['requirements']['iron'] = 10;
$game['consumables']['missions']['id04536']['rewards']['blackbeard_standing'] = 5;

// When a player turns in this mission card with its requirements
$game['players']['id3214']['blackbeard_standing'] += 5;

这是一款网页游戏,因此将信息存储在数据库中是有道理的。我需要数据库的功能:可以同时从多个浏览器访问游戏状态,它是非易失性的,易于备份等。

基本上,我希望语法像从任意深度的关联数组中读取/写入一样简单。我需要处理关联数组的所有功能:不仅仅是简单的读取和写入,而是能够针对它运行foreach循环,等等。我需要的效果实际上是从数据库执行所有读/写操作,而不是从易失性存储器执行。

我个人非常喜欢原始的Ruby,但是如果有一个特定的语言或框架给我这个功能,它将使这个项目的其余部分足够容易使用。

2 个答案:

答案 0 :(得分:1)

任何语言,框架? python + sqlalchemy + postgresql

怎么样?
  • python因为您可以轻松创建适用于全世界的新类型,例如常规dict
  • postgres因为它有两个特别有趣的类型在其他sql数据库中不常见,我们马上就会谈到它。
  • sqlalchemy因为它可以简单地完成处理rdbms的所有肮脏工作。

使用像这样的sql数据库很尴尬,因为在表中你想要的普通'key'必须是一组固定的列,所以理想情况下,你需要一个整列“path”的列。深层嵌套的映射。

更令人恼火的问题是你似乎想在树叶上存放一系列不同类型的东西。这不太理想。

幸运的是,postgres可以帮助我们解决这两个问题,第一个使用TEXT[],我们可以有一个列,这样每个条目都可以简洁地表示整个路径,一直到树下。对于第二种,我们可以使用JSON,这听起来很像,允许任意json可编码类型,这显然包括代码示例中的字符串和数字。

因为我很懒,所以我会使用sqlalchemy来完成大多数的工作。首先,我们需要一个使用上述类型的表:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects import postgres as pg

Base = declarative_base()

class AssocStorage(Base):
    __tablename__ = 'assoc_storage'
    key = Column(pg.ARRAY(pg.TEXT, as_tuple=True), primary_key=True)
    value = Column(pg.JSON)

这将为我们提供深度嵌套映射中单个条目的关系版本。我们已经完成了大部分工作:

>>> engine = create_engine('postgres:///database')
>>> Base.metadata.create_all(engine)  ## beware, extreem lazyness
>>> session = Session(bind=engine)    ## also unusually lazy, real applications should use `sessionmaker`
>>> session.add(AssocStorage(key=('foo','bar'), value=5))
>>> session.commit()
>>> x = session.query(AssocStorage).get((('foo', 'bar'),))
>>> x.key
(u'foo', u'bar')
>>> x.value
5

好吧,不是太糟糕,但是使用起来有点烦人,就像我之前提到的那样,python类型系统足够兼容,使它看起来像普通的dict,我们只需要提供一个实现正确的类的类协议:

import collections
class PersistentDictView(collections.MutableMapping):
    # a "production grade" version should actually implement these:
    __delitem__ = __iter__ = __len__ = NotImplemented

    def __init__(self, session):
        self.session = session

    def __getitem__(self, key):
        return self.session.query(AssocStorage).get((key, )).value

    def __setitem__(self, key, value):
        existing_item = self.session.query(AssocStorage).get((key, ))
        if existing_item is None:
            existing_item = AssocStorage(key=key)
            self.session.add(existing_item)
        existing_item.value = value

这与您发布的代码略有不同,您需要x[a][b][c],这需要x[a, b, c]

>>> d = PersistentDictView(session)
>>> d['foo', 'bar', 'baz'] = 5
>>> d['foo', 'bar', 'baz']
5
>>> d['foo', 'bar', 'baz'] += 5
>>> session.commit()
>>> d['foo', 'bar', 'baz']
10

如果你真的需要你的嵌套由于某种原因,你可以只用更多的工作来获得这种行为,但这需要更多的努力。此外,这完全支持事务管理,请注意上面的显式session.commit()

答案 1 :(得分:0)

MongoDB(+它的Ruby gem)是我找到的最佳答案。这样易于安装和学习。

它让我90%的方式将整个游戏状态视为多级哈希。足够近。我可以将集合视为第一个哈希级别,并使用它来存储&检索多级哈希。