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

org.eclipse.rdf4j.federated.algebra.FedXStatementPattern Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2019 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.federated.algebra;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.eclipse.rdf4j.federated.structures.QueryInfo;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.algebra.AbstractQueryModelNode;
import org.eclipse.rdf4j.query.algebra.QueryModelNode;
import org.eclipse.rdf4j.query.algebra.QueryModelVisitor;
import org.eclipse.rdf4j.query.algebra.StatementPattern;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base class providing all common functionality for FedX StatementPatterns
 *
 * @author Andreas Schwarte
 * @see StatementSourcePattern
 * @see ExclusiveStatement
 *
 */
public abstract class FedXStatementPattern extends StatementPattern
		implements StatementTupleExpr, FilterTuple, BoundJoinTupleExpr {
	private static final Logger log = LoggerFactory.getLogger(FedXStatementPattern.class);

	private static final long serialVersionUID = 6588020780262348806L;

	protected final List statementSources = new ArrayList<>();
	protected final String id;
	protected final QueryInfo queryInfo;
	protected final List freeVars = new ArrayList<>(3);
	protected FilterValueExpr filterExpr = null;
	protected QueryBindingSet boundFilters = null; // contains bound filter bindings, that need to be added as
	// additional bindings
	protected long upperLimit = -1; // if set to a positive number, this upper limit is applied to any subquery

	public FedXStatementPattern(StatementPattern node, QueryInfo queryInfo) {
		super(node.getScope(), node.getSubjectVar().clone(), node.getPredicateVar().clone(),
				node.getObjectVar().clone(), node.getContextVar() != null ? node.getContextVar().clone() : null);
		this.id = NodeFactory.getNextId();
		this.queryInfo = queryInfo;
		initFreeVars();
	}

	@Override
	public  void visitChildren(QueryModelVisitor visitor)
			throws X {
		super.visitChildren(visitor);
		for (StatementSource s : sort(statementSources)) {
			s.visit(visitor);
		}

		if (boundFilters != null) {
			BoundFiltersNode.visit(visitor, boundFilters);
		}

		if (upperLimit > 0) {
			new UpperLimitNode(upperLimit).visit(visitor);
		}

		if (filterExpr != null) {
			filterExpr.visit(visitor);
		}
	}

	@Override
	public  void visit(QueryModelVisitor visitor)
			throws X {
		visitor.meetOther(this);
	}

	protected void initFreeVars() {
		if (getSubjectVar().getValue() == null) {
			freeVars.add(getSubjectVar().getName());
		}
		if (getPredicateVar().getValue() == null) {
			freeVars.add(getPredicateVar().getName());
		}
		if (getObjectVar().getValue() == null) {
			freeVars.add(getObjectVar().getName());
		}
	}

	@Override
	public int getFreeVarCount() {
		return freeVars.size();
	}

	@Override
	public List getFreeVars() {
		return freeVars;
	}

	@Override
	public QueryInfo getQueryInfo() {
		return this.queryInfo;
	}

	@Override
	public String getId() {
		return id;
	}

	@Override
	public boolean hasFreeVarsFor(BindingSet bindings) {
		for (String var : freeVars) {
			if (!bindings.hasBinding(var)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public List getStatementSources() {
		return statementSources;
	}

	public int getSourceCount() {
		return statementSources.size();
	}

	@Override
	public FilterValueExpr getFilterExpr() {
		return filterExpr;
	}

	@Override
	public BindingSet getBoundFilters() {
		return this.boundFilters;
	}

	@Override
	public boolean hasFilter() {
		return filterExpr != null;
	}

	@Override
	public void addFilterExpr(FilterExpr expr) {

		if (filterExpr == null) {
			filterExpr = expr;
		} else if (filterExpr instanceof ConjunctiveFilterExpr) {
			((ConjunctiveFilterExpr) filterExpr).addExpression(expr);
		} else if (filterExpr instanceof FilterExpr) {
			filterExpr = new ConjunctiveFilterExpr((FilterExpr) filterExpr, expr);
		} else {
			throw new RuntimeException("Unexpected type: " + filterExpr.getClass().getCanonicalName());
		}
	}

	@Override
	public void addBoundFilter(String varName, Value value) {

		if (!freeVars.contains(varName)) {
			log.debug("Invalid call to addBoundFilter: variable " + varName + " is not known as a free variable");
			return;
		}

		// lazy initialization of bound filters
		if (boundFilters == null) {
			boundFilters = new QueryBindingSet();
		}

		// visit Var nodes and set value for matching var names
		if (getSubjectVar().getName().equals(varName)) {
			Var var = getSubjectVar();
			var.replaceWith(new Var(var.getName(), value, var.isAnonymous(), var.isConstant()));
		}
		if (getPredicateVar().getName().equals(varName)) {
			Var var = getPredicateVar();
			var.replaceWith(new Var(var.getName(), value, var.isAnonymous(), var.isConstant()));
		}
		if (getObjectVar().getName().equals(varName)) {
			Var var = getObjectVar();
			var.replaceWith(new Var(var.getName(), value, var.isAnonymous(), var.isConstant()));
		}

		boundFilters.addBinding(varName, value);

		freeVars.remove(varName);

		// XXX recheck owned source if it still can deliver results, otherwise prune it
		// optimization: keep result locally for this query
		// if no free vars AND hasResults => replace by TrueNode to avoid additional remote requests
	}

	/**
	 * Set the upper limit for this statement expression (i.e. applied in the evaluation to individual subqueries of
	 * this expr)
	 *
	 * @param upperLimit the upper limit, a negative number means unlimited
	 */
	public void setUpperLimit(long upperLimit) {
		this.upperLimit = upperLimit;
	}

	/**
	 *
	 * @return the upper limit or a negative number (meaning no LIMIT)
	 */
	public long getUpperLimit() {
		return this.upperLimit;
	}

	private List sort(List stmtSources) {
		List res = new ArrayList<>(stmtSources);
		res.sort(Comparator.comparing((StatementSource o) -> o.id));
		return res;
	}

	static class UpperLimitNode extends AbstractQueryModelNode {

		private static final long serialVersionUID = -1331709574582152474L;

		private final long upperLimit;

		public UpperLimitNode(long upperLimit) {
			super();
			this.upperLimit = upperLimit;
		}

		@Override
		public String getSignature() {
			return "Upper Limit: " + upperLimit;
		}

		@Override
		public  void visit(QueryModelVisitor visitor) throws X {
			visitor.meetOther(this);
		}

		@Override
		public  void visitChildren(QueryModelVisitor visitor) throws X {
			// no-op
		}

		@Override
		public void replaceChildNode(QueryModelNode current, QueryModelNode replacement) {
			throw new IllegalArgumentException("Node is not a child node: " + current);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy