package com.bigdata.rdf.sail; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.log4j.Logger; import com.bigdata.bop.BOp; import com.bigdata.bop.Constant; import com.bigdata.bop.IBindingSet; import com.bigdata.bop.IVariable; import com.bigdata.bop.IVariableOrConstant; import com.bigdata.bop.ap.Predicate; import com.bigdata.btree.IIndex; import com.bigdata.btree.IRangeQuery; import com.bigdata.btree.ITuple; import com.bigdata.btree.ITupleIterator; import com.bigdata.btree.keys.IKeyBuilder; import com.bigdata.btree.keys.SuccessorUtil; import com.bigdata.rdf.changesets.IChangeLog; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.internal.IVUtility; import com.bigdata.rdf.internal.impl.bnode.SidIV; import com.bigdata.rdf.sail.BigdataSail.BigdataSailConnection; import com.bigdata.rdf.sparql.ast.GraphPatternGroup; import com.bigdata.rdf.sparql.ast.IGroupMemberNode; import com.bigdata.rdf.sparql.ast.StatementPatternNode; import com.bigdata.rdf.sparql.ast.service.BigdataNativeServiceOptions; import com.bigdata.rdf.sparql.ast.service.BigdataServiceCall; import com.bigdata.rdf.sparql.ast.service.CustomServiceFactory; import com.bigdata.rdf.sparql.ast.service.IServiceOptions; import com.bigdata.rdf.sparql.ast.service.ServiceCall; import com.bigdata.rdf.sparql.ast.service.ServiceCallCreateParams; import com.bigdata.rdf.sparql.ast.service.ServiceNode; import com.bigdata.rdf.spo.ISPO; import com.bigdata.rdf.spo.SPOKeyOrder; import com.bigdata.rdf.store.AbstractTripleStore; import com.bigdata.striterator.ChunkedWrappedIterator; import cutthecrap.utils.striterators.Expander; import cutthecrap.utils.striterators.Filter; import cutthecrap.utils.striterators.ICloseableIterator; import cutthecrap.utils.striterators.IStriterator; import cutthecrap.utils.striterators.Resolver; import cutthecrap.utils.striterators.Striterator; /** * */ public class RDRHistoryServiceFactory implements CustomServiceFactory { static private transient final Logger log = Logger .getLogger(RDRHistoryServiceFactory.class); private final BigdataNativeServiceOptions serviceOptions; public RDRHistoryServiceFactory() { serviceOptions = new BigdataNativeServiceOptions(); /* * TODO Review decision to make this a runFirst service. The rational is * that this service can only apply a very limited set of restrictions * during query, therefore it will often make sense to run it first. * However, the fromTime and toTime could be bound by the query and the * service can filter some things more efficiently internally than if we * generated a bunch of intermediate solutions for those things. */ serviceOptions.setRunFirst(true); } @Override public IServiceOptions getServiceOptions() { return serviceOptions; } /** * Instantiate the service call with the supplied params. */ @Override public ServiceCall<?> create(final ServiceCallCreateParams params) { // if (params == null) // throw new IllegalArgumentException(); // // final AbstractTripleStore store = params.getTripleStore(); // // if (store == null) // throw new IllegalArgumentException(); // // final ServiceNode serviceNode = params.getServiceNode(); // // if (serviceNode == null) // throw new IllegalArgumentException(); // // final GraphPatternGroup<IGroupMemberNode> group = // serviceNode.getGraphPattern(); // // verifyGroup(group); // // return new RDRHistoryServiceCall(store, getServiceOptions(), group); throw new UnsupportedOperationException("deprecated"); } /** * Make sure we've got the right stuff in there - two statement patterns * plus optional filters. */ private void verifyGroup(final GraphPatternGroup<IGroupMemberNode> group) { if (log.isDebugEnabled()) { log.debug(group); } } /** * Register an {@link IChangeLog} listener that will manage the maintenance * of the describe cache. */ @Override public void startConnection(final BigdataSailConnection conn) { final AbstractTripleStore database = conn.getTripleStore(); if (database.isRDRHistory()) { final RDRHistory history = database.getRDRHistoryInstance(); history.init(); conn.addChangeLog(history); } } private static class RDRHistoryServiceCall implements BigdataServiceCall { private final AbstractTripleStore database; private final IServiceOptions options; private final GraphPatternGroup<IGroupMemberNode> subgroup; private final StatementPatternNode sidNode; private final StatementPatternNode historyNode; public RDRHistoryServiceCall(final AbstractTripleStore database, final IServiceOptions options, final GraphPatternGroup<IGroupMemberNode> subgroup) { this.database = database; this.options = options; this.subgroup = subgroup; final List<StatementPatternNode> spNodes = subgroup.getChildren(StatementPatternNode.class); if (spNodes.size() != 2) { throw new IllegalArgumentException(); } if (spNodes.get(0).sid() != null) { sidNode = spNodes.get(0); historyNode = spNodes.get(1); } else { sidNode = spNodes.get(1); historyNode = spNodes.get(0); } } @Override public IServiceOptions getServiceOptions() { return options; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public ICloseableIterator<IBindingSet> call( final IBindingSet[] bindingSets) throws Exception { /* * << <s> ?p ?o >> ?action ?time . # SPO * << <s> ?p ?o >> <added> ?time . # SPO + filter * << <s> ?p ?o >> <added> "t" . # SPO + filter */ final Predicate<?> pred = new Predicate(new BOp[] { sidNode.s().getValueExpression(), sidNode.p().getValueExpression(), sidNode.o().getValueExpression(), historyNode.p().getValueExpression(), historyNode.o().getValueExpression() }); final byte flags = SidIV.toFlags(); final IStriterator results = new Striterator(Collections.emptyIterator()); final Map<Predicate, List<IBindingSet>> coalesced = new LinkedHashMap<Predicate, List<IBindingSet>>(); for (final IBindingSet bs : bindingSets) { final Predicate<?> asBound = pred.asBound(bs); final List<IBindingSet> values; if (coalesced.containsKey(asBound)) { values = coalesced.get(asBound); } else { coalesced.put(asBound, values = new LinkedList<IBindingSet>()); } values.add(bs); } final IV[] ivs = new IV[5]; for (final Entry<Predicate,List<IBindingSet>> entry : coalesced.entrySet()) { final Predicate asBound = entry.getKey(); final SPOKeyOrder keyOrder = SPOKeyOrder.SPO; final IIndex ndx = database.getSPORelation().getIndex(keyOrder); final IKeyBuilder keyBuilder = ndx.getIndexMetadata().getKeyBuilder(); keyBuilder.reset(); keyBuilder.appendSigned(flags); for (int i = 0; i < 5; i++) { final IVariableOrConstant term = asBound.get(i); if (term.isConstant()) { final IV iv = ((Constant<IV>) term).get(); IVUtility.encode(keyBuilder, iv); } else { break; } } final byte[] fromKey = keyBuilder.getKey(); final byte[] toKey = SuccessorUtil.successor(fromKey.clone()); final ITupleIterator<ISPO> titr = ndx.rangeIterator(fromKey, toKey, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.KEYS, null/* filter */); final IStriterator sitr = new Striterator(titr); /* * Resolve ITuple -> ISPO. */ sitr.addFilter(new Resolver() { private static final long serialVersionUID = 1L; @Override protected Object resolve(final Object e) { final ITuple<ISPO> t = (ITuple<ISPO>) e; return t.getObject(); } }); /* * Filter against bound terms. */ sitr.addFilter(new Filter() { private static final long serialVersionUID = 1L; @Override public boolean isValid(final Object e) { toIVs((ISPO) e, ivs); /* * Compare against the asBound predicate. */ for (int i = 0; i < 5; i++) { final IVariableOrConstant term = asBound.get(i); if (term.isConstant()) { final IV iv = ((Constant<IV>) term).get(); if (!iv.equals(ivs[i])) { return false; } } } return true; } }); /* * Resolve ISPO -> IBindingSet (one to many). */ sitr.addFilter(new Expander() { private static final long serialVersionUID = 1L; @Override protected Iterator expand(final Object e) { toIVs((ISPO) e, ivs); final Striterator it = new Striterator( entry.getValue().iterator()); it.addFilter(new Resolver() { private static final long serialVersionUID = 1L; @Override protected Object resolve(final Object e) { final IBindingSet bs = ((IBindingSet) e).clone(); /* * Bind variables in the result. */ for (int i = 0; i < 5; i++) { final IVariableOrConstant term = asBound.get(i); if (term.isVar()) { final IVariable var = (IVariable<IV>) term; bs.set(var, new Constant<IV>(ivs[i])); } } return bs; } }); return it; } }); results.append(sitr); } return new ChunkedWrappedIterator<IBindingSet>(results); } @SuppressWarnings("rawtypes") private void toIVs(final ISPO spo, final IV[] ivs) { final ISPO sid = ((SidIV) spo.s()).getInlineValue(); ivs[0] = sid.s(); ivs[1] = sid.p(); ivs[2] = sid.o(); ivs[3] = spo.p(); ivs[4] = spo.o(); } } //TODO: See https://jira.blazegraph.com/browse/BLZG-1360 @Override public Set<IVariable<?>> getRequiredBound(ServiceNode serviceNode) { throw new RuntimeException("Method not yet implemented."); } //TODO: See https://jira.blazegraph.com/browse/BLZG-1360 @Override public Set<IVariable<?>> getDesiredBound(ServiceNode serviceNode) { throw new RuntimeException("Method not yet implemented."); } }