在python

时间:2018-01-28 21:28:47

标签: python json python-3.x abstract-syntax-tree

我正在寻找一种方法,在我的工作场所向其他开发人员和可选的客户公开过滤功能。

问题

我想基于我的其他开发人员以及后来发给我们客户的用户定义过滤器,对我的数据(python dicts)实现一种简单的查询语言。

  • 语言应该足够简单,以供非开发人员使用
  • 足够安全以避免在我的服务器上执行远程代码
  • 表达足以查询数据,如下例

在我的dict / json数据上公开SQL接口会很棒(我不想设置服务器)

示例场景

db = [
  {'first': 'john', 'last': 'doe', 'likes': ['cookies', 'http']},
  {'first': 'jane', 'last': 'doe', 'likes': ['cookies', 'donuts']},
  {'first': 'danny', 'last': 'foo', 'likes': ['http', 'donuts']},
]

query = '(first == "john" or last == "doe") and likes contains "cookies"'
results = run_query(db, query)

这应该返回(结果):

[
  {'first': 'john', 'last': 'doe', 'likes': ['cookies', 'http']},
  {'first': 'jane', 'last': 'doe', 'likes': ['cookies', 'donuts']},
]

注意:我不介意更改运营商名称,例如or -> OR contains -> inside或其他任何内容,只要它具有人类可读性并保持语言的相同表现力

我试过的解决方案

DSL

我查看了一些像PLY这样的DSL库,但在我看来它们太复杂了,并且需要一些魔法才能完成任务(不确定从哪里开始,如果值得的话)

插件

没有找到任何插件系统来为我的用户公开沙盒功能(即更安全的评估)

JSON查询包

我查看了TinyDB和其他在json上实现某种SQL的人,但是在没有很多自定义的情况下找不到可行的东西。 我还看了pandasql这个整体看起来很好但没有维护的库:(

有一个lucene包解析器 - luqum基于PLY但它与我的语法树不同(它们有更多方法)并且lib没有真正维护,(我确实考虑操纵这个lib有点得到我想要的东西)

SQLite的

使用SQLiteDB加载我的所有数据(在内存中或不在内存中),然后对其运行SQL查询。没有测试它但是这应该是非常简单的,因为将我的整个数据加载到SQL只是为了运行我不想做的数据。

我愿意接受建议,甚至是如何改进上述解决方案以使其发挥作用

2 个答案:

答案 0 :(得分:4)

在将PLY用于基于文本的查询之前,我会从像这样的常规Python类构建核心语言:

class Match:
    def __init__(self, **target):
        [[self.key, self.value]] = target.items()
    def __call__(self, obj):
        return self.key in obj and self.value == obj[self.key]

class Contains:        
    def __init__(self, **target):
        [[self.key, self.value]] = target.items()
    def __call__(self, obj):
        return self.key in obj and self.value in obj[self.key]        

class Or:
    def __init__(self, *predicates):
        self.predicates = predicates
    def __call__(self, record):
        return any(predicate(record) for predicate in self.predicates)

class And:
    def __init__(self, *predicates):
        self.predicates = predicates
    def __call__(self, record):
        return all(predicate(record) for predicate in self.predicates)

def run_query(db, query):
    return filter(query, db)

if __name__ == '__main__':

    db = [
      {'first': 'john', 'last': 'doe', 'likes': ['cookies', 'http']},
      {'first': 'jane', 'last': 'doe', 'likes': ['cookies', 'donuts']},
      {'first': 'danny', 'last': 'foo', 'likes': ['http', 'donuts']},
    ]
    query = And(Or(Match(first='john'), Match(last='doe')), Contains(likes='cookies'))
    for result in run_query(db, query):
        print(result)

这将输出:

{'first': 'john', 'last': 'doe', 'likes': ['cookies', 'http']}
{'first': 'jane', 'last': 'doe', 'likes': ['cookies', 'donuts']}

答案 1 :(得分:0)

SQL广为人知,并且最终用户通常会请求它。我知道有几个选项可以在python中实现这一点。他们依赖外部库,但得到很好的支持。

小数据解决方案

将字典输入pd.DataFrame,例如见pd.DataFrame.from_dict。然后通过pandasql库查询。看来你已经尝试了这个,但我提到它(根据我的经验)它做了它所说的。

大数据解决方案

以HDF5格式保存数据。 pandas数据框和numpy数组可以通过h5py库以HDF5格式轻松存储。然后使用HDFql库查询HDF5文件。