0

我正在搜索我的博客。无论如何,我在实施它时遇到了一些问题。

我有两个模型:

class Article(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()

class Tag(models.Model):
    article = models.ForeignKey(Article)
    content = models.CharField(max_length=255)

实际搜索有两个字段:词组和标签。短语应该在Article.titleand中查找Article.content,但标签应该查找具有Tag对象 by的文章Tag.content

我也要考试!

def test_by_phrase_and_tags(self):
    article_content = "spam"
    tag_content1 = "eggs"
    tag_content2 = "cheese"
    article1 = test_utilities.create_article(content=article_content)
    article2 = test_utilities.create_article(content=article_content)
    test_utilities.create_tag(article1, content=tag_content1)
    test_utilities.create_tag(article2, content=tag_content1)
    test_utilities.create_tag(article2, content=tag_content2)
    response = self.client.get(reverse("blog_search"), {
        "phrase": article_content,
        "tags": "{}, {}".format(tag_content1, tag_content2)
    })
    found_articles = response.context[-1]["found_articles"]
    self.assertEqual(found_articles, [article2])

它创建了两篇内容相同的文章,为两篇文章设置的相同标签和仅为第二篇文章设置的唯一标签。

然后我请求具有内容(两篇文章都应该匹配)和两个标签(只有第二篇文章应该匹配)的文章。总的来说,我断言只返回第二篇文章。

我用原始 SQL 和 Django ORM 尝试了很多东西,但似乎都没有。

使用子查询:

SELECT * FROM blog_article
WHERE blog_article.content = "spam"
AND blog_article.id IN (
    SELECT blog_tag.article_id FROM blog_tag
    WHERE blog_tag.content = "eggs"
    OR blog_tag.content = "cheese"
);

加入:

SELECT * FROM blog_article
JOIN blog_tag
ON blog_article.id = blog_tag.article_id
WHERE blog_article.content = "spam"
AND blog_tag.content = "eggs"
AND blog_tag.content = "cheese";

与 ORM 相同和其他的东西......

那么如何获得具有spam标题或内容具有标签的文章?我难住了。

4

2 回答 2

2

如果我对您的理解正确,那么您正在寻找具有两个标签而不是其中一个标签的文章eggscheese那么您可以这样做:

SELECT * 
FROM blog_article a
INNER JOIN
(
   SELECT article_id
   FROM blog_tag
   WHERE content IN ('eggs', 'cheese')
   GROUP BY article_id
   HAVING COUNT(DISTINCT content) = 2
) b ON a.id = b.article_id
WHERE a.content = 'spam';

SQL 小提琴演示

这个查询背后的想法是,他们称之为Relational Division,这样做的一种方法是子查询的作用:

   SELECT article_id
   FROM blog_tag
   WHERE content IN ('eggs', 'cheese')
   GROUP BY article_id
   HAVING COUNT(DISTINCT content) = 2

哪一个是:

   GROUP BY article_id
   HAVING COUNT(DISTINCT content) = 2

这将确保每个分组article_id的 , 都有两个标签,如果文章有其中一个,则COUNT(DISTINCT content)= 1,这将被消除。

于 2013-02-01T17:53:31.760 回答
1

如果我理解正确,您想通过短语或标签过滤article.title文章article.content

首先related_name在标签模型中定义一个 for article:

class Tag(models.Model):
    article = models.ForeignKey(Article, related_name='tags')

现在查询:

from django.db.models import Q

# supposed input
phrase = 'my search string'
tags = ['tag1', 'tag2']

articles = Article.objects.filter(
   Q(title__icontains=phrase) |
   Q(content__icontains=phrase)) \
   .filter(tags__content__in=tags) \
   .annotate(num_tags=Count('tags')) \
   .filter(num_tags=len(tags))
于 2013-02-01T17:58:50.710 回答