原创
[django项目] 利用elasticsearch实现搜索功能
2019-08-25 11:45:54 Makesths
阅读数 123
更多
分类专栏: django笔记
新闻搜索
I. 搜索功能分析
本节我们来完成新闻搜索功能, 首先让我们来思考一下,要做一个通过关键词搜索文章的功能,需要搜索哪些字段,以及使用什么技术方案呢?
既然我们是准备做新闻博客网站, 那我们就可以拿同类型网站的做一下对比, 例如CSDN, 简书, 博客园等, 这些常见的博客网站其主要覆盖的搜索字段有:
- 标题
- 内容
- 作者
实现这些字段检索的技术方案有以下两种:
- mysql的模糊查询
%like%
- 优点:实现起来简单
- 缺点:数据量比较大的情况下,查询效率极低
- 全文检索引擎
- 优点:专业的全文检索引擎,效率高
- 缺点:实现起来比较复杂
本项目选择使用过全文检索引擎。自行实现django框架和全文检索引擎的代码比较麻烦,抱着不重复造轮子的原则,这里我们选用django的第三方包djangohaystack
。它支持多种全文检索引擎,本项目选择最流行的全文检索引擎之一elasticsearch
。
II. elasticsearch介绍
elasticsearch 原理:http://developer.51cto.com/art/201904/594615.htm
工作原理示意图
![[django项目] 利用elasticsearch实现搜索功能_第1张图片](http://img.e-com-net.com/image/info8/f27d0f5b981342158ec6594b9b7fbe21.jpg)
- 首先来看图中, 用户将
全文搜索
的请求发送至django, 即输入搜索内容
- 全文搜索需要
分词
和模糊查询
, 这些操作在mysql中也可以使用, 但如果遇到数据量大的项目, 效率会很低, 因此, 就需要借助搜索引擎elasticsearch
- 要实现查询, 那么我们的django需要连接
mysql
和elasticsearch
:
- 连接mysql使用的是
mysqlclient
- 连接elasticsearch使用的是
django-haystack
, 以及python的es驱动
- elasticsearch会去到mysql中
获取数据
, 然后进行索引
, 并储存到它自己那里
- 然后django就会利用haystack到elasticsearch中查询想要的数据, 即执行搜索
- es查询到后返回给haystack(haystack是属于django项目中的一个从外部引入的app)
- haystack会返回给django框架, 然后django再展示给用户看, 即展示搜索结果
III. docker介绍与安装
1>docker介绍
1.1>什么是docker?
- 简化创建,部署,运行应用程序的一个工具
- 打包应用程序所需的库和依赖环境
- 精简的虚拟机
![[django项目] 利用elasticsearch实现搜索功能_第2张图片](http://img.e-com-net.com/image/info8/6134fe74ad234803992917c153ee1c57.jpg)
1.2>为什么使用docker?
![[django项目] 利用elasticsearch实现搜索功能_第3张图片](http://img.e-com-net.com/image/info8/f943b16c69be411297cd083106c0aa62.jpg)
流行,方便,强大
1.3>docker vs 虚拟机
![[django项目] 利用elasticsearch实现搜索功能_第4张图片](http://img.e-com-net.com/image/info8/ab7bf16d0a9f4042a5619576bef2c0df.jpg)
![[django项目] 利用elasticsearch实现搜索功能_第5张图片](http://img.e-com-net.com/image/info8/7da73586873e4e1f801a75c324f5ce5c.jpg)
1.4>docker架构
![[django项目] 利用elasticsearch实现搜索功能_第6张图片](http://img.e-com-net.com/image/info8/5f0bbd80faba4e9db6fa3e9b239e058f.jpg)
-
架构
-
docker 对象
-
docker Hub
-
安装
官方安装文档
lsb_release -a
uname -a
2>ubuntu下安装
如果是第一次安装,你需要先添加docker的源然后再安装
2.1>添加阿里云源
- 百度搜索"阿里云源"
- 选择Ubuntu然后复制源链接
- 桌面创建一个txt文件, 重命名为
sources.list
- 使用NotePad++或是pycharm打开, 粘贴源链接, 保存
- 粘贴文件到linux的家目录
cd /etc/apt/
- 备份原有的
sources.list
, 使用命令: sudo cp sources.list sources.dbk.list
- 然后
sudo rm sources.list
, 再将你刚才创建的sources.list
拷贝到/etc/apt/
- 运行命令:
sudo apt-get update
, 刷新源
- 如果有问题则
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:
- The Docker client contacted the Docker daemon.
- The Docker daemon pulled the “hello-world” image from the Docker Hub.
(amd64)
- The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
- 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/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
为了方便使用,不用sudo就可以运行docker命令,安装好docker后再命令行输入如下命令:
$ sudo usermod -aG docker $USER
运行正常后,重新连接即可。
IIII. 搜索功能环境搭建
1>在docker中安装elasticsearch
1.1>获取镜像
$ docker pull elasticsearch:2.4.6
1.2>安装中文分词插件
可以创建容器之后再安装插件,为了后面部署方便,我们创建镜像。elasticsearch的中文分词插件是elasticsearch-ik
,国人开发,github地址。
根据文档介绍,2.4.6版本对应的ik是1.10.16
![[django项目] 利用elasticsearch实现搜索功能_第7张图片](http://img.e-com-net.com/image/info8/b0d04b01e4a94fc68ab6b05d340bb679.jpg)
因为直接使用elasticsearch的plugin命令安装会报错,所以通过下载后解压到相应文件夹的方式安装。
)]
a.下载es-ik后,将其解压到名为ik的文件夹
~$ unzip elasticsearch-analysis-ik-1.10.6.zip -d ./ik
b.在ik所在文件下创建名为Dockerfile
的文件,内容如下
FROM elasticsearch:2.4.6
MAINTAINER Fisher "[email protected]"
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
拷贝到家目录下,然后解压
#然后在家目录中解压
~$ unzip elasticsearch.zip
然后运行下面的命令创建容器
~$ 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"
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
3>安装djangohaystack
官方文档
3.1>安装
pip install django-haystack
3.2>配置文件
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'
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:9200/',
'INDEX_NAME': 'tzpython',
},
}
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
HAYSTACK_SIGNAL_PROCESSOR = ‘haystack.signals.RealtimeSignalProcessor’
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
3.3>安装elasticsearch-py
haystack操作es还需要python的es驱动。兼容性见官网
![[django项目] 利用elasticsearch实现搜索功能_第9张图片](http://img.e-com-net.com/image/info8/679dff75e03947b6aad03462165e71eb.jpg)
根据官网,选择2.4.1版本
pip install elasticsearch==2.4.1
至此,环境搭建完成。相对应的es,es-ik,haystack,es-python的版本请保持一致。
V. 新闻搜索功能实现
1>业务流程分析
设查询文章时输入的参数为q
- 判断是否传递查询参数
q
- 如果没有传递
q
,则直接返回热门新闻数据
- 如果有传递,则返回查询结果
- 分页
2>接口设计
- 接口说明:
类目 |
说明 |
请求方法 |
GET |
url定义 |
/news/search/ |
参数格式 |
查询参数 |
- 参数说明:
参数名 |
类型 |
是否必须 |
描述 |
q |
字符串 |
否 |
查询的关键字 |
page |
整数 |
否 |
页码 |
-
返回结果:
搜索页面html
3>后端代码
3.1>创建haystack数据模型
在apps/news/目录下创建search_indexes.py
文件,注意文件名必须使用search_indexes.py,代码如下:
from haystack import indexes
from .models import News
class NewsIndex(indexes.SearchIndex, indexes.Indexable):
“”"
这个模型的作用类似于django的模型,它告诉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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
3.2>创建索引数据模板
根据上面创建的模型中的第一个text
字段中的use_template=True
参数,还需要创建一个索引数据模板,用来告诉搜索引擎需要索引哪些字段。
在templates中创建文件search/indexes//_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’
def get(self, request, *args, **kwargs):
query = request.GET.get('q')
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')
paginator = Paginator(hot_news, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
try:
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,
'query': query
})
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']:
context['page'] = context['page_obj']
return context
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
3.5>路由
在news/urls.py中添加如下路由
path('news/search/', views.NewsSearchView.as_view(), name='news_search')
4>前端代码
4.1>自定义过滤器
在news/templatetags/news_template_filters.py中定义一个处理分页的过滤器
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
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 %}
<div class=“main-contain “>
<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>
</div>
<div class="content">
{% if query %}
<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>
{% else %}
<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>
{% endif %}
<div class="page-box" id="pages">
<div class="pagebar" id="pageBar">
<a class="al">{{ page.paginator.count|default:0 }}条</a>
{% if page.has_previous %}
{% if query %}
<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 %}
{% 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 %}
{% 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 %}
</div>
</div>
</div>
</div>
{% endblock %}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
4.3>css代码
修改static/css/news/search.css如下:
#main {
margin-top: 25px;
min-height: 700px;
}
#main .main-contain {
width: 800px;
float: left;
background: #fff;
}
.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;
}
.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;
}
.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;
}
#pages {
padding: 32px 0 10px;
}
.page-box {
text-align: center;
}
#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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
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('已全部加载,没有更多内容!')
}
}
}
});
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('服务器超时,请重试!')
})
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
编写完上述代码后就可以运行搜索了,如果运行过程中出现搜索不到结果的问题,请先去检查docker容器的运行状态,到控制台中输入:curl http://127.0.0.1:9200
查看状态,若连接失败并且输入docker ps
时没有内容,那么说明docker没有运行,使用以下方法可以设置docker容器的启动
VI. docker 容器相关启动
docker 是安装在ubuntu中的,所以如果你重启了ubuntu,创建好的elasticsearch容器当然也会关闭。那怎么打开关闭的容器呢?
-
先查询容器名, 运行命令:
docker ps -a
-
找到你要启动的容器名,然后运行命令
docker start 容器名
即可启动容器
如果希望docker容器能够遇到错误自动重启, 则可以在创建容器时携带参数:
docker run --restart=on-failure
如果已经创建了docker容器, 则可以通过update来更新设置:
docker update --restart=on-failure <CONTAINER ID>
其他参数的参考如下:
Flag |
Description |
no |
不自动重启容器. (默认value) |
on-failure |
容器发生error而退出(容器退出状态不为0)重启容器 |
unless-stopped |
在容器已经stop掉或Docker stoped/restarted的时候才重启容器 |
always |
在容器已经stop掉或Docker stoped/restarted的时候才重启容器 |
如果想要docker容器开机自启, 可以参考如下设置方法:[reboot 后 Docker服务及容器自动启动设置](https://blog.csdn.net/wxb880114/article/details/82904765