Query.java
/*******************************************************************************
* Copyright (c) 2018 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.sparqlbuilder.core.query;
import java.util.Optional;
import java.util.function.Consumer;
import org.eclipse.rdf4j.sparqlbuilder.constraint.Expression;
import org.eclipse.rdf4j.sparqlbuilder.constraint.Values;
import org.eclipse.rdf4j.sparqlbuilder.core.Dataset;
import org.eclipse.rdf4j.sparqlbuilder.core.From;
import org.eclipse.rdf4j.sparqlbuilder.core.GroupBy;
import org.eclipse.rdf4j.sparqlbuilder.core.Groupable;
import org.eclipse.rdf4j.sparqlbuilder.core.Having;
import org.eclipse.rdf4j.sparqlbuilder.core.OrderBy;
import org.eclipse.rdf4j.sparqlbuilder.core.Orderable;
import org.eclipse.rdf4j.sparqlbuilder.core.QueryElement;
import org.eclipse.rdf4j.sparqlbuilder.core.QueryPattern;
import org.eclipse.rdf4j.sparqlbuilder.core.SparqlBuilder;
import org.eclipse.rdf4j.sparqlbuilder.core.Variable;
import org.eclipse.rdf4j.sparqlbuilder.graphpattern.GraphPattern;
import org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf;
import org.eclipse.rdf4j.sparqlbuilder.rdf.RdfBlankNode;
import org.eclipse.rdf4j.sparqlbuilder.util.SparqlBuilderUtils;
/**
* The base class for all SPARQL Queries. Contains elements and methods common to all queries.
*
* @param <T> They type of query. Used to support fluency.
*/
@SuppressWarnings("unchecked")
public abstract class Query<T extends Query<T>> implements QueryElement {
protected static final String LIMIT = "LIMIT";
protected static final String OFFSET = "OFFSET";
protected Optional<Dataset> from = Optional.empty();
protected QueryPattern where = SparqlBuilder.where();
protected Optional<GroupBy> groupBy = Optional.empty();
protected Optional<OrderBy> orderBy = Optional.empty();
protected Optional<Having> having = Optional.empty();
protected Optional<Values> values = Optional.empty();
protected int limit = -1, offset = -1, varCount = -1, bnodeCount = -1;
/**
* Add datasets to this query
*
* @param graphs the graph specifiers to add
* @return this
*/
public T from(From... graphs) {
from = SparqlBuilderUtils.getOrCreateAndModifyOptional(from, SparqlBuilder::dataset, f -> f.from(graphs));
return (T) this;
}
/**
* Set the Dataset clause for this query
*
* @param from the {@link Dataset} clause to set
* @return this
*/
public T from(Dataset from) {
this.from = Optional.of(from);
return (T) this;
}
/**
* Add graph patterns to this query's query pattern
*
* @param queryPatterns the patterns to add
* @return this
* @see QueryPattern
*/
public T where(GraphPattern... queryPatterns) {
where.where(queryPatterns);
return (T) this;
}
/**
* Set the query pattern of this query
*
* @param where the query pattern to set
* @return this
*/
public T where(QueryPattern where) {
this.where = where;
return (T) this;
}
/**
* Add grouping specifiers for the query results.
*
* @param groupables the objects to group on, in order (appended to the end of any existing grouping specifiers)
* @return this
* @see GroupBy
*/
public T groupBy(Groupable... groupables) {
groupBy = SparqlBuilderUtils.getOrCreateAndModifyOptional(groupBy, SparqlBuilder::groupBy,
gb -> gb.by(groupables));
return (T) this;
}
/**
* Set this query's Group By clause
*
* @param groupBy the {@link GroupBy} clause to set
* @return this
*/
public T groupBy(GroupBy groupBy) {
this.groupBy = Optional.of(groupBy);
return (T) this;
}
/**
* Specify orderings for the query results
*
* @param conditions the objects to order on, in order
* @return this
* @see OrderBy
*/
public T orderBy(Orderable... conditions) {
orderBy = SparqlBuilderUtils.getOrCreateAndModifyOptional(orderBy, SparqlBuilder::orderBy,
ob -> ob.by(conditions));
return (T) this;
}
/**
* Set this query's Order By clause
*
* @param orderBy the {@link OrderBy} clause to set
* @return this
*/
public T orderBy(OrderBy orderBy) {
this.orderBy = Optional.of(orderBy);
return (T) this;
}
/**
* Specify constraints for this query's Having clause.
*
* @param constraints the constraints to add to the clause
* @return this
* @see Having
*/
public T having(Expression<?>... constraints) {
having = SparqlBuilderUtils.getOrCreateAndModifyOptional(having, SparqlBuilder::having,
h -> h.having(constraints));
return (T) this;
}
/**
* Set this query's Having clause
*
* @param having the Having clause to set
* @return this
*/
public T having(Having having) {
this.having = Optional.of(having);
return (T) this;
}
/**
* Set a limit on the number of results returned by this query.
*
* @param limit
* @return this
* @see <a href="http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#modResultLimit"> Limits in SPARQL
* Queries</a>
*/
public T limit(int limit) {
this.limit = limit;
return (T) this;
}
/**
* Specify an offset in query results.
*
* @param offset
* @return this
* @see <a href="http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#modOffset">Offsets in SPARQL Queries</a>
*/
public T offset(int offset) {
this.offset = offset;
return (T) this;
}
public T values(Consumer<Values.VariablesBuilder> valuesConfigurer) {
Values.Builder builder = (Values.Builder) Values.builder();
valuesConfigurer.accept(builder);
this.values = Optional.of(builder.build());
return (T) this;
}
/**
* A shortcut. Each call to this method returns a new {@link Variable} that is unique (i.e., has a unique alias) to
* this query instance.
*
* @return a {@link Variable} object that is unique to this query instance
*/
public Variable var() {
return SparqlBuilder.var("x" + ++varCount);
}
/**
* A shortcut. Each call to this method returns a new
* {@link org.eclipse.rdf4j.sparqlbuilder.rdf.RdfBlankNode.LabeledBlankNode RdfBlankNode.LabeledBlankNode} that is
* unique (i.e., has a unique alias) to this query instance.
*
* @return a {@link org.eclipse.rdf4j.sparqlbuilder.rdf.RdfBlankNode.LabeledBlankNode RdfBlankNode.LabeledBlankNode}
* object that is unique to this query instance
*/
public RdfBlankNode.LabeledBlankNode bNode() {
return Rdf.bNode("b" + ++bnodeCount);
}
protected abstract String getQueryActionString();
@Override
public String getQueryString() {
StringBuilder query = new StringBuilder();
query.append(getQueryActionString()).append("\n");
SparqlBuilderUtils.appendAndNewlineIfPresent(from, query);
query.append(where.getQueryString()).append("\n");
SparqlBuilderUtils.appendAndNewlineIfPresent(groupBy, query);
SparqlBuilderUtils.appendAndNewlineIfPresent(having, query);
SparqlBuilderUtils.appendAndNewlineIfPresent(orderBy, query);
if (limit >= 0) {
query.append(LIMIT + " ").append(limit).append("\n");
}
if (offset >= 0) {
query.append(OFFSET + " ").append(offset).append("\n");
}
SparqlBuilderUtils.appendAndNewlineIfPresent(values, query);
return query.toString();
}
}