在上一篇中我们已经准备好了数据,接下来就是创建索引、将数据添加到索引中了。
在Elasticsearch 使用 Java High Level REST Client 操作索引、文档中,我们引入了 Spring Data Elasticsearch 的依赖,这样我们就可以利用 Spring Data 的一些特性来简化 ES 的操作。
先贴一下参考官方文档 Spring Data Elasticsearch - Reference Documentation
1、连接 ES
整合 Spring Boot 后,我们连接 ES 节点的方式就很简单了,只需要在application.properties
添加各个节点的 http 地址,多个地址用英文逗号隔开:
spring.elasticsearch.rest.uris=http://127.0.0.1:9200,http://127.0.0.1:9201,http://127.0.0.1:9202
2、创建文档数据实体类
前边我们将采集到的的数据保存成了 JSON 格式,结构如下:
我们先要创建 JSON 数据对应的实体类Book
:
@Document(indexName = "book")
public class Book {
private String id;
@Field(type = FieldType.Keyword)
private String skuId;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String name;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String author;
private Float price;
@Field(type = FieldType.Integer)
private Integer commentCount;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String shop;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String publisher;
@Field(type = FieldType.Text)
private String img;
...省略get\set方法...
}
@Document
标记该类对应索引库中的文档信息,同时指定了索引库的名字为book
,还可以指定分片数量;类里边的id
属性是必需的,对应文档 id;类里边其它属性上的@Field
注解相当于定义了文档中各个字段的类型、分词。
3、创建索引、添加文档数据
3.1、ElasticsearchRepository
3.1.1、创建索引
使用 Spring Data 的特性,创建BookRepository
,继承ElasticsearchRepository
,泛型参数Book
就是上边的实体类,String
则是文档 id 的类型:
public interface BookRepository extends ElasticsearchRepository<Book, String> {
}
完成上边的工作后,启动项目,会自动帮我们创建好索引库。
3.1.2、添加文档
创建好了索引库,就可以添加文档了。
创建一个BookService
类,通过addBook()
方法来批量添加文档数据:
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
public void addBook(List<Book> books) {
elasticsearchRestTemplate.save(books);
}
}
由于我们采集到的数据事先保存在了文件中,所以需要从文件中读取数据,在调用上边的addBook()
来添加:
@Service
public class BookFileService {
@Autowired
BookService bookService;
/**
* 将去重后的数据写入 ES
*/
public void writeBookDataToES() {
String filePath = System.getProperty("user.dir") + File.separator + "jd_book2.txt";
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
fileReader = new FileReader(filePath);
bufferedReader = new BufferedReader(fileReader);
String line;
ArrayList<Book> books = new ArrayList<>();
while ((line = bufferedReader.readLine()) != null) {
books.add(JSON.parseObject(line, Book.class));
if (books.size() >= 500) {
// 添加数据到 ES
bookService.addBook(books);
books.clear();
}
}
bookService.addBook(books);
} catch (Exception e) {
e.printStackTrace();
} finally {
......
}
}
}
代码比较简单,就是逐行读取文件,然后批量添加即可。
最后执行writeBookDataToES()
方法即可完成添加,速度还是很快的,基本10秒左右就完成了:
@RunWith(SpringRunner.class)
@SpringBootTest
class LearnElasticsearchApplicationTests {
@Autowired
BookFileService bookFileService;
@Test
void testES() throws IOException {
bookFileService.writeBookDataToES();
}
}
最后可以在 head 工具中浏览添加好的数据。
前边我们通过继承ElasticsearchRepository
,扩展了BookRepository
类,利用 Spring Data Repository 的特性,不需要再编写额外的代码,使用BookRepository
就可以完成文档的添加、删除、查询等操作。遵照按照 Spring Data 的规范,我们还可以根据需要来扩展其它的方法来操作文档。如果你之前用过 Spring Data JPA,那么这个就很容易理解了。
3.2、ElasticsearchRestTemplate
ElasticsearchRestTemplate
相当于一个 ES 客户端,它的内部基于 Java High Level REST Client 实现的。你可能还见过ElasticsearchTemplate
,但是从 Spring Data 4.0开始已经过时了,目前推荐使用 ElasticsearchRestTemplate。
相比 ElasticsearchRepository 它的功能更丰富一些、效率更高,而且不用和数据体类绑定,使用 ElasticsearchRestTemplate 除了可以操作文档,还可以操作索引。
我们重点要学习的就是 ElasticsearchRestTemplate,它的主要功能通过如下三个接口定义的:
-
IndexOperations
,定义索引相关的操作,例如创建、删除索引。 -
DocumentOperations
,定义文档相关的操作,例如添加、删除、更新、基于文档 id 的简单查询。 -
SearchOperations
,定义各种文档查询的操作。
3.2.1、创建索引
前边是通过定义BookRepository
来实现索引的自动创建,其实有了ElasticsearchRestTemplate
,我们可以完全不使用 Spring Data Repository 相关的扩展类,注释掉BookRepository
,我们现在使用 ElasticsearchRestTemplate 来创建索引:
@Service
public class BookService {
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
public void createIndex() {
// 指定文档的数据实体类
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
// 创建索引
indexOperations.create();
// 创建字段映射
Document mapping = indexOperations.createMapping();
// 给索引设置字段映射
indexOperations.putMapping(mapping);
}
}
删除索引就简单了,顺便说一下:
public void deleteIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
boolean result = indexOperations.delete();
System.out.println(result);
}
3.2.2、添加文档
首先我们可以使用 ElasticsearchRestTemplatesave()
方法添加文档:
@Service
public class BookService {
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
public void addBook(List<Book> books) {
elasticsearchRestTemplate.save(books);
}
}
除了save()
方法,我们还可以使用bulkIndex()
方法,它是一次批量添加数据的,而save()
是逐个添加。来看如何使用bulkIndex()
方法:
@Service
public class BookService {
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
public void bulkAddBook(List<Book> books) {
List<IndexQuery> indexQueryList = new ArrayList<>();
books.forEach(book -> {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setObject(book);
indexQueryList.add(indexQuery);
});
elasticsearchRestTemplate.bulkIndex(indexQueryList, IndexCoordinates.of("book"));
}
}
主要是通过IndexQuery
的setObject()
方法设置要添加的数据,bulkIndex()
方法第二个参数用来指定索引信息。
如果需要的话可以使用IndexQuery
的setId()
方法来设置文档 id。
我的采集到的数据是以 JSON 格式保存在文件中,能否直接添加 JSON 数据呢,而不是转换成实体类的对象再添加。这个需求我们同样可以用bulkIndex()
来完成:
@Service
public class BookService {
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
public void bulkAddBook2(List<String> books) {
List<IndexQuery> indexQueryList = new ArrayList<>();
books.forEach(book -> {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setSource(book);
indexQueryList.add(indexQuery);
});
elasticsearchRestTemplate.bulkIndex(indexQueryList, IndexCoordinates.of("book"));
}
}
其实就是通过IndexQuery
的setSource()
方法设置要添加的原始 JSON 数据。
这样从文件读取的数据就可以直接添加了:
public void writeBookDataToES2() {
......
try {
fileReader = new FileReader(filePath);
bufferedReader = new BufferedReader(fileReader);
String line;
ArrayList<String> books = new ArrayList<>();
while ((line = bufferedReader.readLine()) != null) {
books.add(line);
if (books.size() >= 500) {
bookService.bulkAddBook2(books);
books.clear();
}
}
bookService.bulkAddBook2(books);
} catch (Exception e) {
e.printStackTrace();
} finally {
......
}
}
注意,使用ElasticsearchRestTemplate
的save()
或者bulkIndex()
方法添加文档时,如果添加了相同文档 id 的数据,则之前的会被覆盖掉。如果是使用ElasticsearchRepository
的save()
方法,则无法正常添加。
本文详细的代码可以参考:https://github.com/SheHuan/LearnElasticsearch
网友评论