3

这将是一个“长篇”。我包含尽可能多的代码和解释......如果有必要,我不会放弃代码。

我正在尝试在 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'

将返回 Sample1。我使用了一个疯狂的 string-eval hack 来创建一组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 OR TagB) AND NOT TagD应该返回 Sample1、Sample4 但不返回。我已经实现了一个“一次一个”函数,它可以获取一个 Sample 对象并执行查询。但是,在我的实际数据库中,我有 ~40,000 个样本和 ~400 个标签(每个样本约 ~7 个),迭代技术需要 ~4 分钟才能完成所有样本。所以我每晚计算篮子,然后在白天把它们冷冻起来。我担心当我开始策划更多的篮子、样本和标签时,这将无法扩展。

有什么建议么?

4

1 回答 1

1

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

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

其次,为了解析用户查询,我建议使用几个好的基于 Python 的解析器之一,例如PyParsingPLY。这些乍一看可能令人生畏,但实际上并不难,尤其是像您这样的简单语法。

如果这些对您来说太多了,那么请尝试使用 Fredrik 的指南Simple Top-Down Parsing in Python自行开发。

于 2010-01-05T10:07:22.457 回答