Elasticsearch快速入门以及基本语法总结

一、基础概念

1、基础了解

Elasticsearch是基于语言Lucene语言的一款强大的分布式搜索引擎。elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK技术栈),通常用于数据的搜索、监控、日志和日志等作用。

es和Mysql是有所区别的,Mysql作为关系型数据库,用于存储数据,因为其事务保证数据一致性,而ES则是用于搜索数据,没有事务。

2、es相关概念

倒排索引:所谓倒排索引是相对于正向索引来说的,举例而言,在下表中,是常规的正向索引,通过foodId查询到对应的一条数据。当查询name="芋泥奶茶"时,需要全表查询,遍历到第三条数据,如果数据量大,显然是不可取的。而倒排索引,则是根据文本查询数据,这样查询"芋泥奶茶",查询到的就是"芋泥:3,4"和"奶茶:3".

foodId name price
1 巧克力牛奶 5
2 牛奶麻薯 25
3 芋泥奶茶 12
4 芋泥千层 30
term 数据id
巧克力 1
牛奶 1,2
芋泥 3,4
奶茶 3

文档:文档是Json的数据,比如下面面的数据,那么文档就是下图所示:

json 复制代码
{
  "foodId": 1,
  "name": "巧克力牛奶",
  "price": 5
}

字段:类似于数据库中的列,在上文中就是foodid这样。

索引:相同类型的文档集合,如下所示,就是一个索引.eg:所有食物文档,就可以组织在一起,称为食物的索引;所有人员的文档,可以组织在一起,称为人员的索引;

json 复制代码
{
  "foodId": 1,
  "name": "巧克力牛奶",
  "price": 5
}
{
  "foodId": 2,
  "name": "巧克力蛋糕",
  "price": 30
}
{
  "foodId": 3,
  "name": "芋泥奶茶",
  "price": 12
}
json 复制代码
{
  "nameId": 1,
  "name": "张三",
  "gender": "女",
  "age": 15
}
{
  "nameId": 2,
  "name": "李四",
  "gender": "女",
  "age": 17
}
{
  "nameId": 3,
  "name": "王五",
  "gender": "男",
  "age": 15
}

映射:索引中文档字段的约束信息。

分词器:根据设置的分词器不同,分词的规则也不同,ik_smart:智能切分,粗粒度;ik_max_word:最细切分,细粒度。text字段必须使用分词器。

二、Es的基本操作

1、索引

创建索引:PUT/索引库名

json 复制代码
PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart" //中文分析器
      },
      "字段名2":{
        "type": "keyword",
        "index": "false" //不会被索引
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type": "keyword"
          }
        }
      },
      // ...略
    }
  }
}

查询索引:GET/索引库名

删除索引:DELETE/索引库名

json 复制代码
{
	"acknowledged" : true
}

添加字段:PUT/索引库名/_mapping。不难发现索引一旦创建,就不允许修改了,因为索引库一旦创建,无法修改mapping。但是可以添加新的Mapping,不会对原来的索引造成影响。

bash 复制代码
PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

2、文档操作

新增文档:POST/索引库名/_doc/文档id { json文档 }

bash 复制代码
POST /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
        "子属性1": "值3",
        "子属性2": "值4"
    },
    // ...
}

获取文档:GET/索引库名/_doc/文档id

bash 复制代码
GET /heima/_doc/1

删除文档:DELETE/索引库名/_doc/文档id

bash 复制代码
# 根据id删除数据
DELETE /heima/_doc/1

修改文档:有两种方式,全量修改或者增量修改

    • 全量修改:本质是删除,原本的id,然后更新新的内容,PUT/索引库名/_doc/文档id {json文档}
    • 增量修改:在原本的基础上,进行增加,POST/索引库名/_update/文档id {"doc":{字段}}
bash 复制代码
PUT /{索引库名}/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}
bash 复制代码
POST /{索引库名}/_update/文档id
{
    "doc": {
         "字段名": "新的值",
    }
}

3、查询

常见的查询类型包括:

  • 查询所有:查询出所有数据,一般测试用。例如:match_all
  • 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
    • match_query
    • multi_match_query
  • 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
    • ids
    • range
    • term
  • 地理(geo)查询:根据经纬度查询。例如:
    • geo_distance
    • geo_bounding_box
  • 复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
    • bool
    • function_score
(1)、查询语法

虽然查询类型颇多,但是查询语句是一样的~,无非就是类型、条件、条件值的变化。

bash 复制代码
GET /indexName/_search
{
  "query": {
    "查询类型": {
      "查询条件": "条件值"
    }
  }
}
(2)、全文检索

常见的全文检索查询包括:

  • match查询:单字段查询
  • multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件

match查询语法如下:

bash 复制代码
GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  }
}

mulit_match语法如下:

bash 复制代码
GET /indexName/_search
{
  "query": {
    "multi_match": {
      "query": "TEXT",
      "fields": ["FIELD1", " FIELD12"]
    }
  }
}
(3)、精确检索

精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:

  • term:根据词条精确值查询
  • range:根据值的范围查询

term:因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是不分词的词条。查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。

bash 复制代码
// term查询
GET /indexName/_search
{
  "query": {
    "term": {
      "FIELD": {
        "value": "VALUE"
      }
    }
  }
}

range:范围查询,一般应用在对数值类型做范围过滤的时候。比如做价格范围过滤。

json 复制代码
// range查询
GET /indexName/_search
{
  "query": {
    "range": {
      "FIELD": {
        "gte": 10, // 这里的gte代表大于等于,gt则代表大于
        "lte": 20 // lte代表小于等于,lt则代表小于
      }
    }
  }
}
(4)、地理坐标检索

所谓的地理坐标查询,其实就是根据经纬度查询,有以下常见的两种:

geo_bounding_box:矩形范围查询,也就是geo_bounding_box查询,查询坐标落在某个矩形范围的所有文档:查询时,需要指定矩形的左上右下两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。

语法如下:

json 复制代码
// geo_bounding_box查询
GET /indexName/_search
{
  "query": {
    "geo_bounding_box": {
      "FIELD": {
        "top_left": { // 左上点
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": { // 右下点
          "lat": 30.9,
          "lon": 121.7
        }
      }
    }
  }
}

geo_distance:附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。也就是,在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件:

json 复制代码
// geo_distance 查询
GET /indexName/_search
{
  "query": {
    "geo_distance": {
      "distance": "15km", // 半径
      "FIELD": "31.21,121.5" // 圆心
    }
  }
}
(5)、复合检索

复合(compound)查询:复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑。常见的有两种:

  • fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名;
  • bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索,在使用es的时候非常常见。

当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。之前是TF-IDF算法,elasticsearch5.1版本后采用的BM25算法,TF-IDF算法有一各缺陷,就是词条频率越高,文档得分也会越高,单个词条对文档影响较大。而BM25则会让单个词条的算分有一个上限,曲线更加平滑。

(a)、function score

查询中包含四部分内容:

  • 原始查询 条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分、 (query score)
  • 过滤条件:filter部分,符合该条件的文档才会重新算分
  • 算分函数 :符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数
    • weight:函数结果是常量
    • field_value_factor:以文档中的某个字段值作为函数结果
    • random_score:以随机数作为函数结果
    • script_score:自定义算分函数算法
  • 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
    • multiply:相乘
    • replace:用function score替换query score
    • 其它,例如:sum、avg、max、min

function score的运行流程如下:

  • 1)根据原始条件 查询搜索文档,并且计算相关性算分,称为原始算分(query score)
  • 2)根据过滤条件,过滤文档
  • 3)符合过滤条件 的文档,基于算分函数 运算,得到函数算分(function score)
  • 4)将原始算分 (query score)和函数算分 (function score)基于运算模式做运算,得到最终结果,作为相关性算分。
json 复制代码
GET /索引名/_search
{
  "query": {
    "function_score": { // 原始查询,可以是任意条件
      "query": {
        "match": {
          "查询条件": "查询值"
        }
      },
      "functions": [ 
        {
          "filter": {
            "term": {
              "查询条件": "查询值"
            }
          },
          "weight": 10 // 算分权重为2
        }
      ],
      "boost_mode": "sum" // 加权模式,求和
    }
  }
}

(b)、bool query

布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:

  • must:必须匹配每个子查询,类似"与"
  • should:选择性匹配子查询,类似"或"
  • must_not:必须不匹配,不参与算分,类似"非"
  • filter:必须匹配,不参与算分

搜索时,参与打分的字段越多,查询的性能也越差。因此这种多条件查询时,建议这样做:搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分;其它过滤条件,采用filter查询。不参与算分。

bash 复制代码
GET /索引名/_search
{
  "query": {
     
    "bool": {
      "must": [
        {
          "term": {
          	"查询条件": "查询值"
          }
        }
      ],
      "should": [
        {
          "term": {
          	"查询条件": "查询值"
          }
        },
        {
          "term": {
        	  "查询条件": "查询值"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "price": {
              "gte": 100,
              "lte": 2000
            }
          }
        }
      ]
    }
  }
}

三、Java客户端操作ES

以下是demo的数据库建表语句,使用es,建立索引很重要,如何完成es索引建立,需要考虑清楚,这个字段是什么,字段类型是什么,是否需要参与查询,是否需要分词,分词又该如何分词。

less 复制代码
CREATE TABLE `tb_hotel` (
  `id` bigint(20) NOT NULL COMMENT '酒店id',
  `name` varchar(255) NOT NULL COMMENT '酒店名称;例:7天酒店',
  `address` varchar(255) NOT NULL COMMENT '酒店地址;例:航头路',
  `price` int(10) NOT NULL COMMENT '酒店价格;例:329',
  `score` int(2) NOT NULL COMMENT '酒店评分;例:45,就是4.5分',
  `brand` varchar(32) NOT NULL COMMENT '酒店品牌;例:如家',
  `city` varchar(32) NOT NULL COMMENT '所在城市;例:上海',
  `star_name` varchar(16) DEFAULT NULL COMMENT '酒店星级,从低到高分别是:1星到5星,1钻到5钻',
  `business` varchar(255) DEFAULT NULL COMMENT '商圈;例:虹桥',
  `latitude` varchar(32) NOT NULL COMMENT '纬度;例:31.2497',
  `longitude` varchar(32) NOT NULL COMMENT '经度;例:120.3925',
  `pic` varchar(255) DEFAULT NULL COMMENT '酒店图片;例:/img/1.jpg',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这是根据以上问题建立的索引,其中copy_to是将多个字段组合进行查询,有copy to的字段会被复制到all中,通过all进行查询。

json 复制代码
PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword",
        "copy_to": "all"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword"
      },
      "location":{
        "type": "geo_point" //地理坐标,包括包括精度和维度。
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

1、对索引进行操作

  • 首先注册服务,初始化RestHighLevelClient;
  • 通过xxxindexrequest,进行操作,xxx可以是Creat,Delete,GET,
  • 准备DSL,只用在创建的时候需要;
  • 然后通过RestHighLevelClient.indeces.xxx(),进行对应的操作,xxx是creat、exist、delect。
java 复制代码
    /**
     * 创建索引
     * @throws IOException
     */
    @Test
    void createHotelIndex() throws IOException {
        // 1. Create Request object
        CreateIndexRequest request = new CreateIndexRequest("hotel");
        // 2. Prepare request parameters: DSL statement
        request.source(MAPPING_TEMPLATE, XContentType.JSON);
        // 3. Send request
        highClient.indices().create(request,RequestOptions.DEFAULT);
        System.out.println("Index created successfully");
    }

    /**
     * 删除索引
     * @throws IOException
     */
    @Test
    void deleteIndex() throws IOException{
        DeleteIndexRequest request = new DeleteIndexRequest("hotel");
        //请求
        highClient.indices().delete(request,RequestOptions.DEFAULT);
    }

    /**
     * 获取索引
     * 判断索引是否存在
     * @throws IOException
     */
    @Test
    void existIndex() throws IOException{
        GetIndexRequest request = new GetIndexRequest("hotel");
        GetIndexResponse getIndexResponse = highClient.indices().get(request, RequestOptions.DEFAULT);
        System.out.println(Arrays.toString(getIndexResponse.getIndices()));
        boolean exists = highClient.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println(exists ? "索引存在":"索引不存在");
    }

2、对文本进行操作:

如果需要进行序列化和反序列化操作,用fastjson就行。

  • 初始化RestHighLevelClient;
  • 创建XxxRequest。XXX是Index、Get、Update、Delete、Bulk;
  • 准备参数(Index、Update、Bulk时需要);
  • 发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
  • 解析结果(Get时需要)。
java 复制代码
    /**
     * 创建文档 + 获取文档数据
     */
    @Test
    void CreateDoc() throws IOException {
        //根据ID查询信息,并且完成序列化
        Hotel hotel = hotelService.getById(47066L);
        HotelDoc hotelDoc = new HotelDoc(hotel);
        JSON.toJSONString(hotelDoc);

        //发送请求
        GetRequest request = new GetRequest("hotel").id("1");
        GetResponse documentFields = highClient.get(request, RequestOptions.DEFAULT);
        System.out.println(documentFields.getSourceAsString());
    }

    /**
     * 删除文档
     * @throws IOException
     */
    @Test
    void DeleteDoc() throws IOException{
        // 1.准备Request
        DeleteRequest request1 = new DeleteRequest("hotel", "1");
        // 2.发送请求
        System.out.println(highClient.delete(request1, RequestOptions.DEFAULT).getResult());
    }

    /**
     * 增量修改
     * 全量修改:字段数据一一对应即可
     * @throws IOException
     */
    @Test
    void testUpdateDocument() {
        // 1.准备Request
        UpdateRequest request = new UpdateRequest("hotel", "1");
        // 2.准备请求参数
        request.doc(
                "price", "999",
                "starName", "钻"
        );
        // 3.发送请求
        try {
            highClient.update(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            System.out.println(JSON.toJSONString(e.getLocalizedMessage()));
        }
    }

    /**
     * 批量导入
     * @throws IOException
     */
    @Test
    void testBulkRequest(){
        //查询数据
        List<Hotel> hotelList = hotelService.list();

        //1、创建Request
        BulkRequest request = new BulkRequest();
        //2、准备参数
        for (Hotel hotel : hotelList) {
            //转换成为文档类型
            HotelDoc hotelDoc = new HotelDoc(hotel);
            IndexRequest indexRequest = new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON);
            //创建新增的文档对象
            request.add(indexRequest);
        }
        //3、发送请求
        try {
            highClient.bulk(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            System.out.println(JSON.toJSONString(e.getLocalizedMessage()));
        }
    }

3、查询:

建议参考:Java中ElasticSearch的各种查询(普通,模糊,前缀,高亮,聚合,范围)_java elasticsearch博客

相关推荐
喵个咪18 分钟前
go-wind-cms 微服务架构设计:为什么基于 Kratos?
后端·微服务·cms
神奇小汤圆24 分钟前
百度面试官:Redis 内存满了怎么办?你有想过吗?
后端
喵个咪26 分钟前
Headless 架构优势:内容与展示解耦,一套 API 打通全端生态
前端·后端·cms
开心就好202528 分钟前
HTTPS超文本传输安全协议全面解析与工作原理
后端·ios
小江的记录本30 分钟前
【JEECG Boot】 JEECG Boot——数据字典管理 系统性知识体系全解析
java·前端·spring boot·后端·spring·spring cloud·mybatis
神奇小汤圆31 分钟前
Spring Batch实战
后端
喵个咪33 分钟前
传统 CMS 太笨重?试试 Headless 架构的 GoWind,轻量又强大
前端·后端·cms
程序员木圭35 分钟前
07-数组入门必看!Java数组的内存分析02
java·后端
喵个咪1 小时前
Go 语言 CMS 横评:风行 GoWind 对比传统 PHP/Java CMS 核心优势
前端·后端·cms
面向Google编程1 小时前
从零学习Kafka:位移与高水位
大数据·后端·kafka