2016-06-14 9 views
0

私は以下のモデルでdjangoプロジェクトを進めています。逆の関係でAND検索

class User(models.Model): 
    pass 

class Item(models.Model): 
    user = models.ForeignKey(User) 
    item_id = models.IntegerField() 

約1,000万のアイテムと10万人のユーザーがあります。

私の目標は、永遠にかかるデフォルトの管理者の検索を無効にすることです。 は、指定したアイテムIDの「すべて」を所有する一致するすべてのユーザーを適切な時間枠で返します。

これは私の基準をよりよく説明するために使用する2つのテストです。

class TestSearch(TestCase): 
    def search(self, searchterm): 
     """A tuple is returned with the first element as the queryset""" 
     return do_admin_search(User.objects.all()) 

    def test_return_matching_users(self): 
     user = User.objects.create() 
     Item.objects.create(item_id=12345, user=user) 
     Item.objects.create(item_id=67890, user=user) 

     result = self.search('12345 67890') 
     assert_equal(1, result[0].count()) 
     assert_equal(user, result[0][0]) 

    def test_exclude_users_that_do_not_match_1(self): 
     user = User.objects.create() 
     Item.objects.create(item_id=12345, user=user) 

     result = self.search('12345 67890') 
     assert_false(result[0].exists()) 

    def test_exclude_users_that_do_not_match_2(self): 
     user = User.objects.create() 

     result = self.search('12345 67890') 
     assert_false(result[0].exists()) 

次のスニペットは、annotateを使用して50秒以上かかる最高の試みです。

def search_by_item_ids(queryset, item_ids): 
    params = {} 
    for i in item_ids: 
     cond = Case(When(item__item_id=i, then=True), output_field=BooleanField()) 
     params['has_' + str(i)] = cond 

    queryset = queryset.annotate(**params) 

    params = {} 
    for i in item_ids: 
     params['has_' + str(i)] = True 
    queryset = queryset.filter(**params) 
    return queryset 

スピードアップのためにできることはありますか?

答えて

0

ここでは、パフォーマンスを大幅に向上させるためのヒントをいくつか紹介します。

初期クエリセットに使用prefetch_related`が関連を取得する項目

queryset = User.objects.filter(...).prefetch_related('user_set') 

__inオペレータとフィルターの代わりに、IDが

def search_by_item_ids(queryset, item_ids): 
    return queryset.filter(item__item_id__in=item_ids) 

のリストをループしないでくださいすでにクエリの条件である場合に注釈を付ける

このクエリーセットは、item_idsリストにIDを持つレコードのみで構成されていることがわかっているので、そのオブジェクトごとに書き込む必要はありません。

queryset = User.objects.filter(
    item__item_id__in=item_ids 
).prefetch_related('user_set') 

をフルクエリのための唯一の2デシベルヒットと -

あなたはあなただけ呼び出すことによって、大幅に何をしているかをスピードアップすることができ、一緒

をすべてを置きます。

+0

残念ながら、私は、 'item__item__id__in'が私の場合にはうまくいかないため、idのいずれかを所有するすべてのユーザーではなく、指定したアイテムIDの' all 'を所有するすべてのユーザーを取得する必要があります。 – k8tems