elasticsearch实现博客搜索
通过drf、drf-haystack、elasticsearch实现搜索
一、服务器中安装elasticsearch
es的启动需要java环境,所以我们先安装jdk
1、安装jdk
官网下载:jdk
在服务器/opt目录下新建个java文件夹,并修改权限
cd /opt
mkdir java
chmod java 777
将下载好的压缩包上传至服务器/opt/java目录下
解压
tar -zxvf jdk-8u202-linux-x64.tar.gz
配置环境变量
vi /etc/profile
将以下环境变量添加到文件下面
export JAVA_HOME=/opt/java/jdk1.8.0_202
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
重新加载配置文件
source /etc/profile
查看是否安装成功
java -version
2、安装elasticsearch
官网下载elasticsearch
将下载文件上传至服务器/opt目录下
解压文件
tar -zxvf elasticsearch-2.4.1.tar.gz
进入解压文件夹下的bin目录
cd /opt/elasticsearch-2.4.1/bin
看到elasticsearch启动文件
因为elasticsearch不能使用root用户启动,创建一个用户
创建用户
useradd user-es
创建所属组
chown user-es:user-es -R /opt/elasticsearch-2.4.1
切换用户
su user-es
进入启动文件目录
cd /opt/elasticsearch-2.4.1/bin
启动elasticsearch,后面加-d使用后台启动
./elasticsearch -d
没有报错即启动成功
3、django项目配置
安装以下依赖
djangorestframework==3.13.1
elasticsearch==2.4.1
django-haystack==3.1.1
drf-haystack==1.8.11
在项目setting.py文件添加haystack配置并添加app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'rest_framework',
'blog', # 博客app
'haystack',
]
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:9200/', # 此处为elasticsearch运行的服务器ip地址,端口号固定为9200
'INDEX_NAME': 'blogindex', # 指定elasticsearch建立的索引库的名称
}
}
# 添加此项,当数据库改变时,会自动更新索引,非常方便
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
在博客app编写模型文件model.py
class Blog(BaseModel):
title = models.CharField('标题', max_length=50)
content = models.TextField('博客内容')
description = models.TextField('博客描述')
created_time = indexes.DateTimeField(model_attr='created_time')
在blog文件夹下添加search_indexes.py,固定文件名称
from haystack import indexes
from .models import Blog
# 类名必须为需要检索的Model_name+Index,这里需要检索Blog,所以创建BlogIndex
class BlogIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
id = indexes.IntegerField(model_attr='id')
title = indexes.CharField(model_attr='title')
description = indexes.CharField(model_attr='description')
created_time = indexes.DateTimeField(model_attr='created_time')
# 设置模型
def get_model(self):
return Blog
# 设置查询范围
def index_queryset(self, using=None):
return self.get_model().objects.all()
text=indexes.CharField ,指定了将模型类中的哪些字段建立索引,而use_template=True说明后续我们还要指定一个模板文件,告知具体是哪些字段
每个索引里面必须有且只能有一个字段为 document=True,这代表 django haystack 和搜索引擎将使用此字段的内容作为索引进行检索(primary field)。注意,如果使用一个字段设置了document=True,则一般约定此字段名为text,这是在 SearchIndex 类里面一贯的命名,以防止后台混乱
在项目的“templates/search/indexes/应用名称/”下创建“模型类名称_text.txt”文件(例如 templates/search/indexes/blog/blog_text.txt),全小写即可。
{{ object.title}}
{{ object.content }}
{{ object.description }}
这样在使用text查询时将会从title、content、description三个中查询
修改settings.py中的模板路径
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'),], # 修改此处,否则django找不到模板文件
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
在blog目录下编写serializers.py序列化文件
from rest_framework import serializers
from .models import Blog
from drf_haystack.serializers import HaystackSerializer
from .search_indexes import BlogIndex
class BlogIndexSerializer(HaystackSerializer):
"""
Article索引结果数据序列化器
"""
class Meta:
index_classes = [BlogIndex]
fields = ['id', 'title', 'description', 'created_time'] # 需要序列化的字段,如果在search_indexes.py中没有配置这些字段,将会序列化成null
search_fields = ['text'] # 将使用text作为搜索字段
编写blog/views.py
from rest_framework import mixins
from .models import Blog
from . import serializers
from drf_haystack.generics import HaystackGenericAPIView
from rest_framework.pagination import PageNumberPagination
from django.core.paginator import InvalidPage
# 分页器
class MyPageNumberPagination(PageNumberPagination):
page_size = 5
page_size_query_param = 'page_size'
def paginate_queryset(self, queryset, request, view=None):
"""
Paginate a queryset if required, either returning a
page object, or `None` if pagination is not configured for this view.
"""
page_size = self.get_page_size(request)
if not page_size:
return None
paginator = self.django_paginator_class(queryset, page_size)
page_number = self.get_page_number(request, paginator)
try:
self.page = paginator.page(page_number)
except InvalidPage:
# 当超出最大页码时返回最后一页数据
self.page = paginator.page(paginator.num_pages)
if paginator.num_pages > 1 and self.template is not None:
# The browsable API should display pagination controls.
self.display_page_controls = True
self.request = request
return list(self.page)
class BlogSearchView(mixins.ListModelMixin, HaystackGenericAPIView):
"""
返回博客文章搜索列表
"""
index_models = [Blog] # 索引模型类
pagination_class = MyPageNumberPagination # 分页器
serializer_class = serializers.BlogIndexSerializer # 序列化器
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
编写blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('search', views.BlogSearchView.as_view())
]
编写项目urls.py
from django.urls import path, include
urlpatterns = [
path('api/blog/', include('blog.urls')),
]
创建数据库并迁移后,需要生成索引
python manage.py rebuild_index
启动项目,访问http://192.168.1.5:8000/api/blog/search?text=测试