为什么 QuerySet 迭代这么慢?

She*_*Rox 1 python django postgresql django-queryset

我正在尝试为系统创建准确且高效的搜索算法。我安装了 Postgresql 来利用它的三元组相似性查询,这就是我搜索对象的方式:

objects_found = Question.objects.extra(where=["CHAR_LENGTH(answer) > 300"])).filter(question__trigram_similar=message
Run Code Online (Sandbox Code Playgroud)

这速度快得令人难以置信,执行大部分查询只用了不到 0.5 秒的时间。查询集的所有对象objects_found都与查询文本相似,但我需要找出最相似的一个。

我知道在这种情况下有两种非常好的算法,第一个是余弦相似度,第二个是Ratcliff/Obershelp 模式识别它在 Python 中有内置实现)。

我尝试进行一次迭代,对它们中的每一个进行测试,在大多数情况下,余弦相似度大约快 1.5 倍(正如预期的那样,考虑到向量的测量速度要快得多),但 SequenceMatcher 会给出更准确的结果。所以我还是选择了SequenceMatcher。请注意,这次迭代花了很长时间。

最后,我尝试在代码中实现SequenceMatcher:

objects_found = (Question.objects.extra(where=["CHAR_LENGTH(answer) > 300"])).filter(question__trigram_similar=message).iterator()
zsim = ("", 0)
for i in objects_found:
    rsim = _search.ratcliff_obershelp(querytext, i.question)
    if zsim[1] < rsim:
       zsim = (i.answer, rsim)
       if rsim > 0.75:  # works in most of the cases
            break
response = zsim[0]
Run Code Online (Sandbox Code Playgroud)

数据库中有约 1GB 的约 500 万行,postgresql 需要不到 0.5 秒的时间来选择具有三元相似性的正确行。在大约 500 万行中,只有 10-90 行被过滤,并且查询集迭代需要大约 62 秒才能找到最相似的行。

即使迭代在开始时中断,情况也是如此,例如,如果只有 4 行需要迭代以达到 75% 以上的相似度,Django 仍会加载 90 行。

我真的怀疑相似度算法本身就是问题,它似乎只是查询集需要很长时间来加载行,并且一旦加载它们,算法几乎会立即完成所有操作。

为什么会发生这种情况?有什么方法可以让 Queryset 迭代更加省时吗?数据库级迭代会产生更快的结果吗?

ps 时间由python的time模块测量。

Ada*_*nes 6

您面临的困惑是由 Django 对QuerySets 的延迟评估引起的。您等待 Django 的 0.5 秒实际上只是准备SQL - 也就是说,将 ORM 调用转换为 SQL 查询(或多个 SQL 查询),以便稍后执行。

QuerySet尽可能晚地评估然后缓存,因此要了解 Django 实际花费了多长时间,您需要强制评估QuerySet, 以使 Django 执行 SQL。您可以通过多种方式执行此操作,例如:

print(objects_found)
Run Code Online (Sandbox Code Playgroud)

或者

list(objects_found)
Run Code Online (Sandbox Code Playgroud)

或者

for item in objects_found:
    pass
Run Code Online (Sandbox Code Playgroud)