/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Sep 9, 2011
*/
package com.bigdata.rdf.sparql.ast.eval;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.URIImpl;
import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpContextBase;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.NV;
import com.bigdata.bop.ap.SampleIndex;
import com.bigdata.bop.bindingSet.EmptyBindingSet;
import com.bigdata.bop.bindingSet.ListBindingSet;
import com.bigdata.bop.fed.QueryEngineFactory;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.sparql.ast.GroupNodeBase;
import com.bigdata.rdf.sparql.ast.IGroupMemberNode;
import com.bigdata.rdf.sparql.ast.TermNode;
import com.bigdata.rdf.sparql.ast.eval.SampleServiceFactory.SampleParams;
import com.bigdata.rdf.sparql.ast.service.BigdataNativeServiceOptions;
import com.bigdata.rdf.sparql.ast.service.BigdataServiceCall;
import com.bigdata.rdf.sparql.ast.service.IServiceOptions;
import com.bigdata.rdf.sparql.ast.service.ServiceCallCreateParams;
import com.bigdata.rdf.sparql.ast.service.ServiceFactory;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;
import com.bigdata.rdf.spo.ISPO;
import com.bigdata.rdf.store.AbstractTripleStore;
import com.bigdata.rdf.store.BD;
import com.bigdata.relation.accesspath.EmptyCloseableIterator;
import com.bigdata.relation.accesspath.ThickCloseableIterator;
import cutthecrap.utils.striterators.ICloseableIterator;
/**
* A factory for a service that simulates the VALUES syntax in SPARQL:
*
* service bd:values {
* # service params
* bd:serviceParam bd:values.var ?var .
* bd:serviceParam bd:values.val "val1" .
* bd:serviceParam bd:values.val "val2" .
* ...
* }
*/
public class ValuesServiceFactory extends AbstractServiceFactory {
private static final Logger log = Logger
.getLogger(ValuesServiceFactory.class);
/**
* The URI service key.
*/
public static final URI SERVICE_KEY = new URIImpl(BD.NAMESPACE+"values");
/**
* The service params for this service.
*/
public static interface ValuesParams {
/**
* The limit on the sample.
*/
URI VAR = new URIImpl(SERVICE_KEY.stringValue() + ".var");
/**
* The seed on the sample.
*/
URI VAL = new URIImpl(SERVICE_KEY.stringValue() + ".val");
}
/*
* Note: This could extend the base class to allow for search service
* configuration options.
*/
private final BigdataNativeServiceOptions serviceOptions;
public ValuesServiceFactory() {
serviceOptions = new BigdataNativeServiceOptions();
serviceOptions.setRunFirst(true);
}
@Override
public BigdataNativeServiceOptions getServiceOptions() {
return serviceOptions;
}
public BigdataServiceCall create(
final ServiceCallCreateParams params,
final ServiceParams serviceParams) {
final AbstractTripleStore store = params.getTripleStore();
final ServiceNode serviceNode = params.getServiceNode();
/*
* Validate the predicates for a given service call.
*/
verifyGraphPattern(store, serviceNode.getGraphPattern(), serviceParams);
/*
* Create and return the ServiceCall object which will execute this
* query.
*/
return new ValuesCall(store, getServiceOptions(), serviceParams);
}
/**
* Verify that there is only a single statement pattern node and that the
* service parameters are valid.
*/
private void verifyGraphPattern(
final AbstractTripleStore database,
final GroupNodeBase<IGroupMemberNode> group,
final ServiceParams serviceParams) {
final Iterator<Map.Entry<URI, List<TermNode>>> it = serviceParams.iterator();
if (!serviceParams.contains(ValuesParams.VAR)) {
throw new RuntimeException("must provide a variable for: " + ValuesParams.VAR);
}
if (!serviceParams.contains(ValuesParams.VAL)) {
throw new RuntimeException("must provide at least one value for: " + ValuesParams.VAL);
}
while (it.hasNext()) {
final URI param = it.next().getKey();
if (ValuesParams.VAR.equals(param)) {
final List<TermNode> vars = serviceParams.get(param);
if (vars == null || vars.size() != 1 || vars.get(0).isConstant()) {
throw new RuntimeException("must provide exactly one variable for: " + param);
}
} else if (ValuesParams.VAL.equals(param)) {
final List<TermNode> vals = serviceParams.get(param);
if (vals == null || vals.size() == 0) {
throw new RuntimeException("must provide at least one value for: " + param);
}
for (TermNode val : vals) {
if (val.isVariable()) {
throw new RuntimeException("must provide constant values for: " + param);
}
}
} else {
throw new RuntimeException("unrecognized param: " + param);
}
}
}
/**
*
* Note: This has the {@link AbstractTripleStore} reference attached. This
* is not a {@link Serializable} object. It MUST run on the query
* controller.
*/
private static class ValuesCall implements BigdataServiceCall {
private final AbstractTripleStore db;
private final IServiceOptions serviceOptions;
private final ServiceParams serviceParams;
public ValuesCall(
final AbstractTripleStore db,
final IServiceOptions serviceOptions,
final ServiceParams serviceParams) {
if(db == null)
throw new IllegalArgumentException();
if(serviceOptions == null)
throw new IllegalArgumentException();
if(serviceParams == null)
throw new IllegalArgumentException();
this.db = db;
this.serviceOptions = serviceOptions;
this.serviceParams = serviceParams;
}
/**
* Run a sample index op over the access path.
*/
@Override
public ICloseableIterator<IBindingSet> call(
final IBindingSet[] bc) {
if (log.isInfoEnabled()) {
log.info(bc.length);
log.info(Arrays.toString(bc));
log.info(serviceParams);
}
if (bc != null && bc.length > 0 && !bc[0].equals(EmptyBindingSet.INSTANCE)) {
throw new RuntimeException("cannot run with incoming bindings");
}
final IVariable<IV> var = serviceParams.getAsVar(ValuesParams.VAR);
final List<TermNode> vals = serviceParams.get(ValuesParams.VAL);
final IBindingSet[] bSets = new IBindingSet[vals.size()];
for (int i = 0; i < bSets.length; i++) {
bSets[i] = new ListBindingSet();
bSets[i].set(var, (IConstant<IV>) vals.get(i).getValueExpression());
}
return new ThickCloseableIterator<IBindingSet>(bSets, bSets.length);
}
@Override
public IServiceOptions getServiceOptions() {
return serviceOptions;
}
}
}