/*
* Copyright 2014-2016 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.rest;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
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.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import com.ajah.elasticsearch.ElasticSearchClient;
import com.ajah.elasticsearch.ElasticSearchException;
import com.ajah.elasticsearch.SearchList;
import com.ajah.util.Identifiable;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
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 AbstractElasticSearchRestClient<K extends Comparable<K>, T extends Identifiable<K>, C extends T> implements ElasticSearchClient<K, T, C> {
protected RestClient<C> client;
private final ObjectMapper mapper = new ObjectMapper();
@Getter
protected String index;
@Getter
protected String type;
@Getter
@Setter
protected Class<?> clazz;
protected String hostname;
protected int port;
protected CloseableHttpClient http;
/**
* Closes the client.
*
* @throws ElasticSearchException
* If there was an {@link IOException} when closing the client.
*
* @see java.lang.AutoCloseable#close()
*/
@Override
public void close() throws ElasticSearchException {
try {
this.client.close();
} catch (IOException e) {
throw new ElasticSearchException(e);
}
}
@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.
*/
@Override
public IndexResponse index(final T entity) {
throw new NotImplementedException("Indexing not implemented yet");
}
/**
* 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 SearchRequest requestBuilder = new SearchRequest().indices(this.index).types(this.type).searchType(SearchType.DEFAULT);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().from(page * count).size(count).query(queryBuilder);
if (sortBuilders != null) {
for (final SortBuilder sortBuilder : sortBuilders) {
sourceBuilder.sort(sortBuilder);
}
}
if (queryBuilder != null) {
sourceBuilder.postFilter(queryBuilder);
}
if (getDefaultSort() != null) {
sourceBuilder.sort(getDefaultSort());
}
log.severe(sourceBuilder.toString());
final RestSearchResponse<C> response = this.client.search(this.hostname, this.port, this.index, this.type, sourceBuilder.toString(), this.clazz, this.http);
sourceBuilder.toString();
// log.severe(sourceBuilder.buildAsBytes().toUtf8());
// 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 e) {
log.warning(e.getMessage());
}
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;
}
@SuppressWarnings("unchecked")
@Override
public C load(K id) throws ElasticSearchException {
HttpGet get = new HttpGet("http://" + this.hostname + ":" + this.port + "/" + this.index + "/" + this.type + "/" + id);
log.fine(get.getURI().toString());
try (CloseableHttpResponse response = this.http.execute(get)) {
HttpEntity entity = response.getEntity();
// EntityUtils.consume(entity);
// for (final SearchHit hit : response.getHits()) {
// final C result = this.mapper.readValue(hit.getSourceAsString(),
// getTargetClass());
// results.add(result);
// }
return ((RestGetResponse<C>) this.mapper.readValue(entity.getContent(), this.clazz)).source;
} catch (IOException e) {
throw new ElasticSearchException(e);
}
}
}