我正在为Django项目构建一个小型搜索系统(是的,我知道,已经有很多产品在做这个但是我想尝试一下,只是为了好玩)。 我基本上有以下型号:
class Word(models.Model):
""" A searchable word.
We only store the slugified value
"""
slug = models.SlugField(unique = True)
class Searchable(models.Model):
""" Superclass for Searchable objects.
"""
words = models.ManyToManyField(
Word,
through='WordCount')
class WordCount(models.Model):
""" Occurences of a word in a Searchable object.
"""
word = models.ForeignKey(Word)
item = models.ForeignKey(Searchable)
count = models.IntegerField()
例如,我创建了一个对象页面(子类化Searchable),文本为“Hello StackOverflow,我有一个Django问题”。系统将为此句子中的每个单词创建一个Word实例,并为每个WordCount实例创建一个单词实例,说明每个单词在文本中出现一次。
进行查询以获取包含一个更多单词的所有Searchable实例正常工作(searchable_text提取单词并从中生成一个列表):
def search(query)
tokens = searchable_text(query)
words = Word.objects.filter(
reduce(operator.or_,
[models.Q(slug__contains = t)
for t in tokens]))
return Searchable.objects.filter(words__in = words)
现在我想做的是使用中间关系来排序结果。我想保留一个QuerySet,所以下面的代码不起作用,但是给出了我想要做的事情(用丑陋的修补来做注释):
def search(query)
tokens = searchable_text(query)
words = Word.objects.filter(
reduce(operator.or_,
[models.Q(slug__contains = t)
for t in tokens]))
results = []
for obj in Searchable.objects.filter(words__in = words):
matching_words = obj.wordcount_set.filter(word__in = words)
obj.weight = sum([w.count for w in matching_words])
results.append(obj)
return sorted(results,
reverse = True,
key = lambda x: x.weight)
所以基本上: - 我得到查询中包含的所有Word对象(或者部分匹配,如果我搜索“Stack”,则会考虑Word“StackOverflow”) - 我得到所有与这些单词有关系的对象 - 对于每个对象,我选择与先前计算的Word列表中的Word相关的所有相关WordCount对象,然后执行'count'属性的总和并将其存储为注释'weight' - 我在“重量”上对对象进行排序
我不知道是否可以使用QuerySet,但我想保留一些额外操作的格式(例如过滤掉一些结果)。
我知道有很多可能的改进,但这将是一个良好的开端。
感谢您的回答, 文森特
答案 0 :(得分:2)
尝试
Searchable.objects.filter(words__in=words).annotate(
weight=models.Sum('wordcount__count')).order_by('-weight')