【问题标题】:How to implement full text search in Django?如何在 Django 中实现全文搜索?
【发布时间】:2010-03-17 10:09:44
【问题描述】:

我想在 django 博客应用程序中实现搜索功能。现状是我有一个用户提供的字符串列表,并且查询集被每个字符串缩小,只包括那些与字符串匹配的对象。

见:

if request.method == "POST":
    form = SearchForm(request.POST)
    if form.is_valid():
        posts = Post.objects.all()
        for string in form.cleaned_data['query'].split():
            posts = posts.filter(
                    Q(title__icontains=string) | 
                    Q(text__icontains=string) |
                    Q(tags__name__exact=string)
                    )
        return archive_index(request, queryset=posts, date_field='date')

现在,如果我不想将通过逻辑 AND 搜索但使用逻辑 OR 搜索的每个单词连接起来怎么办?我该怎么做?有没有办法使用 Django 自己的 Queryset 方法来做到这一点,还是必须退回到原始 SQL 查询?

一般来说,像这样进行全文搜索是否是一个合适的解决方案,或者您会推荐使用像 Solr、Whoosh 或 Xapian 这样的搜索引擎。它们有什么好处?

【问题讨论】:

    标签: python django django-queryset full-text-search


    【解决方案1】:

    我建议你采用搜索引擎。

    我们使用了Haystack search,这是一个用于 django 的模块化搜索应用程序,支持许多搜索引擎(Solr、Xapian、Whoosh 等...)

    优点:

    • 更快
    • 即使不查询数据库也可以执行搜索查询。
    • 突出显示搜索字词
    • “更像这样”功能
    • 拼写建议
    • 更好的排名
    • 等等……

    缺点:

    • 搜索索引的大小可以快速增长
    • 最好的搜索引擎之一 (Solr) 作为 Java servlet 运行(Xapian 没有)

    我们对这个解决方案非常满意,而且很容易实施。

    【讨论】:

      【解决方案2】:

      实际上,您发布的查询确实使用 OR 而不是 AND - 您使用 \ 来分隔 Q 对象。 AND 将是&。

      一般来说,我强烈建议使用合适的搜索引擎。我们在 Solr 之上使用 Haystack 取得了很好的成功 - Haystack 管理所有 Solr 配置,并公开了一个非常类似于 Django 自己的 ORM 的漂亮 API。

      【讨论】:

      • 如果他想要全文搜索,他应该使用 OR。他的代码很清楚,但描述有点混乱。
      • 但是 .filter() 语句是 AND'ed 在一起的,不是吗?因此,在帖子标题或内容或相关标签中搜索每个搜索参数(单词)。但要显示为结果,帖子需要在其标题或内容或标签中包含所有搜索参数。这很好,正是我想要实现的。我只是对如何实现结果感到好奇,结果只需要其中一个词出现在它们的一个属性中(不是全部)。
      【解决方案3】:

      回答您的一般问题:一定要为此使用适当的应用程序。

      通过您的查询,您始终检查字段(标题、文本、标签)的全部内容。您不会从索引等中获得任何好处。

      使用适当的全文搜索引擎(或任何您称之为的),每次插入新记录时,文本(单词)都会被编入索引。因此查询会快很多,尤其是当您的数据库增长时。

      【讨论】:

        【解决方案4】:

        SOLR 很容易设置并与 Django 集成。 Haystack 让它变得更加简单。

        【讨论】:

          【解决方案5】:

          要在 Python 中进行全文搜索,请查看 PyLucene。它允许非常复杂的查询。这里的主要问题是您必须找到一种方法来告诉您的搜索引擎哪些页面发生了更改并最终更新索引。

          或者,您可以使用Google Sitemaps 告诉 Google 更快地为您的网站编制索引,然后在您的网站中嵌入自定义查询字段。这样做的好处是您只需要告诉 Google 更改的页面,Google 就会完成所有艰苦的工作(索引、解析查询等)。最重要的是,大多数人习惯于使用 Google 进行搜索,而且它还能让您的网站在全球 Google 搜索中保持最新。

          【讨论】:

            【解决方案6】:

            我认为应用程序级别的全文搜索更多地取决于您拥有什么以及您期望它如何扩展。如果您运行一个使用率低的小型网站,我认为花一些时间进行自定义全文搜索而不是安装应用程序来为您执行搜索可能更实惠。并且应用程序在存储数据时会产生更多的依赖、维护和额外的工作。通过自己进行搜索,您可以构建出色的自定义功能。例如,如果您的文本与一个标题完全匹配,您可以将用户引导到该页面而不是显示结果。另一种方法是允许 title: 或 author: 关键字前缀。

            这是我用来从网络查询中生成相关搜索结果的一种方法。

            import shlex
            
            class WeightedGroup:
                def __init__(self):  
                    # using a dictionary will make the results not paginate
                    # but it will be a lot faster when storing data          
                    self.data = {}
            
                def list(self, max_len=0):
                    # returns a sorted list of the items with heaviest weight first
                    res = []
                    while len(self.data) != 0:
                        nominated_weight = 0                      
                        for item, weight in self.data.iteritems():
                            if weight > nominated_weight:
                                nominated = item
                                nominated_weight = weight
                        self.data.pop(nominated)
                        res.append(nominated)
                        if len(res) == max_len:
                            return res
                    return res
            
                def append(self, weight, item):
                    if item in self.data:
                        self.data[item] += weight
                    else:
                        self.data[item] = weight
            
            
            def search(searchtext):
                candidates = WeightedGroup()
            
                for arg in shlex.split(searchtext): # shlex understand quotes
            
                    # Search TITLE
                    # order by date so we get most recent posts
                    query = Post.objects.filter_by(title__icontains=arg).order_by('-date')
                    arg_hits = query.count() # count is cheap
            
                    if arg_hits > 1000:
                        continue # skip keywords which has too many hits
            
                    # Each of these are expensive as it would transfer data
                    #  from the db and build a python object, 
                    for post in query[:50]: # so we limit it to 50 for example                
                        # more hits a keyword has the lesser it's relevant
                        candidates.append(100.0 / arg_hits, post.post_id)
            
                    # TODO add searchs for other areas
                    # Weight might also be adjusted with number of hits within the text
                    #  or perhaps you can find other metrics to value an post higher,
                    #  like number of views
            
                # candidates can contain a lot of stuff now, show most relevant only
                sorted_result = Post.objects.filter_by(post_id__in=candidates.list(20))
            

            【讨论】:

              猜你喜欢
              • 2021-10-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-29
              • 2013-03-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多