Repository: jena
Updated Branches:
  refs/heads/master 7caa93b3f -> f68a67072


JENA-1414: deleteFrom without using dst.size()


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/c0332b26
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/c0332b26
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/c0332b26

Branch: refs/heads/master
Commit: c0332b267d8f7ccc5baa02902e2b540b7e8273b4
Parents: c264cee
Author: Andy Seaborne <[email protected]>
Authored: Sat Nov 11 20:10:51 2017 +0000
Committer: Andy Seaborne <[email protected]>
Committed: Mon Nov 20 21:49:57 2017 +0000

----------------------------------------------------------------------
 .../java/org/apache/jena/graph/GraphUtil.java   | 225 ++++++++++++++-----
 .../org/apache/jena/graph/TestGraphUtil.java    |  59 +++++
 .../org/apache/jena/graph/test/TestPackage.java |   4 +
 3 files changed, 232 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/c0332b26/jena-core/src/main/java/org/apache/jena/graph/GraphUtil.java
----------------------------------------------------------------------
diff --git a/jena-core/src/main/java/org/apache/jena/graph/GraphUtil.java 
b/jena-core/src/main/java/org/apache/jena/graph/GraphUtil.java
index a7fdd45..c8b0220 100644
--- a/jena-core/src/main/java/org/apache/jena/graph/GraphUtil.java
+++ b/jena-core/src/main/java/org/apache/jena/graph/GraphUtil.java
@@ -23,6 +23,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.graph.impl.GraphWithPerform;
 import org.apache.jena.util.IteratorCollection;
 import org.apache.jena.util.iterator.ExtendedIterator;
@@ -98,7 +99,7 @@ public class GraphUtil
      * @return an iterator over all the graph's triples
      */
     public static ExtendedIterator<Triple> findAll(Graph g) {
-        return g.find(Triple.ANY) ;
+        return g.find() ;
     }
     
     public static void add(Graph graph, Triple[] triples) {
@@ -185,34 +186,69 @@ public class GraphUtil
             deleteIteratorWorker(graph, it);
     }
     
-    private static int MIN_SRC_SIZE   = 1000 ;
-    private static int DST_SRC_RATIO  = 2 ;
+    private static final int sliceSize = 1000 ;
+
+    /** A safe and cautious remove() function that converts the remove to
+     *  a number of {@link Graph#delete(Triple)} operations. 
+     *  <p>
+     *  To avoid any possible ConcurrentModificationExceptions,
+     *  it finds batches of triples, deletes them and tries again until
+     *  no more triples matching the input can be found. 
+     */
+    public static void remove(Graph g, Node s, Node p, Node o) {
+        // Beware of ConcurrentModificationExceptions.
+        // Delete in batches.
+        // That way, there is no active iterator when a delete
+        // from the indexes happens.
     
-    /** Delete triples in the destination (arg 1) as given in the source (arg 
2) */
-    public static void deleteFrom(Graph dstGraph, Graph srcGraph) {
-        boolean events = requireEvents(dstGraph);
-        
-        if ( dstGraph == srcGraph && ! events ) {
-            dstGraph.clear();
-            return;
+        Triple[] array = new Triple[sliceSize] ;
+    
+        while (true) {
+            // Convert/cache s,p,o?
+            // The Node Cache will catch these so don't worry unduely.
+            ExtendedIterator<Triple> iter = g.find(s, p, o) ;
+    
+            // Get a slice
+            int len = 0 ;
+            for ( ; len < sliceSize ; len++ ) {
+                if ( !iter.hasNext() )
+                    break ;
+                array[len] = iter.next() ;
+            }
+    
+            // Delete them.
+            for ( int i = 0 ; i < len ; i++ ) {
+                g.delete(array[i]) ;
+                array[i] = null ;
+            }
+            // Finished?
+            if ( len < sliceSize )
+                break ;
         }
-        
+    }
+
+    /**
+     * Delete triples in {@code srcGraph} from {@code dstGraph}
+     * by looping on {@code srcGraph}.
+     * 
+     */
+    public static void deleteLoopSrc(Graph dstGraph, Graph srcGraph) {
+        deleteIteratorWorker(dstGraph, findAll(srcGraph)) ;
+        dstGraph.getEventManager().notifyDeleteGraph(dstGraph, srcGraph) ;
+    }
+
+    /** 
+     * Delete the triple in {@code srcGraph} from {@code dstGraph}
+     * by checking the contents of {@code dsgGraph} against the {@code 
srcGraph}.
+     * This involves calling {@code srcGraph.contains}. 
+     * @implNote
+     * {@code dstGraph.size()} is used by this method. 
+     */
+    public static void deleteLoopDst(Graph dstGraph, Graph srcGraph) {
+        // Size the list to avoid reallocation on growth.
         int dstSize = dstGraph.size();
-        int srcSize = srcGraph.size();
-        
-        // Whether to loop on dstGraph or srcGraph.
-        // Loop on src if:
-        // * srcGraph is below the threshold MIN_SRC_SIZE (a "Just Do it" 
number)
-        // * dstGraph is "much" larger than src where "much" is given by 
DST_SRC_RATIO
-        boolean loopOnSrc = (srcSize < MIN_SRC_SIZE || dstSize > 
DST_SRC_RATIO*srcSize) ;
-        
-        if ( loopOnSrc ) {
-            deleteIteratorWorker(dstGraph, findAll(srcGraph)) ;
-            dstGraph.getEventManager().notifyDeleteGraph(dstGraph, srcGraph) ;
-            return;
-        }
-        // Loop on dstGraph, not srcGraph, but need to use srcGraph.contains 
on this code path.
         List<Triple> toBeDeleted = new ArrayList<>(dstSize);
+        
         Iterator<Triple> iter = findAll(dstGraph);
         for( ; iter.hasNext() ; ) {
            Triple t = iter.next();
@@ -246,43 +282,120 @@ public class GraphUtil
         }
     }
 
-    private static final int sliceSize = 1000 ;
-    /** A safe and cautious remove() function that converts the remove to
-     *  a number of {@link Graph#delete(Triple)} operations. 
+    private static int MIN_SRC_SIZE   = 1000 ;
+    // If source and destination are large, limit the search for the best way 
round to "deleteFrom" 
+    private static int MAX_SRC_SIZE   = 1000*1000 ;
+    private static int DST_SRC_RATIO  = 2 ;
+
+    /**
+     * Delete triples in the destination (arg 1) as given in the source (arg 
2).
+     *
+     * @implNote
+     *  This is designed for the case of {@code dstGraph} being comparable or 
much larger than
+     *  {@code srcGraph} or {@code srcGraph} having a lot of triples to 
actually be
+     *  deleted from {@code dstGraph}. This includes teh case of large, 
persistent {@code dstGraph}.
+     *  <p>  
+     *  It is not designed for a large {@code srcGraph} and large {@code 
dstGraph} 
+     *  with only a few triples in common to delete from {@code dstGraph}. It 
is better to
+     *  calculate the difference in some way, and copy into a small graph to 
use as the {@srcGraph}.  
      *  <p>
-     *  To avoid any possible ConcurrentModificationExceptions,
-     *  it finds batches of triples, deletes them and tries again until
-     *  no more triples matching the input can be found. 
+     *  To force delete by looping on {@code srcGraph}, use {@link 
#deleteLoopSrc(Graph, Graph)}.
+     *  <p>
+     *  For large {@code srcGraph} and small {@code dstGraph}, use {@link 
#deleteLoopDst}.
+     *  
+     * See discussion on <a 
href=""https://github.com/apache/jena/pull/212";>jena/pull/212</a>, 
+     * (archived at <a 
href="https://issues.apache.org/jira/browse/JENA-1284";>JENA-1284</a>).
      */
-    public static void remove(Graph g, Node s, Node p, Node o) {
-        // Beware of ConcurrentModificationExceptions.
-        // Delete in batches.
-        // That way, there is no active iterator when a delete
-        // from the indexes happens.
+    public static void deleteFrom(Graph dstGraph, Graph srcGraph) {
+        boolean events = requireEvents(dstGraph);
+        
+        if ( dstGraph == srcGraph && ! events ) {
+            dstGraph.clear();
+            return;
+        }
+        
+        boolean loopOnSrc = decideHowtoExecute(dstGraph, srcGraph);
+        
+        if ( loopOnSrc ) {
+            // Normal path.
+            deleteLoopSrc(dstGraph, srcGraph);
+            return;
+        }
 
-        Triple[] array = new Triple[sliceSize] ;
+        // Loop on dstGraph, not srcGraph, but need to use srcGraph.contains 
on this code path.
+        deleteLoopDst(dstGraph, srcGraph);
+    }
+    
+    private static final int CMP_GREATER = 1;
+    private static final int CMP_EQUAL   = 0;
+    private static final int CMP_LESS    = -1;
+    
+    /**
+     * Decide whether to loop on dstGraph or srcGraph.
+     * @param dstGraph
+     * @param srcGraph
+     * @return boolean true for "loop on src"
+     */
+    private static boolean decideHowtoExecute(Graph dstGraph, Graph srcGraph) {
+        //return decideHowtoExecuteBySizeSize(dstGraph, srcGraph);
 
-        while (true) {
-            // Convert/cache s,p,o?
-            // The Node Cache will catch these so don't worry unduely.
-            ExtendedIterator<Triple> iter = g.find(s, p, o) ;
+        // Avoid calling dstGraph.size()
+        return decideHowtoExecuteBySizeStep(dstGraph, srcGraph);
+    }
+    
+    /**
+     * Decide using dstGraph.size() and srcGraph.size()
+     */
+    private static boolean decideHowtoExecuteBySizeSize(Graph dstGraph, Graph 
srcGraph) {
+        // Loop on src if:
+        //     size(src) <= MIN_SRC_SIZE : srcGraph is below the threshold 
MIN_SRC_SIZE (a "Just Do it" number)
+        //     size(src)*DST_SRC_RATIO <= size(dst)
+        // dstGraph is "much" larger than src where "much" is given by 
DST_SRC_RATIO
+        //     Assumes dstGraph.size is efficient.
+        
+        int srcSize = srcGraph.size();
+        if ( srcSize <= MIN_SRC_SIZE )
+            return true ;
+        int dstSize = dstGraph.size();
+        
+        boolean loopOnSrc = (srcSize <= MIN_SRC_SIZE || dstSize > 
DST_SRC_RATIO*srcSize) ;
+        return loopOnSrc;
+    }
 
-            // Get a slice
-            int len = 0 ;
-            for ( ; len < sliceSize ; len++ ) {
-                if ( !iter.hasNext() )
-                    break ;
-                array[len] = iter.next() ;
-            }
+    /**
+     *  Avoid dstGraph.size(). Instead step through {@codedstGraph.find} to 
compare to {@code srcGraph.size()} 
+     *  
+     */
+    private static boolean decideHowtoExecuteBySizeStep(Graph dstGraph, Graph 
srcGraph) {
+        // loopOnSrc if:
+        //     size(src) <= MIN_SRC_SIZE
+        //     size(src)*DST_SRC_RATIO <= |find(dst)|
+        int srcSize = srcGraph.size();
+        if ( srcSize <= MIN_SRC_SIZE )
+            return true ;
+        boolean loopOnSrc = (srcSize <= MIN_SRC_SIZE || 
compareSizeTo(dstGraph, DST_SRC_RATIO*srcSize) == CMP_GREATER) ;
+        return loopOnSrc;
+    }
 
-            // Delete them.
-            for ( int i = 0 ; i < len ; i++ ) {
-                g.delete(array[i]) ;
-                array[i] = null ;
-            }
-            // Finished?
-            if ( len < sliceSize )
-                break ;
+    /** Compare the size of a graph to {@code size}, without calling Graph.size
+     *  by iterating on {@code graph.find()} as necessary.
+     *  <p>
+     *  Return -1 , 0, 1 for the comparison.  
+     */
+    /*package*/ static int compareSizeTo(Graph graph, int size) {
+        ExtendedIterator<Triple> it = graph.find();
+        try {
+            int stepsTake = Iter.step(it, size);
+            if ( stepsTake < size )
+                // Iterator ran out.
+                return CMP_LESS;
+            if ( !it.hasNext())
+                // Finsiehd at the same timne. 
+                return CMP_EQUAL;
+            // Still more to go
+            return CMP_GREATER;
+        } finally {
+            it.close();
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/c0332b26/jena-core/src/test/java/org/apache/jena/graph/TestGraphUtil.java
----------------------------------------------------------------------
diff --git a/jena-core/src/test/java/org/apache/jena/graph/TestGraphUtil.java 
b/jena-core/src/test/java/org/apache/jena/graph/TestGraphUtil.java
new file mode 100644
index 0000000..04ecaed
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/graph/TestGraphUtil.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.graph;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jena.graph.test.GraphTestBase;
+import org.junit.Test;
+
+// Test for the compare by src.size and step dst case.
+public class TestGraphUtil {
+    private static Graph graph0 = make(0);
+    private static Graph graph1 = make(1);
+    private static Graph graph2 = make(2);
+
+    private static Graph make(int N) {
+        Graph graph = Factory.createGraphMem();
+        for ( int i = 0 ; i < N ; i++ ) {
+            Triple t = GraphTestBase.triple("a P 'x"+i+"'");
+            graph.add(t);
+        }
+        return graph ;
+    }
+    
+    @Test public void compareSizeTo_1() {
+        assertEquals(1, GraphUtil.compareSizeTo(graph2, 0));
+        assertEquals(1, GraphUtil.compareSizeTo(graph2, 1));
+        assertEquals(0, GraphUtil.compareSizeTo(graph2, 2));
+        assertEquals(-1, GraphUtil.compareSizeTo(graph2, 3));
+        assertEquals(-1, GraphUtil.compareSizeTo(graph2, 4));
+    }
+    
+    @Test public void compareSizeTo_2() {
+        assertEquals(1, GraphUtil.compareSizeTo(graph1, 0));
+        assertEquals(0, GraphUtil.compareSizeTo(graph1, 1));
+        assertEquals(-1, GraphUtil.compareSizeTo(graph1, 2));
+    }
+
+    @Test public void compareSizeTo_3() {
+        assertEquals(0, GraphUtil.compareSizeTo(graph0, 0));
+        assertEquals(-1, GraphUtil.compareSizeTo(graph0, 1));
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c0332b26/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage.java 
b/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage.java
index 08aeda1..99cf925 100644
--- a/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage.java
+++ b/jena-core/src/test/java/org/apache/jena/graph/test/TestPackage.java
@@ -20,6 +20,7 @@ package org.apache.jena.graph.test;
 
 import junit.framework.JUnit4TestAdapter ;
 import junit.framework.TestSuite ;
+import org.apache.jena.graph.TestGraphUtil;
 
 /**
     Collected test suite for the .graph package.
@@ -55,5 +56,8 @@ public class TestPackage extends TestSuite {
         addTest( TestGraphMatchWithInference.suite());
         addTestSuite( TestGraphEvents.class );
         addTestSuite( TestGraphBaseToString.class );
+        
+        // Has to be in a different package.
+        addTest( new JUnit4TestAdapter(TestGraphUtil.class ) );
     }
 }

Reply via email to