/drools-core/src/main/java/org/drools/core/util/index/IndexUtil.java

https://github.com/esteban-aliverti/drools · Java · 423 lines · 354 code · 62 blank · 7 comment · 112 complexity · b4b1c5357ccdd47c7bcc98e6b9cb500b MD5 · raw file

  1. package org.drools.core.util.index;
  2. import org.drools.core.RuleBaseConfiguration;
  3. import org.drools.core.util.AbstractHashTable.FieldIndex;
  4. import org.drools.core.reteoo.BetaMemory;
  5. import org.drools.core.reteoo.LeftTupleMemory;
  6. import org.drools.core.reteoo.NodeTypeEnums;
  7. import org.drools.core.reteoo.RightTupleMemory;
  8. import org.drools.core.rule.ContextEntry;
  9. import org.drools.core.rule.IndexableConstraint;
  10. import org.drools.core.rule.constraint.MvelConstraint;
  11. import org.drools.core.spi.BetaNodeFieldConstraint;
  12. import org.drools.core.spi.Constraint;
  13. import org.kie.internal.conf.IndexPrecedenceOption;
  14. import java.util.ArrayList;
  15. import java.util.List;
  16. public class IndexUtil {
  17. private static final boolean USE_COMPARISON_INDEX = true;
  18. private static final boolean USE_RANGE_INDEX = USE_COMPARISON_INDEX && false;
  19. public static boolean compositeAllowed(BetaNodeFieldConstraint[] constraints, short betaNodeType) {
  20. // 1) If there is 1 or more unification restrictions it cannot be composite
  21. // 2) Ensures any non unification restrictions are first
  22. int firstUnification = -1;
  23. int firstNonUnification = -1;
  24. for ( int i = 0, length = constraints.length; i < length; i++ ) {
  25. if ( isIndexable(constraints[i], betaNodeType) ) {
  26. final boolean isUnification = ((IndexableConstraint) constraints[i]).isUnification();
  27. if ( isUnification && firstUnification == -1 ) {
  28. firstUnification = i;
  29. } else if ( !isUnification &&firstNonUnification == -1 ) {
  30. firstNonUnification = i;
  31. }
  32. }
  33. if ( firstUnification != -1 && firstNonUnification != -1) {
  34. break;
  35. }
  36. }
  37. if (firstNonUnification != -1 && firstNonUnification > 0) {
  38. // Make sure a nonunification indexable constraint is first
  39. swap(constraints, 0, firstNonUnification);
  40. }
  41. return (firstUnification == -1);
  42. }
  43. public static boolean isIndexable(BetaNodeFieldConstraint constraint, short nodeType) {
  44. return constraint instanceof IndexableConstraint && ((IndexableConstraint)constraint).isIndexable(nodeType);
  45. }
  46. private static boolean canHaveRangeIndex(short nodeType) {
  47. return USE_COMPARISON_INDEX && ( nodeType == NodeTypeEnums.NotNode || nodeType == NodeTypeEnums.ExistsNode );
  48. }
  49. public static boolean isIndexableForNode(short nodeType, BetaNodeFieldConstraint constraint) {
  50. if ( !(constraint instanceof IndexableConstraint) ) {
  51. return false;
  52. }
  53. ConstraintType constraintType = ((IndexableConstraint)constraint).getConstraintType();
  54. return constraintType.isIndexableForNode(nodeType);
  55. }
  56. public static boolean[] isIndexableForNode(IndexPrecedenceOption indexPrecedenceOption, short nodeType, int keyDepth, BetaNodeFieldConstraint[] constraints) {
  57. if (keyDepth < 1) {
  58. return new boolean[constraints.length];
  59. }
  60. return indexPrecedenceOption == IndexPrecedenceOption.EQUALITY_PRIORITY ?
  61. findIndexableWithEqualityPriority(nodeType, keyDepth, constraints) :
  62. findIndexableWithPatternOrder(nodeType, keyDepth, constraints);
  63. }
  64. private static boolean[] findIndexableWithEqualityPriority(short nodeType, int keyDepth, BetaNodeFieldConstraint[] constraints) {
  65. boolean[] indexable = new boolean[constraints.length];
  66. if (hasEqualIndexable(keyDepth, indexable, constraints)) {
  67. return indexable;
  68. }
  69. if (!canHaveRangeIndex(nodeType)) {
  70. return indexable;
  71. }
  72. for (int i = 0; i < constraints.length; i++) {
  73. if (isIndexable(constraints[i], nodeType)) {
  74. sortRangeIndexable(constraints, indexable, i);
  75. break;
  76. }
  77. }
  78. return indexable;
  79. }
  80. private static boolean[] findIndexableWithPatternOrder(short nodeType, int keyDepth, BetaNodeFieldConstraint[] constraints) {
  81. boolean[] indexable = new boolean[constraints.length];
  82. for (int i = 0; i < constraints.length; i++) {
  83. if (isIndexable(constraints[i], nodeType)) {
  84. if (isEqualIndexable(constraints[i])) {
  85. sortEqualIndexable(keyDepth, indexable, constraints, i);
  86. } else {
  87. sortRangeIndexable(constraints, indexable, i);
  88. }
  89. break;
  90. }
  91. }
  92. return indexable;
  93. }
  94. private static boolean hasEqualIndexable(int keyDepth, boolean[] indexable, BetaNodeFieldConstraint[] constraints) {
  95. return sortEqualIndexable(keyDepth, indexable, constraints, 0);
  96. }
  97. private static boolean sortEqualIndexable(int keyDepth, boolean[] indexable, BetaNodeFieldConstraint[] constraints, int start) {
  98. boolean hasEqualIndexable = false;
  99. int indexableCouter = 0;
  100. for (int i = start; i < constraints.length; i++) {
  101. if (isEqualIndexable(constraints[i])) {
  102. hasEqualIndexable = true;
  103. if (keyDepth > indexableCouter) {
  104. swap(constraints, i, indexableCouter);
  105. indexable[indexableCouter++] = true;
  106. }
  107. }
  108. }
  109. return hasEqualIndexable;
  110. }
  111. private static void sortRangeIndexable(BetaNodeFieldConstraint[] constraints, boolean[] indexable, int i) {
  112. int dualConstraintPosition = findDualConstraint(constraints, i);
  113. swap(constraints, i, 0);
  114. indexable[0] = true;
  115. if (dualConstraintPosition > 0) {
  116. swap(constraints, dualConstraintPosition, 1);
  117. indexable[1] = true;
  118. }
  119. }
  120. private static int findDualConstraint(BetaNodeFieldConstraint[] constraints, int comparisonPos) {
  121. if ( !(USE_RANGE_INDEX && constraints[comparisonPos] instanceof MvelConstraint) ) {
  122. return -1;
  123. }
  124. MvelConstraint firstConstraint = (MvelConstraint) constraints[comparisonPos];
  125. String leftValue = getLeftValueInExpression(firstConstraint.getExpression());
  126. for (int i = comparisonPos+1; i < constraints.length; i++) {
  127. if (constraints[i] instanceof MvelConstraint) {
  128. MvelConstraint dualConstraint = (MvelConstraint) constraints[i];
  129. if (isDual(firstConstraint, leftValue, dualConstraint)) {
  130. return i;
  131. }
  132. }
  133. }
  134. return -1;
  135. }
  136. private static boolean isEqualIndexable(BetaNodeFieldConstraint constraint) {
  137. return constraint instanceof IndexableConstraint && ((IndexableConstraint)constraint).getConstraintType() == ConstraintType.EQUAL;
  138. }
  139. private static void swap(BetaNodeFieldConstraint[] constraints, int p1, int p2) {
  140. if (p1 != p2) {
  141. BetaNodeFieldConstraint temp = constraints[p2];
  142. constraints[p2] = constraints[p1];
  143. constraints[p1] = temp;
  144. }
  145. }
  146. public enum ConstraintType {
  147. EQUAL(true),
  148. NOT_EQUAL(false),
  149. GREATER_THAN(true),
  150. GREATER_OR_EQUAL(true),
  151. LESS_THAN(true),
  152. LESS_OR_EQUAL(true),
  153. RANGE(true),
  154. UNKNOWN(false);
  155. private final boolean indexable;
  156. private ConstraintType(boolean indexable) {
  157. this.indexable = indexable;
  158. }
  159. public boolean isComparison() {
  160. return isAscending() || isDescending();
  161. }
  162. public boolean isEquality() {
  163. return this == EQUAL || this == NOT_EQUAL;
  164. }
  165. public boolean isAscending() {
  166. return this == GREATER_THAN || this == GREATER_OR_EQUAL;
  167. }
  168. public boolean isDescending() {
  169. return this == LESS_THAN || this == LESS_OR_EQUAL;
  170. }
  171. public boolean isIndexable() {
  172. return indexable;
  173. }
  174. public boolean isIndexableForNode(short nodeType) {
  175. switch (this) {
  176. case EQUAL:
  177. return true;
  178. case NOT_EQUAL:
  179. case UNKNOWN:
  180. return false;
  181. default:
  182. return canHaveRangeIndex(nodeType);
  183. }
  184. }
  185. public static ConstraintType decode(String operator) {
  186. if (operator.equals("==")) {
  187. return EQUAL;
  188. }
  189. if (operator.equals("!=")) {
  190. return NOT_EQUAL;
  191. }
  192. if (operator.equals(">")) {
  193. return GREATER_THAN;
  194. }
  195. if (operator.equals(">=")) {
  196. return GREATER_OR_EQUAL;
  197. }
  198. if (operator.equals("<")) {
  199. return LESS_THAN;
  200. }
  201. if (operator.equals("<=")) {
  202. return LESS_OR_EQUAL;
  203. }
  204. return UNKNOWN;
  205. }
  206. public static ConstraintType getType(Constraint constraint) {
  207. return constraint instanceof IndexableConstraint ? ((IndexableConstraint)constraint).getConstraintType() : UNKNOWN;
  208. }
  209. }
  210. public static class Factory {
  211. public static BetaMemory createBetaMemory(RuleBaseConfiguration config, short nodeType, BetaNodeFieldConstraint... constraints) {
  212. int keyDepth = config.getCompositeKeyDepth();
  213. if (config.getCompositeKeyDepth() < 1) {
  214. return new BetaMemory( config.isSequential() ? null : new LeftTupleList(),
  215. new RightTupleList(),
  216. createContext(constraints),
  217. nodeType );
  218. }
  219. IndexSpec indexSpec = new IndexSpec(config.getIndexPrecedenceOption(), keyDepth, nodeType, constraints);
  220. return new BetaMemory( createLeftMemory(config, indexSpec),
  221. createRightMemory(config, indexSpec),
  222. createContext(constraints),
  223. nodeType );
  224. }
  225. private static RightTupleMemory createRightMemory(RuleBaseConfiguration config, IndexSpec indexSpec) {
  226. if ( !config.isIndexRightBetaMemory() || !indexSpec.constraintType.isIndexable() ) {
  227. return new RightTupleList();
  228. }
  229. if (indexSpec.constraintType == ConstraintType.EQUAL) {
  230. return new RightTupleIndexHashTable( indexSpec.indexes );
  231. }
  232. if (indexSpec.constraintType.isComparison()) {
  233. return new RightTupleIndexRBTree( indexSpec.constraintType, indexSpec.indexes[0] );
  234. }
  235. if (indexSpec.constraintType == ConstraintType.RANGE) {
  236. // missing TreeMap based implementation for range indexes
  237. return new RightTupleIndexRangeRBTree( indexSpec.ascendingConstraintType, indexSpec.indexes[0],
  238. indexSpec.descendingConstraintType, indexSpec.indexes[1] );
  239. }
  240. return new RightTupleList();
  241. }
  242. private static LeftTupleMemory createLeftMemory(RuleBaseConfiguration config, IndexSpec indexSpec) {
  243. if (config.isSequential()) {
  244. return null;
  245. }
  246. if ( !config.isIndexLeftBetaMemory() || !indexSpec.constraintType.isIndexable() ) {
  247. return new LeftTupleList();
  248. }
  249. if (indexSpec.constraintType == ConstraintType.EQUAL) {
  250. return new LeftTupleIndexHashTable( indexSpec.indexes );
  251. }
  252. if (indexSpec.constraintType.isComparison()) {
  253. return new LeftTupleIndexRBTree( indexSpec.constraintType, indexSpec.indexes[0] );
  254. }
  255. if (indexSpec.constraintType == ConstraintType.RANGE) {
  256. // missing TreeMap based implementation for range indexes
  257. return new LeftTupleIndexRangeRBTree( indexSpec.ascendingConstraintType, indexSpec.indexes[0],
  258. indexSpec.descendingConstraintType, indexSpec.indexes[1] );
  259. }
  260. return new LeftTupleList();
  261. }
  262. public static ContextEntry[] createContext(BetaNodeFieldConstraint... constraints) {
  263. ContextEntry[] entries = new ContextEntry[constraints.length];
  264. for (int i = 0; i < constraints.length; i++) {
  265. entries[i] = constraints[i].createContextEntry();
  266. }
  267. return entries;
  268. }
  269. private static class IndexSpec {
  270. private ConstraintType constraintType = ConstraintType.UNKNOWN;
  271. private FieldIndex[] indexes;
  272. private ConstraintType ascendingConstraintType = null;
  273. private ConstraintType descendingConstraintType = null;
  274. private IndexSpec(IndexPrecedenceOption indexPrecedenceOption, int keyDepth, short nodeType, BetaNodeFieldConstraint[] constraints) {
  275. init(indexPrecedenceOption, keyDepth, nodeType, constraints);
  276. }
  277. private void init(IndexPrecedenceOption indexPrecedenceOption, int keyDepth, short nodeType, BetaNodeFieldConstraint[] constraints) {
  278. int firstIndexableConstraint = indexPrecedenceOption == IndexPrecedenceOption.EQUALITY_PRIORITY ?
  279. determineTypeWithEqualityPriority(nodeType, constraints) :
  280. determineTypeWithPatternOrder(nodeType, constraints);
  281. if (constraintType == ConstraintType.EQUAL) {
  282. List<FieldIndex> indexList = new ArrayList<FieldIndex>();
  283. indexList.add(((IndexableConstraint)constraints[firstIndexableConstraint]).getFieldIndex());
  284. // look for other EQUAL constraint to eventually add them to the index
  285. for (int i = firstIndexableConstraint+1; i < constraints.length && indexList.size() < keyDepth; i++) {
  286. if ( ConstraintType.getType(constraints[i]) == ConstraintType.EQUAL && ! ((IndexableConstraint) constraints[i]).isUnification() ) {
  287. indexList.add(((IndexableConstraint)constraints[i]).getFieldIndex());
  288. }
  289. }
  290. indexes = indexList.toArray(new FieldIndex[indexList.size()]);
  291. } else if (constraintType.isComparison()) {
  292. // look for a dual constraint to create a range index
  293. if (USE_RANGE_INDEX && constraints[firstIndexableConstraint] instanceof MvelConstraint) {
  294. MvelConstraint firstConstraint = (MvelConstraint) constraints[firstIndexableConstraint];
  295. String leftValue = getLeftValueInExpression(firstConstraint.getExpression());
  296. for (int i = firstIndexableConstraint+1; i < constraints.length; i++) {
  297. if (constraints[i] instanceof MvelConstraint) {
  298. MvelConstraint dualConstraint = (MvelConstraint) constraints[i];
  299. if (isDual(firstConstraint, leftValue, dualConstraint)) {
  300. constraintType = ConstraintType.RANGE;
  301. if (firstConstraint.getConstraintType().isAscending()) {
  302. ascendingConstraintType = firstConstraint.getConstraintType();
  303. descendingConstraintType = dualConstraint.getConstraintType();
  304. indexes = new FieldIndex[]{ firstConstraint.getFieldIndex(), dualConstraint.getFieldIndex() };
  305. } else {
  306. ascendingConstraintType = dualConstraint.getConstraintType();
  307. descendingConstraintType = firstConstraint.getConstraintType();
  308. indexes = new FieldIndex[]{ dualConstraint.getFieldIndex(), firstConstraint.getFieldIndex() };
  309. }
  310. return;
  311. }
  312. }
  313. }
  314. }
  315. indexes = new FieldIndex[]{ ((IndexableConstraint)constraints[firstIndexableConstraint]).getFieldIndex() };
  316. }
  317. }
  318. private int determineTypeWithEqualityPriority(short nodeType, BetaNodeFieldConstraint[] constraints) {
  319. int indexedConstraintPos = 0;
  320. for (int i = 0; i < constraints.length; i++) {
  321. if (constraints[i] instanceof IndexableConstraint) {
  322. IndexableConstraint indexableConstraint = (IndexableConstraint) constraints[i];
  323. ConstraintType type = indexableConstraint.getConstraintType();
  324. if (type == ConstraintType.EQUAL) {
  325. constraintType = type;
  326. return i;
  327. } else if (constraintType == ConstraintType.UNKNOWN && type.isIndexableForNode(nodeType)) {
  328. constraintType = type;
  329. indexedConstraintPos = i;
  330. }
  331. }
  332. }
  333. return indexedConstraintPos;
  334. }
  335. private int determineTypeWithPatternOrder(short nodeType, BetaNodeFieldConstraint[] constraints) {
  336. for (int i = 0; i < constraints.length; i++) {
  337. ConstraintType type = ConstraintType.getType(constraints[i]);
  338. if ( type.isIndexableForNode(nodeType) ) {
  339. constraintType = type;
  340. return i;
  341. }
  342. }
  343. return constraints.length;
  344. }
  345. }
  346. }
  347. private static boolean isDual(MvelConstraint firstConstraint, String leftValue, MvelConstraint dualConstraint) {
  348. return dualConstraint.getConstraintType().isComparison() &&
  349. dualConstraint.getConstraintType().isAscending() != firstConstraint.getConstraintType().isAscending() &&
  350. leftValue.equals( getLeftValueInExpression(dualConstraint.getExpression()) );
  351. }
  352. private static String getLeftValueInExpression(String expression) {
  353. for (int i = 0; i < expression.length(); i++) {
  354. char ch = expression.charAt(i);
  355. if ( !Character.isJavaIdentifierPart(ch) && ch != '.' ) {
  356. return expression.substring(0, i);
  357. }
  358. }
  359. return expression;
  360. }
  361. }