You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by al...@apache.org on 2011/12/01 14:05:40 UTC

svn commit: r1209063 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/query/lucene/ test/java/org/apache/jackrabbit/core/query/ test/java/org/apache/jackrabbit/core/query/lucene/

Author: alexparvulescu
Date: Thu Dec  1 13:05:38 2011
New Revision: 1209063

URL: http://svn.apache.org/viewvc?rev=1209063&view=rev
Log:
JCR-2906 Multivalued property sorted by last/random value

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/ComparableArrayTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java?rev=1209063&r1=1209062&r2=1209063&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedFieldCache.java Thu Dec  1 13:05:38 2011
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.core.query.lucene;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -112,6 +113,78 @@ public class SharedFieldCache {
         }
     }
 
+    static class ComparableArray implements Comparable<ComparableArray> {
+
+        private int offset = 0;
+
+        private Comparable<?>[] c = new Comparable[0];
+
+        public ComparableArray(Comparable<?> item, int index) {
+            insert(item, index);
+        }
+
+        public int compareTo(ComparableArray o) {
+            return Util.compare(c, o.c);
+        }
+
+        /**
+         * testing purpose only.
+         * 
+         * @return the offset
+         */
+        int getOffset() {
+            return offset;
+        }
+
+        public ComparableArray insert(Comparable<?> item, int index) {
+            // optimize for most common scenario
+            if (c.length == 0) {
+                offset = index;
+                c = new Comparable<?>[] { item };
+                return this;
+            }
+
+            // inside
+            if (index >= offset && index < offset + c.length) {
+                c[index - offset] = item;
+                return this;
+            }
+
+            // before
+            if (index < offset) {
+                int relativeOffset = offset - index;
+                Comparable<?>[] newC = new Comparable[relativeOffset + c.length];
+                newC[0] = item;
+                System.arraycopy(c, 0, newC, relativeOffset, c.length);
+                c = newC;
+                offset = index;
+                return this;
+            }
+
+            // after
+            if (index >= offset + c.length) {
+                c = Arrays.copyOf(c, index - offset + 1);
+                c[index - offset] = item;
+                return this;
+            }
+            return this;
+        }
+
+        /*
+         * This is needed by {@link UpperCaseSortComparator} and {@link LowerCaseSortComparator}
+         */
+        @Override
+        public String toString() {
+            if (c == null) {
+                return null;
+            }
+            if (c.length == 1) {
+                return c[0].toString();
+            }
+            return Arrays.toString(c);
+        }
+    }
+
     /**
      * Reference to the single instance of <code>SharedFieldCache</code>.
      */
@@ -152,9 +225,10 @@ public class SharedFieldCache {
         field = field.intern();
         ValueIndex ret = lookup(reader, field, prefix);
         if (ret == null) {
-            Comparable<?>[] retArray = new Comparable[reader.maxDoc()];
+            final int maxDocs = reader.maxDoc();
+            ComparableArray[] retArray = new ComparableArray[maxDocs];
             int setValues = 0;
-            if (retArray.length > 0) {
+            if (maxDocs > 0) {
                 IndexFormatVersion version = IndexFormatVersion.getVersion(reader);
                 boolean hasPayloads = version.isAtLeast(IndexFormatVersion.V3);
                 TermDocs termDocs;
@@ -167,8 +241,6 @@ public class SharedFieldCache {
                     termDocs = reader.termDocs();
                 }
                 TermEnum termEnum = reader.terms(new Term(field, prefix));
-
-                char[] tmp = new char[16];
                 try {
                     if (termEnum.term() == null) {
                         throw new RuntimeException("no terms in field " + field);
@@ -178,30 +250,28 @@ public class SharedFieldCache {
                         if (term.field() != field || !term.text().startsWith(prefix)) {
                             break;
                         }
-
-                        // make sure term is compacted
-                        String text = term.text();
-                        int len = text.length() - prefix.length();
-                        if (tmp.length < len) {
-                            // grow tmp
-                            tmp = new char[len];
-                        }
-                        text.getChars(prefix.length(), text.length(), tmp, 0);
-                        String value = new String(tmp, 0, len);
-
-                        termDocs.seek(termEnum);
+                        final String value = termValueAsString(term, prefix);
+                        termDocs.seek(term);
                         while (termDocs.next()) {
+                            int termPosition = 0;
                             type = PropertyType.UNDEFINED;
                             if (hasPayloads) {
                                 TermPositions termPos = (TermPositions) termDocs;
-                                termPos.nextPosition();
+                                termPosition = termPos.nextPosition();
                                 if (termPos.isPayloadAvailable()) {
                                     payload = termPos.getPayload(payload, 0);
                                     type = PropertyMetaData.fromByteArray(payload).getPropertyType();
                                 }
                             }
                             setValues++;
-                            retArray[termDocs.doc()] = getValue(value, type);
+                            Comparable<?> v = getValue(value, type);
+                            int doc = termDocs.doc();
+                            ComparableArray ca = retArray[doc];
+                            if (ca == null) {
+                                retArray[doc] = new ComparableArray(v, termPosition);
+                            } else {
+                                retArray[doc] = ca.insert(v, termPosition);
+                            }
                         }
                     } while (termEnum.next());
                 } finally {
@@ -217,6 +287,22 @@ public class SharedFieldCache {
     }
 
     /**
+     * Extracts the value from a given Term as a String
+     * 
+     * @param term
+     * @param prefix
+     * @return string value contained in the term
+     */
+    private static String termValueAsString(Term term, String prefix) {
+        // make sure term is compacted
+        String text = term.text();
+        int length = text.length() - prefix.length();
+        char[] tmp = new char[length];
+        text.getChars(prefix.length(), text.length(), tmp, 0);
+        return new String(tmp, 0, length);
+    }
+
+    /**
      * See if a <code>ValueIndex</code> object is in the cache.
      */
     ValueIndex lookup(IndexReader reader, String field, String prefix) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java?rev=1209063&r1=1209062&r2=1209063&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java Thu Dec  1 13:05:38 2011
@@ -236,6 +236,25 @@ public class Util {
     }
 
     /**
+     * Compares two arrays of comparables.
+     */
+    public static int compare(Comparable<?>[] c1, Comparable<?>[] c2) {
+        if (c1 == null) {
+            return -1;
+        }
+        if (c2 == null) {
+            return 1;
+        }
+        for (int i = 0; i < c1.length && i < c2.length; i++) {
+            int d = compare(c1[i], c2[i]);
+            if (d != 0) {
+                return d;
+            }
+        }
+        return c1.length - c2.length;
+    }
+
+    /**
      * Compares the two values. If the values have differing types, then an
      * attempt is made to convert the second value into the type of the first
      * value.

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java?rev=1209063&r1=1209062&r2=1209063&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/OrderByTest.java Thu Dec  1 13:05:38 2011
@@ -60,6 +60,38 @@ public class OrderByTest extends Abstrac
         checkResult(result, 3);
     }
 
+    /**
+     * Test for JCR-2906
+     */
+    public void testOrderByMVP() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+        Node n4 = testRootNode.addNode("node4");
+        Node n5 = testRootNode.addNode("node5");
+
+        n1.setProperty("extra", new String[] { "12345" });
+        n1.setProperty("text", new String[] { "ccc" });
+
+        n2.setProperty("text", new String[] { "eee", "bbb" });
+        n3.setProperty("text", new String[] { "aaa" });
+        n4.setProperty("text", new String[] { "bbb", "aaa" });
+        n5.setProperty("text", new String[] { "eee", "aaa" });
+
+        testRootNode.getSession().save();
+
+        String sql = "SELECT value FROM nt:unstructured WHERE "
+                + "jcr:path LIKE '" + testRoot + "/%' ORDER BY text";
+        checkResultSequence(executeQuery(sql).getRows(), new Node[] { n3, n4,
+                n1, n5, n2 });
+
+        String xpath = "/"
+                + testRoot
+                + "/*[@jcr:primaryType='nt:unstructured'] order by jcr:score(), @text";
+        checkResultSequence(executeQuery(xpath).getRows(), new Node[] { n3, n4,
+                n1, n5, n2 });
+    }
+
     public void testOrderByUpperCase() throws RepositoryException {
         Node n1 = testRootNode.addNode("node1");
         Node n2 = testRootNode.addNode("node2");

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java?rev=1209063&r1=1209062&r2=1209063&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/SQL2OrderByTest.java Thu Dec  1 13:05:38 2011
@@ -73,6 +73,30 @@ public class SQL2OrderByTest extends Abs
 
     }
 
+    /**
+     * SQL2 Test for JCR-2906
+     */
+    public void testOrderByMVP() throws RepositoryException {
+        Node n1 = testRootNode.addNode("node1");
+        Node n2 = testRootNode.addNode("node2");
+        Node n3 = testRootNode.addNode("node3");
+        Node n4 = testRootNode.addNode("node4");
+        Node n5 = testRootNode.addNode("node5");
+
+        n1.setProperty("text", new String[] { "ccc" });
+        n2.setProperty("text", new String[] { "eee", "bbb" });
+        n3.setProperty("text", new String[] { "aaa" });
+        n4.setProperty("text", new String[] { "bbb", "aaa" });
+        n5.setProperty("text", new String[] { "eee", "aaa" });
+
+        testRootNode.getSession().save();
+
+        String sql = "SELECT value FROM [nt:unstructured] WHERE ISCHILDNODE(["
+                + testRoot + "]) ORDER BY text";
+
+        checkSeq(executeSQL2Query(sql), new Node[] { n3, n4, n1, n5, n2 });
+    }
+
     public void testOrderByVal() throws RepositoryException {
 
         Node n1 = testRootNode.addNode("node1");

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/ComparableArrayTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/ComparableArrayTest.java?rev=1209063&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/ComparableArrayTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/ComparableArrayTest.java Thu Dec  1 13:05:38 2011
@@ -0,0 +1,53 @@
+/*
+ * 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.jackrabbit.core.query.lucene;
+
+import static junit.framework.Assert.assertEquals;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.query.lucene.SharedFieldCache.ComparableArray;
+import org.junit.Test;
+
+public class ComparableArrayTest {
+
+    /**
+     * Test for JCR-2906 to make sure the SharedFieldCache arranges the entries
+     * properly and keeps the internal array creation efficient.
+     */
+    @Test
+    public void testInsert() throws RepositoryException {
+        ComparableArray ca = new ComparableArray("a", 1);
+        assertEquals("a", ca.toString());
+        assertEquals(1, ca.getOffset());
+
+        // insert before
+        ca.insert("b", 0);
+        assertEquals("[b, a]", ca.toString());
+        assertEquals(0, ca.getOffset());
+
+        // insert after
+        ca.insert("c", 3);
+        assertEquals("[b, a, null, c]", ca.toString());
+        assertEquals(0, ca.getOffset());
+
+        // insert inside
+        ca.insert("d", 2);
+        assertEquals("[b, a, d, c]", ca.toString());
+        assertEquals(0, ca.getOffset());
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/ComparableArrayTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain