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

org.redkalex.source.search.SearchInfo Maven / Gradle / Ivy

There is a newer version: 2.7.7
Show newest version
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkalex.source.search;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntFunction;
import java.util.logging.*;
import org.redkale.annotation.ConstructorParameters;
import org.redkale.annotation.LogExcludeLevel;
import org.redkale.annotation.LogLevel;
import org.redkale.convert.json.*;
import org.redkale.persistence.*;
import org.redkale.persistence.SearchColumn;
import org.redkale.persistence.VirtualEntity;
import org.redkale.source.*;
import org.redkale.util.*;

/**
 * Search Entity操作类
 *
 * 

详情见: https://redkale.org * * @since 2.4.0 * @author zhangjx * @param Search Entity类的泛型 */ @SuppressWarnings("unchecked") public final class SearchInfo { // 全局静态资源 private static final ConcurrentHashMap entityInfos = new ConcurrentHashMap<>(); private static final ReentrantLock infosLock = new ReentrantLock(); // 日志 private static final Logger logger = Logger.getLogger(SearchInfo.class.getSimpleName()); // Entity类名 private final Class type; // 类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null private final String table; // JsonConvert private final JsonConvert jsonConvert; // Entity构建器 private final Creator creator; // Entity数值构建器 private final IntFunction arrayer; // Entity构建器参数 private final String[] constructorParameters; // Entity构建器参数Attribute private final Attribute[] constructorAttributes; // Entity构建器参数Attribute private final Attribute[] unconstructorAttributes; // 主键 private final Attribute primary; // 用于存储绑定在EntityInfo上的对象 private final ConcurrentHashMap subobjectMap = new ConcurrentHashMap<>(); // key是field的name, 不是sql字段。 // 存放所有与数据库对应的字段, 包括主键 private final HashMap> attributeMap = new HashMap<>(); // 存放所有与数据库对应的字段, 包括主键 private final Attribute[] attributes; // key是field的name, value是Column的别名,即数据库表的字段名 // 只有field.name 与 Column.name不同才存放在aliasmap里. private final Map aliasmap; // 所有可更新字段,即排除了主键字段和标记为@Column(updatable=false)的字段 private final Map> updateAttributeMap = new HashMap<>(); // 分表 策略 private final DistributeTableStrategy tableStrategy; // 数据库中所有字段 private final Attribute[] queryAttributes; // 数据库中所有可新增字段 private final Attribute[] insertAttributes; // 数据库中所有可更新字段 private final Attribute[] updateAttributes; // 存放highlight虚拟主键字段 private final Attribute highlightAttributeId; // 存放highlight虚拟索引字段 private final Attribute highlightAttributeIndex; // 存放highlight虚拟字段 private final HashMap> highlightAttributeMap = new HashMap<>(); // 存放html定制的analyzer private final HashMap customAnalyzerMap = new HashMap<>(); private final Map mappingTypes; // 日志级别,从LogLevel获取 private final int logLevel; private final boolean virtual; // 日志控制 private final Map excludeLogLevels; private final Type findResultType; private final Type searchResultType; public static SearchInfo load(Class clazz) { return load(clazz, null); } public static SearchInfo load(Class clazz, final Properties conf) { SearchInfo rs = entityInfos.get(clazz); if (rs != null) { return rs; } infosLock.lock(); try { rs = entityInfos.get(clazz); if (rs == null) { rs = new SearchInfo(clazz, conf); entityInfos.put(clazz, rs); } return rs; } finally { infosLock.unlock(); } } /** * 给PrepareCompiler使用,用于预动态生成Attribute * * @since 2.5.0 * @param 泛型 * @param clazz Entity 实体类 * @param source 数据源 */ public static void compile(Class clazz, SearchSource source) { SearchInfo.load(clazz); } /** * 构造函数 * * @param type Entity类 * @param conf 配置信息 */ private SearchInfo(Class type, Properties conf) { this.type = type; this.virtual = type.getAnnotation(VirtualEntity.class) != null; this.findResultType = TypeToken.createParameterizedType(null, FindResult.class, type); this.searchResultType = TypeToken.createParameterizedType(null, SearchResult.class, type); // --------------------------------------------- LogLevel ll = type.getAnnotation(LogLevel.class); this.logLevel = ll == null ? Integer.MIN_VALUE : Level.parse(ll.value()).intValue(); Map> logmap = new HashMap<>(); for (LogExcludeLevel lel : type.getAnnotationsByType(LogExcludeLevel.class)) { for (String onelevel : lel.levels()) { int level = Level.parse(onelevel).intValue(); HashSet set = logmap.get(level); if (set == null) { set = new HashSet<>(); logmap.put(level, set); } for (String key : lel.keys()) { set.add(key); } } } if (logmap.isEmpty()) { this.excludeLogLevels = null; } else { this.excludeLogLevels = new HashMap<>(); logmap.forEach((l, set) -> excludeLogLevels.put(l, set.toArray(new String[set.size()]))); } // --------------------------------------------- Table t = type.getAnnotation(Table.class); this.table = (t == null || t.name().isEmpty()) ? type.getSimpleName().toLowerCase() : t.name(); DistributeTable dt = type.getAnnotation(DistributeTable.class); DistributeTableStrategy dts = null; try { dts = (dt == null) ? null : dt.strategy().getDeclaredConstructor().newInstance(); if (dts != null) { RedkaleClassLoader.putReflectionDeclaredConstructors( dt.strategy(), dt.strategy().getName()); } } catch (Exception e) { logger.log(Level.SEVERE, type + " init DistributeTableStrategy error", e); } this.tableStrategy = dts; this.arrayer = Creator.funcArray(type); this.creator = Creator.create(type); ConstructorParameters cp = null; try { cp = this.creator.getClass().getMethod("create", Object[].class).getAnnotation(ConstructorParameters.class); } catch (Exception e) { logger.log(Level.SEVERE, type + " cannot find ConstructorParameters Creator", e); } this.constructorParameters = (cp == null || cp.value().length < 1) ? null : cp.value(); Attribute idAttr0 = null; Map aliasmap0 = null; Class cltmp = type; Set fields = new HashSet<>(); List> queryattrs = new ArrayList<>(); List insertcols = new ArrayList<>(); List> insertattrs = new ArrayList<>(); List updatecols = new ArrayList<>(); List> updateattrs = new ArrayList<>(); Map mappings = new LinkedHashMap<>(); JsonFactory factory = JsonFactory.root(); Attribute highlightAttrId = null; Attribute highlightAttrIndex = null; do { for (Field field : cltmp.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers())) { continue; } if (Modifier.isFinal(field.getModifiers())) { continue; } if (field.getAnnotation(Transient.class) != null) { continue; } if (fields.contains(field.getName())) { continue; } final String fieldname = field.getName(); final Column col = field.getAnnotation(Column.class); int strlen = col == null || col.length() < 1 ? 255 : col.length(); final String sqlfield = col == null || col.name().isEmpty() ? fieldname : col.name(); if (!fieldname.equals(sqlfield)) { if (aliasmap0 == null) { aliasmap0 = new HashMap<>(); } aliasmap0.put(fieldname, sqlfield); } Attribute attr; try { attr = Attribute.create(type, cltmp, field); } catch (RuntimeException e) { continue; } boolean text = false; SearchColumn sc = field.getAnnotation(SearchColumn.class); if (sc != null) { if (!sc.highlight().isEmpty()) { if (!sc.ignore()) { throw new SourceException( "@SearchColumn.ignore must be true when highlight is not empty on field(" + field + ")"); } if (field.getType() != String.class) { throw new SourceException("@SearchColumn.ignore must be on String field(" + field + ")"); } if (SearchColumn.HighLights.HIGHLIGHT_NAME_ID.equals(sc.highlight())) { highlightAttrId = attr; } else if (SearchColumn.HighLights.HIGHLIGHT_NAME_INDEX.equals(sc.highlight())) { highlightAttrIndex = attr; } else { highlightAttributeMap.put(sc.highlight(), attr); } } text = sc.text(); if (sc.ignore()) { if (factory == JsonFactory.root()) { factory = JsonFactory.create(); } factory.register(type, true, fieldname); continue; } } if (field.getAnnotation(org.redkale.persistence.Id.class) != null && idAttr0 == null) { idAttr0 = attr; insertcols.add(sqlfield); insertattrs.add(attr); } else { if (col == null || col.insertable()) { insertcols.add(sqlfield); insertattrs.add(attr); } if (col == null || col.updatable()) { updatecols.add(sqlfield); updateattrs.add(attr); updateAttributeMap.put(fieldname, attr); } } if (attr.type() == boolean.class || attr.type() == Boolean.class) { mappings.put(attr.field(), Utility.ofMap("type", "boolean", "index", false)); } else if (attr.type() == float.class || attr.type() == Float.class) { mappings.put(attr.field(), Utility.ofMap("type", "double", "index", false)); } else if (attr.type() == double.class || attr.type() == Double.class) { mappings.put(attr.field(), Utility.ofMap("type", "double", "index", false)); } else if (attr.type().isPrimitive() || Number.class.isAssignableFrom(attr.type())) { mappings.put( attr.field(), Utility.ofMap("type", sc != null && sc.date() ? "date" : "long", "index", false)); } else if (CharSequence.class.isAssignableFrom(attr.type())) { Map m = new LinkedHashMap<>(); if (sc != null && !sc.options().isEmpty()) { if ("false".equalsIgnoreCase(sc.options())) { m.put("index", false); } else { m.put("index_options", sc.options()); } } if (sc != null && (sc.html() || !sc.analyzer().isEmpty())) { String analyzer = (sc.html() ? "html_" : "") + sc.analyzer(); if ("html_".equals(analyzer)) { analyzer = "html_standard"; } if (analyzer.startsWith("html_")) { customAnalyzerMap.put( analyzer, Utility.ofMap( "type", "custom", "tokenizer", analyzer.replace("html_", ""), "char_filter", new String[] {"html_strip"})); } m.put("analyzer", analyzer); } if (sc != null && (sc.html() || !sc.searchAnalyzer().isEmpty())) { String searchAnalyzer = (sc.html() ? "html_" : "") + sc.searchAnalyzer(); if ("html_".equals(searchAnalyzer)) { searchAnalyzer = "html_standard"; } if (searchAnalyzer.startsWith("html_")) { customAnalyzerMap.put( searchAnalyzer, Utility.ofMap( "type", "custom", "tokenizer", searchAnalyzer.replace("html_", ""), "char_filter", new String[] {"html_strip"})); } m.put("search_analyzer", searchAnalyzer); } if (text) { m.put("type", sc != null && sc.date() ? "date" : (sc != null && sc.ip() ? "ip" : "text")); } else { m.put("type", "keyword"); m.put("ignore_above", strlen); } mappings.put(attr.field(), m); } else { if (sc != null && "false".equalsIgnoreCase(sc.options())) { mappings.put(attr.field(), Utility.ofMap("type", "object", "index", false)); } else { mappings.put(attr.field(), Utility.ofMap("type", "object")); } } queryattrs.add(attr); fields.add(fieldname); attributeMap.put(fieldname, attr); } } while ((cltmp = cltmp.getSuperclass()) != Object.class); if (idAttr0 == null) { throw new SourceException(type.getName() + " have no primary column by @org.redkale.persistence.Id"); } this.jsonConvert = factory.getConvert(); this.primary = idAttr0; this.aliasmap = aliasmap0; this.highlightAttributeId = highlightAttrId; this.highlightAttributeIndex = highlightAttrIndex; this.attributes = attributeMap.values().toArray(new Attribute[attributeMap.size()]); this.queryAttributes = queryattrs.toArray(new Attribute[queryattrs.size()]); this.insertAttributes = insertattrs.toArray(new Attribute[insertattrs.size()]); this.updateAttributes = updateattrs.toArray(new Attribute[updateattrs.size()]); this.mappingTypes = mappings; if (this.constructorParameters == null) { this.constructorAttributes = null; this.unconstructorAttributes = null; } else { this.constructorAttributes = new Attribute[this.constructorParameters.length]; List> unconstructorAttrs = new ArrayList<>(); for (Attribute attr : queryAttributes) { int pos = -1; for (int i = 0; i < this.constructorParameters.length; i++) { if (attr.field().equals(this.constructorParameters[i])) { pos = i; break; } } if (pos >= 0) { this.constructorAttributes[pos] = attr; } else { unconstructorAttrs.add(attr); } } this.unconstructorAttributes = unconstructorAttrs.toArray(new Attribute[unconstructorAttrs.size()]); } } public Class getType() { return type; } public DistributeTableStrategy getTableStrategy() { return tableStrategy; } public String getOriginTable() { return table; } /** * 根据主键值获取Entity的表名 * * @param primary Entity主键值 * @return String */ public String getTable(Serializable primary) { if (tableStrategy == null) { return table; } String t = tableStrategy.getTable(table, primary); if (t == null || t.isEmpty()) { throw new SourceException(table + " tableStrategy.getTable is empty, primary=" + primary); } return t; } /** * 根据过滤条件获取Entity的表名,多表用逗号隔开 * * @param node 过滤条件 * @return String */ public String getTable(FilterNode node) { if (tableStrategy == null) { return table; } String[] ts = tableStrategy.getTables(table, node); if (Utility.isEmpty(ts)) { throw new SourceException(table + " tableStrategy.getTable is empty, filter=" + node); } if (ts.length == 1) { return ts[0]; } return Utility.joining(ts, ','); } /** * 根据Entity对象获取Entity的表名 * * @param bean Entity对象 * @return String */ public String getTable(T bean) { if (tableStrategy == null) { return table; } String t = tableStrategy.getTable(table, bean); if (t == null || t.isEmpty()) { throw new SourceException(table + " tableStrategy.getTable is empty, entity=" + bean); } return t; } /** * 获取Entity数组构建器 * * @return Creator */ public IntFunction getArrayer() { return arrayer; } /** * 获取主键字段的Attribute * * @return Attribute */ public Attribute getPrimary() { return this.primary; } /** * 根据Entity字段名获取字段的Attribute * * @param fieldname Class字段名 * @return Attribute */ public Attribute getAttribute(String fieldname) { if (fieldname == null) { return null; } return this.attributeMap.get(fieldname); } /** * 根据Entity字段名获取可更新字段的Attribute * * @param fieldname Class字段名 * @return Attribute */ public Attribute getUpdateAttribute(String fieldname) { return this.updateAttributeMap.get(fieldname); } public Type getFindResultType() { return findResultType; } public Type getSearchResultType() { return searchResultType; } public Attribute[] getAttributes() { return attributes; } public Attribute[] getUpdateAttributes() { return updateAttributes; } public Map getMappingTypes() { return mappingTypes; } public Map getCustomAnalyzerMap() { return customAnalyzerMap; } public Map createIndexMap() { Map map = Utility.ofMap("mappings", Utility.ofMap("properties", getMappingTypes())); if (!customAnalyzerMap.isEmpty()) { map.put("settings", Utility.ofMap("analysis", Utility.ofMap("analyzer", customAnalyzerMap))); } return map; } public JsonConvert getConvert() { return jsonConvert; } /** * 获取主键字段的表字段名 * * @return String */ public String getPrimarySQLColumn() { return getSQLColumn(this.primary.field()); } /** * 根据field字段名获取数据库对应的字段名 * * @param fieldname 字段名 * @return String */ public String getSQLColumn(String fieldname) { return this.aliasmap == null ? fieldname : aliasmap.getOrDefault(fieldname, fieldname); } public Attribute getHighlightAttribute(String fieldname) { return this.highlightAttributeMap.get(fieldname); } public Attribute getHighlightAttributeId() { return highlightAttributeId; } public Attribute getHighlightAttributeIndex() { return highlightAttributeIndex; } public boolean isVirtual() { return virtual; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy