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()

たとえば、「こんにちはStackOverflow、Djangoの質問があります」というテキストを使用してオブジェクトページ(Searchableをサブクラス化)を作成します。システムは、この文の各単語に対して、および各単語がテキストに1回出現することを示すWordCountインスタンスごとにWordインスタンスを作成します。

もう1つの単語を含むすべての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」が考慮されます)-これらのいずれかと関係のあるすべてのオブジェクトを取得しますwords-オブジェクトごとに、以前に計算されたWordのリストでWordに関連するすべての関連するWordCountオブジェクトを選択し、「count」属性の合計を実行して、注釈「weight」として保存します-オブジェクトを並べ替えます「重量」について

それが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 に答える