文章目录
前言
ElasticSearch学习笔记(一)倒排索引、ES和Kibana安装、索引操作
3 文档操作
3.1 新增文档
语法:
POST /{索引库名}/_doc/{文档id}
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1":"值3",
"子属性2":"值4"
}
// ...
}
3.2 查询文档
语法:
GET /{索引库名}/_doc/{文档id}
3.3 修改文档
3.3.1 全量修改
全量修改是覆盖原来的文档,其本质是先根据指定的id删除文档(id对应的文档不存在也可以),再新增一个相同id的文档。
语法:
PUT /{索引库名}/_doc/{文档id}
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1":"值3",
"子属性2":"值4"
}
// ...
}
3.3.2 增量修改
增量修改是只修改指定id匹配的文档中的部分字段。
语法:
POST /{索引库名}/_update/{文档id}
{
"doc": {
"修养修改的字段": "新值"
}
}
3.4 删除文档
语法:
DELETE /{索引库名}/_doc/{文档id}
4 RestAPI
ES官方提供了各种不同语言的客户端用来操作ES,这些客户端的本质是组装DSL语句,通过Http请求发送给ES。其官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html
其中Java语言的客户端分为两种:
本文章学习的是high-level REST client。
4.1 创建数据库和表
CREATE DATABASE hsgx;
USE hsgx;
CREATE TABLE tb_hotel (
`id` BIGINT(20) NOT NULL PRIMARY KEY COMMENT '酒店id',
`name` VARCHAR(255) NOT NULL COMMENT '酒店名称',
`address` VARCHAR(255) NOT NULL COMMENT '酒店地址',
`price` INT(10) NOT NULL COMMENT '酒店价格',
`score` INT(2) NOT NULL COMMENT '酒店评分',
`brand` VARCHAR(32) NOT NULL COMMENT '酒店品牌',
`city` VARCHAR(32) NOT NULL COMMENT '所在城市',
`star_name` VARCHAR(16) NOT NULL COMMENT '酒店星级',
`business` VARCHAR(255) NOT NULL COMMENT '商圈',
`latitude` VARCHAR(32) NOT NULL COMMENT '纬度',
`longitude` VARCHAR(32) NOT NULL COMMENT '经度',
`pic` VARCHAR(255) DEFAULT NULL COMMENT '酒店图片'
);
INSERT INTO tb_hotel(`id`, `name`, `address`, `price`, `score`, `brand`, `city`, `star_name`, `business`, `latitude`, `longitude`, `pic`)
VALUES (1, '白天鹅', '中山路', 888, 5, '白天鹅', '广州', '五星', '太古汇', '123.456', '456.748', 'a.png'),
(2, '希尔顿', '南京路', 456, 4.5, '希尔顿', '上海', '四星', '外滩', '123.456', '456.748', 'b.png');
4.2 创建项目
在IDEA中创建一个maven项目,结构如下:
4.3 mapping映射分析
mapping映射分析要考虑的信息包括:
- 字段名:参考表结构。
- 字段数据类型:参考表结构。
- 是否参与搜索:根据具体业务进行判断。
- 是否需要分词:根据具体内容进行判断,如果内容是一个整体就无需分词,反之则要分词。
- 分词器是什么:可以统一使用ik_max_word。
对应到tb_hotel
表,我们可以新建如下索引:
PUT /hotel
{
"mappings": {
"properties": {
"id": {
"type": "integer"
},
"name":{
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all"
},
"address":{
"type": "text",
"analyzer": "ik_max_word",
"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"
},
"pic":{
"type": "keyword",
"index": false
},
"location":{
"type": "geo_point"
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
其中,有两个比较特殊的字段:
- location:地理坐标,类型是
geo_point
,表示由经度(latitude)和纬度(longitude)确定一个点。 - all:一个组合字段,其目的是将多字段的值 利用
copy_to
属性合并,提供给用户搜索。在上面的例子中,name
、brand
、city
字段会合并到一起。
4.4 初始化客户端
Java客户端中,与ES一切交互都封装在一个名为RestHighLevelClient
的类中,必须先完成这个对象的初始化,建立与ES的连接。主要步骤如下:
- 1)引入依赖,注意版本号和安装的ES版本一致
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.12.1</version>
</dependency>
- 2)初始化RestHighLevelClient
private RestHighLevelClient client;
@Before
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.153.128:9200")
));
}
@After
void close() throws IOException {
this.client.close();
}
4.5 创建索引库
private static final String DSL = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"address\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"index\": false\n" +
" },\n" +
" \"price\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"score\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"brand\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"city\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"starName\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"business\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"pic\":{\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"location\":{\n" +
" \"type\": \"geo_point\"\n" +
" },\n" +
" \"all\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
@Test
public void testCreateHotelIndex() throws IOException {
// 1.参数为索引库名称
CreateIndexRequest createIndexRequest = new CreateIndexRequest("hotel");
// 2.设置mapping映射
createIndexRequest.source(DSL, XContentType.JSON);
// 3.发起创建索引库请求
client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
}
由以上代码可知,创建索引库的步骤主要又三步:
- 1)创建Request对象。创建索引库的操作对应的Request对象是CreateIndexRequest。
- 2)设置mapping映射,其实就是DSL的JSON参数部分。因为JSON字符串很长,所以定义了一个静态字符串常量来表示,让代码看起来更加优雅。
- 3)发送创建索引库请求,
client.indices()
方法的返回值是IndicesClient类型,封装了所有与索引库操作有关的方法。
执行以上单元测试,在DevTools工具中查询该索引库:
4.6 判断索引库是否存在
判断索引库是否存在,本质是使用GET
命令查询索引库,因此它对应的Request对象是GetIndexRequest。
@Test
public void testExistsHotelIndex() throws IOException {
// 1.参数为索引库名称
GetIndexRequest request = new GetIndexRequest("hotel");
// 2.发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}
执行以上单元测试,结果如下:
4.7 删除索引库
删除索引库对应的Request对象是DeleteIndexRequest。
@Test
public void testDeleteHotelIndex() throws IOException {
// 1.参数为索引库名称
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
// 2.发送请求
client.indices().delete(request, RequestOptions.DEFAULT);
}
执行以上单元测试,在DevTools工具中查询该索引库:
5 RestClient操作文档
5.1 准备工作
由于上文定义的索引库hotel
的mapping映射与数据库表结构有一些差异,因此还需要定义一个新的实体类,与索引库的mapping映射对应起来:
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
}
}
主要的区别在于,将latitude
、longitude
两个字段合并为location
一个字段。
5.2 新增文档
新增文档的DSL语句示例如下:
POST /hotel/_doc/1
{
"name": "白天鹅",
"score": 5
}
对应的Java代码如下:
@Test
public void testCreateDocIndex() throws IOException {
// 1.POST /hotel/_doc/1 { "name": "白天鹅", "score": 5 }
IndexRequest request = new IndexRequest("hotel").id("1");
request.source("{\"name\": \"白天鹅\", \"score\": 5}", XContentType.JSON)
// 2.发送请求
client.index(request, RequestOptions.DEFAULT);
}
执行以上单元测试,在DevTools工具中查询该文档:
下面实现把数据库tb_hotel
表的数据读取出来,并保存到ES中:
@Test
public void testSaveHotel() throws IOException {
// 1.根据id查询酒店数据
Hotel hotel = hotelService.getById(2);
// 2.转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
// 3.将HotelDoc转json
String json = JSON.toJSONString(hotelDoc);
// 4.准备Request对象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
// 5.准备Json文档
request.source(json, XContentType.JSON);
// 6.发送请求
client.index(request, RequestOptions.DEFAULT);
}
执行以上单元测试,在DevTools工具中查询该文档:
5.3 查询文档
新增文档的DSL语句示例如下:
GET /hotel/_doc/2
对应的Java代码如下:
@Test
public void testQueryHotelDoc() throws IOException {
// 1.创建Request对象
GetRequest request = new GetRequest("hotel", "2");
// 2.发送请求
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析结果
String json = response.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println(hotelDoc);
}
执行以上单元测试,结果如下:
…
本节完,更多内容请查阅分类专栏:微服务学习笔记
感兴趣的读者还可以查阅我的另外几个专栏: