5

我正在为一个 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()

因此,例如,我创建了一个带有文本“Hello StackOverflow,我有一个 Django 问题”的对象 Page(子类化 Searchable)。系统将为该句子中的每个单词创建一个 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”,将考虑单词“StackOverflow”) - 我得到所有与其中任何一个有关系的对象单词-对于每个对象,我选择与先前计算的单词列表中的单词相关的所有相关 WordCount 对象,然后对“计数”属性求和并将其存储为注释“权重”-我对对象进行排序关于“重量”

我不知道 QuerySet 是否可行,但我想在之后保留一些额外操作的格式(例如过滤掉一些结果)。

我知道有很多改进可能,但这将是一个好的开始。

谢谢你的回答,文森特

4

1 回答 1

2

尝试

Searchable.objects.filter(words__in=words).annotate(
    weight=models.Sum('wordcount__count')).order_by('-weight')
于 2012-04-21T13:28:49.100 回答