Luk*_*kas 8 python django postgresql django-queryset trigram
我必须实现一个容错的搜索功能.
目前,我有以下情况:
楷模:
class Tag(models.Model):
name = models.CharField(max_length=255)
class Illustration(models.Model):
name = models.CharField(max_length=255)
tags = models.ManyToManyField(Tag)
Run Code Online (Sandbox Code Playgroud)
查询:
queryset.annotate(similarity=TrigramSimilarity('name', fulltext) + TrigramSimilarity('tags__name', fulltext))
Run Code Online (Sandbox Code Playgroud)
示例数据:
插图:
ID | Name | Tags |
---|--------|-------------------|
1 | "Dog" | "Animal", "Brown" |
2 | "Cat" | "Animals" |
Run Code Online (Sandbox Code Playgroud)
插图有标签:
ID_Illustration | ID_Tag |
----------------|--------|
1 | 1 |
1 | 2 |
2 | 3 |
Run Code Online (Sandbox Code Playgroud)
标签:
ID_Tag | Name |
-------|----------|
1 | Animal |
2 | Brown |
3 | Animals |
Run Code Online (Sandbox Code Playgroud)
当我运行查询时"Animal",相似性"Dog"应该高于for "Cat",因为它是完美的匹配.
不幸的是,两个标签都以某种方式一起考虑.
目前,它看起来像是在一个字符串中连接标签,然后检查相似性:
TrigramSimilarity("Animal Brown", "Animal") => X
Run Code Online (Sandbox Code Playgroud)
但我想以一种方式对其进行调整,以使Illustration实例名称与其标签之间具有最高的相似性:
Max([
TrigramSimilarity('Name', "Animal"),
TrigramSimilarity("Tag_1", "Animal"),
TrigramSimilarity("Tag_2", "Animal"),
]) => X
Run Code Online (Sandbox Code Playgroud)
Edit1:我正在尝试查询所有插图,其中标题或其中一个标签的相似度大于X.
Edit2:附加示例:
fulltext ='动物'
TrigramSimilarity('Animal Brown',fulltext)=> x TrigramSimilarity('Animals',fulltext)=> y
其中x <y
但实际上我想要的是
TrigramSimilarity(Max(['Animal','Brown]),fulltext)=> x(与Animal相似)TrigramSimilarity('Animals',fulltext)=> y
其中x> y
你不能分手tags__name(至少我不知道)。
从您的示例中,我可以假设2种可能的解决方案(第一种解决方案并非严格使用Django):
并非所有内容都必须严格通过Django
我们具有Python功能,因此让我们使用它们:
让我们首先组成查询:
from difflib import SequenceMatcher
from django.db.models import Q
def create_query(fulltext):
illustration_names = Illustration.objects.values_list('name', flat=True)
tag_names = Tag.objects.values_list('name', flat=True)
query = []
for name in illustration_names:
score = SequenceMatcher(None, name, fulltext).ratio()
if score == 1:
# Perfect Match for name
return [Q(name=name)]
if score >= THRESHOLD:
query.append(Q(name=name))
for name in tag_names:
score = SequenceMatcher(None, name, fulltext).ratio()
if score == 1:
# Perfect Match for name
return [Q(tags__name=name)]
if score >= THRESHOLD:
query.append(Q(tags__name=name))
return query
Run Code Online (Sandbox Code Playgroud)
然后创建您的查询集:
from functools import reduce # Needed only in python 3
from operator import or_
queryset = Illustration.objects.filter(reduce(or_, create_query(fulltext)))
Run Code Online (Sandbox Code Playgroud)
解码以上内容:
我们将对照我们的每个Illustration和Tag名称进行检查,并fulltext使用每个相似性都通过的名称组成一个查询THRESHOLD。
SequenceMatcher方法比较序列并返回比率0 < ratio < 1,其中0表示No-Match,1表示Perfect-Match。查看此答案以获取另一个用法示例:查找两个字符串之间的相似性百分比(注意:还有其他字符串比较模块,请找到一个适合您的模块)Q() Django对象,允许创建复杂的查询(有关链接文档的更多信息)。operator和reduce将Q()对象列表转换为OR分隔的查询参数:Q(name=name_1) | Q(name=name_2) | ... | Q(tag_name=tag_name_1) | ...注意:
您需要定义一个acceptable THRESHOLD。
可以想象,这会有点慢,但是在需要进行“模糊”搜索时可以预料到。
(Django方式:)
使用具有较高相似性阈值的查询,并以该相似率对查询集进行排序:
queryset.annotate(
similarity=Greatest(
TrigramSimilarity('name', fulltext),
TrigramSimilarity('tags__name', fulltext)
)).filter(similarity__gte=threshold).order_by('-similarity')
Run Code Online (Sandbox Code Playgroud)
解码以上内容:
Greatest()接受aggregate表达式或模型字段的集合(不要与Django方法混淆)并返回max项目。TrigramSimilarity(word, search)返回0和1之间的速率越接近率是1,则越相似word是search。.filter(similarity__gte=threshold),将过滤相似度低于的相似度threshold。0 < threshold < 1。您可以将阈值设置0.6得很高(考虑默认值是0.3)。您可以尝试一下以调整性能。similarity降序对查询集进行排序。| 归档时间: |
|
| 查看次数: |
1427 次 |
| 最近记录: |