Java/Kotlin Spring操作Elasticsearch一遍就会

Elasticsearch基本概念

Elasticsearch有四种构成存储空间的基本结构:Indices、Types、Documents、Fields。
将这四种结构和关系型数据库对比,能更好说明它们是什么:

  • Indices看作Database(数据库);
  • Types看作Tables(表);
  • Documents看作Rows(行);
  • Fields看作Columns(列)。

选择操作ES的客户端

官方提供很多操作ES的客户端,比如:

  • Transport Client
  • High Level REST Client
  • 更多客户端详细信息可以去Spring Data Elasticsearch查看。如果你没那么多时间,本文可以让你快速学会,在Spring中对ES增删改查和条件分页查询的简单操作。

本文使用High Level REST Client对ES增删改查。因为这是官方强烈推荐的。

Gradle引入High Level REST Client依赖

Gradle中引入依赖:

implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.4.2'

Java 实例

依赖注入Bean

@Autowired
private RestHighLevelClient restHighLevelClient;

插入

IndexRequest request = new IndexRequest("order")
		.id(data.roundId)
		.source(gson.toJson(data), XContentType.JSON);

restHighLevelClient.index(request, RequestOptions.DEFAULT);
  • IndexRequest("order"),表示index名;
  • id(data.roundId),表示document名字;
  • source(gson.toJson(data), XContentType.JSON),表示存入data对象转json的字符串。

删除

DeleteRequest deleteRequest = new DeleteRequest("order", data.roundId);
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);

更新

UpdateRequest updateRequest = new UpdateRequest("order", data.roundId)
		.doc(gson.toJson(data), XContentType.JSON);
		
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);

查询(条件分页)

SearchRequest request = new SearchRequest("order");
request.source(
        new SearchSourceBuilder()
                .query((QueryBuilder) QueryBuilders.boolQuery()
                    .must(()->{
                            if(path != null){
                                QueryBuilders.termQuery(String.valueOf(Player.class.getField("name")), ip);
                            }
                        }
                    )
                    .must(
                        // 如有多个条件,添加参照上个 must
                    )
                )
                .sort(JsonUtils.class.getField("inTime"), SortOrder.DESC)
                .size(limit)
                .from(offset)
);

SearchHits hits = restHighLevelClient.search(request, RequestOptions.DEFAULT).getHits();
long total = hits.getTotalHits().value;
List<Player> result = null;
Stream.of(hits.getHits()).forEach(y -> result.add( gson.fromJson(y.getSourceAsString(), Player.class) ));
  • must()表示查询条件,还有should()mustNot()等,must里的lambda表达式表示有值的时候才查询,更方便根据前端传入的参数进行条件查询。
  • limit为前端传入参数,表示查多少个。 offset为前端传入参数,表示从哪里开始查。

Kotlin 实例

依赖注入Bean

@Autowired
lateinit var restHighLevelClient: RestHighLevelClient

插入

val request = IndexRequest("order")
		.id(data.roundId)
        .source(gson.toJson(data), XContentType.JSON)
        
restHighLevelClient.index(request, RequestOptions.DEFAULT)
  • IndexRequest("order"),表示index名;
  • id(data.roundId),表示document名字;
  • source(gson.toJson(data), XContentType.JSON),表示存入data对象转json的字符串。

删除

val deleteRequest = DeleteRequest("order", roomPlayer.roomId.toString() + "rpl" + roomPlayer.playerId)
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT)

更新

val updateRequest = UpdateRequest("order", data.orderId)
		.doc(gson.toJson(data), XContentType.JSON)
                
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT)

查询(条件分页)

inline fun <T> T.applyIf(validate: Boolean, block: T.() -> Unit): T {
    if (validate) {
        block()
    }
    return this
}
val request = SearchRequest("order")
request.source(
        SearchSourceBuilder()
                .query(QueryBuilders.boolQuery()
                        .applyIf(startTime != null && endTime != null) {
                            must(
                                    QueryBuilders.rangeQuery(MainVisitRecordPO::inTime.name)
                                            .gte(startTime!! * 1000)
                                            .lte(endTime!! * 1000)
                            )
                        }
                        .applyIf(ip != null) {
                            must(
                                    QueryBuilders.termQuery(MainVisitRecordPO::ip.name, ip)
                            )
                        }
                )
                .sort(MainVisitRecordPO::inTime.name, SortOrder.DESC)
                .size(pageable.limit)
                .from(pageable.offset)
)
val hits = restHighLevelClient.search(request, RequestOptions.DEFAULT).hits
val total = hits.totalHits?.value?:0
val result = hits.hits.map { gson.fromJson(it.sourceAsString, MainVisitRecordPO::class.java) }
  • must()表示查询条件,还有should()mustNot()等,must里的lambda表达式表示有值的时候才查询,更方便根据前端传入的参数进行条件查询。
  • limit为前端传入参数,表示查多少个。 offset为前端传入参数,表示从哪里开始查。

注意

对Elasticsearch增、删、改的操作,并不是实时的。也就是更改A数据后立刻查询A数据,得到的结果依旧是未更新的A数据。
需要立刻更新ES需要使用setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)设置,比如更新操作:

val updateRequest = UpdateRequest("order", data.orderId)
                .doc(gson.toJson(data), XContentType.JSON)
                .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)

这样会对ES性能影响,所以并不推荐使用ES做业务,来尽量规避这类问题。
所以ES终究是ES,别拿它当数据库用。

无关技术的奇怪姿势

科普一些无关技术的奇怪的姿势,多学点东西总归是有用的吧。

智和德

  • 智:是指抛开利益、权利去思考问题。这可能不是此刻解决问题的最优解,但这样做可以得到最接近真理的答案,是寻找真理的最有效方法。
  • 德:世界上不可能每个人都和你思想一致,做到理解和包容别人的思想就是德。

利用智和德去看待美国现在的种族歧视,去思考他们左派(民主党)和右派(共和党)的思想,真能受益匪浅。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章