django查询中逻辑解析器的实现

django查询中逻辑解析器的实现,django,django-queryset,logical-operators,Django,Django Queryset,Logical Operators,这将是一个“漫长的过程”。我包括尽可能多的代码和解释。。。如果必要的话,我不会放弃代码 我试图在django查询系统中实现一个逻辑解析器。其中,用户可以针对应用于样本的标记提供复杂查询。这本质上是科学样本库的一部分,用户可以在其中应用定义的标签(组织类型、研究的疾病等)。然后,他们可以创建由这些标记上的逻辑查询定义的样本的持久“篮子” #models.py class Sample(models.Model): name = models.CharField(max_length =

这将是一个“漫长的过程”。我包括尽可能多的代码和解释。。。如果必要的话,我不会放弃代码

我试图在django查询系统中实现一个逻辑解析器。其中,用户可以针对应用于样本的标记提供复杂查询。这本质上是科学样本库的一部分,用户可以在其中应用定义的标签(组织类型、研究的疾病等)。然后,他们可以创建由这些标记上的逻辑查询定义的样本的持久“篮子”

#models.py

class Sample(models.Model):
    name = models.CharField(max_length = 255)


class Tag(models.Model):
    name = models.CharField(max_length = 255)
    samples = models.ManyToManyField(Sample)

A quick example:
#example data:
Sample1 has TagA, TagB, TagC
Sample2 has       TagB, TagC, TagD
Sample3 has TagA,       TagC, TagD
Sample4 has       TagB

#example query:
'TagB AND TagC AND NOT TagD'
将返回样本1。我使用疯狂的字符串求值技巧创建了一组
Q()
对象:

def MakeQObject(expression):
    """
    Takes an expression and uses a crazy string-eval hack to make the qobjects.
    """
    log_set = {'AND':'&','OR':'|','NOT':'~'}

    exp_f = []
    parts = expression.split()
    #if there is a ) or ( then we can't use this shortcut
    if '(' in parts or ')' in parts:
        return None

    for exp in parts:
        if exp in log_set:
            exp_f.append(log_set[exp])
        else:
            exp_f.append("Q(tags__name__iexact = '%s')" % exp)
    st = ' '.join(exp_f)
    qobj = eval(st)
    return qobj
但是,对于任何需要复杂的操作顺序或按()分组的操作,这都会失败。给定相同的示例数据,查询:
(TagA或TagB)而不是TagD
应返回Sample1、Sample4,但不返回。我实现了一个“一次一个”函数,它可以获取单个样本对象并执行查询。然而,在我的实际数据库中,我有约40000个样本和约400个标记(每个样本约7个),迭代技术需要约4分钟才能在所有样本上完成。所以我每晚计算篮子,然后白天把它们冷冻起来。我担心,随着我开始策划更多的篮子、样品和标签,这将很难扩展


有什么建议吗?

首先,为了提高性能,在标记名字段上添加索引可能会有所帮助,因为您正在使用它进行查询。因此,在列中添加db_index=True

class Tag(models.Model):
    name = models.CharField(max_length = 255, db_index=True)
    samples = models.ManyToManyField(Sample)
其次,对于解析用户查询,我建议使用几种优秀的基于Python的解析器之一,例如或。这些一开始可能看起来很吓人,但实际上并没有那么难,特别是对于像你这样的简单语法


如果这些对您来说太多了,那么请尝试使用Fredrik的指南滚动您自己的查询。

我也尝试过将
()
类型查询“分解”为更长但等效的版本,但没有paren,但也无法使其工作。另外注意:您需要重置数据库,以便syncdb可以看到db_索引。如果无法重置,则需要手动将索引添加到数据库中或使用迁移工具(如South())。。。我想我可以用他们的例子来表示简单的数学表达式“”。只需将
math.operations
替换为queryset操作,然后让它处理嵌套等。