This is an automated email from the ASF dual-hosted git repository.
arne-bdt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/main by this push:
new 5a53de40b6 GH-3969: Native stream() implementations for DatasetGraph
5a53de40b6 is described below
commit 5a53de40b60b3dfea7ab455b936253e710574ff5
Author: bern-SOPTIM <[email protected]>
AuthorDate: Wed Jun 3 00:23:44 2026 +0200
GH-3969: Native stream() implementations for DatasetGraph
Replace the find()-wrapping default of DatasetGraph.stream(g,s,p,o) with
first-class stream support across the hierarchy. DatasetGraphBaseFind gets a
stream() path mirroring the find() default-/named-/union-graph split, backed
by streamInDftGraph / streamInSpecificNamedGraph / streamInAnyNamedGraphs
primitives implemented per dataset. GraphView streams graph-over-dataset
through DatasetGraph#stream; adds G stream helpers. The interface method
stays
default, so implementors are not broken. Includes stream()==find() parity
tests and a forEachRemaining check in IteratorTxnTracker so stream bulk
operations stay inside their transaction.
---
.../jena/riot/writer/c14n/DatasetGraphOrdered.java | 11 +++
.../jena/sparql/core/DatasetGraphBaseFind.java | 47 ++++++++-
.../jena/sparql/core/DatasetGraphCollection.java | 20 ++++
.../jena/sparql/core/DatasetGraphFilteredView.java | 15 +++
.../apache/jena/sparql/core/DatasetGraphMap.java | 18 ++++
.../apache/jena/sparql/core/DatasetGraphNull.java | 16 ++++
.../apache/jena/sparql/core/DatasetGraphOne.java | 18 ++++
.../jena/sparql/core/DatasetGraphWrapper.java | 9 ++
.../org/apache/jena/sparql/core/GraphView.java | 19 ++++
.../jena/sparql/core/mem/DatasetGraphInMemory.java | 33 +++++++
.../jena/sparql/util/DyadicDatasetGraph.java | 15 +++
.../src/main/java/org/apache/jena/system/G.java | 19 ++++
.../system/buffering/BufferingDatasetGraph.java | 52 ++++++++++
.../jena/sparql/core/AbstractDatasetGraphFind.java | 105 +++++++++++++++++++++
.../core/AbstractTestGraphOverDatasetGraph.java | 27 ++++++
.../jena/sparql/core/DatasetGraphSimpleMem.java | 42 +++++++--
.../sparql/core/TestDatasetGraphFilteredView.java | 32 +++++++
.../jena/sparql/core/TestSpecialDatasets.java | 46 +++++++++
.../java/org/apache/jena/system/TestG_Quad.java | 15 +++
.../java/org/apache/jena/system/TestG_Triple.java | 9 ++
.../buffering/TestBufferingDatasetGraph.java | 66 ++++++++++++-
.../dboe/storage/system/DatasetGraphStorage.java | 19 +++-
.../dboe/transaction/txn/IteratorTxnTracker.java | 7 ++
.../apache/jena/tdb1/store/DatasetGraphTDB.java | 13 +++
.../jena/tdb2/store/GraphViewSwitchable.java | 7 ++
25 files changed, 667 insertions(+), 13 deletions(-)
diff --git
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
index 4051d5c7b2..465357eb31 100644
---
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
+++
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
@@ -23,6 +23,7 @@ package org.apache.jena.riot.writer.c14n;
import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.graph.Graph;
@@ -65,6 +66,16 @@ public class DatasetGraphOrdered extends DatasetGraphQuads {
return iterator;
}
+ @Override
+ public Stream<Quad> stream(Node g, Node s, Node p, Node o) {
+ return quads.stream().filter(quad -> matches(quad, g, s, p, o));
+ }
+
+ @Override
+ public Stream<Quad> stream() {
+ return quads.stream();
+ }
+
@Override
public void add(Quad quad) {
quads.add(quad);
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphBaseFind.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphBaseFind.java
index 10850be837..9692e8c689 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphBaseFind.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphBaseFind.java
@@ -82,12 +82,46 @@ abstract public class DatasetGraphBaseFind extends
DatasetGraphBase
return Iter.append(iter1, iter2);
}
+ @Override
+ public Stream<Quad> stream(Node g, Node s, Node p, Node o) {
+ if ( Quad.isDefaultGraph(g))
+ return streamInDftGraph(s, p, o) ;
+ if ( ! isWildcard(g) )
+ return streamNG(g, s, p, o) ;
+ return streamAny(s, p, o) ;
+ }
+
+ /** Stream equivalent of {@link #findNG(Node, Node, Node, Node)}. */
+ public Stream<Quad> streamNG(Node g, Node s, Node p , Node o) {
+ Stream<Quad> stream ;
+ if ( Quad.isUnionGraph(g))
+ stream = streamQuadsInUnionGraph(s, p, o) ;
+ else if ( isWildcard(g) )
+ stream = streamInAnyNamedGraphs(s, p, o) ;
+ else if ( Quad.isDefaultGraph(g) )
+ stream = streamInDftGraph(s, p, o) ;
+ else
+ // Not wildcard, not union graph, not default graph.
+ stream = streamInSpecificNamedGraph(g, s, p, o) ;
+ if ( stream == null )
+ return Stream.empty() ;
+ return stream ;
+ }
+
+ protected Stream<Quad> streamAny(Node s, Node p, Node o) {
+ return Stream.concat(streamInDftGraph(s, p, o),
streamInAnyNamedGraphs(s, p, o)) ;
+ }
+
/** Find matches in the default graph.
* Return as quads; the default graph is {@link Quad#defaultGraphIRI}
* To get Triples, use {@code DatasetGraph.getDefaultGraph().find(...)}.
*/
protected abstract Iterator<Quad> findInDftGraph(Node s, Node p , Node o) ;
+ /** Stream equivalent of {@link #findInDftGraph}.
+ */
+ protected abstract Stream<Quad> streamInDftGraph(Node s, Node p, Node o) ;
+
/** Find matches in the notional union of all named graphs - return as
triples.
* No duplicates - the union graph is a <em>set</em> of triples.
* See {@link #findInAnyNamedGraphs}, where there may be duplicates.
@@ -110,6 +144,11 @@ abstract public class DatasetGraphBaseFind extends
DatasetGraphBase
return findUnionGraphTriples(s,p,o).map(t ->
Quad.create(Quad.unionGraph, t)).iterator() ;
}
+ /** Stream equivalent of {@link #findQuadsInUnionGraph}. */
+ public Stream<Quad> streamQuadsInUnionGraph(Node s, Node p , Node o) {
+ return findUnionGraphTriples(s,p,o).map(t ->
Quad.create(Quad.unionGraph, t)) ;
+ }
+
/** Find matches in the notional union of all named graphs - return as
triples.
* No duplicates - the union graph is a <em>set</em> of triples.
* See {@link #findInAnyNamedGraphs}, where there may be duplicates.
@@ -118,15 +157,21 @@ abstract public class DatasetGraphBaseFind extends
DatasetGraphBase
* may be possible to avoid "distinct".
*/
private Stream<Triple> findUnionGraphTriples(Node s, Node p , Node o) {
- return
Iter.asStream(findInAnyNamedGraphs(s,p,o)).map(Quad::asTriple).distinct() ;
+ return streamInAnyNamedGraphs(s,p,o).map(Quad::asTriple).distinct() ;
}
/** Find in a specific named graph - {@code g} is a ground term (IRI or
bNode), not a wild card (or null). */
protected abstract Iterator<Quad> findInSpecificNamedGraph(Node g, Node s,
Node p , Node o) ;
+ /** Stream equivalent of {@link #findInSpecificNamedGraph}. */
+ protected abstract Stream<Quad> streamInSpecificNamedGraph(Node g, Node s,
Node p, Node o) ;
+
/** Find in any named graph - return quads.
* If a triple matches in two different graph, return a quad for each.
* See {@link #findInUnionGraph} for matching without duplicate triples.
*/
protected abstract Iterator<Quad> findInAnyNamedGraphs(Node s, Node p ,
Node o) ;
+
+ /** Stream equivalent of {@link #findInAnyNamedGraphs}. */
+ protected abstract Stream<Quad> streamInAnyNamedGraphs(Node s, Node p,
Node o) ;
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
index 56bd86ba87..ea6fc4edda 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
@@ -24,6 +24,7 @@ package org.apache.jena.sparql.core;
import java.util.Iterator ;
import java.util.List ;
import java.util.Objects ;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.iterator.IteratorConcat ;
@@ -63,6 +64,11 @@ public abstract class DatasetGraphCollection extends
DatasetGraphBaseFind
return G.triples2quadsDftGraph(getDefaultGraph().find(s, p, o)) ;
}
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return G.triples2quadsDftGraph(getDefaultGraph().stream(s, p, o)) ;
+ }
+
@Override
protected Iter<Quad> findInSpecificNamedGraph(Node g, Node s, Node p ,
Node o)
{
@@ -72,6 +78,14 @@ public abstract class DatasetGraphCollection extends
DatasetGraphBaseFind
return G.triples2quads(g, graph.find(s, p, o)) ;
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ Graph graph = fetchGraph(g) ;
+ if ( graph == null )
+ return Stream.empty() ;
+ return G.triples2quads(g, graph.stream(s, p, o)) ;
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o)
{
@@ -89,6 +103,12 @@ public abstract class DatasetGraphCollection extends
DatasetGraphBaseFind
return iter ;
}
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ return Iter.asStream(listGraphNodes())
+ .flatMap(gn -> streamInSpecificNamedGraph(gn, s, p, o));
+ }
+
@Override
public abstract Iterator<Node> listGraphNodes() ;
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
index 2fd97212e8..9756b80c2b 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
@@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.function.Predicate;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.atlas.logging.Log;
@@ -90,6 +91,12 @@ public class DatasetGraphFilteredView extends
DatasetGraphReadOnly implements Da
return iter;
return Iter.filter(iter, this::filter);
}
+
+ private Stream<Quad> filter(Stream<Quad> stream) {
+ if ( this.quadFilter == null )
+ return stream;
+ return stream.filter(this::filter);
+ }
// Need to intercept these because otherwise that are a GraphView of the
wrapped "dsg", not this one.
@@ -149,6 +156,14 @@ public class DatasetGraphFilteredView extends
DatasetGraphReadOnly implements Da
return filter(super.find(g, s, p, o)).hasNext();
}
+ @Override public Stream<Quad> stream(Node g, Node s, Node p, Node o) {
+ return filter(super.stream(g, s, p, o));
+ }
+
+ @Override public Stream<Quad> stream() {
+ return filter(super.stream());
+ }
+
@Override public boolean contains(Quad quad) {
return filter(super.find(quad)).hasNext();
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java
index 40f390dc1c..1e1320a994 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java
@@ -26,7 +26,9 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.stream.Stream;
+import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.atlas.iterator.IteratorConcat;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
@@ -157,12 +159,22 @@ public class DatasetGraphMap extends
DatasetGraphTriplesQuads
return G.triples2quadsDftGraph(iter) ;
}
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return G.triples2quadsDftGraph(getDefaultGraph().stream(s, p, o));
+ }
+
@Override
protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
Iterator<Triple> iter = getGraph(g).find(s, p, o);
return G.triples2quads(g, iter);
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ return G.triples2quads(g, getGraph(g).stream(s, p, o));
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
Iterator<Node> gnames = listGraphNodes();
@@ -178,6 +190,12 @@ public class DatasetGraphMap extends
DatasetGraphTriplesQuads
return iter;
}
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ return Iter.asStream(listGraphNodes())
+ .flatMap(gn -> streamInSpecificNamedGraph(gn, s, p, o));
+ }
+
@Override
public Graph getDefaultGraph() {
return defaultGraph;
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphNull.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphNull.java
index 55b97497f8..5d0bb4a446 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphNull.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphNull.java
@@ -22,6 +22,7 @@
package org.apache.jena.sparql.core;
import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.graph.Graph;
@@ -73,16 +74,31 @@ public abstract class DatasetGraphNull extends
DatasetGraphBaseFind {
return Iter.nullIterator();
}
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return Stream.empty();
+ }
+
@Override
protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
return Iter.nullIterator();
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ return Stream.empty();
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
return Iter.nullIterator();
}
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ return Stream.empty();
+ }
+
@Override
public Graph getDefaultGraph() {
return dftGraph;
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java
index 4d1cada1fc..a2b26472e1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java
@@ -22,6 +22,7 @@
package org.apache.jena.sparql.core;
import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.graph.Graph;
@@ -202,18 +203,35 @@ public class DatasetGraphOne extends DatasetGraphBaseFind
{
return G.triples2quadsDftGraph(graph.find(s, p, o));
}
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return G.triples2quadsDftGraph(graph.stream(s, p, o));
+ }
+
@Override
protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
// There are no named graphs
return Iter.nullIterator();
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ // There are no named graphs
+ return Stream.empty();
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
// There are no named graphs
return Iter.nullIterator();
}
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ // There are no named graphs
+ return Stream.empty();
+ }
+
protected static boolean isDefaultGraph(Quad quad) {
return isDefaultGraph(quad.getGraph());
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
index 2ceb5054e5..3482012b2a 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
@@ -22,6 +22,7 @@
package org.apache.jena.sparql.core;
import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.atlas.lib.Sync;
import org.apache.jena.graph.Graph;
@@ -211,6 +212,14 @@ public class DatasetGraphWrapper implements DatasetGraph,
Sync
public Iterator<Quad> findNG(Node g, Node s, Node p, Node o)
{ return getR().findNG(g, s, p, o); }
+ @Override
+ public Stream<Quad> stream(Node g, Node s, Node p, Node o)
+ { return getR().stream(g, s, p, o); }
+
+ @Override
+ public Stream<Quad> stream()
+ { return getR().stream(); }
+
@Override
public boolean contains(Quad quad)
{ return getR().contains(quad); }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/GraphView.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/GraphView.java
index 1819306082..a28d6ccb5a 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/GraphView.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/GraphView.java
@@ -22,6 +22,7 @@
package org.apache.jena.sparql.core;
import java.util.Iterator ;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.lib.Sync ;
@@ -125,6 +126,24 @@ public class GraphView extends GraphBase implements
NamedGraph, Sync
return WrappedIterator.createNoRemove(iter) ;
}
+ /**
+ * Stream equivalent of {@link #graphBaseFind} / {@link #graphUnionFind}:
route through DatasetGraph#stream
+ * so stream-native dataset (e.g. an in-memory one) stays on streams
rather than iterator-wrapping.
+ * @param s subject match
+ * @param p predicate match
+ * @param o object match
+ * @return stream of matching triples
+ */
+ @Override
+ public Stream<Triple> stream(Node s, Node p, Node o) {
+ Node g = graphNode(graphName) ;
+ Stream<Triple> stream = G.quads2triples(dsg.stream(g, s, p, o)) ;
+ if ( Quad.isUnionGraph(graphName) )
+ // Suppress duplicates after projecting to triples.
+ stream = stream.distinct() ;
+ return stream ;
+ }
+
private static Node graphNode(Node gn) {
return ( gn == null ) ? Quad.defaultGraphNodeGenerated : gn ;
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
b/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
index 8f490660bd..f36bc605b1 100644
---
a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
+++
b/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
@@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicLong ;
import java.util.concurrent.locks.ReentrantLock ;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import org.apache.jena.atlas.lib.InternalErrorException ;
import org.apache.jena.graph.Graph;
@@ -321,6 +322,11 @@ public class DatasetGraphInMemory extends
DatasetGraphTriplesQuads implements Tr
return quadsIndex().find(g, s, p, o).iterator();
}
+ private Stream<Quad> quadsStreamer(final Node g, final Node s, final Node
p, final Node o) {
+ if (isUnionGraph(g)) return streamInUnionGraph$(s, p, o);
+ return quadsIndex().find(g, s, p, o);
+ }
+
/**
* Union graph is the merge of named graphs.
*/
@@ -329,10 +335,22 @@ public class DatasetGraphInMemory extends
DatasetGraphTriplesQuads implements Tr
return access(() -> quadsIndex().findInUnionGraph(s, p, o).iterator());
}
+ /**
+ * Union graph is the merge of named graphs.
+ */
+ // Temp - Should this be replaced by DatasetGraphBaseFind code?
+ private Stream<Quad> streamInUnionGraph$(final Node s, final Node p, final
Node o) {
+ return access(() -> quadsIndex().findInUnionGraph(s, p, o));
+ }
+
private Iterator<Quad> triplesFinder(final Node s, final Node p, final
Node o) {
return G.triples2quadsDftGraph(defaultGraph().find(s, p,
o).iterator());
}
+ private Stream<Quad> triplesStreamer(final Node s, final Node p, final
Node o) {
+ return G.triples2quadsDftGraph(defaultGraph().find(s, p, o));
+ }
+
@Override
public Graph getGraph(final Node graphNode) {
return GraphView.createNamedGraph(this, graphNode);
@@ -436,13 +454,28 @@ public class DatasetGraphInMemory extends
DatasetGraphTriplesQuads implements Tr
return access(() -> triplesFinder(s, p, o));
}
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return access(() -> triplesStreamer(s, p, o));
+ }
+
@Override
protected Iterator<Quad> findInSpecificNamedGraph(final Node g, final Node
s, final Node p, final Node o) {
return access(() -> quadsFinder(g, s, p, o));
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ return access(() -> quadsStreamer(g, s, p, o));
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(final Node s, final Node p,
final Node o) {
return findInSpecificNamedGraph(ANY, s, p, o);
}
+
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ return streamInSpecificNamedGraph(ANY, s, p, o);
+ }
}
diff --git
a/jena-arq/src/main/java/org/apache/jena/sparql/util/DyadicDatasetGraph.java
b/jena-arq/src/main/java/org/apache/jena/sparql/util/DyadicDatasetGraph.java
index a8d2a11422..d05602e83a 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/DyadicDatasetGraph.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/DyadicDatasetGraph.java
@@ -30,6 +30,7 @@ import static
org.apache.jena.sparql.core.Quad.defaultGraphIRI;
import java.util.Iterator;
import java.util.Objects;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.atlas.lib.PairOfSameType;
@@ -207,6 +208,20 @@ public abstract class DyadicDatasetGraph extends
PairOfSameType<DatasetGraph> im
return G.triples2quads(g, getGraph(g).find(s, p, o));
}
+ @Override
+ public Stream<Quad> stream(Node g, Node s, Node p, Node o) {
+ if ( g.isConcrete() )
+ return streamInOneGraph(g, s, p, o);
+ return Stream.concat(
+ Iter.asStream(listGraphNodes()).flatMap(gn ->
streamInOneGraph(gn, s, p, o)),
+ streamInOneGraph(defaultGraphIRI, s, p, o)
+ );
+ }
+
+ protected Stream<Quad> streamInOneGraph(Node g, Node s, Node p, Node o) {
+ return G.triples2quads(g, getGraph(g).stream(s, p, o));
+ }
+
@Override
public Graph getUnionGraph() {
return new MultiUnion(map(listGraphNodes(), this::getGraph));
diff --git a/jena-arq/src/main/java/org/apache/jena/system/G.java
b/jena-arq/src/main/java/org/apache/jena/system/G.java
index e9c1cab044..538f41bd33 100644
--- a/jena-arq/src/main/java/org/apache/jena/system/G.java
+++ b/jena-arq/src/main/java/org/apache/jena/system/G.java
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.atlas.lib.Copyable;
@@ -792,6 +793,10 @@ public class G {
public static Iter<Triple> quads2triples(Iterator<Quad> iter)
{ return Iter.iter(iter).map(Quad::asTriple); }
+ /** Project quads to triples */
+ public static Stream<Triple> quads2triples(Stream<Quad> stream)
+ { return stream.map(Quad::asTriple); }
+
/** Project quad to graph name */
public static Iterator<Node> quad2graphName(Iterator<Quad> iter)
{ return Iter.map(iter, Quad::getGraph); }
@@ -897,6 +902,11 @@ public class G {
return Iter.iter(iter).map(t -> Quad.create(graphNode, t));
}
+ /** Convert a stream of triples into quads for the specified graph name. */
+ public static Stream<Quad> triples2quads(Node graphNode, Stream<Triple>
stream) {
+ return stream.map(t -> Quad.create(graphNode, t));
+ }
+
/**
* Convert an iterator of triples into quads for the default graph. This is
* {@link Quad#defaultGraphIRI}, not {@link
Quad#defaultGraphNodeGenerated}, which is
@@ -906,6 +916,15 @@ public class G {
return triples2quads(Quad.defaultGraphIRI, iter);
}
+ /**
+ * Convert a stream of triples into quads for the default graph. This is
+ * {@link Quad#defaultGraphIRI}, not {@link
Quad#defaultGraphNodeGenerated}, which is
+ * for quads outside a dataset, usually the output of parsers.
+ */
+ public static Stream<Quad> triples2quadsDftGraph(Stream<Triple> stream) {
+ return triples2quads(Quad.defaultGraphIRI, stream);
+ }
+
/**
* Execute a graph transaction if the graph supports transactions else
apply
* without a transaction wrapper.
diff --git
a/jena-arq/src/main/java/org/apache/jena/system/buffering/BufferingDatasetGraph.java
b/jena-arq/src/main/java/org/apache/jena/system/buffering/BufferingDatasetGraph.java
index abe42c46c2..16f6087f63 100644
---
a/jena-arq/src/main/java/org/apache/jena/system/buffering/BufferingDatasetGraph.java
+++
b/jena-arq/src/main/java/org/apache/jena/system/buffering/BufferingDatasetGraph.java
@@ -27,6 +27,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.graph.Graph;
@@ -321,24 +322,70 @@ public class BufferingDatasetGraph extends
DatasetGraphTriplesQuads implements D
return iter;
}
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ readOperation();
+ DatasetGraph base = get();
+ Stream<Quad> extra = streamInAddedTriples(s, p, o);
+ Stream<Quad> stream =
+ Stream.concat(
+ base.stream(Quad.defaultGraphIRI, s, p, o)
+ .filter(q->! deletedQuads.contains(q)),
+ extra);
+ if ( ! UNIQUE )
+ stream = stream.distinct();
+ return stream;
+ }
+
private Iterator<Quad> findInAddedTriples(Node s, Node p, Node o) {
return Iter.iter(addedTriples.iterator())
.filter(t->match(t,s,p,o))
.map(t->Quad.create(Quad.defaultGraphIRI,t));
}
+ private Stream<Quad> streamInAddedTriples(Node s, Node p, Node o) {
+ return addedTriples.stream()
+ .filter(t->match(t,s,p,o))
+ .map(t->Quad.create(Quad.defaultGraphIRI,t));
+ }
+
@Override
protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
readOperation();
return findQuads(g, s, p, o);
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ readOperation();
+ return streamQuads(g, s, p, o);
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
readOperation();
return findQuads(Node.ANY, s, p, o);
}
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ readOperation();
+ return streamQuads(Node.ANY, s, p, o);
+ }
+
+ private Stream<Quad> streamQuads(Node g, Node s, Node p, Node o) {
+ DatasetGraph base = get();
+ Stream<Quad> extra = streamInAddedQuads(g, s, p, o);
+ Stream<Quad> stream =
+ Stream.concat(
+ base.stream(g, s, p, o)
+ .filter(q->! deletedQuads.contains(q)),
+ extra);
+ if ( ! UNIQUE )
+ stream = stream.distinct();
+ return stream;
+ }
+
private Iterator<Quad> findQuads(Node g, Node s, Node p, Node o) {
DatasetGraph base = get();
Iterator<Quad> extra = findInAddedQuads(g, s, p, o);
@@ -356,6 +403,11 @@ public class BufferingDatasetGraph extends
DatasetGraphTriplesQuads implements D
.filter(t->match(t,g,s,p,o));
}
+ private Stream<Quad> streamInAddedQuads(Node g, Node s, Node p, Node o) {
+ return addedQuads.stream()
+ .filter(t-> match(t,g,s,p,o));
+ }
+
// Graphs: read/write operations will come back to the dataset.
@Override
public Graph getDefaultGraph() {
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractDatasetGraphFind.java
b/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractDatasetGraphFind.java
index 1fa65b54fd..f189bf26d5 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractDatasetGraphFind.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractDatasetGraphFind.java
@@ -286,6 +286,111 @@ public abstract class AbstractDatasetGraphFind {
assertTrue(x.contains(q5));
}
+ // stream(g,s,p,o) must route to the same primitives as find(g,s,p,o) and
yield the same
+ // results, regardless of whether the implementation is stream-native,
graph-backed, or
+ // iterator-native (bridging default). These mirror the find_* tests above.
+
+ @Test public void stream_specific_01() {
+ List<Quad> x = dsg.stream(g1, null, null, null).toList();
+ assertEquals(2, x.size());
+ assertTrue(x.contains(q4));
+ assertTrue(x.contains(q3));
+ }
+
+ @Test public void stream_specific_02() {
+ List<Quad> x = dsg.stream(g1, null, null, NodeConst.nodeOne).toList();
+ assertEquals(1, x.size());
+ assertTrue(x.contains(q4));
+ }
+
+ @Test public void stream_dft_01() {
+ List<Quad> x = dsg.stream(Quad.defaultGraphIRI, null, null,
null).toList();
+ assertEquals(2, x.size());
+ assertTrue(x.contains(q1));
+ assertTrue(x.contains(q2));
+ }
+
+ @Test public void stream_dft_02() {
+ List<Quad> x = dsg.stream(Quad.defaultGraphIRI, null, null,
NodeConst.nodeOne).toList();
+ assertEquals(0, x.size());
+ }
+
+ @Test public void stream_dft_03() {
+ List<Quad> x = dsg.stream(Quad.defaultGraphIRI, null, null,
NodeConst.nodeZero).toList();
+ assertEquals(1, x.size());
+ assertTrue(x.contains(q2));
+ }
+
+ @Test public void stream_union_01() {
+ List<Quad> x = dsg.stream(Quad.unionGraph, null, null, null).toList();
+ assertEquals(3, x.size());
+
assertTrue(x.stream().allMatch(q->q.getGraph().equals(Quad.unionGraph)));
+ List<Triple> z = x.stream().map(Quad::asTriple).toList();
+ assertTrue(z.contains(q4.asTriple()));
+ assertTrue(z.contains(q5.asTriple()));
+ assertTrue(x.contains(Quad.create(Quad.unionGraph, q4.asTriple())));
+ assertFalse(x.contains(Quad.create(Quad.unionGraph, q2.asTriple())));
+ }
+
+ /** stream(g,s,p,o) and find(g,s,p,o) must agree for every access pattern.
*/
+ @Test public void stream_matches_find() {
+ Node[][] patterns = {
+ { null, null, null, null }, //
findAny / streamAny
+ { Quad.defaultGraphIRI, null, null, null }, //
default graph
+ { g1, null, null, null }, //
specific named graph
+ { Quad.unionGraph, null, null, null }, // union
graph (distinct)
+ { null, s, p, o }, //
pattern across all
+ { null, null, null, NodeConst.nodeOne },
+ { Quad.unionGraph, null, null, o }, //
union graph, with object
+ };
+ for ( Node[] pat : patterns ) {
+ List<Quad> viaFind = toList(dsg.find(pat[0], pat[1], pat[2],
pat[3]));
+ List<Quad> viaStream = dsg.stream(pat[0], pat[1], pat[2],
pat[3]).toList();
+ assertEqualsUnordered(viaFind, viaStream);
+ }
+ }
+
+ // DatasetGraphBaseFind specific: the stream-first union helpers and
streamNG.
+
+ @Test public void stream_dsgFind_union_02() {
+ assumeTrue(dsg instanceof DatasetGraphBaseFind, ()->"Not a
DatasetGraphBaseFind");
+ DatasetGraphBaseFind dsgx = (DatasetGraphBaseFind)dsg;
+ List<Triple> x = dsgx.streamQuadsInUnionGraph(null, null,
null).map(Quad::asTriple).toList();
+ assertEquals(3, x.size());
+ assertTrue(x.contains(q4.asTriple()));
+ assertTrue(x.contains(q5.asTriple()));
+ assertTrue(x.contains(q10.asTriple()));
+ }
+
+ @Test public void stream_dsgFind_union_03() {
+ assumeTrue(dsg instanceof DatasetGraphBaseFind, ()->"Not a
DatasetGraphBaseFind");
+ DatasetGraphBaseFind dsgx = (DatasetGraphBaseFind)dsg;
+ List<Triple> x1 = dsgx.streamQuadsInUnionGraph(null, null,
null).map(Quad::asTriple).toList();
+ List<Triple> x2 = toList(dsgx.findInUnionGraph(null, null, null));
+ assertEqualsUnordered(x1, x2);
+ List<Quad> x3 = dsgx.streamQuadsInUnionGraph(null, null,
null).toList();
+ assertEquals(3, x3.size());
+
assertTrue(x3.stream().allMatch(q->q.getGraph().equals(Quad.unionGraph)));
+ }
+
+ @Test public void stream_dsgFind_union_05() {
+ assumeTrue(dsg instanceof DatasetGraphBaseFind, ()->"Not a
DatasetGraphBaseFind");
+ DatasetGraphBaseFind dsgx = (DatasetGraphBaseFind)dsg;
+ List<Triple> x1 = dsgx.streamQuadsInUnionGraph(null, null,
o).map(Quad::asTriple).toList();
+ List<Triple> x2 = quadsToDistinctTriples(dsg.find(Quad.unionGraph,
null, null, o));
+ assertEqualsUnordered(x1, x2);
+ assertEquals(1, x2.size());
+ }
+
+ @Test public void stream_dsgFind_ng() {
+ assumeTrue(dsg instanceof DatasetGraphBaseFind, ()->"Not a
DatasetGraphBaseFind");
+ DatasetGraphBaseFind dsgx = (DatasetGraphBaseFind)dsg;
+ assertEqualsUnordered(toList(dsgx.findNG(null, null, null, null)),
+ dsgx.streamNG(null, null, null, null).toList());
+ assertEqualsUnordered(toList(dsgx.findNG(null, s, p, o)),
+ dsgx.streamNG(null, s, p, o).toList());
+ }
+
public static <T> void assertEqualsUnordered(List<T> list1, List<T> list2)
{
if ( ! ListUtils.equalsUnordered(list1, list2) )
fail(msg(null, list1, list2));
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractTestGraphOverDatasetGraph.java
b/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractTestGraphOverDatasetGraph.java
index 1f35694ed1..c0f7f43bee 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractTestGraphOverDatasetGraph.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/core/AbstractTestGraphOverDatasetGraph.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Iterator;
+import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -112,6 +113,32 @@ public abstract class AbstractTestGraphOverDatasetGraph
assertEquals(2, g.size());
}
+ // ---- Graph#stream() over a dataset must match Graph#find()
(GraphView.stream override).
+
+ @Test
+ public void graphDSG_stream_dft() {
+ Graph g = makeDefaultGraph(baseDSG);
+ assertEquals(1, g.stream(null, null, null).count());
+ assertEquals(SSE.parseTriple("(<s> <p> 0)"),
g.stream().findFirst().orElse(null));
+ assertEquals(Iter.toSet(g.find(null, null, null)), g.stream(null,
null, null).collect(Collectors.toSet()));
+ }
+
+ @Test
+ public void graphDSG_stream_named() {
+ Graph g = makeNamedGraph(baseDSG, gn1);
+ assertEquals(1, g.stream(null, null, null).count());
+ assertEquals(Iter.toSet(g.find(null, null, null)), g.stream(null,
null, null).collect(Collectors.toSet()));
+ }
+
+ @Test
+ public void graphDSG_stream_union() {
+ Graph g = makeNamedGraph(baseDSG, Quad.unionGraph);
+ // The union view de-duplicates the triple shared by g2 and g3 -> 2
distinct triples.
+ assertEquals(2, g.stream(null, null, null).count());
+ assertEquals(Iter.toSet(g.find(null, null, null)), g.stream(null,
null, null).collect(Collectors.toSet()));
+ }
+
+
// ---- prefixes
@Test
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/core/DatasetGraphSimpleMem.java
b/jena-arq/src/test/java/org/apache/jena/sparql/core/DatasetGraphSimpleMem.java
index e82c0dcca0..838a3302c2 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/core/DatasetGraphSimpleMem.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/core/DatasetGraphSimpleMem.java
@@ -24,6 +24,7 @@ package org.apache.jena.sparql.core;
import static org.apache.jena.system.G.nullAsAny;
import java.util.*;
+import java.util.stream.Stream;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
@@ -90,33 +91,60 @@ public class DatasetGraphSimpleMem extends
DatasetGraphTriplesQuads implements T
return false;
}
- @Override
- public Iterator<Quad> findInDftGraph(Node s, Node p, Node o) {
+ private List<Quad> collectInDftGraph(Node s, Node p, Node o) {
List<Quad> results = new ArrayList<>();
for ( Triple t : triples )
if ( matches(t, s, p, o) )
// ?? Quad.defaultGraphNodeGenerated
// Quad.defaultGraphIRI
results.add(Quad.create(Quad.defaultGraphIRI, t));
- return results.iterator();
+ return results;
}
@Override
- public Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ public Iterator<Quad> findInDftGraph(Node s, Node p, Node o) {
+ return collectInDftGraph(s, p, o).iterator();
+ }
+
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return collectInDftGraph(s, p, o).stream();
+ }
+
+ private List<Quad> collectInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
List<Quad> results = new ArrayList<>();
for ( Quad q : quads )
if ( matches(q, g, s, p, o) )
results.add(q);
- return results.iterator();
+ return results;
}
@Override
- public Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
+ public Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ return collectInSpecificNamedGraph(g, s, p, o).iterator();
+ }
+
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ return collectInSpecificNamedGraph(g, s, p, o).stream();
+ }
+
+ private List<Quad> collectInAnyNamedGraphs(Node s, Node p, Node o) {
List<Quad> results = new ArrayList<>();
for ( Quad q : quads )
if ( matches(q, Node.ANY, s, p, o) )
results.add(q);
- return results.iterator();
+ return results;
+ }
+
+ @Override
+ public Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
+ return collectInAnyNamedGraphs(s, p, o).iterator();
+ }
+
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ return collectInAnyNamedGraphs(s, p, o).stream();
}
private boolean matches(Triple t, Node s, Node p, Node o) {
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
index 001323a48a..6f4f980f56 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
@@ -22,12 +22,14 @@
package org.apache.jena.sparql.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
@@ -178,6 +180,36 @@ public class TestDatasetGraphFilteredView {
});
}
+ // stream() must apply the same filter as find().
DatasetGraphWrapper.stream(...) forwards to
+ // the wrapped dataset, so these guard against the filter being bypassed
for stream access.
+
+ @Test public void filtered_stream_2() {
+ Predicate<Quad> filter = x->x.getGraph().equals(g2);
+ Txn.executeRead(basedsg(),()->{
+ DatasetGraph dsg = new DatasetGraphFilteredView(basedsg(),filter,
Collections.singleton(g1));
+ assertEquals(2, dsg.stream(null, null, null, null).count());
+ assertEquals(2, dsg.stream(g2, null, null, null).count());
+ assertEquals(2, dsg.stream(null, s2, null, null).count());
+ assertEquals(0, dsg.stream(g1, null, null, null).count());
+ // No leakage: stream() returns exactly the filtered find() result.
+ assertEquals(Iter.toSet(dsg.find()),
dsg.stream().collect(Collectors.toSet()));
+ });
+ }
+
+ @Test public void filtered_stream_matches_find() {
+ Predicate<Quad> filter = x-> x.getSubject().equals(s2) ||
x.getSubject().equals(s1);
+ Txn.executeRead(basedsg(),()->{
+ DatasetGraph dsg = new DatasetGraphFilteredView(basedsg(),filter,
Arrays.asList(g1, g2));
+ // Sanity: the unfiltered base really does contain more quads than
the filtered view.
+ assertEquals(3, dsg.stream().count());
+ assertTrue(Iter.count(basedsg().find()) > 3);
+ // stream() and find() agree under filtering, for the whole
dataset and per-pattern.
+ assertEquals(Iter.toSet(dsg.find()),
dsg.stream().collect(Collectors.toSet()));
+ assertEquals(Iter.toSet(dsg.find(g2, null, null, null)),
+ dsg.stream(g2, null, null,
null).collect(Collectors.toSet()));
+ });
+ }
+
private void assertSame(DatasetGraph dsg1, DatasetGraph dsg2) {
Set<Quad> quads1 = Iter.toSet(dsg1.find());
Set<Quad> quads2 = Iter.toSet(dsg2.find());
diff --git
a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestSpecialDatasets.java
b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestSpecialDatasets.java
index 0ed2d09cae..dbc4aa359b 100644
---
a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestSpecialDatasets.java
+++
b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestSpecialDatasets.java
@@ -23,6 +23,7 @@ package org.apache.jena.sparql.core;
import static org.junit.jupiter.api.Assertions.*;
+import org.apache.jena.atlas.iterator.Iter;
import org.junit.jupiter.api.Test;
import org.apache.jena.graph.Graph;
@@ -38,6 +39,8 @@ import org.apache.jena.sparql.graph.GraphZero;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.system.Txn;
+import java.util.stream.Collectors;
+
/** Tests for
* {@link DatasetGraphZero},
* {@link DatasetGraphSink} and
@@ -286,4 +289,47 @@ public class TestSpecialDatasets {
TransactionHandler h = g.getTransactionHandler();
assertThrows(JenaException.class,()-> h.commit() );
}
+
+ // -- one : DatasetGraphOne wraps a single graph as the default graph.
+ // Exercises the direct stream(g,s,p,o) override.
+
+ private static Node o2 = SSE.parseNode(":o2");
+
+ private static DatasetGraph oneWithData() {
+ Graph g = GraphFactory.createGraphMem();
+ g.add(triple); // (:s :p :o)
+ g.add(SSE.parseTriple("(:s :p :o2)"));
+ return DatasetGraphFactory.wrap(g);
+ }
+
+ @Test public void one_stream_all() {
+ DatasetGraph dsg = oneWithData();
+ assertEquals(2, dsg.stream().count());
+ assertEquals(Iter.toSet(dsg.find()),
dsg.stream().collect(Collectors.toSet()));
+ }
+
+ @Test public void one_stream_dft() {
+ DatasetGraph dsg = oneWithData();
+ assertEquals(2, dsg.stream(Quad.defaultGraphIRI, null, null,
null).count());
+ assertEquals(1, dsg.stream(Quad.defaultGraphIRI, null, null,
o2).count());
+ }
+
+ @Test public void one_stream_named_empty() {
+ DatasetGraph dsg = oneWithData();
+ // DatasetGraphOne has no named graphs: a specific named graph streams
nothing.
+ assertEquals(0, dsg.stream(gn, null, null, null).count());
+ }
+
+ @Test public void one_stream_matches_find() {
+ DatasetGraph dsg = oneWithData();
+ Node[][] patterns = {
+ { null, null, null, null },
+ { Quad.defaultGraphIRI, null, null, null },
+ { gn, null, null, null },
+ { null, null, null, o2 },
+ };
+ for ( Node[] p : patterns )
+ assertEquals(Iter.toSet(dsg.find(p[0], p[1], p[2], p[3])),
+ dsg.stream(p[0], p[1], p[2],
p[3]).collect(Collectors.toSet()));
+ }
}
diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java
b/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java
index f68a1935a8..64f222879d 100644
--- a/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java
+++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java
@@ -34,6 +34,8 @@ import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.sys.JenaSystem;
+import java.util.stream.Stream;
+
public class TestG_Quad {
static { JenaSystem.init(); }
@@ -69,4 +71,17 @@ public class TestG_Quad {
String setup = "PREFIX : <http://example/>\n";
return RDFParser.fromString(setup+trigBody,
Lang.TRIG).toDatasetGraph();
}
+
+ @Test
+ public void triples2quads() {
+ var quad = SSE.parseQuad("(:g :s :p :o)");
+ var q = G.triples2quads(quad.getGraph(),
Stream.of(quad.asTriple())).findFirst().orElseThrow();
+ assertEquals(quad, q);
+ }
+
+ @Test void triples2quadsDftGraph() {
+ var triple = SSE.parseTriple("(:s :p :o)");
+ var quad =
G.triples2quadsDftGraph(Stream.of(triple)).findFirst().orElseThrow();
+ assertEquals(Quad.create(Quad.defaultGraphIRI, triple), quad);
+ }
}
diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java
b/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java
index f3a44b1114..89ef811fb4 100644
--- a/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java
+++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java
@@ -35,6 +35,8 @@ import org.apache.jena.riot.RDFParser;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.sys.JenaSystem;
+import java.util.stream.Stream;
+
public class TestG_Triple {
static { JenaSystem.init(); }
@@ -100,4 +102,11 @@ public class TestG_Triple {
String setup = "PREFIX : <http://example/>\n";
return RDFParser.fromString(setup+ttlBody,
Lang.TURTLE).toGraph();
}
+
+ @Test
+ public void quads2triples() {
+ var quad = SSE.parseQuad("(:g :s :p :o)");
+ var triple =
G.quads2triples(Stream.of(quad)).findFirst().orElseThrow();
+ assertEquals(SSE.parseTriple("(:s :p :o)"), triple);
+ }
}
diff --git
a/jena-arq/src/test/java/org/apache/jena/system/buffering/TestBufferingDatasetGraph.java
b/jena-arq/src/test/java/org/apache/jena/system/buffering/TestBufferingDatasetGraph.java
index 407822290a..97a7ce5577 100644
---
a/jena-arq/src/test/java/org/apache/jena/system/buffering/TestBufferingDatasetGraph.java
+++
b/jena-arq/src/test/java/org/apache/jena/system/buffering/TestBufferingDatasetGraph.java
@@ -21,14 +21,15 @@
package org.apache.jena.system.buffering;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
+import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.graph.Node;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedClass;
import org.junit.jupiter.params.provider.Arguments;
@@ -41,6 +42,8 @@ import org.apache.jena.sparql.core.DatasetGraphFactory;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.sse.SSE;
+import static org.junit.jupiter.api.Assertions.*;
+
@ParameterizedClass
@MethodSource("provideArgs")
@@ -190,4 +193,59 @@ public class TestBufferingDatasetGraph {
buffered.reset();
assertTrue(buffered.isEmpty());
}
+
+ // ---- stream() must agree with find(), including for named graphs.
+
+ @Test public void stream_buffered_named_graph() {
+ Quad q = SSE.parseQuad("(:g :s :p :o)"); // a buffered, un-flushed
named-graph quad
+ buffered.execute(() -> {
+ buffered.add(q);
+ Node g = q.getGraph();
+ assertEquals(1L, buffered.stream(g, Node.ANY, Node.ANY,
Node.ANY).count()); // specific named graph
+ assertEquals(1L, buffered.stream().count());
// all quads
+ assertEquals(1L, buffered.stream(Quad.unionGraph, Node.ANY,
Node.ANY, Node.ANY).count()); // union graph
+ // find(unionGraph, ...) shares the same primitive: guard against
the iterator regression.
+ assertEquals(1L, Iter.count(buffered.find(Quad.unionGraph,
Node.ANY, Node.ANY, Node.ANY)));
+ // Whole dataset: stream() returns exactly what find() returns.
+ assertEquals(Iter.toSet(buffered.find()),
buffered.stream().collect(Collectors.toSet()));
+ });
+ }
+
+ @Test public void stream_matches_find_overlay() {
+ Node g1 = SSE.parseNode(":g1");
+ Node g2 = SSE.parseNode(":g2");
+ Node s = SSE.parseNode(":s");
+ Quad baseG1 = SSE.parseQuad("(:g1 :s :p 1)");
+ Quad baseG2 = SSE.parseQuad("(:g2 :s :p 2)");
// deleted in the buffer
+ Quad baseDft = Quad.create(Quad.defaultGraphIRI, SSE.parseTriple("(:s
:p 3)"));
+ Quad addG1 = SSE.parseQuad("(:g1 :s :p 4)");
// buffered add (named)
+ Quad addDft = Quad.create(Quad.defaultGraphIRI, SSE.parseTriple("(:s
:p 5)")); // buffered add (default)
+
+ buffered.executeWrite(() -> {
+ base.add(baseG1); base.add(baseG2); base.add(baseDft);
+ buffered.add(addG1); buffered.add(addDft); buffered.delete(baseG2);
+
+ // The buffered overlay (base + added - deleted) must be identical
via stream() and find().
+ Node[][] patterns = {
+ { Node.ANY, Node.ANY, Node.ANY, Node.ANY }, //
everything
+ { Quad.defaultGraphIRI, Node.ANY, Node.ANY, Node.ANY }, //
default graph
+ { g1, Node.ANY, Node.ANY, Node.ANY }, //
specific named graph (base + added)
+ { g2, Node.ANY, Node.ANY, Node.ANY }, //
named graph emptied by a buffered delete
+ { Quad.unionGraph, Node.ANY, Node.ANY, Node.ANY }, //
union of named graphs (distinct triples)
+ { Node.ANY, s, Node.ANY, Node.ANY }, //
pattern spanning graphs
+ };
+ for ( Node[] pat : patterns ) {
+ Set<Quad> viaFind = Iter.toSet(buffered.find(pat[0], pat[1],
pat[2], pat[3]));
+ Set<Quad> viaStream = buffered.stream(pat[0], pat[1], pat[2],
pat[3]).collect(Collectors.toSet());
+ assertEquals(viaFind, viaStream, () -> "stream() != find() for
" + Arrays.toString(pat));
+ }
+
+ // Concrete overlay expectations. (Union parity is covered by the
loop above; its exact
+ // contents are left to find()/stream() agreement, since
BufferingDatasetGraph's
+ // "any named graph" view also surfaces the default graph -
pre-existing, out of scope here.)
+ assertEquals(2L, buffered.stream(g1, Node.ANY, Node.ANY,
Node.ANY).count()); // base + buffered add
+ assertEquals(0L, buffered.stream(g2, Node.ANY, Node.ANY,
Node.ANY).count()); // fully deleted
+ assertEquals(2L, buffered.stream(Quad.defaultGraphIRI, Node.ANY,
Node.ANY, Node.ANY).count()); // base + buffered add
+ });
+ }
}
diff --git
a/jena-db/jena-dboe-storage/src/main/java/org/apache/jena/dboe/storage/system/DatasetGraphStorage.java
b/jena-db/jena-dboe-storage/src/main/java/org/apache/jena/dboe/storage/system/DatasetGraphStorage.java
index bdacc7b08a..ecb72e20b1 100644
---
a/jena-db/jena-dboe-storage/src/main/java/org/apache/jena/dboe/storage/system/DatasetGraphStorage.java
+++
b/jena-db/jena-dboe-storage/src/main/java/org/apache/jena/dboe/storage/system/DatasetGraphStorage.java
@@ -22,6 +22,7 @@
package org.apache.jena.dboe.storage.system;
import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.dboe.storage.DatabaseRDF;
@@ -45,6 +46,7 @@ import org.apache.jena.sparql.core.DatasetGraphBaseFind;
import org.apache.jena.sparql.core.DatasetGraphTriplesQuads;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.core.Transactional;
+import org.apache.jena.system.G;
/** Alternative: DatasetGraph over RDFStorage, using DatasetGraphBaseFind
* Collapses DatasetGraphTriplesQuads into this adapter class.
@@ -131,7 +133,12 @@ public class DatasetGraphStorage extends
DatasetGraphBaseFind implements Databas
@Override
protected Iterator<Quad> findInDftGraph(Node s, Node p, Node o) {
- return Iter.map(findStorage(s, p, o), t ->
Quad.create(Quad.defaultGraphIRI, t));
+ return G.triples2quadsDftGraph(findStorage(s, p, o));
+ }
+
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o) {
+ return Iter.asStream(findInDftGraph(s, p, o));
}
@Override
@@ -139,12 +146,22 @@ public class DatasetGraphStorage extends
DatasetGraphBaseFind implements Databas
return findStorage(g, s, p, o);
}
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o) {
+ return Iter.asStream(findInSpecificNamedGraph(g, s, p, o));
+ }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
// Implementations may wish to do better.
return findStorage(Node.ANY, s, p, o);
}
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o) {
+ return Iter.asStream(findInAnyNamedGraphs(s, p, o));
+ }
+
@Override
public Graph getDefaultGraph() {
return GraphViewStorage.createDefaultGraphStorage(this, prefixes);
diff --git
a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/IteratorTxnTracker.java
b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/IteratorTxnTracker.java
index d04b446275..f8db201112 100644
---
a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/IteratorTxnTracker.java
+++
b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/IteratorTxnTracker.java
@@ -23,6 +23,7 @@ package org.apache.jena.dboe.transaction.txn;
import java.util.Iterator;
import java.util.Objects;
+import java.util.function.Consumer;
import org.apache.jena.atlas.iterator.IteratorWrapper;
@@ -43,6 +44,12 @@ public class IteratorTxnTracker<T> extends
IteratorWrapper<T> {
@Override public void remove() { check() ; super.remove() ; }
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ check() ;
+ super.forEachRemaining(action) ;
+ }
+
private void check() {
Transaction txn = txnSystem.getThreadTransaction();
if ( txn == null )
diff --git
a/jena-tdb1/src/main/java/org/apache/jena/tdb1/store/DatasetGraphTDB.java
b/jena-tdb1/src/main/java/org/apache/jena/tdb1/store/DatasetGraphTDB.java
index 3bd4af27f0..ffe0ce51a2 100644
--- a/jena-tdb1/src/main/java/org/apache/jena/tdb1/store/DatasetGraphTDB.java
+++ b/jena-tdb1/src/main/java/org/apache/jena/tdb1/store/DatasetGraphTDB.java
@@ -23,6 +23,7 @@ package org.apache.jena.tdb1.store;
import java.util.Iterator ;
+import java.util.stream.Stream;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.lib.Closeable ;
@@ -83,14 +84,26 @@ public class DatasetGraphTDB extends
DatasetGraphTriplesQuads
protected Iterator<Quad> findInDftGraph(Node s, Node p, Node o)
{ return G.triples2quadsDftGraph(getTripleTable().find(s, p, o)) ; }
+ @Override
+ protected Stream<Quad> streamInDftGraph(Node s, Node p, Node o)
+ { return Iter.asStream(findInDftGraph(s, p, o)) ; }
+
@Override
protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p,
Node o)
{ return getQuadTable().find(g, s, p, o) ; }
+ @Override
+ protected Stream<Quad> streamInSpecificNamedGraph(Node g, Node s, Node p,
Node o)
+ { return Iter.asStream(findInSpecificNamedGraph(g, s, p, o)) ; }
+
@Override
protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o)
{ return getQuadTable().find(Node.ANY, s, p, o) ; }
+ @Override
+ protected Stream<Quad> streamInAnyNamedGraphs(Node s, Node p, Node o)
+ { return Iter.asStream(findInAnyNamedGraphs(s, p, o)) ; }
+
@Override
protected void addToDftGraph(Node s, Node p, Node o)
{ getTripleTable().add(s,p,o) ; }
diff --git
a/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
b/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
index 22f04c2f17..a6daa694f2 100644
---
a/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
+++
b/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
@@ -28,6 +28,8 @@ import org.apache.jena.sparql.core.GraphView;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.util.iterator.ExtendedIterator;
+import java.util.stream.Stream;
+
/**
* A GraphView that is sensitive to {@link DatasetGraphSwitchable} switching.
* This ensures that a graph object remains valid as the {@link
DatasetGraphSwitchable} switches.
@@ -93,6 +95,11 @@ public class GraphViewSwitchable extends GraphView {
return getBaseGraph().find(s, p, o);
}
+ @Override
+ public Stream<Triple> stream(Node s, Node p, Node o) {
+ return getBaseGraph().stream(s, p, o);
+ }
+
private DatasetGraphTDB getDSG() {
return ((DatasetGraphTDB)(getx().get()));
}