基于Django的博客系统之用HayStack连接elasticsearch增加搜索功能(五)

上一篇:搭建基于Django的博客系统数据库迁移从Sqlite3到MySQL(四)
下一篇:基于Django的博客系统之增加类别导航栏(六)

功能概述

  1. 添加搜索框用于搜索博客。

需求详细描述

1. 添加搜索框用于搜索博客

  • 描述: 在博客首页添加搜索框,用户可以通过关键词搜索博客文章。
  • 功能要求:
    • 搜索框位置:导航栏或页面顶部。
    • 支持根据标题和内容进行搜索。
    • 搜索结果显示匹配的博客列表。
  • 用户故事:
    • 作为用户,我希望能够通过搜索框快速找到感兴趣的博客文章。

技术结构

实现一个博客搜索功能,可以同时使用 Elasticsearch 和 MySQL 各自的优势。以下是如何区分和结合使用这两种技术的方法:

使用 MySQL 的部分

  1. 数据存储
    • 存储博客文章的基本信息,如标题、作者、发布时间、分类等。
    • 存储用户信息、评论、标签等结构化数据。
  2. 基础查询和管理
    • 进行常规的 CRUD 操作,如创建、读取、更新、删除博客文章。
    • 进行简单的过滤和排序,如按发布时间、作者、分类等查询文章。

使用 Elasticsearch 的部分

  1. 全文搜索
    • 存储和索引博客文章的全文内容,以支持全文搜索功能。
    • 对用户搜索的关键词进行快速匹配,返回相关的文章列表。
  2. 复杂查询
    • 支持复杂的查询需求,如模糊搜索、布尔搜索、按相关性排序等。
    • 支持自动补全、拼写纠错等高级搜索功能。

集成 MySQL 和 Elasticsearch

  1. 数据同步
    • 需要将 MySQL 中的博客文章数据同步到 Elasticsearch 中,以确保搜索功能能够使用最新的数据。
    • 可以使用定时任务或实时数据同步机制来保持两者数据的一致性。例如,使用工具如 Logstash、Beats 或自定义的同步程序。
  2. 搜索接口设计
    • 提供一个统一的搜索接口,前端用户提交搜索请求时,后台从 Elasticsearch 中查询搜索结果。
    • 查询到的搜索结果中包含文章的基本信息,从 Elasticsearch 返回文章的 ID,然后从 MySQL 中获取详细信息(如作者、评论等)。

实现步骤

要在 Django 博客应用中添加搜索框,以便用户可以搜索博客内容,可以按照以下步骤进行:

1. 安装 Django 搜索库

首先,确保你已经安装了 Django 搜索库。一个常用的选择是 django-haystack 库,它提供了与多个搜索引擎(如 Elasticsearch、Solr 和 Whoosh 等)集成的功能。

pip install django-haystack

2. 配置搜索引擎

选择并配置你想要使用的搜索引擎。例如,如果选择使用 Whoosh 搜索引擎,需要在 settings.py 文件中配置 Haystack 设置:

# settings.py

INSTALLED_APPS = [
    # 其他应用...
    'haystack',
]

HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://localhost:9200/',  # Elasticsearch 服务器的 URL
        'INDEX_NAME': 'haystack',  # Elasticsearch 索引的名称
    },
}

HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

3. 创建搜索索引类

为你的博客模型创建一个搜索索引类,告诉 Haystack 如何索引你的数据。假设你有一个名为 Post 的博客模型:

# search_indexes.py

from haystack import indexes
from .models import Post

class PostIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)

    def get_model(self):
        return Post

在这个示例中,我们使用 text 字段来索引博客内容。你可以根据需要添加更多的字段。

4. 创建搜索模板

在你的博客模板文件夹中创建一个模板文件,用于显示搜索结果。这个模板将根据搜索结果显示匹配的博客文章。

<!-- templates/search/post_text.txt -->

{{ object.title }}
{{ object.content }}

同步索引:运行 Django 的管理命令来创建或更新搜索索引。

python manage.py rebuild_index

5. 更新 URL 配置

将搜索视图添加到你的 URL 配置中,以便用户可以访问搜索页面并执行搜索。

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    # 其他 URL 配置...
    path('search/', views.SearchView.as_view(), name='search'),
]

6. 创建搜索视图

创建一个视图类来处理搜索请求,并将搜索结果呈现给用户。

from haystack.query import SearchQuerySet

def search_view(request):
    query = request.GET.get('q', '')
    results = SearchQuerySet().filter(content=query)
    return render(request, 'search_results.html', {'results': results})

7. 创建搜索模板

创建一个模板文件,用于显示搜索结果。在这个模板中,你可以根据需要定制搜索结果的展示方式。

<!-- templates/search.html -->

{% for result in page.object_list %}
    <h3><a href="{{ result.object.get_absolute_url }}">{{ result.object.title }}</a></h3>
    <p>{{ result.object.content }}</p>
{% empty %}
    <p>No results found.</p>
{% endfor %}

{% if is_paginated %}
    <div class="pagination">
        <span class="step-links">
            {% if page.has_previous %}
                <a href="?q={{ query }}&page=1">&laquo; first</a>
                <a href="?q={{ query }}&page={{ page.previous_page_number }}">previous</a>
            {% endif %}

            <span class="current">
                Page {{ page.number }} of {{ page.paginator.num_pages }}.
            </span>

            {% if page.has_next %}
                <a href="?q={{ query }}&page={{ page.next_page_number }}">next</a>
                <a href="?q={{ query }}&page={{ page.paginator.num_pages }}">last &raquo;</a>
            {% endif %}
        </span>
    </div>
{% endif %}

8. 创建搜索表单

最后,创建一个搜索表单,让用户输入搜索关键字并提交搜索请求。

<!-- templates/search_form.html -->

<form action="{% url 'search' %}" method="get">
    <input type="text" name="q" placeholder="Search...">
    <button type="submit">Search</button>
</form>

将搜索表单包含在你的博客页面中的适当位置,用户就可以使用它来搜索博客内容了。

这就是在 Django 博客应用中添加搜索框的基本步骤。根据你的需求,你可以进一步定制搜索功能,例如添加搜索结果的高亮显示、自定义搜索表单字段等。

连接elasticsearch

具体参看这篇Windows安装ElasticSearch版本7.17.0

要连接 Elasticsearch,你需要配置 Django Haystack 的后端为 Elasticsearch 后端。下面是一些步骤:

  1. 安装 Elasticsearch:首先确保你已经安装了 Elasticsearch 并且它正在运行。你可以从 Elasticsearch 的官方网站上下载并安装它。
  2. 安装 Haystack:确保你已经安装了 Django Haystack。你可以使用 pip 进行安装:pip install django-haystack
  3. 安装 Elasticsearch 后端:你需要安装与 Elasticsearch 兼容的 Haystack 后端。通常情况下,你需要安装 elasticsearch-dsl,它是 Elasticsearch 的 Python 客户端库。你可以使用 pip 进行安装:pip install elasticsearch-dsl
  4. 配置 Haystack 设置:在 Django 项目的 settings.py 文件中,配置 Haystack 设置以使用 Elasticsearch 后端。这包括指定 Elasticsearch 的主机和端口等信息。
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://localhost:9200/',  # Elasticsearch 服务器的 URL
        'INDEX_NAME': 'haystack',  # Elasticsearch 索引的名称
    },
}
  1. 建立索引:运行 python manage.py rebuild_index 命令来建立你的模型的搜索索引。这将会在 Elasticsearch 中创建对应的索引。
  2. 运行你的应用程序:现在你的 Django 应用程序应该能够连接到 Elasticsearch 并使用它来进行全文搜索了。

问题和解决

报错 from django.utils.datetime_safe import date, datetime ModuleNotFoundError: No module named 'django.utils.datetime_safe'

分析解决:

在 Django 3.2 中,django.utils.datetime_safe 模块已被移除。这个模块提供了在处理日期和时间时的安全操作,以防止由于 datetimedate 对象的类型不同而导致的一些问题。

如果你的代码中依赖了 django.utils.datetime_safe 模块,你可以尝试以下替代方法:

  1. 直接使用 datetime 和 date:在绝大多数情况下,直接使用内置的 datetimedate 类应该没有问题。只要确保在处理日期和时间时,类型的转换和比较是正确的即可。
  2. 在需要的地方进行转换:如果你的代码在某些情况下需要确保 datetimedate 对象的类型一致,你可以手动进行转换。比如,使用 timezone.now() 获取当前时间,使用 datetime.strptime() 将字符串转换为 datetime 对象等。
  3. 更新依赖:如果你使用的是第三方库,可以尝试更新这些库的版本,看看是否有与 Django 3.2 兼容的版本。

确保你的代码不再依赖于 django.utils.datetime_safe 模块后,就可以解决这个错误了。切换为elasticsearch连接。报错如下 raise MissingDependency( haystack.exceptions.MissingDependency: The 'elasticsearch5' backend requires the installation of 'elasticsearch>=5.0.0,<6.0.0'. Please refer to the documentation.

在这里插入图片描述

修改settings.pyHAYSTACK_CONNECTIONS连接的Elasticsearch版本为8.x,如下:

HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch8_backend.Elasticsearch8SearchEngine',
        'URL': 'http://localhost:9200/',
        'INDEX_NAME': 'myblog',
    },
}

报错ModuleNotFoundError: No module named 'haystack.backends.elasticsearch8_backend'

原因:Haystack中没有提供一个专门用于Elasticsearch 8.x 的后端引擎。使用 haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine 就可以支持 Elasticsearch 7.x。

重新安装elasticsearch版本号为7.17。见这篇文章

卸载不需要的驱动

pip uninstall elasticsearch
pip uninstall elasticsearch-dsl

安装需要的驱动

 pip install elasticsearch==7.17.0
 pip install elasticsearch-dsl==7.4.1

再次调用启动搜索index命令python manage.py rebuild_index

报错ImportError: cannot import name 'datetime_safe' from 'django.utils'

原因:Django 3.2 中移除了 django.utils.datetime_safe 模块引起的。Haystack 应该已经更新以适应 Django 3.2。

执行命令下载haystack支持django5.0.7

pip install git+https://github.com/django-haystack/django-haystack.git

再次调用启动搜索index命令

python manage.py rebuild_index

运行成功。效果如下:

在这里插入图片描述

调用http://localhost:8000/search/?q=%E6%AF%94%E4%BC%AF后,结果如下:

在这里插入图片描述

分析原因elasticsearch的index有问题。

tree /F命令获取全文件夹下的文件格式表。

通过下面命令获取当前elasticsearch里面的index

Invoke-WebRequest -Uri "http://localhost:9200/_cat/indices?v"

post_text.txt里面添加一段index的指向

<!-- templates/search/indexes/blog/post_text.txt -->

执行index命令python manage.py rebuild_index

执行成功,如下:
在这里插入图片描述
postman调用apihttp://localhost:9200/myblog结果返回成功:

{
    "myblog": {
        "aliases": {},
        "mappings": {
            "properties": {
                "content": {
                    "type": "text",
                    "analyzer": "snowball"
                },
                "django_ct": {
                    "type": "keyword"
                },
                "django_id": {
                    "type": "keyword"
                },
                "id": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "text": {
                    "type": "text",
                    "analyzer": "snowball"
                },
                "title": {
                    "type": "text",
                    "analyzer": "snowball"
                }
            }
        },
        "settings": {
            "index": {
                "max_ngram_diff": "2",
                "routing": {
                    "allocation": {
                        "include": {
                            "_tier_preference": "data_content"
                        }
                    }
                },
                "number_of_shards": "1",
                "provided_name": "myblog",
                "creation_date": "1716964128114",
                "analysis": {
                    "filter": {
                        "haystack_ngram": {
                            "type": "ngram",
                            "min_gram": "3",
                            "max_gram": "4"
                        },
                        "haystack_edgengram": {
                            "type": "edge_ngram",
                            "min_gram": "2",
                            "max_gram": "15"
                        }
                    },
                    "analyzer": {
                        "edgengram_analyzer": {
                            "filter": [
                                "haystack_edgengram",
                                "lowercase"
                            ],
                            "tokenizer": "standard"
                        },
                        "ngram_analyzer": {
                            "filter": [
                                "haystack_ngram",
                                "lowercase"
                            ],
                            "tokenizer": "standard"
                        }
                    }
                },
                "number_of_replicas": "1",
                "uuid": "1GzZNjZzSmOtmFdLuGWhvw",
                "version": {
                    "created": "7170099"
                }
            }
        }
    }
}

搜索index

修改搜索视图以获取匹配的博客文章 ID,并从 MySQL 中获取详细信息:

# blog/views.py
from django.shortcuts import render
from haystack.query import SearchQuerySet
from .models import BlogPost
from .forms import BlogSearchForm

def search(request):
    form = BlogSearchForm(request.GET)
    results = []
    if form.is_valid():
        # 从 Elasticsearch 获取匹配的博客文章 ID
        sqs = form.search()
        matched_ids = [result.pk for result in sqs]

        # 从 MySQL 数据库中获取详细信息
        results = BlogPost.objects.filter(id__in=matched_ids)
    
    return render(request, 'search_results.html', {'form': form, 'results': results})

访问’http://localhost:8000’输入搜索growing,访问’http://localhost:8000/search/?q=growing’,得到结果如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/670248.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1940java swing零售库存管理系统myeclipse开发Mysql数据库CS结构java编程

一、源码特点 java swing 零售库存管理系统 是一套完善的窗体设计系统&#xff0c;对理解SWING java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;&#xff0c;系统主要采用C/S模式开发。 应用技术&#xff1a;javamysql 开发工具&#xff1a;…

如何快速找到 RCE

背景介绍 本文将分享国外白帽子在‘侦察’阶段如何快速发现 RCE 漏洞的经历。以Apache ActiveMQ 的 CVE-2023–46604 为特例&#xff0c;重点介绍如何发现类似此类的漏洞&#xff0c;让我们开始吧。 快速发现过程 在‘侦察’阶段&#xff0c;白帽小哥会保持每周更新一次目标…

域内用户枚举和密码喷洒

一. 域内用户枚举原理和流量 1. 原理 在AS-REQ阶段客户端向AS发送用户名&#xff0c;cname字典存放用户名&#xff0c;AS对用户名进行验证&#xff0c;用户存在和不存在返回的数据包不一样。 不同之处主要是在返回数据包中的状态码不同&#xff0c;根据不同的状态码来区分账…

论文阅读笔记(十二)——Augmenting large language models with chemistry tools

论文阅读笔记(十二)——Augmenting large language models with chemistry tools TOC Abstract 大型语言模型&#xff08;LLMs&#xff09;在跨领域任务中表现出色&#xff0c;但在化学相关问题上却表现不佳。这些模型也缺乏外部知识源的访问权限&#xff0c;限制了它们在科…

Go-知识并发控制Context

Go-知识并发控制Context 1. 介绍2. 实现原理2.1 接口定义2.2 Deadline()2.3 Done()2.4 Err()2.5 Value() 3. 空 context4. cancelCtx4.1 Done()4.2 Err()4.3 cancel()4.4 WithCancel4.5 例子4.6 总结 5. timerCtx5.1 Deadline5.2 cancel5.3 WithDeadline5.4 WithTimeout5.5 例子…

linux tomcat版本漏洞升级

Tomcat Session 反序列化代码执行漏洞(CVE-2020-9484) Tomcat 安全限制绕过漏洞(CVE-2018-8034) Tomcat远程代码执行漏洞(CVE-2017-12615) 以上均可以升级版本处理&#xff0c;小版本升级方法 tomcat安装请查看https://blog.csdn.net/qq_42250832/article/details/139015573 1、…

2024医美如何做抖音医美抖音号,本地团购、短视频直播双ip爆品引流,实操落地课

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89307619 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 01-0-序.mp4 02-01-账号定位.mp4 03-02-误区.mp4 04-03-五件套.mp4 05-04-文案怎么来.mp4 06-05-对标怎么弄.mp4 07-06-人设怎…

计算机网络期末复习(1)计算机网络在信息时代对的作用 计算机网络的定义和分类 三种交换方法

计算机网络在信息时代扮演着至关重要的角色&#xff0c;它极大地改变了我们生活、工作和学习的方式。 计算机网络在信息时代的作用 信息共享与传播&#xff1a;计算机网络使全球范围内的信息快速共享成为可能&#xff0c;无论是新闻、学术研究还是娱乐内容&#xff0c;都可以…

MyBatis源码分析--02:SqlSession建立过程

我们再来看看MyBatis使用流程&#xff1a; InputStream inputStream Resources.getResourceAsStream("myBatis_config.xml"); SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream); SqlSession session sqlSessionFactory.op…

AI编程新手快速体验SpringCloud Alibaba 集成AI功能

上周六写了一篇文章 震撼发布&#xff01;Spring AI 框架重磅上线&#xff0c;Java 集成 AI 轻松搞定&#xff01; 部分同学可能没有科学上网的条件&#xff0c;本地ollama 集成又比较笨重。趁着周六&#xff0c;写一篇基于SpringCloud Alibaba 集成AI的文章。 先简单介绍…

浅析R16移动性增强那些事儿(DAPS/CHO/MRO)

R16移动性增强相关技术总结 Dual Active Protocol Handover Dual Active Protocol Handover意为双激活协议栈切换&#xff0c;下文简称DAPS切换&#xff0c;DAPS切换的核心思想是切换过程中&#xff0c;在UE成功连接到目标基站前继续保持和源基站的连接和数据传输&#xff0c;…

MinIO 使用

MinIO自建对象存储 1、dock-compose 使用dock-compose拉取 minio:image: "minio/minio"container_name: minioports:- "9000:9000"- "9001:9001"volumes:- "./minio/data1:/data1"- "./minio/data2:/data2"restart: on-fai…

网络原理-TCP/IP --传输层(UDP)

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 目录 端口号UDP协议 端口号 我们在学习套接字的时候,涉及到两个概念:IP地址和端口号 IP地址是用来确定主机,这是网络层提供的概念 而端口号就是用来确定主机上的应用程序,就是传输层的概念的…

leetcode102. 二叉树的层序遍历

一、题目描述&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 二、输入输出实例&#xff1a; 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&am…

c++(七)

c&#xff08;七&#xff09; 内联函数内联函数的特点为什么要有内联函数内联函数是如何工作的呢 类型转换异常处理智能指针单例模式懒汉模式饿汉模式 VS中数据库的相关配置 内联函数 修饰类的成员函数&#xff0c;关键字&#xff1a;inline inline 返回值类型 函数名(参数列…

【C++】———list容器

前言 1.list容器简单来说其实就是之前的链表结构。 2.这里的list用的是双向带头结点的循环链表。 目录 前言 一 构造函数 1.1 list (); 1.2 list (size_type n, const value_type& val value_type() ); 1.3 list (InputIterator first, InputIterator last…

21.Redis之分布式锁

1.什么是分布式锁 在⼀个分布式的系统中, 也会涉及到多个节点访问同⼀个公共资源的情况. 此时就需要通过 锁 来做互斥控制, 避免出现类似于 "线程安全" 的问题. ⽽ java 的 synchronized 或者 C 的 std::mutex, 这样的锁都是只能在当前进程中⽣效, 在分布式的这种多…

计算机系统结构之互联网络

一、基本的单级互联网络 1、立方体单级网络 立方体单级网络的名称来源于下图所示的三维立方体结构。每个顶点&#xff08;网络的节点&#xff09;代表一个处理单元&#xff0c;共有8个处理单元&#xff0c;用zyx三位二进制编号。 Cubei函数表式相连的入端和出端的二进制编号只…

海外媒体通稿:9个极具创意的旅游业媒体推广案例分享-华媒舍

如今&#xff0c;旅游业正迅速发展&#xff0c;媒体推广成为吸引游客的关键。为了更好地展示旅游目的地&#xff0c;许多创意而富有创新的媒体推广策略应运而生。本文将介绍九个极富创意的旅游业媒体推广案例&#xff0c;为广大从业者带来灵感和借鉴。 1. 视频系列&#xff1a;…

Hadoop3:MapReduce的序列化和反序列化

一、概念 1、序列化 就是把内存中的对象&#xff0c;转换成字节序列 &#xff08;或其他数据传输协议&#xff09;以便于存储到磁 盘&#xff08;持久化&#xff09;和网络传输。 2、反序列化 就是将收到字节序列&#xff08;或其他数据传输协议&#xff09;或者是磁盘的持…