package datawave.query.tables.edge.contexts; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import datawave.edge.model.EdgeModelAware; import datawave.edge.util.EdgeKey; import datawave.query.tables.edge.EdgeQueryLogic; import com.google.common.collect.HashMultimap; import datawave.util.StringUtils; import org.apache.hadoop.io.Text; /** * A Query context represents a group of ranges over which the same query parameters will be applied The Query context stores 3 data structures: rowContext - * used for storing all info needed to build the ranges (a group of sources and sinks) columnContext - holds all the query terms for the column family, column * qualifier, as well as any exclusion or function statements otherContexts - holds additional query contexts that do not have any row contexts, used to store * some of the more complex query expressions. * * For example the following query: (EDGE_SOURCE == 's1' OR EDGE_SOURCE == 's2') AND EDGE_SINK == 't1' AND EDGE_ATTRIBUTE2 == 'p1' AND ((EDGE_TYPE == 't1' AND * EDGE_RELATIONSHIP == 'r1') OR (EDGE_TYPE =='t2' AND EDGE_RELATIONSHIP == 'r2')) * * would get stored in the following way: The sources and sinks would be stored in the row context The EDGE_ATTRIBUTE2 would be stored in the column context The * two expressions at the end with the edge types/relationships would be two separate query contexts and would be stored in the other contexts list * * The basic flow of how this class is used is the packageIdentities() method is called in query context depending on what type of identities are being stored * it will then send it to the packageIdentities() method in either the rowContext or columnContext * * The enforce rules boolean that gets passed around is to check to make sure that the same sets of identifiers are not being ANDed together. Eg can't and * SOURCE and SOURCE because an edge only has one SOURCE */ public class QueryContext implements EdgeModelAware, EdgeContext { private RowContext rowContext; private ColumnContext columnContext; private Set<QueryContext> otherContexts; private boolean hasCompleteColumnFamilies = false; public void packageIdentities(List<IdentityContext> identityContexts) { packageIdentities(identityContexts, true); } public void packageIdentities(List<IdentityContext> identityContexts, boolean enforceRules) { if (!identityContexts.get(0).isEquivalence()) { buildColumnContexts(identityContexts, enforceRules); return; } String type = identityContexts.get(0).getIdentity(); if (type.equals(EDGE_SOURCE) || type.equals(EDGE_SINK)) { buildRowContexts(identityContexts, enforceRules); } else if (type.equals(EDGE_TYPE) || type.equals(EDGE_RELATIONSHIP) || type.equals(EDGE_ATTRIBUTE1) || type.equals(EDGE_ATTRIBUTE2) || type.equals(EDGE_ATTRIBUTE3) || type.equals(FUNCTION)) { buildColumnContexts(identityContexts, enforceRules); } else { throw new RuntimeException("Invalid identifier: " + type); } } /* * If no edge type+relationship is contained in this query context then return false */ public boolean hasCompleteColumnFamily() { if (columnContext != null) { if (columnContext.hasCompleteColumnFamily()) { if (otherContexts == null) { hasCompleteColumnFamilies = true; return hasCompleteColumnFamilies; } else if (otherContexts != null && allNoColumnFamily()) { hasCompleteColumnFamilies = true; return hasCompleteColumnFamilies; } } else if (columnContext.hasNoColumnFamily()) { if (otherContexts != null && allCompleteColumnFamily()) { hasCompleteColumnFamilies = true; return hasCompleteColumnFamilies; } } } else { if (otherContexts != null && allCompleteColumnFamily()) { hasCompleteColumnFamilies = true; return hasCompleteColumnFamilies; } } return hasCompleteColumnFamilies; } private boolean allCompleteColumnFamily() { for (QueryContext queryContext : otherContexts) { if (queryContext.columnContext == null || !queryContext.columnContext.hasCompleteColumnFamily()) { return false; } } return true; } private boolean allNoColumnFamily() { for (QueryContext queryContext : otherContexts) { if (queryContext.columnContext != null && !queryContext.columnContext.hasNoColumnFamily()) { return false; } } return true; } /* * Should only be called if hasCompleteColumnFamily() == true */ public List<Text> getColumnFamilies(boolean includeStats) { if (!hasCompleteColumnFamilies) { throw new IllegalStateException("Does not have complete column families"); } List<Text> columnFamilies = new ArrayList<>(); if (columnContext != null && columnContext.isCompleteColumnFamilies()) { columnFamilies.addAll(columnContext.computeColumnFamilyUnions(includeStats)); } if (otherContexts != null) { for (QueryContext queryContext : otherContexts) { if (queryContext.columnContext.isCompleteColumnFamilies()) { columnFamilies.addAll(queryContext.columnContext.computeColumnFamilyUnions(includeStats)); } } } return columnFamilies; } /** * Attempts to condense several query contexts that all share the same row sources. This current query context may or may not have row id info present (row * context). Depending on when this method is used that can be good or bad. (During an AND node this qruey context needs to have sources, during an OR node * this query context may or may not have sources) * * @param other * all query contexts in this must all have row context = null * @param optionalSourceInfo * choose to enforce this query context to have its list of sources set or not in order to combine * @return Return true if successful. Along the way if a rule is broken return false. If false is returned this query context should no longer be used (it * may have partially combined) */ public boolean combineQueryContexts(List<QueryContext> other, boolean optionalSourceInfo) { if (optionalSourceInfo || (this.hasSourceList())) { // Initialize other contexts if needed if (otherContexts == null) { otherContexts = new HashSet<>(); } boolean overlappingColumn = false; boolean overlappingSink = false; // Loop over each query context to be combined into this one for (QueryContext oContext : other) { // Cannot have source list if (oContext.hasSourceList()) { return false; } // Other contexts should not be maintaining lists of other contexts // here we strip the otherContexts of the other Contexts and put them all in the same list if (oContext.otherContexts != null) { // loop over the combing query context's other contexts to see if they contain any of the same identity lists that this context has // if so this contexts overlapped identity contexts will need to be moved up into the other context list for (QueryContext qContext : oContext.otherContexts) { if (qContext.columnContext != null) { if (overlappingColumnContext(qContext.columnContext)) { overlappingColumn = true; } } if (qContext.rowContext != null) { if (qContext.rowContext.getSinks() != null && this.rowContext.getSinks() != null) { overlappingSink = true; } } otherContexts.add(qContext); } // clear the pointer so we don't accidentally stumble into them oContext.otherContexts = null; } if (oContext.rowContext != null) { // May need to push this rowContext's list of sinks out into the other contexts if (oContext.rowContext.getSinks() != null && this.rowContext.getSinks() != null) { overlappingSink = true; } } if (oContext.columnContext != null) { // May need to push this columns context out into the other context if (overlappingColumnContext(oContext.columnContext)) { overlappingColumn = true; } } // finally add the combining query context to the list of other context if it has either a non null row or // column context. (it could be null if the query context's otherContexts was populated while its row and column // context was null) if (oContext.columnContext != null || oContext.rowContext != null) { otherContexts.add(oContext); } } // If any parts of this queryContext needs to be moved out into the other context list do so here if (overlappingColumn || overlappingSink) { QueryContext tempContext = new QueryContext(); // if this query context does not have selector list then if there is an over lap in either the row // or column context push them both out into the list of other contexts if (!this.hasSourceList() || overlappingSink) { if (this.rowContext != null && this.getRowContext().sinks != null) { tempContext.packageIdentities(this.rowContext.getSinks()); this.rowContext.sinks = null; } } if (this.columnContext != null && (!this.hasSourceList() || overlappingColumn)) { tempContext.setColumnContext(this.columnContext); this.columnContext = null; } otherContexts.add(tempContext); } return true; } return false; } /* * Helper method used when combining query contexts Checks to see if this column context has any of the same fields set that the other context has. */ private boolean overlappingColumnContext(ColumnContext other) { if (this.columnContext == null) { return false; } else { if (this.columnContext.getEdgeTypes() != null && other.getEdgeTypes() != null) { return true; } else if (this.columnContext.getEdgeRelationships() != null && other.getEdgeRelationships() != null) { return true; } else if (this.columnContext.getEdgeAttribute1Values() != null && other.getEdgeAttribute1Values() != null) { return true; } else if (this.columnContext.getEdgeAttribute2Values() != null && other.getEdgeAttribute2Values() != null) { return true; } else if (this.columnContext.getEdgeAttribute3Values() != null && other.getEdgeAttribute3Values() != null) { return true; } else if (this.columnContext.getExclusions() != null && other.getExclusions() != null) { return true; } else if (this.columnContext.getFunctions() != null && other.getFunctions() != null) { return true; } else { return false; } } } private void buildRowContexts(List<IdentityContext> identityContexts, boolean enforceRules) { if (rowContext == null) { rowContext = new RowContext(); } rowContext.packageIdentities(identityContexts, enforceRules); } private void buildColumnContexts(List<IdentityContext> identityContexts, boolean enforceRules) { if (columnContext == null) { columnContext = new ColumnContext(); } columnContext.packageIdentities(identityContexts, enforceRules); } private void verifyNotSet(List<IdentityContext> contexts, boolean check) { if (check == true && contexts != null) { throw new IllegalArgumentException("Can't AND like identifiers: " + contexts.get(0).getIdentity()); } } /** * Takes two string builders to append the query string for this query context. The includStats boolean tells if we need to build the query stats string The * includeSource tells if we need to include sources in the query string(s) The includeSink tells if we need to include sinks in the query string(s) we can * get away with excluding source and sink if the */ public void buildStrings(StringBuilder normalizedQuery, StringBuilder normalizedStatsQuery, boolean includeStats, boolean includeSource, boolean includeSink, HashMultimap<String,String> preFilterValues, boolean includeColumnFamilyTerms, boolean updateWhitelist) { StringBuilder trimmedQuery = new StringBuilder(); StringBuilder trimmedStatsQuery = new StringBuilder(); NormalizedQuery query = this.toString(includeStats, includeSource, includeSink, preFilterValues, includeColumnFamilyTerms, updateWhitelist); trimmedQuery.append(query.getNormalizedQuery()); if (includeStats) { trimmedStatsQuery.append(query.getNormalizedStatsQuery()); } if (this.otherContexts != null && !otherContexts.isEmpty()) { StringBuilder tempQueryString = new StringBuilder(); StringBuilder tempQueryStatsString = new StringBuilder(); int i = 0; for (QueryContext oContext : this.otherContexts) { if (i > 0) { if (tempQueryString.length() > 7) { tempQueryString.append(OR); } if (includeStats && tempQueryStatsString.length() > 7) { tempQueryStatsString.append(OR); } } if (this.otherContexts.size() > 1) { tempQueryString.append("("); if (includeStats) { tempQueryStatsString.append("("); } } query = oContext.toString(includeStats, includeSource, includeSink, preFilterValues, includeColumnFamilyTerms, updateWhitelist); tempQueryString.append(query.getNormalizedQuery()); if (includeStats) { tempQueryStatsString.append(query.getNormalizedStatsQuery()); } if (this.otherContexts.size() > 1) { tempQueryString.append(")"); if (includeStats) { tempQueryStatsString.append(")"); } } i++; } if (trimmedQuery.length() > 7 && tempQueryString.length() > 7) { trimmedQuery.append(AND); } if (includeStats && trimmedStatsQuery.length() > 7 && tempQueryStatsString.length() > 7) { trimmedStatsQuery.append(AND); } if (tempQueryString.length() > 7) { trimmedQuery.append("(" + tempQueryString + ")"); } if (tempQueryStatsString.length() > 7) { trimmedStatsQuery.append("(" + tempQueryStatsString + ")"); } } normalizedQuery.append(trimmedQuery); if (includeStats) { normalizedStatsQuery.append(trimmedStatsQuery); } } private NormalizedQuery toString(boolean includeStats, boolean includeSource, boolean includeSink, HashMultimap<String,String> preFilterValues, boolean includeColumnFamilyTerms, boolean updateWhitelist) { NormalizedQuery rowString = null, colString = null; if (this.getRowContext() != null) { rowString = this.getRowContext().toString(includeStats, includeSource, includeSink); } if (this.getColumnContext() != null) { colString = this.getColumnContext().toString(includeStats, preFilterValues, includeColumnFamilyTerms, updateWhitelist); } NormalizedQuery ret = new NormalizedQuery(); StringBuilder normalizedQuery = new StringBuilder(); StringBuilder normalizedStatsQuery = new StringBuilder(); if (rowString != null) { normalizedQuery.append(rowString.getNormalizedQuery()); if (includeStats) { normalizedStatsQuery.append(rowString.getNormalizedStatsQuery()); } } if (colString != null) { if (colString.getNormalizedQuery() != null && colString.getNormalizedQuery().length() > 7) { if (normalizedQuery.length() > 7) { normalizedQuery.append(AND); } normalizedQuery.append(colString.getNormalizedQuery()); } if (includeStats && colString.getNormalizedStatsQuery() != null && colString.getNormalizedStatsQuery().length() > 7) { if (normalizedStatsQuery.length() > 7) { normalizedStatsQuery.append(AND); } normalizedStatsQuery.append(colString.getNormalizedStatsQuery()); } } ret.setNormalizedQuery(normalizedQuery.toString()); if (includeStats) { ret.setNormalizedStatsQuery(normalizedStatsQuery.toString()); } else { ret.setNormalizedStatsQuery(""); } return ret; } public RowContext getRowContext() { return rowContext; } public boolean hasSourceList() { if (rowContext == null) { return false; } else { if (rowContext.getSources() == null) { return false; } else { return true; } } } public ColumnContext getColumnContext() { return columnContext; } public Set<QueryContext> getOtherContexts() { return otherContexts; } private void updateWhiteList(IdentityContext expression, HashMultimap<String,String> preFilterValues) { /* * A whitelist is a list of things that you allow, therefore, there is no reason to check for things that you do not allow. This means there is no * reason to check for NOT_EQUALS or NOT_EQUALS_REGEX, because they won't be allowed by default. */ if (expression.getOperation().equals(EQUALS)) { preFilterValues.put(expression.getIdentity(), expression.getLiteral()); } else if (expression.getOperation().equals(EQUALS_REGEX)) { preFilterValues.put(expression.getIdentity(), EdgeQueryLogic.PRE_FILTER_DISABLE_KEYWORD); } } private int populateQuery(List<IdentityContext> terms, StringBuilder trimmedQuery, StringBuilder trimmedStatsQuery, String operator, boolean includeStats, HashMultimap<String,String> preFilterValues, boolean addToPrefilter) { int numTermsAdded = 0; boolean createStats = includeStats; boolean expandStats = false; StringBuilder tempStatsStringBuilder = new StringBuilder(); trimmedQuery.append("("); if (createStats) { tempStatsStringBuilder.append("("); } for (int i = 0; i < terms.size(); i++) { IdentityContext iContext = terms.get(i); if (includeStats == false || iContext.getIdentity().equals(EDGE_SINK)) { createStats = false; } else { createStats = true; } if (iContext.getIdentity().equals(EDGE_RELATIONSHIP) || iContext.getIdentity().equals(EDGE_ATTRIBUTE1)) { expandStats = true; } else { expandStats = false; } numTermsAdded++; if (i > 0) { trimmedQuery.append(operator); if (createStats && tempStatsStringBuilder.length() > 7) { tempStatsStringBuilder.append(operator); } } if (!iContext.getIdentity().equals(FUNCTION)) { trimmedQuery.append(iContext.getIdentity() + " " + iContext.getOperation() + " " + "'" + iContext.getEscapedLiteral() + "'"); if (createStats) { if (expandStats) { tempStatsStringBuilder.append(splitCompoundValue(iContext.getIdentity(), iContext.getOperation(), iContext.getEscapedLiteral(), preFilterValues, addToPrefilter)); } else { tempStatsStringBuilder.append(iContext.getIdentity() + " " + iContext.getOperation() + " " + "'" + iContext.getEscapedLiteral() + "'"); } } } else { trimmedQuery.append(iContext.getLiteral()); if (createStats) { tempStatsStringBuilder.append(iContext.getLiteral()); } } if (addToPrefilter) { updateWhiteList(iContext, preFilterValues); } } createStats = includeStats; trimmedQuery.append(")"); if (createStats) { tempStatsStringBuilder.append(")"); } if (createStats && tempStatsStringBuilder.length() > 7) { if (trimmedStatsQuery.length() > 7) { trimmedStatsQuery.append(AND); } trimmedStatsQuery.append(tempStatsStringBuilder); } return numTermsAdded; } /* * Used for creating the stats query. Splits up EDGE_RELATIONSHIP=A-B into (EDGE_RELATIONSHIP=A) */ private static StringBuilder splitCompoundValue(String name, String operator, String value, HashMultimap<String,String> preFilterValues, boolean updateWhitelist) { StringBuilder sb = new StringBuilder(); String[] parts = value.split("-"); // parts should be length 1 or 2 if its not return nothing if (parts.length == 1) { sb.append(name).append(" " + operator + " '").append(parts[0]).append("'"); } else if (parts.length == 2) { sb.append(name).append(" " + operator + " '").append(parts[0]).append("'"); // don't need the second value since that would be the sink's relationship and we don't return stats edges for the sink // If we do split then we need to add the two new values to the pre-filter white list if (updateWhitelist) { preFilterValues.put(name, parts[0]); } } return sb; } public class ColumnContext implements EdgeModelAware { // Each list (except exclusions and funtions) is expected to have identity contexts all with the same opperation private List<IdentityContext> edgeTypes; private List<IdentityContext> edgeRelationships; private List<IdentityContext> edgeAttribute1Values; private List<IdentityContext> edgeAttribute2Values; private List<IdentityContext> edgeAttribute3Values; private List<IdentityContext> exclusions; private List<IdentityContext> functions; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ColumnContext)) return false; ColumnContext that = (ColumnContext) o; if (completeColumnFamilies != that.completeColumnFamilies) return false; if (edgeTypes != null ? !edgeTypes.equals(that.edgeTypes) : that.edgeTypes != null) return false; if (edgeRelationships != null ? !edgeRelationships.equals(that.edgeRelationships) : that.edgeRelationships != null) return false; if (edgeAttribute1Values != null ? !edgeAttribute1Values.equals(that.edgeAttribute1Values) : that.edgeAttribute1Values != null) return false; if (edgeAttribute2Values != null ? !edgeAttribute2Values.equals(that.edgeAttribute2Values) : that.edgeAttribute2Values != null) return false; if (edgeAttribute3Values != null ? !edgeAttribute3Values.equals(that.edgeAttribute3Values) : that.edgeAttribute3Values != null) return false; if (exclusions != null ? !exclusions.equals(that.exclusions) : that.exclusions != null) return false; return !(functions != null ? !functions.equals(that.functions) : that.functions != null); } @Override public int hashCode() { int result = edgeTypes != null ? edgeTypes.hashCode() : 0; result = 31 * result + (edgeRelationships != null ? edgeRelationships.hashCode() : 0); result = 31 * result + (edgeAttribute1Values != null ? edgeAttribute1Values.hashCode() : 0); result = 31 * result + (edgeAttribute2Values != null ? edgeAttribute2Values.hashCode() : 0); result = 31 * result + (edgeAttribute3Values != null ? edgeAttribute3Values.hashCode() : 0); result = 31 * result + (exclusions != null ? exclusions.hashCode() : 0); result = 31 * result + (functions != null ? functions.hashCode() : 0); result = 31 * result + (completeColumnFamilies ? 1 : 0); return result; } private boolean completeColumnFamilies = false; public void packageIdentities(List<IdentityContext> identityContexts) { packageIdentities(identityContexts, true); } public void packageIdentities(List<IdentityContext> identityContexts, boolean enforceRules) { if (!identityContexts.get(0).isEquivalence()) { addExclusion(identityContexts); return; } String type = identityContexts.get(0).getIdentity(); if (type.equals(EDGE_TYPE)) { verifyNotSet(edgeTypes, enforceRules); addEdgeTypes(identityContexts); } else if (type.equals(EDGE_RELATIONSHIP)) { verifyNotSet(edgeRelationships, enforceRules); addEdgeRelationships(identityContexts); } else if (type.equals(EDGE_ATTRIBUTE1)) { verifyNotSet(edgeAttribute1Values, enforceRules); addEdgeAttribute1Values(identityContexts); } else if (type.equals(EDGE_ATTRIBUTE2)) { verifyNotSet(edgeAttribute2Values, enforceRules); addAttribute2Values(identityContexts); } else if (type.equals(EDGE_ATTRIBUTE3)) { verifyNotSet(edgeAttribute3Values, enforceRules); addAttribute3Values(identityContexts); } else if (type.equals(FUNCTION)) { verifyNotSet(functions, enforceRules); functions = identityContexts; } else { throw new RuntimeException("Invalid identifier: " + type); } } /* * Complete column family means has both a edge type list and edge relation list and all operations in both lists are == operations (if =~ it is not * complete) * * Used to determine if we can set a list of column families to the scanner and omit them from the normalized query string */ public boolean hasCompleteColumnFamily() { completeColumnFamilies = true; if (edgeTypes == null) { completeColumnFamilies = false; } else if (edgeTypes.get(0).getOperation() != EQUALS) { completeColumnFamilies = false; } if (edgeRelationships == null) { completeColumnFamilies = false; } else if (edgeRelationships.get(0).getOperation() != EQUALS) { completeColumnFamilies = false; } return completeColumnFamilies; } /* * no column family mean no edge types and no edge relationships * * Used to help determine if we can add a list of column families to the scanner and omit them from the normalized query */ public boolean hasNoColumnFamily() { if (edgeTypes == null && edgeRelationships == null) { return true; } else { return false; } } // This should only be called if hasCompleteColumnFamily() == true public List<Text> computeColumnFamilyUnions(boolean includeStats) { List<Text> columnFamilies = new ArrayList<>(); for (IdentityContext edgeType : edgeTypes) { for (IdentityContext edgeRelationship : edgeRelationships) { columnFamilies.add(new Text(edgeType.getLiteral() + "/" + edgeRelationship.getLiteral())); if (includeStats) { for (EdgeKey.STATS_TYPE stats_type : EdgeKey.STATS_TYPE.values()) { String[] parts = StringUtils.split(edgeRelationship.getLiteral(), '-'); columnFamilies.add(new Text("STATS/" + stats_type + "/" + edgeType.getLiteral() + "/" + parts[0])); } } } } return columnFamilies; } public NormalizedQuery toString(boolean includeStats, HashMultimap<String,String> preFilterValues, boolean includeColumnFamilyTerms, boolean updateWhitelist) { StringBuilder trimmedQuery = new StringBuilder(); StringBuilder trimmedStatsQuery = new StringBuilder(); int numTermsAdded = 0; if (includeColumnFamilyTerms && getEdgeTypes() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // if (includeStats && trimmedStatsQuery.length() > 7) {trimmedStatsQuery.append(AND);} numTermsAdded += populateQuery(getEdgeTypes(), trimmedQuery, trimmedStatsQuery, OR, includeStats, preFilterValues, (updateWhitelist)); } if (includeColumnFamilyTerms && getEdgeRelationships() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // if (includeStats && trimmedStatsQuery.length() > 7) {trimmedStatsQuery.append(AND);} numTermsAdded += populateQuery(getEdgeRelationships(), trimmedQuery, trimmedStatsQuery, OR, includeStats, preFilterValues, (updateWhitelist)); } if (getEdgeAttribute1Values() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // if (includeStats && trimmedStatsQuery.length() > 7) {trimmedStatsQuery.append(AND);} numTermsAdded += populateQuery(getEdgeAttribute1Values(), trimmedQuery, trimmedStatsQuery, OR, includeStats, preFilterValues, (updateWhitelist)); } if (getEdgeAttribute2Values() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // if (includeStats && trimmedStatsQuery.length() > 7) {trimmedStatsQuery.append(AND);} numTermsAdded += populateQuery(getEdgeAttribute2Values(), trimmedQuery, trimmedStatsQuery, OR, includeStats, preFilterValues, (updateWhitelist)); } if (getEdgeAttribute3Values() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // if (includeStats && trimmedStatsQuery.length() > 7) {trimmedStatsQuery.append(AND);} numTermsAdded += populateQuery(getEdgeAttribute3Values(), trimmedQuery, trimmedStatsQuery, OR, includeStats, preFilterValues, (updateWhitelist)); } if (getExclusions() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // there could be sinks in this list of exclusions which would not get added do AND'ing in method numTermsAdded += populateQuery(getExclusions(), trimmedQuery, trimmedStatsQuery, AND, includeStats, preFilterValues, false); } if (getFunctions() != null) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // if (includeStats && trimmedStatsQuery.length() > 7) {trimmedStatsQuery.append(AND);} numTermsAdded += populateQuery(getFunctions(), trimmedQuery, trimmedStatsQuery, AND, includeStats, preFilterValues, false); } NormalizedQuery ret = new NormalizedQuery(); if (trimmedQuery.length() > 7) { ret.setNormalizedQuery(trimmedQuery.toString()); } else { ret.setNormalizedQuery(""); } if (trimmedStatsQuery.length() > 7) { ret.setNormalizedStatsQuery(trimmedStatsQuery.toString()); } else { ret.setNormalizedStatsQuery(""); } return ret; } private void addEdgeTypes(List<IdentityContext> identityContexts) { if (edgeTypes == null) { edgeTypes = identityContexts; } else { edgeTypes.addAll(identityContexts); } } private void addEdgeRelationships(List<IdentityContext> identityContexts) { if (edgeRelationships == null) { edgeRelationships = identityContexts; } else { edgeRelationships.addAll(identityContexts); } } private void addEdgeAttribute1Values(List<IdentityContext> identityContexts) { if (edgeAttribute1Values == null) { edgeAttribute1Values = identityContexts; } else { edgeAttribute1Values.addAll(identityContexts); } } private void addAttribute2Values(List<IdentityContext> identityContexts) { if (edgeAttribute2Values == null) { edgeAttribute2Values = identityContexts; } else { edgeAttribute2Values.addAll(identityContexts); } } private void addAttribute3Values(List<IdentityContext> identityContexts) { if (edgeAttribute3Values == null) { edgeAttribute3Values = identityContexts; } else { edgeAttribute3Values.addAll(identityContexts); } } /* * The list of exclusions is the only list that is allowed to be updated and is the only list that is allowed to have multiple identifier types. */ private void addExclusion(List<IdentityContext> identityContexts) { if (exclusions == null) { exclusions = identityContexts; } else { exclusions.addAll(identityContexts); } } public boolean isCompleteColumnFamilies() { return completeColumnFamilies; } public List<IdentityContext> getEdgeTypes() { return edgeTypes; } public List<IdentityContext> getEdgeRelationships() { return edgeRelationships; } public List<IdentityContext> getEdgeAttribute1Values() { return edgeAttribute1Values; } public List<IdentityContext> getEdgeAttribute2Values() { return edgeAttribute2Values; } public List<IdentityContext> getEdgeAttribute3Values() { return edgeAttribute3Values; } public List<IdentityContext> getExclusions() { return exclusions; } public List<IdentityContext> getFunctions() { return functions; } } public class RowContext implements EdgeModelAware { private List<IdentityContext> sources; private List<IdentityContext> sinks; public void packageIdentities(List<IdentityContext> identityContexts) { packageIdentities(identityContexts, true); } public void packageIdentities(List<IdentityContext> identityContexts, boolean enforceRules) { String type = identityContexts.get(0).getIdentity(); if (type.equals(EDGE_SOURCE)) { verifyNotSet(sources, enforceRules); addSources(identityContexts); } else if (type.equals(EDGE_SINK)) { verifyNotSet(sinks, enforceRules); addSinks(identityContexts); } else { throw new RuntimeException("Invalid identifier: " + type); } } public NormalizedQuery toString(boolean includeStats, boolean includingSources, boolean includingSinks) { StringBuilder trimmedQuery = new StringBuilder(); StringBuilder trimmedStatsQuery = new StringBuilder(); if (includingSources) { populateQuery(getSources(), trimmedQuery, trimmedStatsQuery, OR, includeStats, null, false); } if (getSinks() != null && (includingSources || includingSinks)) { if (trimmedQuery.length() > 7) { trimmedQuery.append(AND); } // never add target sources to stats query no need to append populateQuery(getSinks(), trimmedQuery, trimmedStatsQuery, OR, includeStats, null, false); } NormalizedQuery ret = new NormalizedQuery(); if (trimmedQuery.length() > 7) { ret.setNormalizedQuery(trimmedQuery.toString()); } else { ret.setNormalizedQuery(""); } if (trimmedStatsQuery.length() > 7) { ret.setNormalizedStatsQuery(trimmedStatsQuery.toString()); } else { ret.setNormalizedStatsQuery(""); } return ret; } private void addSources(List<IdentityContext> identityContexts) { if (sources == null) { sources = identityContexts; } else { sources.addAll(identityContexts); } } private void addSinks(List<IdentityContext> identityContexts) { if (sinks == null) { sinks = identityContexts; } else { sinks.addAll(identityContexts); } } public List<IdentityContext> getSources() { return sources; } public List<IdentityContext> getSinks() { return sinks; } } private class NormalizedQuery { String normalizedQuery; String normalizedStatsQuery; public String getNormalizedQuery() { return normalizedQuery; } public void setNormalizedQuery(String normalizedQuery) { this.normalizedQuery = normalizedQuery; } public String getNormalizedStatsQuery() { return normalizedStatsQuery; } public void setNormalizedStatsQuery(String normalizedStatsQuery) { this.normalizedStatsQuery = normalizedStatsQuery; } } private void setRowContext(RowContext rowContext) { this.rowContext = rowContext; } private void setColumnContext(ColumnContext columnContext) { this.columnContext = columnContext; } private void setOtherContexts(Set<QueryContext> otherContexts) { this.otherContexts = otherContexts; } }