文章目录
概述
Elasticesearch本身是Java语言开发的,天生对Java支持的最好。因此我们用Java来开发相关接口是一个不错的选择。(当前用于测试的elasticsearch版本均为<es.version>5.6.14</es.version>)
Maven配置
在我们的mvn项目中的pom.xml中增加以下代码
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${es.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>${es.version}</version>
</dependency>
Java API
在我们正式开始之前,我们需要初始化我们的transport client的相关settings:
@Data
@Configuration
@PropertySource("classpath:elasticsearch.properties")
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
private String clusterName;
private String host;
private int port;
}
@Getter
@Component
public final class ClientSetting implements InitializingBean {
private InetSocketTransportAddress address;
private Settings settings;
@Autowired
private ElasticSearchConfig config;
@Override
public void afterPropertiesSet() throws Exception {
address = new InetSocketTransportAddress(InetAddress.getByName(config.getHost()), config.getPort());
settings = Settings.builder()
.put("client.transport.sniff", true)
.put("cluster.name", config.getClusterName())
.build();
}
}
1. Indexing Documents
Index API旨在为指定的索引创建or更新一个JSON类型的文档,并使之成为可搜索的文档。eg:
public IndexResponse createOrUpdateDocument(String index, IndexType type, String jsonObject) {
try (TransportClient client = new PreBuiltTransportClient(setting.getSettings())
.addTransportAddress(setting.getAddress())) {
return client.prepareIndex(index, Objects.isNull(type) ? null : type.name())
.setSource(jsonObject, XContentType.JSON).get();
}
}
以上是我自己在原生的API上再进行了一次封装,以便其它模块的方便使用。从上面的API可以看出,索引,文档内容以及文档类型为必传字段,而文档id(每个文档在es中的唯一标识)为非必传,若我们不传该字段时,es会自动的给我们生成一个(eg:AWvaz8Yop5uxUWtCF426)。
Note: 通常状况下 client.prepareIndex 当es中没有我们指定的索引时,会自动给我们创建。我们也可以通过修改elasticsearch.yml或者直接通过集群的API去禁用该功能,通过将"action.auto_create_index"置为false即可,下面展示的是通过集群API去禁用的代码:
public boolean closeAutoCreateIndexFunction() {
try (TransportClient client = new PreBuiltTransportClient(setting.getSettings()).addTransportAddress(setting.getAddress())) {
Settings.Builder autoCreateIndex = Settings.builder().put("action.auto_create_index", false);
ClusterUpdateSettingsResponse result = client.admin().cluster().prepareUpdateSettings().setPersistentSettings(autoCreateIndex).get();
return result.getPersistentSettings().getAsBoolean("action.auto_create_index", true);
}
}
禁用后我们再次测试就会抛出一个找不到索引的异常:
@Test
public void testCreateDocument() {
Programmer p = Programmer.builder()
.companyName("alibaba")
.name("cold_blade")
.age(31)
.level(7)
.salary(20000)
.build();
IndexResponse response = documentService.createOrUpdateDocument(Indexes.COMPANY, JSON.toJSONString(p), IndexType.PROGRAMMER);
Assert.assertEquals(Result.CREATED, response.getResult());
Assert.assertEquals(Indexes.EMPLOYEE, response.getIndex());
Assert.assertEquals(IndexType.PROGRAMMER.name(), response.getType());
}
[company] IndexNotFoundException[no such index]
at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.concreteIndices(IndexNameExpressionResolver.java:187)
at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.concreteIndices(IndexNameExpressionResolver.java:123)
at org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.concreteSingleIndex(IndexNameExpressionResolver.java:244)
at org.elasticsearch.action.bulk.TransportBulkAction$ConcreteIndices.resolveIfAbsent(TransportBulkAction.java:494)
at org.elasticsearch.action.bulk.TransportBulkAction$BulkOperation.addFailureIfIndexIsUnavailable(TransportBulkAction.java:448)
...
2. Retrieving and Deleting Documents
prepareGet,prepareDelete API相对来说比较简单,二者都需要通过索引及其文档的id从集群中检索or删除文档。eg:
public GetResponse getDocument(String index, @Nullable IndexType type, String docId) {
try (TransportClient client = new PreBuiltTransportClient(setting.getSettings())
.addTransportAddress(setting.getAddress())) {
return client.prepareGet(index, Objects.isNull(type) ? null : type.name(), docId).get();
}
}
public DeleteResponse deleteDocument(String index, IndexType type, String docId) {
try (TransportClient client = new PreBuiltTransportClient(setting.getSettings())
.addTransportAddress(setting.getAddress())) {
return client.prepareDelete(index, type.name(), docId).get();
}
}
@Test
public void testGet() {
String docId = "8888";
// 传type的情形
GetResponse response = documentService.getDocument(Indexes.EMPLOYEE, IndexType.BOSS, docId);
Assert.assertTrue(response.isExists());
// 传错误type的情形
response = documentService.getDocument(Indexes.EMPLOYEE, IndexType.PROGRAMMER, docId);
Assert.assertFalse(response.isExists());
// 不传type的情形
response = documentService.getDocument(Indexes.EMPLOYEE, null, docId);
Assert.assertTrue(response.isExists());
Boss cold = new JSONObject(response.getSource()).toJavaObject(Boss.class);
Assert.assertEquals(30, cold.getAge());
}
从测试情况来看 index和docId是关键信息,而type则可传可传,但是如果传了type,则必须保证type的正确性。
3. Querying Indexed Documents
通过简单的两个example的尝试,我们接下来尝试一个稍微复杂点的API(client.prepareSearch),
// 一个通过年龄范围查询的接口
public SearchResponse rangeAge(int begin, int include) {
try (TransportClient client = new PreBuiltTransportClient(setting.getSettings())
.addTransportAddress(setting.getAddress())) {
return client.prepareSearch()
// DFS_QUERY_THEN_FETCH 需要对结果进行一个算分的额外操作
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setPostFilter(QueryBuilders.rangeQuery("age").from(begin).to(include))
.execute()
.actionGet();
}
}
@Test
public void testSearch() {
SearchResponse response = documentService.rangeAge(35, 40);
List<SearchHit> hits = Arrays.asList(response.getHits().getHits());
Assert.assertFalse(hits.isEmpty());
Assert.assertTrue(hits.stream()
.map(hit -> JSON.parseObject(hit.getSourceAsString(), Programmer.class))
.noneMatch(p -> p.getAge() < 35 || p.getAge() > 40));
}
查询结果对象SearchResponse中包含了很多信息,包括一些统计信息(totalHits等等),其中最重要的数据(数据源)保存在SearchHit中,通过getSourceAsString将结果转换成JSON字串,在通过FastJson转成了我们所定义的对象。这里只是简单的测试了下search接口,其实es还提供了更丰富的支持条件过滤的便使用的对象QueryBuilders,详细可去查看es的源码。