[django项目] 利用elasticsearch实现搜索功能

新闻搜索

I. 搜索功能分析

本节我们来完成新闻搜索功能, 首先让我们来思考一下,要做一个通过关键词搜索文章的功能,需要搜索哪些字段,以及使用什么技术方案呢?

既然我们是准备做新闻博客网站, 那我们就可以拿同类型网站的做一下对比, 例如CSDN, 简书, 博客园等, 这些常见的博客网站其主要覆盖的搜索字段有:

  1. 标题
  2. 内容
  3. 作者

实现这些字段检索的技术方案有以下两种:

  1. mysql的模糊查询 %like%
    1. 优点:实现起来简单
    2. 缺点:数据量比较大的情况下,查询效率极低
  2. 全文检索引擎
    1. 优点:专业的全文检索引擎,效率高
    2. 缺点:实现起来比较复杂

本项目选择使用过全文检索引擎。自行实现django框架和全文检索引擎的代码比较麻烦,抱着不重复造轮子的原则,这里我们选用django的第三方包djangohaystack。它支持多种全文检索引擎,本项目选择最流行的全文检索引擎之一elasticsearch

II. elasticsearch介绍

elasticsearch 原理:http://developer.51cto.com/art/201904/594615.htm

工作原理示意图

[外链图片转存失败(img-HvAY3Wat-1566702560769)(E:\Fire\QQfire\992994875\FileRecv\Django项目_心蓝班\7.新闻搜索\assets\1566393245580.png)]

  1. 首先来看图中, 用户将全文搜索的请求发送至django, 即输入搜索内容
  2. 全文搜索需要分词模糊查询, 这些操作在mysql中也可以使用, 但如果遇到数据量大的项目, 效率会很低, 因此, 就需要借助搜索引擎elasticsearch
  3. 要实现查询, 那么我们的django需要连接mysqlelasticsearch:
    1. 连接mysql使用的是mysqlclient
    2. 连接elasticsearch使用的是django-haystack, 以及python的es驱动
  4. elasticsearch会去到mysql中获取数据, 然后进行索引, 并储存到它自己那里
  5. 然后django就会利用haystack到elasticsearch中查询想要的数据, 即执行搜索
  6. es查询到后返回给haystack(haystack是属于django项目中的一个从外部引入的app)
  7. haystack会返回给django框架, 然后django再展示给用户看, 即展示搜索结果

III. docker介绍与安装

1>docker介绍

1.1>什么是docker?
  • 简化创建,部署,运行应用程序的一个工具
  • 打包应用程序所需的库和依赖环境
  • 精简的虚拟机
1.4>docker架构
  • 架构

    • 客户端
    • 守护进程
    • 仓库
  • docker 对象

    • 镜像 精简的linux
    • 容器
    • 服务
  • docker Hub

  • 安装

    官方安装文档

    lsb_release -a   # 查看系统信息
    uname -a		# 查看位数
    

2>ubuntu下安装

如果是第一次安装,你需要先添加docker的源然后再安装

2.1>添加阿里云源
  1. 百度搜索"阿里云源"
  2. 选择Ubuntu然后复制源链接
  3. 桌面创建一个txt文件, 重命名为sources.list
  4. 使用NotePad++或是pycharm打开, 粘贴源链接, 保存
  5. 粘贴文件到linux的家目录
  6. cd /etc/apt/
  7. 备份原有的sources.list, 使用命令: sudo cp sources.list sources.dbk.list
  8. 然后sudo rm sources.list, 再将你刚才创建的sources.list拷贝到/etc/apt/
  9. 运行命令: sudo apt-get update, 刷新源
  10. 如果有问题则sudo apt-get upgrade更新系统, 然后再刷新源
2.2>安装证书
  $ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
2.3>添加官方GPGkey
  $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
2.4>添加docker源
  $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

3>安装 docker ce

3.1>更新包索引
  $ sudo apt-get update
3.2>安装docker

(这个可能会很久)

$ sudo apt-get install docker-ce
3.3>检测是否安装成功
$ sudo docker run hello-world

安装成功会出现如下输出:

Hello from Docker!
  This message shows that your installation appears to be working correctly.
  
  To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
   2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
   3. The Docker daemon created a new container from that image which runs the
      executable that produces the output you are currently reading.
   4. The Docker daemon streamed that output to the Docker client, which sent it
      to your terminal.
  
  To try something more ambitious, you can run an Ubuntu container with:
   $ docker run -it ubuntu bash
  
  Share images, automate workflows, and more with a free Docker ID:
   https://hub.docker.com/
  
  For more examples and ideas, visit:
   https://docs.docker.com/get-started/

为了方便使用,不用sudo就可以运行docker命令,安装好docker后再命令行输入如下命令:

  $ sudo usermod -aG docker $USER

运行正常后,重新连接即可。

IIII. 搜索功能环境搭建

1>在docker中安装elasticsearch

1.1>获取镜像
# 注意:因为haystack目前支持的elasticsearch版本为 1.x和2.x
# 所以这里选择2.4.6
$ docker pull elasticsearch:2.4.6
1.2>安装中文分词插件

可以创建容器之后再安装插件,为了后面部署方便,我们创建镜像。elasticsearch的中文分词插件是elasticsearch-ik,国人开发,github地址

根据文档介绍,2.4.6版本对应的ik是1.10.16

[外链图片转存失败(img-0oOzRk0J-1566702560772)(新闻搜索.assets/)]

因为直接使用elasticsearch的plugin命令安装会报错,所以通过下载后解压到相应文件夹的方式安装。

新闻搜索.assets/1563190261012.png)]

a.下载es-ik后,将其解压到名为ik的文件夹

~$ unzip elasticsearch-analysis-ik-1.10.6.zip -d ./ik

b.在ik所在文件下创建名为Dockerfile的文件,内容如下

# dockerfile
FROM	elasticsearch:2.4.6
MAINTAINER	Fisher "xinlan@tanzhou.com"
ADD 	./ik/ /usr/share/elasticsearch/plugins/ik/	

然后运行命令

~$ sudo docker build -t xinlan/els-ik:2.4.6 .

如果出现下面的错误是因为没有带sudo

~$ docker build -t xinlan/els-ik:2.4.6 .
error checking context: 'no permission to read from '/home/wcf/.viminfo''.

运行成功后,会在你的docker中创建一个新的镜像

~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
xinlan/els-ik       2.4.6               ecf93deefe2b        26 minutes ago      489MB
elasticsearch       2.4.6               5e9d896dc62c        10 months ago       479MB

如果上面的步骤搞不定请直接下载如下镜像

$ sudo docker image pull wcfdehao/els-ik:2.4.6

2>创建容器

利用上面创建好的镜像创建一个容器。为了能够进行设置elasticsearch,通过卷挂载的方式创建容器。

将提供给大家的es配置文件elasticsearch.zip拷贝到家目录下,然后解压

# 在xshell中使用rz命令将elasticsearch.zip文件传到虚拟机的家目录中
#然后在家目录中解压
~$ unzip elasticsearch.zip

然后运行下面的命令创建容器

# 根据上面创建的镜像创建容器,需要将/home/wcf/elasticsearch/config配置文件路径修改为你自己的路径
~$ docker run -dti --network=host --name es-ik -v /home/pyvip/elasticsearch/config:/usr/share/elasticsearch/config wcfdehao/els-ik:2.4.6

# 查看当前运行的容器
~$ docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS               NAMES
61c42c36a8f2        xinlan/els-ik:2.4.6   "/docker-entrypoint.…"   29 minutes ago      Up 28 minutes                           es-ik

你也可以在创建时携带restart参数, 让容器可以自启, 具体可以参考本文的后半段

最后运行curl命令检测es是否正常

~$ curl http://127.0.0.1:9200
{
  "name" : "Shard",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "Pq6BQQhTQN6q6ML6ThPlbw",
  "version" : {
    "number" : "2.4.6",
    "build_hash" : "5376dca9f70f3abef96a77f4bb22720ace8240fd",
    "build_timestamp" : "2017-07-18T12:17:44Z",
    "build_snapshot" : false,
    "lucene_version" : "5.5.4"
  },
  "tagline" : "You Know, for Search"
}

3>安装djangohaystack

官方文档

3.1>安装
# 安装djangohaystack
# 使用的是当期最新版本 2.8.1
pip install django-haystack
3.2>配置文件
# 将Haystack添加到`INSTALLED_APPS`中
# settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'haystack',
    'user',
    'news',
    'doc',
    'course',
    'verification'
]
# 配置搜索引擎
# 在settings.py中添加如下设置
# 全文搜索引擎haystack 配置
# 不同的搜索引擎,配置不同,详情见官方文档
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://127.0.0.1:9200/',    # 此处为elasticsearch运行的服务器ip地址和端口
        'INDEX_NAME': 'tzpython',           # 指定elasticserach建立的索引库名称
    },
}

# 搜索结果每页显示数量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
# 实时更新index
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
3.3>安装elasticsearch-py

haystack操作es还需要python的es驱动。兼容性见官网

[外链图片转存失败(img-1BesZeeK-1566702560773)()]

根据官网,选择2.4.1版本

pip install elasticsearch==2.4.1

至此,环境搭建完成。相对应的es,es-ik,haystack,es-python的版本请保持一致。

V. 新闻搜索功能实现

1>业务流程分析

设查询文章时输入的参数为q

  • 判断是否传递查询参数q
  • 如果没有传递q,则直接返回热门新闻数据
  • 如果有传递,则返回查询结果
  • 分页

2>接口设计

  1. 接口说明:
类目说明
请求方法GET
url定义/news/search/
参数格式查询参数
  1. 参数说明:
参数名类型是否必须描述
q字符串查询的关键字
page整数页码
  1. 返回结果:

    搜索页面html

3>后端代码

3.1>创建haystack数据模型

在apps/news/目录下创建search_indexes.py文件,注意文件名必须使用search_indexes.py,代码如下:

   # !/usr/bin/env python
   # -*- coding:utf-8 -*-
   # create_time: 2019/7/13
   # Author = '心蓝'
   from haystack import indexes
   from .models import News
   
   
   class NewsIndex(indexes.SearchIndex, indexes.Indexable):
       """
       这个模型的作用类似于django的模型,它告诉haystack哪些数据会被
       放进查询返回的模型对象中,以及通过哪些字段进行索引和查询
       """
       # 这字段必须这么写,用来告诉haystack和搜索引擎要索引哪些字段
       text = indexes.CharField(document=True, use_template=True)
       # 模型字段,打包数据
       id = indexes.CharField(model_attr='id')
       title = indexes.CharField(model_attr='title')
       digest = indexes.CharField(model_attr='digest')
       content = indexes.CharField(model_attr='content')
       image_url = indexes.CharField(model_attr='image_url')
   
       def get_model(self):
           """
           返回建立索引的模型
           :return:
           """
           return News
   
       def index_queryset(self, using=None):
           """
           返回要建立索引的数据查询集
           :param using:
           :return:
           """
           # 这种写法遵从官方文档的指引
           return self.get_model().objects.filter(is_delete=False)
3.2>创建索引数据模板

根据上面创建的模型中的第一个text字段中的use_template=True参数,还需要创建一个索引数据模板,用来告诉搜索引擎需要索引哪些字段。

在templates中创建文件search/indexes/<your app name>/<model name>_text.txt

所以本项目需要创建search/indexes/news/news_text.txt,文件内容如下:

   {{ object.title }}
   {{ object.digest }}
   {{ object.content }}
   {{ object.author.username }}
3.3>创建索引

按上面的步骤配置好后,就可以运行haystack的命令创建索引了

~$ python manage.py rebuild_index	# 看到如下信息,说明运行成功
WARNING: This will irreparably remove EVERYTHING from your search index in connection 'default'.
Your choices after this are to restore from backups or rebuild via the `rebuild_index` command.
Are you sure you wish to continue? [y/N] y
Removing all documents from your index because you said so.
All documents removed.
Indexing 889 新闻文章
GET /tzpython/_mapping [status:404 request:0.005s]

如果出现了get 404错误, 没关系不用管它

3.4>视图代码

在news/views.py中添加如下视图

   from haystack.generic_views import SearchView
   
   class NewsSearchView(SearchView):
       """
       新闻搜索视图
       """
       # 设置搜索模板文件
       template_name = 'news/search.html'
   
       # 重写get请求,如果请求参数q为空,返回模型News的热门新闻数据
       # 否则根据参数q搜索相关数据
       def get(self, request, *args, **kwargs):
           # 1. 获取查询参数
           query = request.GET.get('q')
           # 2. 如果没有查询参数
           if not query:
               # 则返回热门新闻
               # 获取热门新闻对象, 包含外键标签, 查询数据并做筛选和排序
               hot_news = HotNews.objects.select_related('news__tag').only('news__title', 'news__image_url', 'news_id', 'news__tag__name').filter(is_delete=False).order_by('priority', '-news__clicks')
               # 分页, 从配置文件中拿到haystack参数
               paginator = Paginator(hot_news, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
               try:
                   # 拿到前端传递的page,
                   page = paginator.get_page(int(request.GET.get('page')))
               except Exception as e:
                   # 如果出错则返回第一页,保证容错性
                   page = paginator.get_page(1)
   
               return render(request, self.template_name, context={
                   'page': page,
                   #'paginator': paginator,
                   'query': query
               })
           # 3. 如果有查询参数
           else:
               # 则执行搜索
               return super().get(request, *args, **kwargs)
   
       def get_context_data(self, *args, **kwargs):
           """
           在context中添加page变量
           """
           context = super().get_context_data(*args, **kwargs)
           if context['page_obj']:
               # 捕获page_obj,将其赋值到page
               context['page'] = context['page_obj']
           return context
3.5>路由

在news/urls.py中添加如下路由

       path('news/search/', views.NewsSearchView.as_view(), name='news_search')

4>前端代码

4.1>自定义过滤器

在news/templatetags/news_template_filters.py中定义一个处理分页的过滤器

# !/usr/bin/env python
# -*- coding:utf-8 -*-
# create_time: 2019/7/14
# Author = '心蓝'
from django import template

register = template.Library()


@register.filter
def page_bar(page):
    page_list = []
    if page.number != 1:
        page_list.append(1)
    if page.number - 3 > 1:
        page_list.append('...')
    if page.number - 2 > 1:
        page_list.append(page.number - 2)
    if page.number - 1 > 1:
        page_list.append(page.number - 1)
    page_list.append(page.number)
    if page.paginator.num_pages > page.number + 1:
        page_list.append(page.number + 1)
    if page.paginator.num_pages > page.number + 2:
        page_list.append(page.number + 2)
    if page.paginator.num_pages > page.number + 3:
        page_list.append('...')
    if page.paginator.num_pages != page.number:
        page_list.append(page.paginator.num_pages)
    return page_list
4.2>前端html代码
{% extends 'base/base.html' %}
{% load static %}
{% load news_customer_filters %}
{% block title %}新闻搜索{% endblock %}
{% block link %}
    <link rel="stylesheet" href="{% static 'css/news/search.css' %}">
{% endblock %}

{% block main_contain %}
    <!-- main-contain start  -->
    <div class="main-contain ">
        <!-- search-box start -->
        <div class="search-box">
            <form action="" style="display: inline-flex;">

                <input type="search" placeholder="请输入要搜索的内容" name="q" class="search-control">


                <input type="submit" value="搜索" class="search-btn">
            </form>
            <!-- 可以用浮动 垂直对齐 以及 flex  -->
        </div>
        <!-- search-box end -->
        <!-- content start -->
        <div class="content">
            {% if query %}
                <!-- search-list start -->
                <div class="search-result-list">
                    <h2 class="search-result-title">搜索结果 <span>{{ page.paginator.num_pages|default:0 }}</span></h2>
                    <ul class="news-list">
                        {% load highlight %}
                        {% for news in page.object_list %}
                            <li class="news-item clearfix">
                                <a href="{% url 'news:news_detail' news.id %}" class="news-thumbnail" target="_blank"><img src="{{ news.image_url }}" alt=""></a>
                                <div class="news-content">
                                    <h4 class="news-title">
                                        <a href="{% url 'news:news_detail' news.id %}">{% highlight news.title with query %}</a>
                                    </h4>
                                    <p class="news-details">{{ news.digest }}</p>
                                    <div class="news-other">
                                        <span class="news-type">{{ news.object.tag.name }}</span>
                                        <span class="news-time">{{ news.object.update_time }}</span>
                                        <span class="news-author">{% highlight news.object.author.username with query %}</span>
                                    </div>
                                </div>

                            </li>
                        {% empty %}
                            <li class="news-item clearfix">
                                <p>没有找到你想要的找的内容.</p>
                            </li>
                        {% endfor %}
                    </ul>
                </div>

                <!-- search-list end -->
            {% else %}
                <!-- news-contain start -->

                <div class="news-contain">
                    <div class="hot-recommend-list">
                        <h2 class="hot-recommend-title">热门推荐</h2>
                        <ul class="news-list">
                            {% for hotnews in page %}
                                <li class="news-item clearfix">
                                    <a href="#" class="news-thumbnail">
                                        <img src="{{ hotnews.news.image_url }}">
                                    </a>
                                    <div class="news-content">
                                        <h4 class="news-title">
                                            <a href="{% url 'news:news_detail' hotnews.news_id %}">{{ hotnews.news.title }}</a>
                                        </h4>
                                        <p class="news-details">{{ hotnews.news.digest }}</p>
                                        <div class="news-other">
                                            <span class="news-type">{{ hotnews.news.tag.name }}</span>
                                            <span class="news-time">{{ hotnews.update_time }}</span>
                                            <span class="news-author">{{ hotnews.news.author.username }}</span>
                                        </div>
                                    </div>
                                </li>
                            {% endfor %}


                        </ul>
                    </div>
                </div>


                <!-- news-contain end -->
            {% endif %}
            <!-- Pagination start-->
            <div class="page-box" id="pages">
                <div class="pagebar" id="pageBar">
                    <a class="al">{{ page.paginator.count|default:0 }}条</a>
                    <!-- prev page start-->
                    {% if page.has_previous %}
                    <!-- has_previous判断是否有上一页-->
                        {% if query %}
                    	<!-- previous_page_number上一页的页码-->
                            <a href="{% url 'news:news_search' %}?q={{ query }}&page={{ page.previous_page_number }}"
                               class="prev">上一页</a>
                        {% else %}
                            <a href="{% url 'news:news_search' %}?page={{ page.previous_page_number }}"
                               class="prev">上一页</a>
                        {% endif %}
                    {% endif %}
                    <!-- prev page end-->

                 <!-- page bar start-->
                 <!-- 有上一页或是有下一页时执行,若都没有说明不需要分页 -->
                {% if page.has_previous or page.has_next %}
                <!-- 使用自定义的过滤器来处理分页 -->
                    {% for n in page|page_bar %}
                        {% if query %}
                            {% if n == '...' %}
                            <span class="point">{{ n }}</span>
                            {% else %}
                                {% if n == page.number %}
                                    <span class="sel">{{ n }}</span>
                                {% else %}
                                    <a href="{% url 'news:news_search' %}?page={{ n }}&q={{ query }}">{{ n }}</a>
                                {% endif %}
                            {% endif %}
                        {% else %}
                            {% if n == '...' %}
                                <span class="point">{{ n }}</span>
                            {% else %}
                                {% if n == page.number %}
                                    <span class="sel">{{ n }}</span>
                                {% else %}
                                    <a href="{% url 'news:news_search' %}?page={{ n }}">{{ n }}</a>
                                {% endif %}
                            {% endif %}
                        {% endif %}
                    {% endfor %}
                {% endif %}
                    <!-- page bar end-->

                    <!-- next page start-->
                    {% if page.has_next %}
                        {% if query %}
                            <a href="{% url 'news:news_search' %}?q={{ query }}&page={{ page.next_page_number }}"
                               class="prev">下一页</a>
                        {% else %}
                            <a href="{% url 'news:news_search' %}?page={{ page.next_page_number }}"
                               class="prev">下一页</a>
                        {% endif %}
                    {% endif %}
                    <!-- next page end-->


                </div>
            </div>
            <!-- Pagination end-->
        </div>
        <!-- content end -->
    </div>
    <!-- main-contain  end -->
{% endblock %}
4.3>css代码

修改static/css/news/search.css如下:

/* ================= main start ================= */
#main {
    margin-top: 25px;
    min-height: 700px;
}
/* ========= main-contain start ============ */
#main .main-contain {
    width: 800px;
    float: left;
    background: #fff;
}

/* ===  search-box start === */
.main-contain .search-box {
    padding: 40px 50px;
    width: 700px;
    box-shadow: 1px 2px rgba(0,0,0,.1);
    display: inline-flex;
}
.main-contain .search-box .search-control {
    width: 600px;
    height: 40px;
    border-radius: 20px 0 0 20px;
    border: 1px solid #ddd;
    border-right: none;
    padding-left: 0.88em;
    font-size: 20px;
}
.main-contain .search-box .search-btn {
    width: 100px;
    height: 40px;
    border: 1px solid red;
    background: red;
    color: #fff;
    font-size: 20px;
    border-radius:  0 20px 20px 0;
    cursor: pointer;
}
/* ===  search-box end === */

/* === content start === */
/* == search-list start == */
.content .search-result-list {
    padding-top: 20px;
}
.content .search-result-list .search-result-title {
    padding-left: 20px;
    font-size: 20px;
    line-height: 26px;
}
.content .search-result-list .search-result-title span {
    font-weight: 700;
    color: #ff6620;
}
/* == search-list end == */
/* == news-contain start == */
.content .news-contain .hot-recommend-list {
    padding-top: 20px;
}
.hot-recommend-list .hot-recommend-title {
    padding-left: 20px;
    font-size: 20px;
    line-height: 26px;
}
.content .news-contain li {
    border-bottom: 1px solid #ededed;
}
.news-list .news-item {
    padding: 20px;
}
.news-list .news-item .news-thumbnail {
    float: left;
    width: 224px;
    height: 160px;
    margin-right: 30px;
    overflow: hidden;
}
.news-item .news-thumbnail img {
    width: 100%;
    height: 100%;
    transition: all 0.3s ease-out;
}
.news-item .news-thumbnail:hover img {
    transform: scale(1.1);
    transition: all 0.3s ease-in;
}
.news-list .news-item .news-content {
    width: 500px;
    height: 170px;
    float: right;
    color: #878787;
    font-size: 14px;
}
.news-item .news-content .news-title{
    color: #212121;
    font-size: 22px;
    height: 52px;
    line-height: 26px;
    transition:all 0.3s ease-out;
}
.news-item .news-content .news-title:hover {
    color: #5b86db;
    transition:all 0.3s ease-in;
}
.news-item .news-content .news-details {
    height: 44px;
    line-height: 22px;
    margin-top: 19px;
    text-align: justify;
}
.news-item .news-content .news-other {
    margin-top: 30px;
}
.news-content .news-other .news-type {
    color: #5b86db;
}
.news-content .news-other .news-author {
    float: right;
    margin-right: 15px;
}
.news-content .news-other .news-time {
    float: right;
}
/* === current index start === */
#pages {
	padding: 32px 0 10px;
}

.page-box {
	text-align: center;
    /*font-size: 14px;*/
}

#pages a.prev, a.next {
	width: 56px;
	padding: 0
}

#pages a {
	display: inline-block;
	height: 26px;
	line-height: 26px;
	background: #fff;
	border: 1px solid #e3e3e3;
	text-align: center;
	color: #333;
	padding: 0 10px
}

#pages .sel {
	display: inline-block;
	height: 26px;
	line-height: 26px;
	background: #0093E9;
	border: 1px solid #0093E9;
	color: #fff;
	text-align: center;
	padding: 0 10px
}
#pages .point {
	display: inline-block;
	height: 26px;
	line-height: 26px;
	background: #fff;
	border: 1px solid #e3e3e3;
	text-align: center;
	color: #333;
	padding: 0 10px
}
.highlighted {
    font-weight: 700;
    color: #ff6620;
}
/* === current index end === */
/* === content end === */
/* ================= main end ================= */
4.4>js代码

搜索页面的js代码于新闻页面的雷同, 仅仅只需要改一些参数即可

$(() => {
    let iPage = 1;       // 当前页面页数
    let iTotalPage = 1;      // 总页数
    let bIsLoadData =false;      // 是否正在加载
    fn_load_docs();      //  加载文件列表
    // 页面滚动加载
    $(window).scroll(function () {
       // 浏览器窗口高度
        let showHeight = $(window).height();
       // 整个网页高度
        let pageHeight = $(document).height();
        //页面可以滚动的距离
        let canScrollHeight = pageHeight - showHeight;
        // 页面滚动了多少, 整个是随着页面滚动实时变化的
        let nowScroll = $(document).scrollTop();
        if ((canScrollHeight - nowScroll) < 100){
            if(!bIsLoadData){
                bIsLoadData = true;
                //判断页数,去更新新闻,小于总数才加载
                if(iPage < iTotalPage){
                    iPage += 1;
                    fn_load_docs();

                }else {
                    message.showInfo('已全部加载,没有更多内容!');
                    $('a.btn-more').html('已全部加载,没有更多内容!')
                }

            }
        }
    });

    // 获取docs信息
    function fn_load_docs() {
        $
            .ajax({
                url: '/doc/docs',
                type: 'GET',
                data: {page: iPage},
                dataType: 'json'
            })
            .done((res) => {
                if (res.errno === '0') {
                    iTotalPage = res.data.total_page;
                    res.data.docs.forEach((doc) => {
                        let content = `<li class="pay-item">
                        <div class="pay-img doc"></div>
                           <img src="${ doc.image_url }" alt="" class="pay-img doc">
                        <div class="d-contain">
                            <p class="doc-title">${ doc.title }</p>
                            <p class="doc-desc">${ doc.desc }</p>

                            <!-- /www/?xxx -->
                            <a href="${ doc.file_url }" class="pay-price" download="${ doc.file_name }">下载</a>
                        </div>
                    </li>`;
                        $('.pay-list').append(content);
                        bIsLoadData = false;
                        $('a.btn-more').html('滚动加载更多');
                    })
                } else {
                    message.showError(res.errmsg)
                }
            })
            .fail(() => {
                message.showError('服务器超时,请重试!')
            })
    }
});

编写完上述代码后就可以运行搜索了,如果运行过程中出现搜索不到结果的问题,请先去检查docker容器的运行状态,到控制台中输入:curl http://127.0.0.1:9200查看状态,若连接失败并且输入docker ps时没有内容,那么说明docker没有运行,使用以下方法可以设置docker容器的启动

VI. docker 容器相关启动

docker 是安装在ubuntu中的,所以如果你重启了ubuntu,创建好的elasticsearch容器当然也会关闭。那怎么打开关闭的容器呢?

  1. 先查询容器名, 运行命令:

    docker ps -a
    
  2. 找到你要启动的容器名,然后运行命令

    docker start 容器名
    

    即可启动容器

如果希望docker容器能够遇到错误自动重启, 则可以在创建容器时携带参数:

docker run --restart=on-failure

如果已经创建了docker容器, 则可以通过update来更新设置:

docker update --restart=on-failure <CONTAINER ID>

其他参数的参考如下:

FlagDescription
no不自动重启容器. (默认value)
on-failure容器发生error而退出(容器退出状态不为0)重启容器
unless-stopped在容器已经stop掉或Docker stoped/restarted的时候才重启容器
always在容器已经stop掉或Docker stoped/restarted的时候才重启容器

如果想要docker容器开机自启, 可以参考如下设置方法:reboot 后 Docker服务及容器自动启动设置
项目源码: https://gitee.com/hao4875/newssite

  • 7
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值