Elasticsearch 整合 Spring Boot(2)

在上一篇中我们已经准备好了数据,接下来就是创建索引、将数据添加到索引中了。

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) {
        bookRepository.saveAll(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"));
    }
}

主要是通过IndexQuerysetObject()方法设置要添加的数据,bulkIndex()方法第二个参数用来指定索引信息。

如果需要的话可以使用IndexQuerysetId()方法来设置文档 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"));
    }
}

其实就是通过IndexQuerysetSource()方法设置要添加的原始 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 {
        ......
    }
}

注意,使用ElasticsearchRestTemplatesave()或者bulkIndex()方法添加文档时,如果添加了相同文档 id 的数据,则之前的会被覆盖掉。如果是使用ElasticsearchRepositorysave()方法,则无法正常添加。

本文详细的代码可以参考:https://github.com/SheHuan/LearnElasticsearch

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容