让我们从一个列表开始。您可以使用单个哈希图:
- 在行中存储
0用户的订单计数
- 为每个新订单存储一个新行并增加计数
所以 yoru hashmap 如下所示:
key | value
-------------
0 | 5
1 | tomato
2 | celery
3 | apple
4 | pie
5 | meat
密钥的稳定递增确保每个密钥都是唯一的。鉴于 db 是键排序的,并且 pack 函数将整数转换为一组正确排序的字节数组,您可以获取列表的切片。要获取 5000 到 5050 之间的订单,您可以使用 bsddb Cursor.set_range 或 leveldb 的 createReadStream (js api)
现在让我们扩展到多个用户订单。如果你可以打开几个hashmap你可以使用上面使用的几个hashmap。也许您会遇到一些系统问题(打开 fds 的最大 nb 或每个目录的最大文件数)。因此,您可以使用单个并为多个用户共享相同的 hashmap。
我在下面解释的内容适用于 leveldb 和 bsddb,因为您 pack 正确使用字典顺序(字节顺序)键。所以我假设你有一个pack 函数。在 bsddb 中,您必须自己构建一个 pack 函数。看看wiredtiger.packing 或bytekey 以获得灵感。
原理是使用用户的 id 来命名键。它也称为键组合。
假设您的数据库如下所示:
key | value
-------------------
1 | 0 | 2 <--- count column for user 1
1 | 1 | tomato
1 | 2 | orange
... ...
32 | 0 | 1 <--- count column for user 32
32 | 1 | banna
... | ...
您使用以下(伪)代码创建此数据库:
db.put(pack(1, make_uid(1)), 'tomato')
db.put(pack(1, make_uid(1)), 'orange')
...
db.put(pack(32, make_uid(32)), 'bannana')
make_uid 的实现如下所示:
def make_uid(user_uid):
# retrieve the current count
counter_key = pack(user_uid, 0)
value = db.get(counter_key)
value += 1 # increment
# save new count
db.put(counter_key, value)
return value
然后您必须进行正确的范围查找,它类似于单个复合键。使用 bsddb api cursor.set_range(key) 我们检索所有项目
对于用户42,在5000 和5050 之间:
def user_orders_slice(user_id, start, end):
key, value = cursor.set_range(pack(user_id, start))
while True:
user_id, order_id = unpack(key)
if order_id > end:
break
else:
# the value is probably packed somehow...
yield value
key, value = cursor.next()
未进行错误检查。除此之外,如果您从列表中删除项目,则切片 user_orders_slice(42, 5000, 5050) 不能保证撕掉 51 个项目。查询 50 项目的正确方法是实现 user_orders_query(user_id, start, limit)`。
我希望你能明白。