Django和elasticsearch搜索引擎网站后端功能实现

Stella981
• 阅读 830

一、输入框智能提示(es提供了接口 )
修改type
需要在mapping中设置一个字段 suggest:{“type”:“completion”}
所以要修改我们定义的type:
在type中新增一个字段:suggest,由于es-dsl源码有一些问题,所以这样定义是会报错的,要自己定义一个CustomAnalyzer,再声明一个自定义的对象,ik_analyzer,再把对象赋给type中的suggest :

...
from elasticsearch_dsl.analysis import CustomAnalyzer as _CustomAnalyzer

class CustomAnalyzer(_CustomAnalyzer):

def get_analysis_definition(self):
# 这里什么都不做,只是为了避免报错的问题
return ()

# 声明一个自定义的对象,传递ik_max_word并且做大小写转换
ik_analyzer = CustomAnalyzer('ik_max_word', filter=['lowercase'])

class DuowanType(DocType):
...
# 定义suggest是为了完成自动补全功能。
# 由于es-dsl源码有一些问题,所以这样定义是会报错的,要自己定义一个CustomAnalyzer
suggest = Completion(analyzer=ik_analyzer)

生成suggest值
在save_to_es里面生成搜索建议
要通过生成suggest的接口来生成自己的结构。
在items类中定义一个全局函数gen_suggests,传递index和info_tuple用于weight信息,新建一个set用于去重,一个suggest数组用于保存返回的内容。遍历info_tuple,如果text字符串不为空,则调用es的analyze接口来分析字符串,再整理好需要返回的结构

def gen_suggests(index, info_tuple): # 用tuple 就可以传递多个weight信息 并且还可以按顺序
# 根据字符串生成搜索建议数组
uesd_words = set() # 用于去重
suggests = [] # 用于返回
for text, weight in info_tuple:
if text: # 排除空字符串
# 调用es的analyze接口来分析字符串
words = es.indices.analyze(index=index, analyzer='ik_max_word', params={'filter': ['lowercase']}, body=text)
anylyzed_words = set(r["token"] for r in words["tokens"] if len(r["token"]) > 1) # 用来过滤单个字
new_words = anylyzed_words - uesd_words # 去重
else:
new_words = set()
if new_words:
suggests.append({'input': list(new_words), 'weight': weight})
return suggests

然后在save_to_es中调用这个函数:

info_tuple = ((duowan.title, 10), (duowan.author, 7))
duowan.suggest = (gen_suggests(DuowanType._doc_type.index, info_tuple))

搭建django搜索网站
创建新的虚拟环境
进入虚拟环境并安装django包 pip install -i https://pypi.douban.com/simple/ django
然后用pycharm新建一个django的项目,直接运行,可以在log中看到服务器地址。
再新建一个static目录,把css,html,js文件粘贴进去,把html文件粘贴到templates目录下。
在urls文件中新增一个url

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from django.views.generic import TemplateView

urlpatterns = [
path('admin/', admin.site.urls),
url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
]

在settings中添加一行设置;

# 这里也可以用tuple 用tuple的话路径后面要加逗号
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static') # 可以传递多个
]

在index.html中把之类的导入css和js的语句改为

{% load staticfiles %}

... ...

这样就可以把settings中的static_url join到‘ ’ 内容前面,这样就可以找得到html文件了。

搜索建议
在虚拟环境中安装同版本的es-dsl
f模糊搜索
fuzzy:

GET duowan/video/_search
{
"query": {
"fuzzy": {
"title": {
"value": "军团骑士",
"fuzziness": 2,
"prefix_length": 3
}
}
},
"_source": ["title"]
}

fuzziness:编辑距离
prefix_length:前面的不参与变换的词的长度
“_source”: [“title”]:指明字段

suggest:

POST duowan/video/_search
{
"suggest": {
"my-suggest": {
"text":"PVQ",
"completion": {
"field": "suggest",
"fuzzy": {
"fuzziness":1
}
}
}
},
"_source": ["title"]
}

my-suggest可以自定义,field不能变,

在index.html文件中嵌入了js脚本,绑定了input事件,当里面的内容发生变化时,向服务器发送请求,参数包括input内容,和type类型

$(function(){
$('.searchInput').bind(' input propertychange ',function(){
var searchText = $(this).val();
var tmpHtml = ""
$.ajax({
cache: false,
type: 'get', //get方法获取
dataType:'json',
url:suggest_url+"?s="+searchText+"&s_type="+$(".searchItem.current").attr('data-type'),
async: true,
success: function(data) {
for (var i=0;i<data.length;i++){
tmpHtml += '

  • '+data[i]+'
  • '
    }
    $(".dataList").html("")
    $(".dataList").append(tmpHtml);
    if (data.length == 0){
    $('.dataList').hide()
    }else {
    $('.dataList').show()
    }
    }
    });
    } );
    })

    在urls中新增:

    url(r'^suggest/$', TemplateView.as_view(template_name='index.html'), name='index')
    1
    然后把爬虫文件中es-type中的内容复制到django项目的models中
    再编辑views文件:

    import json
    from django.shortcuts import render
    from django.views.generic.base import View
    from search.models import DuowanType
    from django.http import HttpResponse

    # Create your views here.
    # 继承 View
    class SearchSuggest(View):
    def get(self, request):
    key_words = request.GET.get('s', '') # 用request获取传过来的参数s 默认值为空
    re_dates = [] # 用来保存搜索建议返回来的title
    if key_words:
    s = DuowanType.search()
    # 写好查询语句
    s = s.suggest('my_suggest', key_words, completion={
    "field": "suggest",
    "fuzzy": {
    "fuzziness": 2
    },
    "size": 10
    })
    # 执行并获取结果
    suggestions = s.execute_suggest()
    for match in suggestions.my_suggest[0].options:
    source = match._source
    re_dates.append(source['title'])
    # 用HttpResponse来返回结果,把数组转成json返回
    return HttpResponse(json.dumps(re_dates), content_type='application/json')

    把urls中的

    url(r'^suggest/$', TemplateView.as_view(template_name='index.html'), name='index')
    改为

    url(r'^suggest/$', SearchSuggest.as_view(), name='suggest')

    记得是SearchSuggest.as_view(),不是SearchSuggest.as_view,否则会报错如下:
    TypeError: as_view() takes 1 positional argument but 2 were given

    二 搜索功能
    urls中:

    from search.views import SearchSuggest, SearchView
    ...
    url(r'^search/$', SearchView.as_view(), name='search')

    在views中添加一个 SearchView(View):
    接收传过来的查询关键词参数和页码参数,
    创建一个client连接es服务器,使用client.search可以执行原始的语句,使用client.search来执行查询语句,在接收返回来的值,把返回来的结果取出来存放到list中,最后用render返回给页面,
    查询时间:记录client.search运行前后的时间,再做减法

    from elasticsearch import Elasticsearch
    from datetime import datetime
    client = Elasticsearch(hosts='127.0.0.1')
    .......
    class SearchView(View):
    def get(self, request):
    key_words = request.GET.get('q', '')
    pagesize = 10
    page = request.GET.get('p', '')
    try:
    page = int(page)
    except:
    page = 1

    # client.search允许像最原始的写法一样写
    body = {
    "query": {
    "multi_match": {
    "query": key_words,
    "fields": ["title", "author"]
    }
    },
    "from": (page-1)*pagesize,
    "size": pagesize,
    # 高亮 返回来的值会把高亮的内容放到highlight字段中
    "highlight": {
    # 可以指明想要加进去的html tag tag里面可以写想知道的值
    "pre_tags": [""],
    "post_tags": ["
    "],
    "fields": {
    "title": {},
    "content": {}
    }
    }
    }
    start_time = datetime.now()
    response = client.search(
    index="duowan",
    body=body
    )
    end_time = datetime.now()
    last_seconds = (end_time-start_time).total_seconds()
    # 不管分不分页都有的总数量
    total_nums = response['hits']['total']
    if (page % 10) > 0:
    page_nums = int(total_nums/10)+1
    else:
    page_nums = int(page/10)
    # 构造一些值 传到数组 在返回给html
    hit_list = []
    for hit in response['hits']['hits']:
    hit_dict = {}
    if 'title' in hit['highlight']:
    hit_dict['title'] = hit['highlight']['title'][0]
    else:
    # 截取长度 hit_dict['title'] = hit['_source']['title'][:100
    hit_dict['title'] = hit['_source']['title']
    hit_dict['len'] = hit['_source']['len']
    hit_dict['tag'] = hit['_source']['tag']
    hit_dict['update_time'] = hit['_source']['update_time']
    hit_dict['author'] = hit['_source']['author']
    hit_dict['playnum_text'] = hit['_source']['playnum_text']
    hit_dict['url'] = hit['_source']['url']
    hit_list.append(hit_dict)
    return render(request, 'result.html', {'page': page,
    'total_nums': total_nums,
    'all_hits': hit_list,
    'key_words': key_words,
    'page_nums': page_nums,
    'last_seconds': last_seconds})

    在页面中:
    找到item的div,用{% for hit in all_hits %}

    ...
    {% endfor %}来使用for循环,遍历传过来的查询结果list all_hits.在页面中填充值

    {% for hit in all_hits %}

    {{ hit.title }} - 分类: {{ hit.tag }} 播放次数: {{ hit.playnum\_text }}
    伯乐在线 {{ hit.update\_time }}
    {% endfor %}

    用js实现搜索记录:
    点击搜索按钮的时候触发add_search()方法,获取关键词,再用KillRepeat()给搜索记录去重,去重后把数组存储到浏览器localStorage,然后再把搜索内容显示出来

    //点击搜索的时候触发
    function add_search(){
    var val = $(".searchInput").val();
    if (val.length>=2){
    //点击搜索按钮时,去重
    KillRepeat(val);
    //去重后把数组存储到浏览器localStorage
    localStorage.search = searchArr;
    //然后再把搜索内容显示出来
    MapSearchArr();
    }

    window.location.href=search_url+'?q='+val+"&s_type="+$(".searchItem.current").attr('data-type')

    }

    function MapSearchArr(){
    var tmpHtml = "";
    var arrLen = 0
    if (searchArr.length >= 5){
    arrLen = 5
    }else {
    arrLen = searchArr.length
    }
    //把数组内容拼接成html内容
    for (var i=0;i<arrLen;i++){
    tmpHtml += ''+searchArr[i]+''
    }
    $(".mysearch .all-search").html(tmpHtml);
    }
    //去重 把以前搜索过的记录删除,并把本次搜索词放在前面
    function KillRepeat(val){
    var kill = 0;
    for (var i=0;i<searchArr.length;i++){
    //判断这个词是否存在历史搜索记录里
    if(val===searchArr[i]){
    kill ++;
    }
    }
    if(kill<1){//不存在
    //放到队列头部
    searchArr.unshift(val);
    }else {//存在
    //把原来的值删除
    removeByValue(searchArr, val)
    searchArr.unshift(val)
    }
    }

    原文:https://blog.csdn.net/qq\_40916110/article/details/87855502

    点赞
    收藏
    评论区
    推荐文章
    blmius blmius
    2年前
    MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
    文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
    Wesley13 Wesley13
    2年前
    java将前端的json数组字符串转换为列表
    记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
    Jacquelyn38 Jacquelyn38
    2年前
    2020年前端实用代码段,为你的工作保驾护航
    有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
    皕杰报表之UUID
    ​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
    Wesley13 Wesley13
    2年前
    mysql设置时区
    mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
    Stella981 Stella981
    2年前
    ELK学习笔记之配置logstash消费kafka多个topic并分别生成索引
    0x00 filebeat配置多个topicfilebeat.prospectors:input_type:logencoding:GB2312fields_under_root:truefields:添加字段
    Stella981 Stella981
    2年前
    Django中Admin中的一些参数配置
    设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
    Wesley13 Wesley13
    2年前
    MYSQL查询用户下多个角色信息
    <resultMapid"baseBeanUser"type"com...vo.system.TUserVoOut"<idcolumn"id"property"id"/<resultcolumn"name"property"name"/
    Wesley13 Wesley13
    2年前
    ThinkPHP 根据关联数据查询 hasWhere 的使用实例
    很多时候,模型关联后需要根据关联的模型做查询。场景:广告表(ad),广告类型表(ad\_type),现在需要筛选出广告类型表中id字段为1且广告表中status为1的列表先看关联的设置部分 publicfunctionadType(){return$thisbelongsTo('A
    Python进阶者 Python进阶者
    3个月前
    Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
    大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这