Skip to content

Part2 implement search and query condition and sorting #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: part1-implement-crud
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/main/java/com/vincent/es/repository/StudentEsRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
import com.vincent.es.entity.Student;
import com.vincent.es.util.IOSupplier;
import com.vincent.es.util.SearchInfo;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class StudentEsRepository {
private final ElasticsearchClient client;
Expand Down Expand Up @@ -120,6 +123,26 @@ public void deleteById(String id) {
execute(() -> client.delete(request));
}

public List<Student> find(SearchInfo info) {
var request = new SearchRequest.Builder()
.index(indexName)
.query(info.toQuery())
.sort(info.getSortOptions())
.from(info.getFrom())
.size(info.getSize())
.build();

return execute(() -> {
var searchResponse = client.search(request, Student.class);
return searchResponse
.hits()
.hits()
.stream()
.map(Hit::source)
.collect(Collectors.toList());
});
}

private Map<String, Property> getPropertyMappings() {
var englishIssuedDateProperty = DateProperty.of(b -> b)._toProperty();
return Map.of("englishIssuedDate", englishIssuedDateProperty);
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/vincent/es/util/SampleData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.vincent.es.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vincent.es.entity.Student;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class SampleData {
public static List<Student> get() throws IOException {
var file = new File("students.json");
return new ObjectMapper().readValue(file, new TypeReference<>() {});
}
}
63 changes: 63 additions & 0 deletions src/main/java/com/vincent/es/util/SearchInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.vincent.es.util;

import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;

import java.util.List;

public class SearchInfo {
private BoolQuery boolQuery; // 查詢條件
private List<SortOptions> sortOptions = List.of(); // 排序方式
private Integer from; // 資料的跳過數量
private Integer size; // 資料的擷取數量

public static SearchInfo of(BoolQuery bool) {
var info = new SearchInfo();
info.boolQuery = bool;

return info;
}

public static SearchInfo of(Query query) {
var bool = BoolQuery.of(b -> b.filter(query));
return of(bool);
}

public BoolQuery getBoolQuery() {
return boolQuery;
}

public void setBoolQuery(BoolQuery boolQuery) {
this.boolQuery = boolQuery;
}

public List<SortOptions> getSortOptions() {
return sortOptions;
}

public void setSortOptions(List<SortOptions> sortOptions) {
this.sortOptions = sortOptions;
}

public Integer getFrom() {
return from;
}

public void setFrom(Integer from) {
this.from = from;
}

public Integer getSize() {
return size;
}

public void setSize(Integer size) {
this.size = size;
}

// library 使用 Query 類別當作條件的傳遞介面
public Query toQuery() {
return boolQuery._toQuery();
}
}
191 changes: 191 additions & 0 deletions src/main/java/com/vincent/es/util/SearchUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package com.vincent.es.util;

import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.json.JsonData;

import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SearchUtils {
private SearchUtils() {}

/**
* <pre>
* {
* "term": {
* "{@param field}": {@param value}
* }
* }
* </pre>
*/
public static Query createTermQuery(String field, Object value) {
var builder = new TermQuery.Builder();
if (value instanceof Integer) {
builder
.field(field)
.value((int) value); // 此方法接受 long 型態
} else if (value instanceof String){
builder
.field(field + ".keyword")
.value((String) value);
} else {
throw new UnsupportedOperationException("Please implement for other type additionally.");
}

return builder.build()._toQuery();
}

/**
* <pre>
* {
* "terms": {
* "{@param field}": [
* {@param values[0]},
* {@param values[1]}, ...
* ]
* }
* }
* </pre>
*/
public static Query createTermsQuery(String field, Collection<?> values) {
var elem = values.stream().findAny().orElseThrow();

Stream<FieldValue> fieldValueStream;
if (elem instanceof Integer) {
fieldValueStream = values.stream()
.map(value -> FieldValue.of(b -> b.longValue((int) value)));
} else if (elem instanceof String) {
fieldValueStream = values.stream()
.map(value -> FieldValue.of(b -> b.stringValue((String) value)));
} else {
throw new UnsupportedOperationException("Please implement for other type additionally.");
}

var fieldValues = fieldValueStream.collect(Collectors.toList());
var termsQueryField = TermsQueryField.of(b -> b.value(fieldValues));

return new TermsQuery.Builder()
.field(field)
.terms(termsQueryField)
.build()
._toQuery();
}

/**
* <pre>
* {
* "range": {
* "{@param field}": {
* "gte": "{@param gte}",
* "lte": "{@param lte}"
* }
* }
* }
* </pre>
*/
public static Query createRangeQuery(String field, Number gte, Number lte) {
var builder = new RangeQuery.Builder().field(field);

if (gte != null) {
builder.gte(JsonData.of(gte));
}

if (lte != null) {
builder.lte(JsonData.of(lte));
}

return builder.build()._toQuery();
}

public static Query createRangeQuery(String field, Date gte, Date lte) {
var builder = new RangeQuery.Builder().field(field);

if (gte != null) {
builder.gte(JsonData.of(gte));
}

if (lte != null) {
builder.lte(JsonData.of(lte));
}

return builder.build()._toQuery();
}

/**
* <pre>
* {
* "bool": {
* "should": [
* {
* "match": {
* "{@param fields[0]}": "{@param searchText}"
* }
* },
* {
* "match": {
* "{@param fields[1]}": "{@param searchText}"
* }
* }, ...
* ]
* }
* }
* </pre>
*/
public static Query createMatchQuery(Set<String> fields, String searchText) {
var bool = new BoolQuery.Builder();
fields.stream()
.map(field -> {
var matchQuery = new MatchQuery.Builder()
.field(field)
.query(searchText)
.build();
return matchQuery._toQuery();
})
.forEach(bool::should);

return bool.build()._toQuery();
}

/**
* <pre>
* {
* "exists": {
* "field": {@param field}
* }
* }
* </pre>
*/
public static Query createFieldExistsQuery(String field) {
return new ExistsQuery.Builder()
.field(field)
.build()
._toQuery();
}

/**
* <pre>
* {
* "{@param field}": {
* "order": {@param order},
* "mode": {@param mode}
* }
* }
* </pre>
*/
public static SortOptions createSortOption(String field, SortOrder order, SortMode mode) {
var fieldSort = new FieldSort.Builder()
.field(field)
.order(order)
.mode(mode)
.build();
return SortOptions.of(b -> b.field(fieldSort));
}

public static SortOptions createSortOption(String field, SortOrder order) {
return createSortOption(field, order, null);
}
}
13 changes: 0 additions & 13 deletions src/test/java/com/vincent/es/ApplicationTests.java

This file was deleted.

Loading