由於目前項目中用的JDK是1.7版本的,而ElasticSearch5.x版本須要使用JDK1.8版本,因此如下的代碼都是基於ElasticSearch 2.4.6版本的,如下的代碼是從項目中提取出來的,因此有些跟業務相關的代碼就不貼出來了,僅供本身只好參考使用,因此直接看如下代碼,可能不少代碼是看不懂的。java
引入Mavenapache
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>2.4.6</version> </dependency>
枚舉工具類json
package com.linewell.ccip.utils.es; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 枚舉工具類 */ public class EnumUtils { // 枚舉字符串轉枚舉類型的轉換方法 private static String FROM_STRING = "fromString"; /** * 供 * 解析通用查詢參數(分頁參數、排序參數) * DataGridUtils.parseQueryInfo方法調用 * * 根據類、字段名稱、字段值(枚舉字符),若字段爲枚舉,返回枚舉值,不然直接返回字段值 * @param entityCls 類,如com.linewell.ccip.servicesbase.bean.log.OrgLog.class * @param fieldName 字段名稱,如com.linewell.ccip.servicesbase.bean.log.type.OperType * @param fieldVal 字段(枚舉字符串),如INSERT * @return 若字段爲枚舉,返回枚舉值,不然直接返回字段值 */ public static Object getValByField(Class<?> entityCls, String fieldName, Object fieldVal) { Object obj = null; try { // 字段類型 Class<?> fieldCls = getFieldType(entityCls, fieldName); // 字段類型是否爲枚舉類型 boolean isEnumCls = fieldCls.isEnum(); // 是枚舉類 if (isEnumCls) { obj = getEnum(fieldCls, (String)fieldVal); } else { obj = fieldVal; } } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return obj; } /** * 根據枚舉字符串獲取枚舉值 * @param fieldCls 枚舉類 * @param fieldVal 枚舉字符串 * @return 枚舉值 * @throws IllegalAccessException * @throws InvocationTargetException */ private static Enum<?> getEnum(Class<?> fieldCls, String fieldVal) throws IllegalAccessException, InvocationTargetException { Enum<?> enumCls = null; // 所有的方法 Method[] methods = fieldCls.getMethods(); // 方法不爲空 if (null != methods && 0 < methods.length) { // 方法名稱 String metName = null; // 遍歷所有方法 for (Method method : methods) { metName = method.getName(); // 枚舉類的字符串轉枚舉的方法 if (FROM_STRING.equalsIgnoreCase(metName)) { enumCls = (Enum<?>) method.invoke(fieldCls, fieldVal); break; } } } return enumCls; } /** * 根據類、屬性名獲取其屬性類型 * @param cls * @param findFieldName * @return */ private static Class<?> getFieldType(Class<?> cls, String findFieldName) { // 字段類型 Class<?> fieldCls = null; try { // 獲取該類自身所聲明的屬性,沒有獲取父類聲明的屬性 List<Field> fields = getFields(cls); // 屬性不爲空 if (null != fields) { // 是否找到屬性 boolean isFind = false; // 屬性名 String fieldName = ""; // 變量屬性數組 for (Field field : fields) { fieldName = field.getName(); // 類自身找到屬性獲取其屬性類型 if (findFieldName.equalsIgnoreCase(fieldName)) { isFind = true; fieldCls = field.getType(); break; } } // 類自身沒有找到屬性獲取其屬性類型,查找其父類聲明的屬性 if (false == isFind) { Field supField = cls.getSuperclass().getDeclaredField(findFieldName); fieldCls = supField.getType(); } } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } return fieldCls; } private static List<Field> getFields(Class<?> objClass) { Field[] fields = objClass.getDeclaredFields(); List<Field> fieldList = new ArrayList<>(); fieldList.addAll(Arrays.asList(fields)); while (null != objClass) { fieldList.addAll(Arrays.asList(objClass.getDeclaredFields())); objClass = objClass.getSuperclass(); } return fieldList; } }
ElasticSearch工具類數組
package com.linewell.ccip.utils.es; import com.google.common.collect.Lists; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; 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.put.PutMappingRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.IndicesAdminClient; import org.elasticsearch.client.Requests; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.InetAddress; import java.util.*; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; /** * ElasticSearch工具類 * * @author zchuanzhao * 2018/7/3 */ public class EsHandler { /** * 須要分詞字段 */ private static List<String> ANALYZED_FIELD = Lists.newArrayList("userName", "authType"); /** * 集羣名稱cluster.name */ public static final String ES_CLUSTER_NAME = "logdb"; /** * 主機IP */ private static final String ES_HOST = "localhost"; /** * 端口號 */ private static final int ES_TCP_PORT = 9300; public ThreadLocal<SearchResponse> responseThreadLocal = new ThreadLocal<>(); private static final Map<String, String> MAPPINGS = new HashMap<>(); // Settings settings = Settings.settingsBuilder() // .put("cluster.name", "bropen") // .put("shield.user","bropen:password") // .build(); static Settings settings = Settings.settingsBuilder().put("cluster.name", ES_CLUSTER_NAME).build(); // 建立ES客戶端 private static TransportClient client; static { try { client = TransportClient.builder().settings(settings).build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ES_HOST), ES_TCP_PORT)); } catch (Exception e) { e.printStackTrace(); } } /** * 取得ES客戶端 */ public static TransportClient getClient() { return client; } /** * 關閉客戶端 * * @param client */ public static void close(TransportClient client) { if (null != client) { client.close(); } } /** * Mapping處理 * * @param obj */ public static void mapping(Object obj) { String type = obj.getClass().getSimpleName().toLowerCase(); if (!MAPPINGS.containsKey(type)) { synchronized (EsHandler.class) { createIndex(); //獲取全部的Mapping ImmutableOpenMap<String, MappingMetaData> mappings = client.admin().cluster().prepareState().execute() .actionGet().getState().getMetaData().getIndices().get(ES_CLUSTER_NAME).getMappings(); MappingMetaData mmd = mappings.get(type); if (null == mmd) { createMapping(obj); } else { CompressedXContent xContent = mappings.get(type).source(); if (xContent == null) { createMapping(obj); } else { String mapping = xContent.toString(); MAPPINGS.put(type, mapping); } } } } } /** * 建立Mapping * * @param obj */ public static void createMapping(Object obj) { String type = obj.getClass().getSimpleName().toLowerCase(); PutMappingRequest mapping = Requests.putMappingRequest(ES_CLUSTER_NAME).type(type).source(setMapping(obj)); EsHandler.getClient().admin().indices().putMapping(mapping).actionGet(); } /** * 設置對象的ElasticSearch的Mapping * * @param obj * @return */ public static XContentBuilder setMapping(Object obj) { List<Field> fieldList = getFields(obj); XContentBuilder mapping = null; try { mapping = jsonBuilder().startObject().startObject("properties"); for (Field field : fieldList) { //修飾符是static的字段不處理 if (Modifier.isStatic(field.getModifiers())){ continue; } String name = field.getName(); if (ANALYZED_FIELD.contains(name)) { mapping.startObject(name) .field("type", getElasticSearchMappingType(field.getType().getSimpleName().toLowerCase())) .field("index", "analyzed") //使用IK分詞器 .field("analyzer", "ik_max_word") .field("search_analyzer", "ik_max_word") .endObject(); } else { mapping.startObject(name) .field("type", getElasticSearchMappingType(field.getType().getSimpleName().toLowerCase())) .field("index", "not_analyzed") .endObject(); } } mapping.endObject().endObject(); } catch (IOException e) { e.printStackTrace(); } return mapping; } /** * 獲取對象全部自定義的屬性 * * @param obj * @return */ private static List<Field> getFields(Object obj) { Field[] fields = obj.getClass().getDeclaredFields(); List<Field> fieldList = new ArrayList<>(); fieldList.addAll(Arrays.asList(fields)); Class objClass = obj.getClass(); while (null != objClass) { fieldList.addAll(Arrays.asList(objClass.getDeclaredFields())); objClass = objClass.getSuperclass(); } return fieldList; } /** * java類型與ElasticSearch類型映射 * * @param varType * @return */ private static String getElasticSearchMappingType(String varType) { String es; switch (varType) { case "date": es = "date"; break; case "double": es = "double"; break; case "long": es = "long"; break; case "int": es = "long"; break; default: es = "string"; break; } return es; } /** * 判斷ElasticSearch中的索引是否存在 */ private static boolean indexExists() { IndicesAdminClient adminClient = client.admin().indices(); IndicesExistsRequest request = new IndicesExistsRequest(ES_CLUSTER_NAME); IndicesExistsResponse response = adminClient.exists(request).actionGet(); if (response.isExists()) { return true; } return false; } /** * 建立索引 */ private static void createIndex() { if (!indexExists()) { CreateIndexRequest request = new CreateIndexRequest(ES_CLUSTER_NAME); client.admin().indices().create(request); } } }
ElasticSearch查詢條件操做工具類app
package com.linewell.ccip.utils.es; import com.linewell.ccip.common.bean.datalist.engine.QueryInfo; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.search.sort.SortOrder; import java.util.Map; import java.util.Set; /** * ElasticSearch查詢條件操做工具類 * @author zchuanzhao * 2018/7/5 */ public class EsDataGridUtils { /** * 排序字段 */ private static final String SORT_FIELD = "sortField"; /** * 排序類型 */ private static final String SORT_ORDER = "sortOrder"; /** * 查詢字段 */ private static final String QUERY_KEY = "query"; /** * 相等條件 */ private static final String EQUALS_KEY = "equals"; /** * 模糊匹配條件 */ private static final String LIKE_KEY = "like"; /** * 大於等於條件 */ private static final String GREATER_EQUALS_KEY = "ge"; /** * 小於等於條件 */ private static final String LESS_EQUALS_KEY = "le"; /** * 查詢條件關係 */ private static final String SELECT_KEY = "select"; /** * AND條件關係 */ private static final String AND_KEY = "and"; /** * ASC */ private static final String ASC_KEY = "asc"; /** * 解析通用查詢參數(分頁參數、排序參數) * @param builder * @param queryInfo 查詢信息 * @param beanClass 查詢對象 * @param defaultSort 默認排序 */ public static void parseQueryInfo(SearchRequestBuilder builder, QueryInfo queryInfo, Class beanClass, String defaultSort) { Map<String, String> requestMap = queryInfo.getRequestMap(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); // 排序類型 SortOrder sortOrder = SortOrder.DESC; // 排序字段 String sortField = defaultSort; // 查詢字段 if (requestMap != null) { //獲取排序信息 if (requestMap.containsKey(SORT_ORDER)) { String sortOrderValue = requestMap.get(SORT_ORDER); if (ASC_KEY.equals(sortOrderValue)){ sortOrder = SortOrder.ASC; } } if (requestMap.containsKey(SORT_FIELD) && null != requestMap.get(SORT_FIELD)) { sortField = requestMap.get(SORT_FIELD); } //獲取查詢條件信息 if (requestMap.containsKey(QUERY_KEY)) { String queryStr = requestMap.get(QUERY_KEY); if(!StringUtils.isEmpty(queryStr)) { JSONObject queryObj = JSONObject.fromObject(queryStr); // 相等的字段 JSONObject equalsObj = queryObj.optJSONObject(EQUALS_KEY); // 匹配字段 JSONObject likeObj = queryObj.optJSONObject(LIKE_KEY); // 匹配字段 JSONObject geObj = queryObj.optJSONObject(GREATER_EQUALS_KEY); // 匹配字段 JSONObject leObj = queryObj.optJSONObject(LESS_EQUALS_KEY); if(equalsObj != null) { Set<String> set = equalsObj.keySet(); // 字段值 Object objVal; // 字段值 Object fieldVal; for (String fieldName: set) { fieldVal = equalsObj.opt(fieldName); // 若字段爲枚舉,返回枚舉值,不然直接返回字段值 objVal = EnumUtils.getValByField(beanClass, fieldName, fieldVal); boolQueryBuilder.must(QueryBuilders.matchQuery(fieldName, objVal)); } } JSONObject selectObj = queryObj.optJSONObject(SELECT_KEY); boolean queryAnd = false; if (null != selectObj) { queryAnd = (Boolean) selectObj.get(AND_KEY); } if(likeObj != null) { Set<String> set = likeObj.keySet(); for(String key: set) { QueryBuilder termQuery = QueryBuilders.termQuery(key, likeObj.optString(key)); if (queryAnd){ boolQueryBuilder.must(termQuery); }else { boolQueryBuilder.should(termQuery); } } } //大於等於 if(geObj != null) { Set<String> set = geObj.keySet(); for(String key: set) { RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(key).gte(geObj.optString(key)); if (queryAnd){ boolQueryBuilder.must(rangeQuery); }else { boolQueryBuilder.should(rangeQuery); } } } //小於等於 if(leObj != null) { Set<String> set = leObj.keySet(); for(String key: set) { RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery(key).lte(leObj.optString(key)); if (queryAnd){ boolQueryBuilder.must(rangeQuery); }else { boolQueryBuilder.should(rangeQuery); } } } } } } builder.setQuery(boolQueryBuilder); //分頁查詢,設置偏移量和每頁查詢數量 builder.setFrom(queryInfo.getCurrentPageNum() * queryInfo.getCountPerPage()).setSize(queryInfo.getCountPerPage()); //排序 builder.addSort(sortField, sortOrder); } }
ElasticSearch操做類接口elasticsearch
package com.linewell.ccip.services.es; import com.linewell.ccip.common.bean.datalist.engine.QueryInfo; import com.linewell.ccip.servicesbase.bean.GridData; import com.linewell.ccip.servicesbase.bean.log.LogBeanEntity; /** * ElasticSearch操做類接口 * @author zchuanzhao * 2018/7/2 */ public interface IEsManager { /** * 保存對象 * @param obj * @return */ boolean save(Object obj); /** * 更新 * @param obj * @return */ boolean update(Object obj); /** * 刪除 * @param unid * @param beanClass * @return */ boolean delete(String unid, Class beanClass); /** * 獲取單個對象 * @param unid * @param beanClass * @param <T> * @return */ <T extends LogBeanEntity> T get(String unid, Class<T> beanClass); /** * 獲取分頁列表 * @param queryInfo * @param beanClass * @param defaultSort * @param <T> * @return */ <T extends LogBeanEntity> GridData<T> getList(QueryInfo queryInfo, Class<T> beanClass, String defaultSort); }
ElasticSearch操做類ide
package com.linewell.ccip.services.es.impl; import com.linewell.ccip.common.bean.datalist.engine.QueryInfo; import com.linewell.ccip.services.es.IEsManager; import com.linewell.ccip.servicesbase.bean.GridData; import com.linewell.ccip.servicesbase.bean.log.LogBeanEntity; import com.linewell.ccip.utils.es.EsHandler; import com.linewell.ccip.utils.es.EsDataGridUtils; import net.sf.json.JSONObject; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.ClearScrollRequest; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import java.util.ArrayList; import java.util.List; /** * ElasticSearch操做類 * @author zchuanzhao * 2018/7/2 */ public class EsManagerImpl implements IEsManager { /** * TCP鏈接客戶端 */ private TransportClient client; public EsManagerImpl() { this.client = EsHandler.getClient(); } /** * 保存對象 * @param obj * @return */ @Override public boolean save(Object obj) { EsHandler.mapping(obj); String className = obj.getClass().getSimpleName().toLowerCase(); JSONObject json = JSONObject.fromObject(obj); json.put("id",json.get("unid")); IndexResponse response = client.prepareIndex(EsHandler.ES_CLUSTER_NAME, className, String.valueOf(json.get("id"))).setSource(json).get(); return response.isCreated(); } /** * 更新 * @param obj * @return */ @Override public boolean update(Object obj) { String className = obj.getClass().getSimpleName().toLowerCase(); JSONObject json = JSONObject.fromObject(obj); UpdateResponse response = client.prepareUpdate(EsHandler.ES_CLUSTER_NAME, className, String.valueOf(json.get("id"))) .setDoc(json) .get(); return !response.isCreated(); } /** * 刪除 * @param unid * @param beanClass * @return */ @Override public boolean delete(String unid, Class beanClass) { DeleteResponse response = client.prepareDelete(EsHandler.ES_CLUSTER_NAME, beanClass.getSimpleName().toLowerCase(), unid).get(); return response.isFound(); } /** * 獲取單個對象 * @param unid * @param beanClass * @param <T> * @return */ @Override public <T extends LogBeanEntity> T get(String unid, Class<T> beanClass) { GetResponse response = client.prepareGet(EsHandler.ES_CLUSTER_NAME, beanClass.getSimpleName().toLowerCase(), unid).get(); if (response.isExists()) { String jsonStr = response.getSourceAsString(); JSONObject json = JSONObject.fromObject(jsonStr); //TODO json中beanFlag保存的是null字符串,轉成bean會報錯,因此暫時先設置爲null json.put("beanFlag",null); T bean = (T) JSONObject.toBean(json, beanClass); //TODO json -> bean後,time會變成系統當前時間,暫時先用如下方式解決 bean.setTime(json.getLong("time")); return bean; } return null; } /** * 獲取分頁列表 * @param queryInfo * @param beanClass * @param defaultSort * @param <T> * @return */ @Override public <T extends LogBeanEntity> GridData<T> getList(QueryInfo queryInfo, Class<T> beanClass, String defaultSort) { SearchRequestBuilder builder = client.prepareSearch(EsHandler.ES_CLUSTER_NAME).setTypes(beanClass.getSimpleName().toLowerCase()). setSearchType(SearchType.DEFAULT); //設置查詢條件信息 EsDataGridUtils.parseQueryInfo(builder, queryInfo, beanClass, defaultSort); SearchResponse response = builder.execute().actionGet(); SearchHits hits = response.getHits(); List<T> list = new ArrayList<>(); for (int i = 0; i < hits.getHits().length; i++) { String jsonStr = hits.getHits()[i].getSourceAsString(); JSONObject json = JSONObject.fromObject(jsonStr); //TODO json中beanFlag保存的是null字符串,轉成bean會報錯,因此暫時先設置爲null json.put("beanFlag", null); T bean = (T) JSONObject.toBean(json, beanClass); //TODO json -> bean後,time會變成系統當前時間,暫時先用如下方式解決 bean.setTime(json.getLong("time")); list.add(bean); } GridData<T> gridData = new GridData<>(); gridData.setData(list); gridData.setTotal(response.getHits().getTotalHits()); return gridData; } }
這裏分頁使用的是from+size方式,由於咱們業務是須要跳頁和上一頁下一頁的,因此沒法使用scoll方式,可是使用from+size默認只能查詢10000之內的數據,因此使用ElasticSearch沒法實現咱們目前的業務,因此改用了solr。工具