/* * Copyright 2013 Eric F. Savage, code@efsavage.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ajah.elasticsearch; import java.io.IOException; import java.lang.reflect.ParameterizedType; import org.elasticsearch.action.ListenableActionFuture; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.sort.SortBuilder; import com.ajah.util.Identifiable; import com.ajah.util.StringUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; import lombok.extern.java.Log; /** * A base client that allows for easily indexing and searching documents. Does * not support cross-index, multi-type searches. * * @author <a href="http://efsavage.com">Eric F. Savage</a>, * <a href="mailto:code@efsavage.com">code@efsavage.com</a>. * @param <K> * The primary key field. * @param <T> * The type of the object stored/returned. * @param <C> * The concrete class of the object stored/returned. */ @Log public abstract class AbstractElasticSearchNativeClient<K extends Comparable<K>, T extends Identifiable<K>, C extends T> implements ElasticSearchClient<K, T, C> { protected Client client; private final ObjectMapper mapper = new ObjectMapper(); @Getter protected String index; @Getter protected String type; @Getter protected String clusterName; /** * Closes the node. * * @see java.lang.AutoCloseable#close() */ @Override public void close() { this.client.close(); } /** * Creates the index for this type. * * @return The acknowledgment of the request. */ public boolean createIndex() { final CreateIndexRequest createIndexRequest = new CreateIndexRequest(this.index); final CreateIndexResponse createResponse = this.client.admin().indices().create(createIndexRequest).actionGet(); return createResponse.isAcknowledged(); } @SuppressWarnings("static-method") protected SortBuilder getDefaultSort() { return null; } @SuppressWarnings("unchecked") protected Class<C> getTargetClass() { return (Class<C>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2]; } /** * Indexes a document. * * @param entity * The document to index. * @return The response of the index operations, synchronously. * @throws ElasticSearchException * If the entity could not be parsed into JSON. */ @Override public IndexResponse index(final T entity) throws ElasticSearchException { final IndexRequestBuilder irb = this.client.prepareIndex(this.index, this.type, entity.getId().toString()); String json; try { json = this.mapper.writeValueAsString(entity); } catch (JsonProcessingException e) { throw new ElasticSearchException(e); } log.finest(json); irb.setSource(json); final ListenableActionFuture<IndexResponse> result = irb.execute(); log.finest("Executed"); return result.actionGet(); } public C load(final K id) throws ElasticSearchException { final GetResponse response = this.client.prepareGet(this.index, this.type, id.toString()).execute().actionGet(); final String source = response.getSourceAsString(); if (StringUtils.isBlank(source)) { return null; } try { return this.mapper.readValue(source, getTargetClass()); } catch (IOException e) { throw new ElasticSearchException(e); } } /** * Returns a result of a search. * * @param count * @param page * * @return The results of the search. */ @Override public SearchList<C> search(final QueryBuilder queryBuilder, final SortBuilder[] sortBuilders, final int page, final int count) throws ElasticSearchException { final SearchList<C> results = new SearchList<>(); final long start = System.currentTimeMillis(); try { final SearchRequestBuilder requestBuilder = this.client.prepareSearch(this.index).setTypes(this.type).setSearchType(SearchType.DEFAULT).setFrom(page * count).setSize(count).setQuery( queryBuilder); if (sortBuilders != null) { for (final SortBuilder sortBuilder : sortBuilders) { requestBuilder.addSort(sortBuilder); } } if (queryBuilder != null) { requestBuilder.setPostFilter(queryBuilder); } if (getDefaultSort() != null) { requestBuilder.addSort(getDefaultSort()); } final SearchResponse response = requestBuilder.execute().actionGet(); log.finest(results.getTotalHits() + " hits"); results.setTotalHits(response.getHits().getTotalHits()); for (final SearchHit hit : response.getHits()) { final C result = this.mapper.readValue(hit.getSourceAsString(), getTargetClass()); results.add(result); } } catch (final IndexNotFoundException | IOException e) { log.warning(e.getMessage()); throw new ElasticSearchException(e); } results.setTime(System.currentTimeMillis() - start); return results; } /** * Returns a result of a search. * * @param query * The search query. * @return The results of the search. */ @Override public SearchList<C> search(final String query) throws ElasticSearchException { final SearchList<C> results = new SearchList<>(); final long start = System.currentTimeMillis(); try { final SearchResponse response = this.client.prepareSearch(this.index).setTypes(this.type).setSearchType(SearchType.DEFAULT).setSize(100).setQuery(QueryBuilders.matchQuery("_all", query)) .execute().actionGet(); results.setTotalHits(response.getHits().getTotalHits()); for (final SearchHit hit : response.getHits()) { final C result = this.mapper.readValue(hit.getSourceAsString(), getTargetClass()); results.add(result); } } catch (final IndexNotFoundException | IOException e) { log.warning(e.getMessage()); throw new ElasticSearchException(e); } results.setTime(System.currentTimeMillis() - start); return results; } }