All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itranswarp.search.SearchableClient Maven / Gradle / Ivy

package com.itranswarp.search;

import java.io.IOException;
import java.lang.reflect.Array;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

/**
 * A searchable client.
 * 
 * @author Michael Liao
 */
public class SearchableClient implements AutoCloseable {

	private final Log log = LogFactory.getLog(getClass());

	private String basePackage;
	private String index = "default";
	private String host = "localhost";
	private int port = 9300;
	private int maxResults = 100;

	private Client client;
	private Map, Mapping> mappings = new HashMap<>();

	public void setBasePackage(String basePackage) {
		this.basePackage = basePackage;
	}

	public void setIndex(String index) {
		this.index = index;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public void setMaxResults(int maxResults) {
		this.maxResults = maxResults;
	}

	public  SearchResults search(Class clazz, String words) {
		Mapping mapping = getMappingFromClass(clazz);
		Span[] spans = SplitUtil.split(words);
		if (spans.length == 0) {
			return null;
		}
		QueryBuilder queryBuilder = null;
		if (spans.length == 1) {
			queryBuilder = createQueryBuilder(spans[0]);
		} else {
			BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
			for (Span span : spans) {
				boolQueryBuilder.should(createQueryBuilder(span));
			}
			queryBuilder = boolQueryBuilder;
		}
		SearchResponse sr = client.prepareSearch(index).setQuery(queryBuilder).setSize(maxResults).get();
		SearchHits shs = sr.getHits();
		long total = shs.getTotalHits();
		SearchHit[] hs = shs.getHits();
		@SuppressWarnings("unchecked")
		T[] results = (T[]) Array.newInstance(clazz, hs.length);
		for (int i = 0; i < hs.length; i++) {
			SearchHit sh = hs[i];
			Map props = sh.getSource();
			results[i] = mapping.createBean(sh.getId(), props);
		}
		return new SearchResults<>(total, Arrays.asList(results));
	}

	QueryBuilder createQueryBuilder(Span span) {
		if (span instanceof Word) {
			return QueryBuilders.termQuery("_all", span.text);
		}
		if (span instanceof Phrase) {
			if (span.text.length() > 3) {
				return QueryBuilders.multiMatchQuery(span.text, "_all").minimumShouldMatch("75%");
			} else {
				return QueryBuilders.matchPhraseQuery("_all", span.text);
			}
		}
		throw new IllegalArgumentException("Unsupported type of Span: " + span.getClass().getName());
	}

	/**
	 * Get document by id.
	 * 
	 * @param clazz
	 *            Class of document.
	 * @param id
	 *            Document id.
	 * @return Java bean.
	 */
	public  T get(Class clazz, String id) {
		Mapping mapping = getMappingFromClass(clazz);
		GetResponse gr = client.prepareGet(index, mapping.getType(), id).get();
		if (!gr.isExists()) {
			return null;
		}
		Map props = gr.getSourceAsMap();
		return mapping.createBean(id, props);
	}

	/**
	 * Index a searchable bean.
	 * 
	 * @param bean
	 *            Searchable bean.
	 */
	public  void index(T bean) {
		Mapping mapping = getMappingFromBean(bean);
		IndexResponse ir = client.prepareIndex(index, mapping.getType(), mapping.getId(bean))
				.setSource(mapping.getSource(bean)).get();
		log.info("Type " + mapping.getType() + "@" + ir.getId() + " indexed.");
	}

	/**
	 * Unindex a searchable bean.
	 * 
	 * @param bean
	 *            Searchable bean.
	 */
	public  void unindex(T bean) {
		Mapping mapping = getMappingFromBean(bean);
		DeleteResponse dr = client.prepareDelete(index, mapping.getType(), mapping.getId(bean)).get();
		log.info("Type " + mapping.getType() + "@" + dr.getId() + " unindexed.");
	}

	/**
	 * Unindex a searchable bean.
	 * 
	 * @param clazz
	 *            Class type.
	 * @param id
	 *            Id as string.
	 */
	public  void unindex(Class clazz, String id) {
		Mapping mapping = getMappingFromClass(clazz);
		DeleteResponse dr = client.prepareDelete(index, mapping.getType(), id).get();
		log.info("Type " + mapping.getType() + "@" + dr.getId() + " unindexed.");
	}

	Mapping getMappingFromBean(Object bean) {
		if (bean == null) {
			throw new IllegalArgumentException("Argument cannot be null.");
		}
		Mapping mapping = mappings.get(bean.getClass());
		if (mapping == null) {
			throw new IllegalArgumentException(
					"Class " + bean.getClass().getName() + " is not defined in searchable packages.");
		}
		return mapping;
	}

	Mapping getMappingFromClass(Class clazz) {
		if (clazz == null) {
			throw new IllegalArgumentException("Argument cannot be null.");
		}
		Mapping mapping = mappings.get(clazz);
		if (mapping == null) {
			throw new IllegalArgumentException("Class " + clazz.getName() + " is not defined in searchable packages.");
		}
		return mapping;
	}

	@SuppressWarnings("resource")
	public void init() throws IOException {
		log.info("Init client...");
		this.client = new PreBuiltTransportClient(Settings.EMPTY)
				.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
		// check doc types:
		List> docTypes = ClassUtil.scan(basePackage, (c) -> {
			return c.isAnnotationPresent(SearchableDocument.class);
		});
		for (Class docType : docTypes) {
			this.mappings.put(docType, new Mapping(docType));
		}
	}

	public boolean createMapping(Class docType) {
		Mapping mapping = getMappingFromClass(docType);
		IndicesAdminClient idc = client.admin().indices();
		GetMappingsResponse gmr = idc.getMappings(new GetMappingsRequest()).actionGet();
		ImmutableOpenMap> mappings = gmr.getMappings();
		if (mappings.containsKey(mapping.getType())) {
			log.info("Found mapping for class " + docType.getName() + ".");
			return false;
		}
		log.info("Mapping not found for class " + docType.getName() + ". Auto-create...");
		PutMappingResponse pmr = idc.preparePutMapping(index).setType(mapping.getType()).setSource(mapping.getSource())
				.get();
		if (!pmr.isAcknowledged()) {
			throw new RuntimeException("Failed to create mapping for class:" + docType.getName() + ".");
		}
		return true;
	}

	/**
	 * Create index.
	 * 
	 * @return True if index create ok, false if index is already exist.
	 */
	public boolean createIndex() {
		// check index exist:
		IndicesAdminClient idc = client.admin().indices();
		IndicesExistsResponse ier = idc.exists(new IndicesExistsRequest(index)).actionGet();
		if (!ier.isExists()) {
			log.info("Index not found. Auto-create...");
			// create index:
			CreateIndexResponse cir = idc.create(new CreateIndexRequest(index)).actionGet();
			if (!cir.isAcknowledged()) {
				throw new RuntimeException("Failed to create index.");
			}
			return true;
		}
		return false;
	}

	@Override
	public void close() {
		if (client != null) {
			client.close();
			client = null;
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy