ELASTIC 搜索开发实战-笔记

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: ELASTIC 搜索开发实战-笔记

本文是《Elastic 搜索开发实战 》的学习笔记

《Elastic 搜索开发实战 》

需求描述:

  • 智能补全提示
  • 结果的高亮显示
  • 结果的聚合统计和过滤
  • 相关搜索结果的推荐
  • 短语纠错(fuzziness)
  • 数据的实时同步与更新(logstash)
  • 查得到
  • 查得全
  • 查得准
  • 支持同义词
  • 支持简繁体
  • 支持拼音(pinyin)
  • 支持 PPT 的搜索
  • 支持自定义排序,按时间,按相关度
  • 支持结果过滤,按分类、按标签、按时间范围等
  • 搜索结果展示待加强,UI 设计

版本:

  • Elasticsearch v6.2.4
  • Kibana v6.2.4
  • Logstash v6.2.4

kibana dev tools快捷键

command + i 自动缩进
command + enter 提交请求

常用 API 介绍

1、增删改查

# 创建文档
POST twitter/doc/1
{
  "name": "Jack",
  "age": 30
}
# 取回文档
GET twitter/doc/1
# 完全替换
PUT twitter/doc/1
{
  "name": "Jack",
  "age": 35
}
# 部分更新
POST twitter/doc/1/_update
{
  "doc": {
    "name": "Mark"
  }
}
# 删除文档
DELETE twitter/doc/1

2、搜索的使用

# 创建两个索引文档
POST twitter/doc/1
{
  "name": "Jack",
  "age": 30
}
POST twitter/doc/2
{
  "name": "Mark",
  "age": 35
}
# 通过名称搜索
GET twitter/_search?q=Jack
# 通过年龄检索
GET twitter/_search?q=35
# 限定查询年龄字段
GET twitter/_search?q=age:35
# QueryDSL查询表达式
POST twitter/_search
{
  "query": {
    "match": {
      "age": 35
    }
  }
}

3、聚合的使用

# 再创建两个索引文档
POST twitter/doc/3
{
  "name":"john",
  "age":30
}
POST twitter/doc/4
{
  "name":"mark",
  "age":40
}
# 统计年龄的分布情况
POST twitter/_search
{
  "size": 0,
  "aggs": {
    "age_stats": {
      "terms": {
        "field": "age",
        "size": 10
      }
    }
  }
}

聚合查询表达式说明


最外层的 size 为 0 不返回搜索命中的文档,只返回聚合的统计结果,

aggs 就是我们描述聚合查询语句根节点,

使用 terms 聚合类型来对 age 字段的值进行统计,并且只返回前 10 个统计值,

然后这些统计结果我们命名为 age_stats

4、索引的管理

# 查看索引列表
GET _cat/indices?v
# 删除索引
DELETE twitter
# 查看集群的健康状况
GET _cluster/health

字段名

说明

health

索引健康状态,green 表示健康;yellow 表示数据完整,但是缺少副本;red 则表示有数据损坏。

status

索引工作状态,open 表示索引打开中,可以被使用;close 表示索引被关闭,不能使用。

index

索引名称。

uuid

索引的唯一 ID 标识。

pri

索引的主分片个数。

rep

索引的副本分片个数。

docs.count

索引内的文档个数。

docs.deleted

索引内已经删除的文档个数。

搜索示例

数据准备

创建表

CREATE TABLE `blog` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `title` varchar(60) DEFAULT NULL COMMENT '标题',
  `author` varchar(60) DEFAULT NULL COMMENT '作者',
  `content` text COMMENT '内容',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4

获取测试数据

# -*- coding: utf-8 -*-
from pprint import pprint
import requests
from parsel import Selector
from puremysql import PureMysql
def get_data(url):
    """
    获取古诗文网数据
    eg: https://www.gushiwen.cn/
    :return: list
    """
    response = requests.get(url)
    sel = Selector(text=response.text)
    rows = sel.css(".main3 .left .sons")
    lst = []
    for row in rows:
        title = row.css("b::text").extract_first()
        author = row.css(".source").xpath("string(.)").extract_first()
        content = row.css(".contson").xpath("string(.)").extract_first()
        if not title:
            continue
        item = {
            "title": title.strip(),
            "author": author.strip(),
            "content": content.replace('\n', ''),
        }
        pprint(item)
        lst.append(item)
    return lst
def insert_data(lst):
    """
    数据入库
    """
    con = PureMysql(db_url="mysql://root:123456@127.0.0.1:3306/data?charset=utf8")
    table = con.table("blog")
    ret = table.insert(lst)
    con.close()
    print("成功入库", ret)
def main():
    # url = "https://www.gushiwen.cn/"
    for page in range(1, 11):
        url = f"https://www.gushiwen.cn/default.aspx?page={page}"
        lst = get_data(url)
        insert_data(lst)
if __name__ == '__main__':
    main()

logstash同步数据配置

config/jdbc.conf

input {
  jdbc {
    jdbc_driver_library => "mysql-connector-java-8.0.16.jar"
    jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://127.0.0.1:3306/data"
    jdbc_user => "root"
    jdbc_password => "123456"
    statement => "SELECT id, title, content, author, create_time, update_time FROM blog"
    jdbc_paging_enabled => "true"
    jdbc_page_size => "5000"
  }
}
filter {
}
output {
  stdout {
    codec => rubydebug
  }
  elasticsearch {
    index => "blog",
    document_id => "%{id}"
  }
}

同步数据

# 检查配置文件
$ ./bin/logstash -t -f config/jdbc.conf
# 执行配置文件
$ ./bin/logstash -f config/jdbc.conf

问题及处理

处理elasticsearch跨域问题

config/elasticsearch.yml

http.cors.enabled: true
http.cors.allow-origin: "*"

搜索提示

高亮结果显示

POST /blog/_search
{
  "query": {
    "match": {
      "author": "李白"
    }
  },
  "highlight": {
    "fields": {
      "author": {}
    }
  }
}

搜索模板

将查询和参数分离

POST /blog/_search/template
{
  "source": {
    "query": {
      "match": {
        "{{key}}": "{{value}}"
      }
    },
    "size": "{{size}}"
  },
  "params": {
    "key": "author",
    "value": "李白",
    "size": 10
  }
}

其他语句

# 调试模板渲染结果: 
GET _render/template
# 取回模板定义的语法: 
GET _scripts/<templatename>
# 删除模板定义的语法: 
DELETE _scripts/<templatename>

创建模板

POST /_scripts/blog_template_v1
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match": {
          "{{key}}": "{{value}}"
        }
      },
      "highlight": {
        "fields": {
          "{{key}}": {}
        }
      },
      "size": "{{size}}"
    }
  }
}

使用模板

POST /blog/_search/template
{
  "id": "blog_template_v1",
  "params": {
    "key": "author",
    "value": "李白",
    "size": 10
  }
}

模糊查询

GET test/_search
{
  "query": {
    "match": {
      "doc":{
        "query": "elastix",
        "fuzziness": "AUTO"
      }
    }
  }
}

优化查询

POST _scripts/blog_template_v1
{
  "script": {
    "lang": "mustache",
    "source": {
      "size": "{{size}}",
      "query": {
        "bool": {
          "should": [
            {
              "prefix": {
                "{{field}}.keyword": {
                  "value": "{{query}}",
                  "boost": 10
                }
              }
            },
            {
              "match_phrase_prefix": {
                "{{field}}": {
                  "query": "{{query}}",
                  "boost": 2
                }
              }
            },
            {
              "match": {
                "{{field}}": "{{query}}"
              }
            }
          ]
        }
      },
      "_source": [
        "title",
        "id",
        "uid",
        "views"
      ]
    }
  }
}

重建索引

# 新建索引
PUT blog_v1
# 查看原索引的mapping
GET blog/_mapping
# 设置索引的mapping
POST blog_v1/doc/_mapping
{
  "doc": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "@version": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "author": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "content": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "create_time": {
        "type": "date"
      },
      "id": {
        "type": "long"
      },
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "update_time": {
        "type": "date"
      }
    }
  }
}
# 索引迁移
POST _reindex
{
  "source": {"index": "blog"},
  "dest": {"index": "blog_v1"}
}
# 查询测试
POST /blog_v1/_search

索引别名

# 查看别名
GET _cat/aliases
# 添加别名
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "blog",
        "alias": "my-blog"
      }
    }
  ]
}
# 切换别名
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "blog_v1",
        "alias": "my-blog"
      }
    },
    {
      "remove": {
        "index": "blog",
        "alias": "my-blog"
      }
    }
  ]
}
#  通过别名搜索
POST my-blog/_search

拼音处理的插件

https://github.com/medcl/elasticsearch-analysis-pinyin/releases/tag/v6.3.2

添加拼音搜索字段

# 关闭索引
POST my-blog/_close
# 设置索引支持拼音分析器
PUT my-blog/_settings
{
  "index": {
    "analysis": {
      "analyzer": {
        "pinyin_analyzer": {
          "tokenizer": "my_pinyin"
        }
      },
      "tokenizer": {
        "my_pinyin": {
          "type": "pinyin",
          "keep_first_letter": true,
          "keep_separate_first_letter": true,
          "keep_full_pinyin": true,
          "keep_original": false,
          "limit_first_letter_length": 16,
          "lowercase": true
        }
      }
    }
  }
}
# 打开索引
POST my-blog/_open
# 获取原索引mapping
GET my-blog/_mapping
# 添加字段
PUT my-blog/doc/_mapping
{
  "doc": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "@version": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "author": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          },
          "pinyin": {
            "type": "text",
            "analyzer": "pinyin_analyzer"
          }
        }
      },
      "content": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "create_time": {
        "type": "date"
      },
      "id": {
        "type": "long"
      },
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "update_time": {
        "type": "date"
      }
    }
  }
}
# 更新索引
POST my-blog/_update_by_query?conflicts=proceed
# 测试拼音搜索
POST my-blog/_search
{
  "query": {"match": {
    "author.pinyin": "libai"
  }}
}

前端显示

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 引入样式 -->
    <link
      rel="stylesheet"
      href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
    />
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <!-- axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <style>
      /* 居中显示 */
      #app {
        width: 200px;
        margin: 0 auto;
        margin-top: 300px;
      }
      /* 搜索结果高亮 */
      em {
        color: red;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <el-autocomplete
        v-model="state"
        :fetch-suggestions="querySearchAsync"
        placeholder="请输入内容"
        @select="handleSelect"
      >
        <!-- 自定义显示 -->
        <template slot-scope="{ item }">
          <div v-html="item.highlight.author[0]"></div>
        </template>
      </el-autocomplete>
    </div>
    <script>
      new Vue({
        el: "#app",
        data() {
          return {
            list: [],
            state: "",
          };
        },
        methods: {
          async querySearchAsync(queryString, cb) {
            // 查询地址
            const QUERY_URL = "http://localhost:9200/blog/_search";
            // 查询语句
            let query = {
              query: {
                match: {
                  author: queryString,
                },
              },
              highlight: {
                fields: { author: {} },
              },
            };
            const res = await axios.post(QUERY_URL, query);
            console.log(res.data.hits.hits);
            cb(res.data.hits.hits);
          },
          handleSelect(item) {
            console.log(item);
          },
        },
      });
    </script>
  </body>
</html>

30.1.png

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
7月前
|
安全 Linux 开发工具
Elasticsearch 搜索入门技术之一
Elasticsearch 搜索入门技术之一
261 1
|
存储 关系型数据库 数据库
ElasticSearch深度解析入门篇:高效搜索解决方案的介绍与实战案例讲解,带你避坑
ElasticSearch深度解析入门篇:高效搜索解决方案的介绍与实战案例讲解,带你避坑
ElasticSearch深度解析入门篇:高效搜索解决方案的介绍与实战案例讲解,带你避坑
|
7月前
|
Kubernetes 监控 安全
百度搜索:蓝易云【【k8s系列】搭建MicroK8s Dashboard教程。】
完成以上步骤后,你就成功搭建了MicroK8s Dashboard,并可以通过Web界面管理和监控你的MicroK8s集群。请确保根据实际需求进行适当的安全配置和访问控制,以保护你的集群和数据安全。
127 2
|
前端开发 微服务 Python
厉害了!如何在 Gihub 快速搜索开源项目?
很多的小伙伴,经常会有这样的困惑,我看了很多技术的学习文档、书籍、甚至视频,我想动手实践,于是我打开了GitHub,想找个开源项目,进行学习,获取项目实战经验。这个时候很多小伙伴就会面临这样的问题:“我不会搜啊,我该怎么找呀?”,最终只能放弃。相信看完这篇文章,你就可以学会如何精准地在GitHub搜索项目。
|
搜索推荐 程序员
助你掌握搜索神器,10个实用的Elasticsearch查询技巧
Elasticsearch是一个非常流行的搜索引擎,已经成为了许多企业的首选解决方案。然而,我们要想成为一个优秀的程序员,就必须掌握各种查询技巧。本文将向大家介绍10个实用ES的查询技巧。
如何使用微信公众号内的搜索功能?
今天给大家分享一个非常有用的小技巧,学会之后能够帮助你快速的在小蚂蚁的中找到自己想要的关于游戏开发制作的教程和文章。 微信内部是自带搜索功能的,想必这个有不少的朋友不知道。因为我经常会看到的后台有各种各样的关键字的留言,这些留言的人应该都是想通过这些关键字找到公众号中的一些自己想找的教程,只不过很可惜的是这些留言发错了地方,不应该直接发到公众号里,而应该直接使用公众号内部的搜索功能。
618 0
|
SQL 自然语言处理 Java
Elasticsearch连续剧之实战搜索文档
前几篇文章中,小编给大家介绍了一些es的基本操作,还有常用分词器的搭建,现在给大家来示范一下es的一些常见文档搜索方式
|
前端开发 索引
ELASTIC 搜索开发实战-笔记(2)
ELASTIC 搜索开发实战-笔记
159 0
ELASTIC 搜索开发实战-笔记(2)
|
机器学习/深度学习 人工智能 运维
《Elasticsearch 实战手册》研读-企业搜索(2) | 学习笔记
快速学习《Elasticsearch 实战手册》研读-企业搜索(2)
|
数据采集 存储 人工智能
《Elasticsearch 实战手册》研读-企业搜索 | 学习笔记
快速学习《Elasticsearch 实战手册》研读-企业搜索
下一篇
DataWorks