package org.vertexium.query;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.vertexium.*;
import org.vertexium.util.SelectManyIterable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
public abstract class QueryBase implements Query, SimilarToGraphQuery {
private final Graph graph;
private final QueryParameters parameters;
private List<Aggregation> aggregations = new ArrayList<>();
protected QueryBase(Graph graph, String queryString, Authorizations authorizations) {
this.graph = graph;
this.parameters = new QueryStringQueryParameters(queryString, authorizations);
}
protected QueryBase(Graph graph, String[] similarToFields, String similarToText, Authorizations authorizations) {
this.graph = graph;
this.parameters = new SimilarToTextQueryParameters(similarToFields, similarToText, authorizations);
}
@Override
public QueryResultsIterable<Vertex> vertices() {
return vertices(getGraph().getDefaultFetchHints());
}
@Override
public QueryResultsIterable<Vertex> vertices(final EnumSet<FetchHint> fetchHints) {
//noinspection unchecked
return (QueryResultsIterable<Vertex>) search(EnumSet.of(VertexiumObjectType.VERTEX), fetchHints);
}
@Override
public QueryResultsIterable<String> vertexIds() {
return new DefaultGraphQueryIdIterable<>(vertices(FetchHint.NONE));
}
@Override
public QueryResultsIterable<Edge> edges() {
return edges(getGraph().getDefaultFetchHints());
}
@Override
public QueryResultsIterable<Edge> edges(final EnumSet<FetchHint> fetchHints) {
//noinspection unchecked
return (QueryResultsIterable<Edge>) search(EnumSet.of(VertexiumObjectType.EDGE), fetchHints);
}
@Override
public QueryResultsIterable<String> edgeIds() {
return new DefaultGraphQueryIdIterable<>(edges(FetchHint.NONE));
}
@Override
public QueryResultsIterable<ExtendedDataRow> extendedDataRows() {
return extendedDataRows(getGraph().getDefaultFetchHints());
}
@Override
public QueryResultsIterable<ExtendedDataRow> extendedDataRows(EnumSet<FetchHint> fetchHints) {
//noinspection unchecked
return (QueryResultsIterable<ExtendedDataRow>) search(EnumSet.of(VertexiumObjectType.EXTENDED_DATA), fetchHints);
}
@Override
public QueryResultsIterable<? extends VertexiumObject> search() {
return search(VertexiumObjectType.ALL, getGraph().getDefaultFetchHints());
}
@Override
public QueryResultsIterable<? extends VertexiumObject> search(EnumSet<VertexiumObjectType> objectTypes, EnumSet<FetchHint> fetchHints) {
List<QueryResultsIterable<? extends VertexiumObject>> items = new ArrayList<>();
if (objectTypes.contains(VertexiumObjectType.VERTEX)) {
items.add(vertices(fetchHints));
}
if (objectTypes.contains(VertexiumObjectType.EDGE)) {
items.add(edges(fetchHints));
}
if (objectTypes.contains(VertexiumObjectType.EXTENDED_DATA)) {
items.add(extendedData(fetchHints));
}
return new SelectManySearch(items);
}
private static class SelectManySearch
extends SelectManyIterable<QueryResultsIterable<? extends VertexiumObject>, VertexiumObject>
implements QueryResultsIterable<VertexiumObject> {
public SelectManySearch(Iterable<? extends QueryResultsIterable<? extends VertexiumObject>> source) {
super(source);
}
@Override
public <TResult extends AggregationResult> TResult getAggregationResult(String name, Class<? extends TResult> resultType) {
throw new VertexiumException("Not implemented");
}
@Override
public void close() throws IOException {
}
@Override
public long getTotalHits() {
long totalHits = 0;
for (QueryResultsIterable queryResultsIterable : getSource()) {
totalHits += queryResultsIterable.getTotalHits();
}
return totalHits;
}
@Override
protected Iterable<? extends VertexiumObject> getIterable(QueryResultsIterable<? extends VertexiumObject> source) {
return source;
}
}
/**
* This method should be overridden if {@link #search(EnumSet, EnumSet)} is not overridden.
*/
protected QueryResultsIterable<? extends VertexiumObject> extendedData(EnumSet<FetchHint> fetchHints) {
throw new VertexiumException("not implemented");
}
protected QueryResultsIterable<? extends VertexiumObject> extendedData(EnumSet<FetchHint> fetchHints, Iterable<? extends Element> elements) {
Iterable<ExtendedDataRow> allExtendedData = new SelectManyIterable<Element, ExtendedDataRow>(elements) {
@Override
protected Iterable<? extends ExtendedDataRow> getIterable(Element element) {
return new SelectManyIterable<String, ExtendedDataRow>(element.getExtendedDataTableNames()) {
@Override
protected Iterable<? extends ExtendedDataRow> getIterable(String tableName) {
return element.getExtendedData(tableName);
}
};
}
};
return new DefaultGraphQueryIterableWithAggregations<>(getParameters(), allExtendedData, true, true, true, getAggregations());
}
@Override
public QueryResultsIterable<ExtendedDataRowId> extendedDataRowIds() {
QueryResultsIterable<? extends VertexiumObject> vertexiumObjects = search(EnumSet.of(VertexiumObjectType.EXTENDED_DATA), FetchHint.NONE);
return new DefaultGraphQueryIdIterable<>(vertexiumObjects);
}
@Override
public Query hasEdgeLabel(String... edgeLabels) {
for (String edgeLabel : edgeLabels) {
getParameters().addEdgeLabel(edgeLabel);
}
return this;
}
@Override
public Query hasEdgeLabel(Collection<String> edgeLabels) {
for (String edgeLabel : edgeLabels) {
getParameters().addEdgeLabel(edgeLabel);
}
return this;
}
@Override
public Query hasExtendedData(ElementType elementType, String elementId) {
return hasExtendedData(elementType, elementId, null);
}
@Override
public Query hasExtendedData(String tableName) {
return hasExtendedData(null, null, tableName);
}
@Override
public Query hasExtendedData(ElementType elementType, String elementId, String tableName) {
hasExtendedData(Lists.newArrayList(new HasExtendedDataFilter(elementType, elementId, tableName)));
return this;
}
@Override
public Query hasExtendedData(Iterable<HasExtendedDataFilter> filters) {
getParameters().addHasContainer(new HasExtendedData(ImmutableList.copyOf(filters)));
return this;
}
@Override
@Deprecated
public QueryResultsIterable<Edge> edges(final String label, EnumSet<FetchHint> fetchHints) {
hasEdgeLabel(label);
return edges(fetchHints);
}
@Override
@Deprecated
public QueryResultsIterable<Edge> edges(final String label) {
hasEdgeLabel(label);
return edges();
}
@Override
public QueryResultsIterable<Element> elements() {
return elements(getGraph().getDefaultFetchHints());
}
@Override
public QueryResultsIterable<Element> elements(EnumSet<FetchHint> fetchHints) {
//noinspection unchecked
return (QueryResultsIterable<Element>) search(VertexiumObjectType.ELEMENTS, fetchHints);
}
@Override
public QueryResultsIterable<String> elementIds() {
return new DefaultGraphQueryIdIterable<>(elements(FetchHint.NONE));
}
@Override
public <T> Query range(String propertyName, T startValue, T endValue) {
return range(propertyName, startValue, true, endValue, true);
}
@Override
public <T> Query range(String propertyName, T startValue, boolean inclusiveStartValue, T endValue, boolean inclusiveEndValue) {
if (startValue != null) {
this.parameters.addHasContainer(new HasValueContainer(propertyName, inclusiveStartValue ? Compare.GREATER_THAN_EQUAL : Compare.GREATER_THAN, startValue, getGraph().getPropertyDefinitions()));
}
if (endValue != null) {
this.parameters.addHasContainer(new HasValueContainer(propertyName, inclusiveEndValue ? Compare.LESS_THAN_EQUAL : Compare.LESS_THAN, endValue, getGraph().getPropertyDefinitions()));
}
return this;
}
@Override
public Query sort(String propertyName, SortDirection direction) {
this.parameters.addSortContainer(new SortContainer(propertyName, direction));
return this;
}
@Override
public <T> Query has(String propertyName, T value) {
this.parameters.addHasContainer(new HasValueContainer(propertyName, Compare.EQUAL, value, getGraph().getPropertyDefinitions()));
return this;
}
@Override
public <T> Query hasNot(String propertyName, T value) {
this.parameters.addHasContainer(new HasValueContainer(propertyName, Contains.NOT_IN, new Object[]{value}, getGraph().getPropertyDefinitions()));
return this;
}
@Override
public <T> Query has(String propertyName, Predicate predicate, T value) {
this.parameters.addHasContainer(new HasValueContainer(propertyName, predicate, value, getGraph().getPropertyDefinitions()));
return this;
}
@Override
public Query has(String propertyName) {
this.parameters.addHasContainer(new HasPropertyContainer(propertyName));
return this;
}
@Override
public Query hasNot(String propertyName) {
this.parameters.addHasContainer(new HasNotPropertyContainer(propertyName));
return this;
}
@Override
public Query skip(int count) {
this.parameters.setSkip(count);
return this;
}
@Override
public Query limit(Integer count) {
this.parameters.setLimit(count);
return this;
}
@Override
public Query limit(Long count) {
this.parameters.setLimit(count);
return this;
}
public Graph getGraph() {
return graph;
}
public QueryParameters getParameters() {
return parameters;
}
public static abstract class HasContainer {
public abstract boolean isMatch(VertexiumObject elem);
@Override
public String toString() {
return this.getClass().getName() + "{}";
}
}
private static abstract class HasContainerSplitElementExtendedDataRows extends HasContainer {
@Override
public boolean isMatch(VertexiumObject vertexiumObject) {
if (vertexiumObject instanceof Element) {
return isMatch((Element) vertexiumObject);
} else if (vertexiumObject instanceof ExtendedDataRow) {
return isMatch((ExtendedDataRow) vertexiumObject);
} else {
throw new VertexiumException("Unhandled VertexiumObject type: " + vertexiumObject.getClass().getName());
}
}
protected abstract boolean isMatch(Element element);
protected abstract boolean isMatch(ExtendedDataRow row);
}
public static class SortContainer {
public final String propertyName;
public final SortDirection direction;
public SortContainer(String propertyName, SortDirection direction) {
this.propertyName = propertyName;
this.direction = direction;
}
@Override
public String toString() {
return this.getClass().getName() + "{" +
"propertyName='" + propertyName + '\'' +
", direction=" + direction +
'}';
}
}
public static class HasValueContainer extends HasContainerSplitElementExtendedDataRows {
public String key;
public Object value;
public Predicate predicate;
private final Collection<PropertyDefinition> propertyDefinitions;
public HasValueContainer(final String key, final Predicate predicate, final Object value, Collection<PropertyDefinition> propertyDefinitions) {
this.key = key;
this.value = value;
this.predicate = predicate;
this.propertyDefinitions = propertyDefinitions;
}
@Override
protected boolean isMatch(ExtendedDataRow extendedDataRow) {
Iterable<String> propertyNames = extendedDataRow.getPropertyNames();
for (String propertyName : propertyNames) {
if (propertyName.equals(this.key)) {
PropertyDefinition propertyDefinition = PropertyDefinition.findPropertyDefinition(this.propertyDefinitions, propertyName);
Object columnValue = extendedDataRow.getPropertyValue(propertyName);
if (this.predicate.evaluate(columnValue, this.value, propertyDefinition)) {
return true;
}
}
}
return false;
}
@Override
protected boolean isMatch(Element element) {
return this.predicate.evaluate(element.getProperties(this.key), this.value, this.propertyDefinitions);
}
@Override
public String toString() {
return this.getClass().getName() + "{" +
"predicate=" + predicate +
", value=" + value +
", key='" + key + '\'' +
'}';
}
}
public static class HasExtendedData extends HasContainer {
private final ImmutableList<HasExtendedDataFilter> filters;
public HasExtendedData(ImmutableList<HasExtendedDataFilter> filters) {
this.filters = filters;
}
public ImmutableList<HasExtendedDataFilter> getFilters() {
return filters;
}
@Override
public boolean isMatch(VertexiumObject elem) {
if (!(elem instanceof ExtendedDataRow)) {
return false;
}
ExtendedDataRow row = (ExtendedDataRow) elem;
ExtendedDataRowId rowId = row.getId();
for (HasExtendedDataFilter filter : filters) {
if (filter.getElementType() == null || rowId.getElementType().equals(filter.getElementType())
&& (filter.getElementId() == null || rowId.getElementId().equals(filter.getElementId()))
&& (filter.getTableName() == null || rowId.getTableName().equals(filter.getTableName()))) {
return true;
}
}
return false;
}
}
public static class HasPropertyContainer extends HasContainerSplitElementExtendedDataRows {
private final String key;
public HasPropertyContainer(String key) {
this.key = key;
}
@Override
protected boolean isMatch(ExtendedDataRow row) {
for (String propertyName : row.getPropertyNames()) {
if (propertyName.equals(this.key)) {
return true;
}
}
return false;
}
@Override
protected boolean isMatch(Element element) {
for (Property prop : element.getProperties()) {
if (prop.getName().equals(this.key)) {
return true;
}
}
return false;
}
public String getKey() {
return key;
}
@Override
public String toString() {
return this.getClass().getName() + "{" +
"key='" + key + '\'' +
'}';
}
}
public static class HasNotPropertyContainer extends HasContainerSplitElementExtendedDataRows {
private final String key;
public HasNotPropertyContainer(String key) {
this.key = key;
}
@Override
protected boolean isMatch(ExtendedDataRow row) {
for (String propertyName : row.getPropertyNames()) {
if (propertyName.equals(this.key)) {
return false;
}
}
return true;
}
@Override
protected boolean isMatch(Element element) {
for (Property prop : element.getProperties()) {
if (prop.getName().equals(this.key)) {
return false;
}
}
return true;
}
public String getKey() {
return key;
}
@Override
public String toString() {
return this.getClass().getName() + "{" +
"key='" + key + '\'' +
'}';
}
}
@Override
public SimilarToGraphQuery minTermFrequency(int minTermFrequency) {
if (!(parameters instanceof SimilarToQueryParameters)) {
throw new VertexiumException("Invalid query parameters, expected " + SimilarToQueryParameters.class.getName() + " found " + parameters.getClass().getName());
}
((SimilarToQueryParameters) this.parameters).setMinTermFrequency(minTermFrequency);
return this;
}
@Override
public SimilarToGraphQuery maxQueryTerms(int maxQueryTerms) {
if (!(parameters instanceof SimilarToQueryParameters)) {
throw new VertexiumException("Invalid query parameters, expected " + SimilarToQueryParameters.class.getName() + " found " + parameters.getClass().getName());
}
((SimilarToQueryParameters) this.parameters).setMaxQueryTerms(maxQueryTerms);
return this;
}
@Override
public SimilarToGraphQuery minDocFrequency(int minDocFrequency) {
if (!(parameters instanceof SimilarToQueryParameters)) {
throw new VertexiumException("Invalid query parameters, expected " + SimilarToQueryParameters.class.getName() + " found " + parameters.getClass().getName());
}
((SimilarToQueryParameters) this.parameters).setMinDocFrequency(minDocFrequency);
return this;
}
@Override
public SimilarToGraphQuery maxDocFrequency(int maxDocFrequency) {
if (!(parameters instanceof SimilarToQueryParameters)) {
throw new VertexiumException("Invalid query parameters, expected " + SimilarToQueryParameters.class.getName() + " found " + parameters.getClass().getName());
}
((SimilarToQueryParameters) this.parameters).setMaxDocFrequency(maxDocFrequency);
return this;
}
/**
* @deprecated As of 2.6.0 this call has no effect in Elasticsearch and will be remove
*/
@Override
@Deprecated
public SimilarToGraphQuery percentTermsToMatch(float percentTermsToMatch) {
if (!(parameters instanceof SimilarToQueryParameters)) {
throw new VertexiumException("Invalid query parameters, expected " + SimilarToQueryParameters.class.getName() + " found " + parameters.getClass().getName());
}
((SimilarToQueryParameters) this.parameters).setPercentTermsToMatch(percentTermsToMatch);
return this;
}
@Override
public SimilarToGraphQuery boost(float boost) {
if (!(parameters instanceof SimilarToQueryParameters)) {
throw new VertexiumException("Invalid query parameters, expected " + SimilarToQueryParameters.class.getName() + " found " + parameters.getClass().getName());
}
((SimilarToQueryParameters) this.parameters).setBoost(boost);
return this;
}
@Override
public boolean isAggregationSupported(Aggregation aggregation) {
return false;
}
@Override
public Query addAggregation(Aggregation aggregation) {
if (!isAggregationSupported(aggregation)) {
throw new VertexiumException("Aggregation " + aggregation.getClass().getName() + " is not supported");
}
this.aggregations.add(aggregation);
return this;
}
public Collection<Aggregation> getAggregations() {
return aggregations;
}
public Aggregation getAggregationByName(String aggregationName) {
for (Aggregation agg : aggregations) {
if (agg.getAggregationName().equals(aggregationName)) {
return agg;
}
}
return null;
}
}