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博客

相关推荐
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
hlsd#1 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@1 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端
幸运小圣1 小时前
Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
开发语言·后端·rust
前端SkyRain3 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁3 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
老猿讲编程3 小时前
Rust编写的贪吃蛇小游戏源代码解读
开发语言·后端·rust
黄小耶@3 小时前
python如何使用Rabbitmq
分布式·后端·python·rabbitmq
宅小海4 小时前
Scala-List列表
开发语言·后端·scala
蔚一5 小时前
Javaweb—Ajax与jQuery请求
前端·javascript·后端·ajax·jquery