Python:支持索引的内存对象数据库?

Dav*_*ver 26 python database data-munging

我正在做一些数据修改,如果我可以在内存数据库中粘贴一堆字典,然后对它进行简单的查询,那将会更加简单.

例如,类似于:

people = db([
    {"name": "Joe", "age": 16},
    {"name": "Jane", "favourite_color": "red"},
])
over_16 = db.filter(age__gt=16)
with_favorite_colors = db.filter(favorite_color__exists=True)
Run Code Online (Sandbox Code Playgroud)

但有三个混淆因素:

  • 一些值将是Python对象,并且序列化它们是不可能的(太慢,破坏身份).当然,我可以解决这个问题(例如,将所有项目存储在一个大的列表中,然后在列表中序列化它们的索引......但这可能需要花费一些时间).
  • 将有数千个数据,我将针对它们运行查找繁重的操作(如图遍历),因此必须能够执行高效(即索引)查询.
  • 如在示例中,数据是非结构化的,因此要求我预定义模式的系统将是棘手的.

那么,这样的事情存在吗?或者我需要一起解决问题吗?

got*_*nes 10

如何通过sqlite3标准库模块使用内存中的SQLite数据库,使用特殊:memory:的连接值?如果您不想编写on SQL语句,则可以始终使用ORM(如SQLAlchemy)来访问内存中的SQLite数据库.

编辑:我注意到你说过这些值可能是Python对象,还需要避免序列化.要求将任意Python对象存储在数据库中也需要序列化.

如果您必须遵守这两项要求,我可以提出切实可行的解决方案吗 为什么不直接使用Python词典作为Python词典集合的索引?听起来你需要建立每个指数的特殊需求; 找出你要查询的值,然后编写一个函数来为每个值生成和索引.您的dicts列表中一个键的可能值将是索引的键; 索引的值将是字典列表.通过给出您要查找的值作为键来查询索引.

import collections
import itertools

def make_indices(dicts):
    color_index = collections.defaultdict(list)
    age_index = collections.defaultdict(list)
    for d in dicts:
        if 'favorite_color' in d:
            color_index[d['favorite_color']].append(d)
        if 'age' in d:
            age_index[d['age']].append(d)
    return color_index, age_index


def make_data_dicts():
    ...


data_dicts = make_data_dicts()
color_index, age_index = make_indices(data_dicts)
# Query for those with a favorite color is simply values
with_color_dicts = list(
        itertools.chain.from_iterable(color_index.values()))
# Query for people over 16
over_16 = list(
        itertools.chain.from_iterable(
            v for k, v in age_index.items() if age > 16)
)
Run Code Online (Sandbox Code Playgroud)


And*_*ark 5

如果内存数据库解决方案最终工作太多,这里有一个自己过滤的方法,你可能觉得它很有用.

get_filter函数接受参数来定义您希望如何过滤字典,并返回一个可以传递给内置filter函数的函数来过滤字典列表.

import operator

def get_filter(key, op=None, comp=None, inverse=False):
    # This will invert the boolean returned by the function 'op' if 'inverse == True'
    result = lambda x: not x if inverse else x
    if op is None:
        # Without any function, just see if the key is in the dictionary
        return lambda d: result(key in d)

    if comp is None:
        # If 'comp' is None, assume the function takes one argument
        return lambda d: result(op(d[key])) if key in d else False

    # Use 'comp' as the second argument to the function provided
    return lambda d: result(op(d[key], comp)) if key in d else False

people = [{'age': 16, 'name': 'Joe'}, {'name': 'Jane', 'favourite_color': 'red'}]

print filter(get_filter("age", operator.gt, 15), people)
# [{'age': 16, 'name': 'Joe'}]
print filter(get_filter("name", operator.eq, "Jane"), people)
# [{'name': 'Jane', 'favourite_color': 'red'}]
print filter(get_filter("favourite_color", inverse=True), people)
# [{'age': 16, 'name': 'Joe'}]
Run Code Online (Sandbox Code Playgroud)

这很容易扩展到更复杂的过滤,例如根据值是否与正则表达式匹配进行过滤:

p = re.compile("[aeiou]{2}") # matches two lowercase vowels in a row
print filter(get_filter("name", p.search), people)
# [{'age': 16, 'name': 'Joe'}]
Run Code Online (Sandbox Code Playgroud)


AFo*_*lia 5

我所知道的唯一解决方案是我几年前在PyPI,PyDbLite上偶然发现的一个软件包.没关系,但问题很少:

  1. 它仍然希望将所有内容序列化为磁盘,作为pickle文件.但这对我来说很简单.(这也是不必要的.如果插入的对象是可序列化的,那么整个集合也是如此.)
  2. 基本记录类型是一个字典,在其中它插入它自己的元数据,根据密钥两个整数__id____version__.
  3. 索引非常简单,仅基于记录字典的值.如果你想要更复杂的东西,比如基于记录中对象的属性,你必须自己编写代码.(我自己打算做的事情,但从来没有去过.)

作者似乎偶尔也在研究它.我使用它时有一些新功能,包括一些很好的复杂查询语法.

假设你剔除了酸洗(我可以告诉你我做了什么),你的例子就是(未经测试的代码):

from PyDbLite import Base

db = Base()
db.create("name", "age", "favourite_color")

# You can insert records as either named parameters
# or in the order of the fields
db.insert(name="Joe", age=16, favourite_color=None)
db.insert("Jane", None, "red")

# These should return an object you can iterate over
# to get the matching records.  These are unindexed queries.
#
# The first might throw because of the None in the second record
over_16 = db("age") > 16
with_favourite_colors = db("favourite_color") != None

# Or you can make an index for faster queries
db.create_index("favourite_color")
with_favourite_color_red = db._favourite_color["red"]
Run Code Online (Sandbox Code Playgroud)

希望它足以让你开始.