作为一个简单的例子,假设我有一个产品类
class Product(models.Model):
tags = models.ManyToManyField('Tag',blank=True,null=True)
我的Tag类看起来像这样
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True, db_index=True)
鉴于一种产品,我如何按大多数常见标签对所有其他产品的结果集进行排序?
例如,我有以下内容:
带有标签A,B和C的P1
P2带有标签B,C
带标签B的P3
带有标签A,B和C的P4
我希望我的P1结果集按顺序为P4,P2,P3,假设我们从结果集中排除P1。
答案 0 :(得分:1)
这是典型的自联接用法,SQL看起来像:
SELECT t3.*, count(t2.tag_id) as similar_tags_count
FROM m2m_tbl t1 INNER JOIN m2m_tbl t2
ON (t1.tag_id = t2.tag_id and t1.product_id != t2.product_id and t1.product_id = pk_of_the_given_product)
INNER JOIN product_tbl t3 ON (t2.product_id = t3.id)
GROUP BY t3.id, t3.name
ORDER BY similar_tags_count DESC;
然后查询可以提供给.raw()
:
Product.objects.raw("""
SELECT t3.*, count(t2.tag_id) as similar_tags_count
FROM {m2m_tbl} t1 INNER JOIN {m2m_tbl} t2
ON (t1.tag_id = t2.tag_id and t1.product_id != t2.product_id and t1.product_id = %s)
INNER JOIN {product_tbl} t3 ON (t2.product_id = t3.id)
GROUP BY t3.id, t3.name
ORDER BY similar_tags_count DESC;
""".format(m2m_tbl=Product.tags.through._meta.db_table, product_tbl=Product._meta.db_table),
[the_given_product.pk])
如果您真的需要QuerySet
,请使用UNDOCUMENTED query.join()
(也in the docstring of the query.join()
)来处理联接:
m2m_tbl = Product.tags.through._meta.db_table
qs = Product.objects.exclude(pk=the_given_product.pk)
alias_1 = qs.query.get_initial_alias()
alias_2 = qs.query.join((alias_1, m2m_tbl, 'id', 'product_id'))
alias_3 = qs.query.join((alias_2, m2m_tbl, 'tag_id', 'tag_id'))
qs = qs.annotate(similar_tags_count=models.Count('tags__id')).extra(where=[
'{alias_2}.product_id != {alias_3}.product_id'.format(alias_2=alias_2, alias_3=alias_3),
'{alias_3}.product_id = %s'.format(alias_3=alias_3)
], params=[the_given_product.pk])
答案 1 :(得分:0)
假设两个回合列表你可以做类似的事情
P1 = ['A', 'B', 'C'] # these being products
P3 = ['B']
P4 = ['A', 'B', 'C']
P1 = set(P1)
P3_INTERSECT = len(P1.intersection(P3))
P4_INTERSECT = len(P1.intersection(P4))
这些将分别返回1和3,然后我会使用它来订购结果。如果您最终需要这样做,您可能需要定义自己的经理来执行此排序。