2017-10-04 62 views
1

最簡單的例子:如何從多對多場排序查詢集的註釋ATTR

class User(models.Model): 
    name = ... 

class Group(models.Model): 
    members = models.ManyToManyField(User, through='GroupMembership') 

class GroupMembership(models.Model): 
    user = ... 
    group = ... 

我想通過成員的註釋字段排序組的列表。

我正在使用trigram搜索來過濾和註釋用戶queryset。 若要註釋的用戶我有類似的東西:

User.objects.annotate(...).annotate(similarity=...) 

現在我想通過用戶的‘相似性’排序組查詢集:

ann_users = User.objects.annotate(...).annotate(similarity=...) 
qs = Group.objects.prefetch_related(Prefetch('members', 
    queryset=ann_users)) 
qs.annotate(similarity=Max('members__similarity')).order_by('similarity') 

但它不工作,因爲prefetch_related在Python中執行「連接」;所以我有錯誤:

"FieldError: Cannot resolve keyword 'members' into field." 

回答

0

我希望你通過巽搜索有名字的相似性數據庫的功能和它的Django綁定或您創建的任何:

from django.db.models import Max, Func, Value, Prefetch 

class Similarity(Func): 
    function = 'SIMILARITY' 
    arity = 2 

SEARCHED_NAME = 'searched_name' 
ann_users = User.objects.annotate(similarity=Similarity('name', Value(SEARCHED_NAME))) 
qs = Group.objects.prefetch_related(Prefetch('members', queryset=ann_users)) 
qs = qs.annotate(
    similarity=Max(Similarity('members__name', Value(SEARCHED_NAME))) 
).order_by('similarity') 

主查詢編譯爲

SELECT app_group.id, MAX(SIMILARITY(app_user.name, %s)) AS similarity 
FROM app_group 
LEFT OUTER JOIN app_groupmembership ON (app_group.id = app_groupmembership.group_id) 
LEFT OUTER JOIN app_user ON (app_groupmembership.user_id = app_user.id) 
GROUP BY app_group.id 
ORDER BY similarity ASC; 
-- params: ['searched_name'] 

這不正是你想要的標題,但結果是一樣的。

注意:評估SIMILARITY函數的次數取決於數據庫查詢優化器。如果原始查詢在某些簡化情況下更好,則EXPLAIN命令的查詢計劃將是一個有趣的答案。

+0

感謝您的回答。我有你的想法。不幸的是,我沒有單一的功能「相似性」;我的搜索更復雜 - 它使用Double Metaphone + Trigrams搜索幾個字段。 –

+0

另一件事:我猜這個相似性不應該被使用兩次,不是嗎? –

+0

如果您編寫相似性表達式,我可以將其重寫爲一個函數。註釋不能被相關字段重用(或者我不能)。關係是模型不是查詢或查詢集。因此目前無法通過相關字段重複使用註釋。檢查相似性評估的數量是否爲len(User)+ len(GroupMembership)或2 * len(User)對於您的後端更重要。比手寫SQL慢兩倍的ORM並不差。 – hynekcer