jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r161425 - in incubator/jackrabbit/trunk/src: java/org/apache/jackrabbit/core/search/lucene/ test/org/apache/jackrabbit/core/search/ test/org/apache/jackrabbit/test/api/query/
Date Fri, 15 Apr 2005 09:34:19 GMT
Author: mreutegg
Date: Fri Apr 15 02:34:14 2005
New Revision: 161425

URL: http://svn.apache.org/viewcvs?view=rev&rev=161425
Log:
JCR-106: Minimize use of fields in lucene index

Added:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingIndexReader.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingMultiReader.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NotQuery.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldCache.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldSortComparator.java   (with props)
Modified:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/AbstractIndex.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DerefQuery.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DescendantSelfAxisQuery.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FieldNames.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MatchAllScorer.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MultiIndex.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java
    incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SimpleQueryTest.java
    incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/GetPersistentQueryPathLevel1Test.java

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/AbstractIndex.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/AbstractIndex.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/AbstractIndex.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/AbstractIndex.java Fri Apr 15 02:34:14 2005
@@ -125,6 +125,9 @@
         }
         if (indexReader == null) {
             indexReader = IndexReader.open(getDirectory());
+            if (useCachingReader()) {
+                indexReader = new CachingIndexReader(indexReader);
+            }
         }
         return indexReader;
     }
@@ -200,6 +203,15 @@
                 directory = null;
             }
         }
+    }
+
+    /**
+     * Returns <code>true</code> if this index should use a
+     * {@link CachingIndexReader}, <code>false</code> otherwise.
+     * @return <code>true</code> if index reader should use caching.
+     */
+    protected boolean useCachingReader() {
+        return false;
     }
 
     //-------------------------< properties >-----------------------------------

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingIndexReader.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingIndexReader.java?view=auto&rev=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingIndexReader.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingIndexReader.java Fri Apr 15 02:34:14 2005
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.search.lucene;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.FilterIndexReader;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.document.Document;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Implements an <code>IndexReader</code> that caches document ids for the
+ * {@link FieldNames.UUID} field.
+ */
+class CachingIndexReader extends FilterIndexReader {
+
+    /**
+     * The document id cache. Maps UUIDs to document number.
+     */
+    private Map cache;
+
+    /**
+     * Creates a new <code>CachingIndexReader</code> based on
+     * <code>delegatee</code>
+     * @param delegatee the base <code>IndexReader</code>.
+     */
+    CachingIndexReader(IndexReader delegatee) {
+        super(delegatee);
+    }
+
+    /**
+     * If the field of <code>term</code> is {@link FieldNames#UUID} this
+     * <code>CachingIndexReader</code> returns a <code>TermDocs</code> instance
+     * with a cached document id. If <code>term</code> has any other field
+     * the call is delegated to the base <code>IndexReader</code>.<br/>
+     * If <code>term</code> is for a {@link FieldNames#UUID} field and this
+     * <code>CachingIndexReader</code> does not have such a document,
+     * {@link #EMPTY} is returned.
+     *
+     * @param term the term to start the <code>TermDocs</code> enumeration.
+     * @return a TermDocs instance.
+     * @throws IOException if an error occurs while reading from the index.
+     */
+    public TermDocs termDocs(Term term) throws IOException {
+        if (term.field() == FieldNames.UUID) {
+            synchronized (this) {
+                cacheInit();
+                final Integer docNo = (Integer) cache.get(term.text());
+                if (docNo == null) {
+                    return EMPTY;
+                } else {
+                    return new TermDocs() {
+
+                        private boolean consumed = false;
+
+                        private int doc = -1;
+
+                        public void seek(Term term) {
+                            throw new UnsupportedOperationException();
+                        }
+
+                        public void seek(TermEnum termEnum) {
+                            throw new UnsupportedOperationException();
+                        }
+
+                        public int doc() {
+                            return doc;
+                        }
+
+                        public int freq() {
+                            return 1;
+                        }
+
+                        public boolean next() {
+                            if (consumed) {
+                                return false;
+                            } else {
+                                doc = docNo.intValue();
+                                consumed = true;
+                                return true;
+                            }
+                        }
+
+                        public int read(int[] docs, int[] freqs) {
+                            docs[0] = docNo.intValue();
+                            freqs[0] = 1;
+                            return 1;
+                        }
+
+                        public boolean skipTo(int target) {
+                            throw new UnsupportedOperationException();
+                        }
+
+                        public void close() {
+                        }
+                    };
+                }
+            }
+        } else {
+            return super.termDocs(term);
+        }
+    }
+
+    /**
+     * Removes the <code>TermEnum</code> from the cache and calls the base
+     * <code>IndexReader</code>.
+     * @param n the number of the document to delete.
+     * @throws IOException if an error occurs while deleting the document.
+     */
+    protected synchronized void doDelete(int n) throws IOException {
+        if (cache != null) {
+            // todo keep a second map which contains the doc number to UUID mapping?
+            for (Iterator it = cache.values().iterator(); it.hasNext();) {
+                if (((Integer) it.next()).intValue() == n) {
+                    it.remove();
+                    break;
+                }
+            }
+        }
+        super.doDelete(n);
+    }
+
+    /**
+     * Initially fills the cache with all the UUID to document number mappings.
+     * @throws IOException if an error occurs while reading from the index.
+     */
+    private void cacheInit() throws IOException {
+        if (cache == null) {
+            Map tmp = new HashMap();
+            for (int i = 0; i < in.maxDoc(); i++) {
+                if (!in.isDeleted(i)) {
+                    Document d = in.document(i);
+                    tmp.put(d.get(FieldNames.UUID), new Integer(i));
+                }
+            }
+            cache = tmp;
+        }
+    }
+
+    /**
+     * Implements an empty TermDocs.
+     */
+    static final TermDocs EMPTY = new TermDocs() {
+
+        public void seek(Term term) {
+        }
+
+        public void seek(TermEnum termEnum) {
+        }
+
+        public int doc() {
+            return -1;
+        }
+
+        public int freq() {
+            return -1;
+        }
+
+        public boolean next() {
+            return false;
+        }
+
+        public int read(int[] docs, int[] freqs) {
+            return 0;
+        }
+
+        public boolean skipTo(int target) {
+            return false;
+        }
+
+        public void close() {
+        }
+    };
+
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingIndexReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingMultiReader.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingMultiReader.java?view=auto&rev=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingMultiReader.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingMultiReader.java Fri Apr 15 02:34:14 2005
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.search.lucene;
+
+import org.apache.lucene.index.MultiReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/**
+ * Extends a <code>MultiReader</code> with support for cached <code>TermDocs</code>
+ * on {@link FieldNames#UUID} field.
+ */
+class CachingMultiReader extends MultiReader {
+
+    /**
+     * The sub readers.
+     */
+    private IndexReader[] subReaders;
+
+    /**
+     * Doc number starts for each sub reader
+     */
+    private int[] starts;
+
+    /**
+     * Creates a new <code>CachingMultiReader</code> based on sub readers.
+     * <p/>
+     * This <code>CachingMultiReader</code> poses type requirements on the
+     * <code>subReaders</code>: all but one sub readers must be a
+     * {@link CachingIndexReader}. The single allowed sub reader not of type
+     * {@link CachingIndexReader} must be the last reader in
+     * <code>subReaders</code>! Otherwise this constructor will throw an
+     * {@link IllegalArgumentException}.
+     *
+     * @param subReaders the sub readers.
+     * @throws IOException if an error occurs while reading from the indexes.
+     * @exception IllegalArgumentException if <code>subReaders</code> does
+     * not comply to the above type requirements.
+     */
+    public CachingMultiReader(IndexReader[] subReaders) throws IOException {
+        super(subReaders);
+        // check readers, all but last must be a CachingIndexReader
+        for (int i = 0; i < subReaders.length - 1; i++) {
+            if (!(subReaders[i] instanceof CachingIndexReader)) {
+                throw new IllegalArgumentException("subReader " + i + " must be of type CachingIndexReader");
+            }
+        }
+        this.subReaders = subReaders;
+        starts = new int[subReaders.length + 1];
+        int maxDoc = 0;
+        for (int i = 0; i < subReaders.length; i++) {
+            starts[i] = maxDoc;
+            maxDoc += subReaders[i].maxDoc();
+        }
+        starts[subReaders.length] = maxDoc;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TermDocs termDocs(Term term) throws IOException {
+        if (term.field() == FieldNames.UUID) {
+            for (int i = 0; i < subReaders.length; i++) {
+                TermDocs docs = subReaders[i].termDocs(term);
+                if (docs != CachingIndexReader.EMPTY) {
+                    // apply offset
+                    return new OffsetTermDocs(docs, starts[i]);
+                }
+            }
+        }
+        return super.termDocs(term);
+    }
+
+    /**
+     * Partial <code>TermDocs</code> implementation that applies an offset
+     * to a base <code>TermDocs</code> instance.
+     */
+    private static final class OffsetTermDocs implements TermDocs {
+
+        /**
+         * The base <code>TermDocs</code> instance.
+         */
+        private final TermDocs base;
+
+        /**
+         * The offset to apply
+         */
+        private final int offset;
+
+        /**
+         * Creates a new <code>OffsetTermDocs</code> instance.
+         * @param base the base <code>TermDocs</code>.
+         * @param offset the offset to apply.
+         */
+        OffsetTermDocs(TermDocs base, int offset) {
+            this.base = base;
+            this.offset = offset;
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public void seek(Term term) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public void seek(TermEnum termEnum) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int doc() {
+            return base.doc() + offset;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int freq() {
+            return base.freq();
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public boolean next() throws IOException {
+            return base.next();
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public int read(int[] docs, int[] freqs) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public boolean skipTo(int target) {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() throws IOException {
+            base.close();
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/CachingMultiReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java Fri Apr 15 02:34:14 2005
@@ -250,20 +250,6 @@
         /**
          * {@inheritDoc}
          */
-        public void score(HitCollector hc) throws IOException {
-            calculateChildren();
-
-            int next = hits.nextSetBit(0);
-            while (next > -1) {
-                hc.collect(next, 1.0f);
-                // move to next doc
-                next = hits.nextSetBit(next + 1);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
         public boolean next() throws IOException {
             calculateChildren();
             nextDoc = hits.nextSetBit(nextDoc + 1);
@@ -281,7 +267,6 @@
          * {@inheritDoc}
          */
         public float score() throws IOException {
-            // todo implement
             return 1.0f;
         }
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DerefQuery.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DerefQuery.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DerefQuery.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DerefQuery.java Fri Apr 15 02:34:14 2005
@@ -51,11 +51,6 @@
     private final String refProperty;
 
     /**
-     * The name of the reference property as multi value.
-     */
-    private final String refPropertyMV;
-
-    /**
      * The nameTest to apply on target node, or <code>null</code> if all
      * target nodes should be selected.
      */
@@ -83,8 +78,6 @@
     DerefQuery(Query context, String refProperty, String nameTest) {
         this.contextQuery = context;
         this.refProperty = refProperty;
-        String[] nameParts = refProperty.split(":");
-        this.refPropertyMV = nameParts[0] + ":" + FieldNames.MVP_PREFIX + nameParts[1];
         this.nameTest = nameTest;
     }
 
@@ -143,23 +136,20 @@
          * {@inheritDoc}
          */
         public float getValue() {
-            // @todo implement properly
-            return 0;
+            return 1.0f;
         }
 
         /**
          * {@inheritDoc}
          */
         public float sumOfSquaredWeights() throws IOException {
-            // @todo implement properly
-            return 0;
+            return 1.0f;
         }
 
         /**
          * {@inheritDoc}
          */
         public void normalize(float norm) {
-            // @todo implement properly
         }
 
         /**
@@ -227,20 +217,6 @@
         /**
          * {@inheritDoc}
          */
-        public void score(HitCollector hc) throws IOException {
-            calculateChildren();
-
-            int next = hits.nextSetBit(0);
-            while (next > -1) {
-                hc.collect(next, 1.0f);
-                // move to next doc
-                next = hits.nextSetBit(next + 1);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
         public boolean next() throws IOException {
             calculateChildren();
             nextDoc = hits.nextSetBit(nextDoc + 1);
@@ -258,7 +234,6 @@
          * {@inheritDoc}
          */
         public float score() throws IOException {
-            // todo implement
             return 1.0f;
         }
 
@@ -301,15 +276,11 @@
 
                 // retrieve uuids of target nodes
                 for (int i = hits.nextSetBit(0); i >= 0; i = hits.nextSetBit(i + 1)) {
-                    String targetUUID = reader.document(i).get(refProperty);
-                    if (targetUUID != null) {
-                        uuids.add(targetUUID);
-                    }
-                    // also find multiple values
-                    String[] targetUUIDs = reader.document(i).getValues(refPropertyMV);
-                    if (targetUUIDs != null) {
-                        for (int j = 0; j < targetUUIDs.length; j++) {
-                            uuids.add(targetUUIDs[j]);
+                    String[] values = reader.document(i).getValues(FieldNames.PROPERTIES);
+                    String prefix = FieldNames.createNamedValue(refProperty, "");
+                    for (int v = 0; v < values.length; v++) {
+                        if (values[v].startsWith(prefix)) {
+                            uuids.add(values[v].substring(prefix.length()));
                         }
                     }
                 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DescendantSelfAxisQuery.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DescendantSelfAxisQuery.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DescendantSelfAxisQuery.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/DescendantSelfAxisQuery.java Fri Apr 15 02:34:14 2005
@@ -148,23 +148,20 @@
          * {@inheritDoc}
          */
         public float getValue() {
-            // @todo implement properly
-            return 0;
+            return 1.0f;
         }
 
         /**
          * {@inheritDoc}
          */
         public float sumOfSquaredWeights() throws IOException {
-            // @todo implement properly
-            return 0;
+            return 1.0f;
         }
 
         /**
          * {@inheritDoc}
          */
         public void normalize(float norm) {
-            // @todo implement properly
         }
 
         /**
@@ -236,12 +233,6 @@
         /**
          * {@inheritDoc}
          */
-        public void score(HitCollector hc) throws IOException {
-            while (next()) {
-                hc.collect(doc(), score());
-            }
-        }
-
         public boolean next() throws IOException {
             calculateSubHits();
             nextDoc = subHits.nextSetBit(nextDoc + 1);
@@ -279,14 +270,23 @@
             return false;
         }
 
+        /**
+         * {@inheritDoc}
+         */
         public int doc() {
             return nextDoc;
         }
 
+        /**
+         * {@inheritDoc}
+         */
         public float score() throws IOException {
             return 1.0f;
         }
 
+        /**
+         * {@inheritDoc}
+         */
         public boolean skipTo(int target) throws IOException {
             nextDoc = target - 1;
             return next();

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FieldNames.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FieldNames.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FieldNames.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/FieldNames.java Fri Apr 15 02:34:14 2005
@@ -23,16 +23,22 @@
 public class FieldNames {
 
     /**
+     * Private constructor.
+     */
+    private FieldNames() {
+    }
+
+    /**
      * Name of the field that contains the UUID of the node. Terms are stored
      * but not tokenized.
      */
-    public static final String UUID = "_:UUID";
+    public static final String UUID = "_:UUID".intern();
 
     /**
      * Name of the field that contains the fulltext index including terms
      * from all properties of a node. Terms are tokenized.
      */
-    public static final String FULLTEXT = "_:FULLTEXT";
+    public static final String FULLTEXT = "_:FULLTEXT".intern();
 
     /**
      * Prefix for all field names that are fulltext indexed by property name.
@@ -43,16 +49,37 @@
      * Name of the field that contains the UUID of the parent node. Terms are
      * stored and but not tokenized.
      */
-    public static final String PARENT = "_:PARENT";
+    public static final String PARENT = "_:PARENT".intern();
 
     /**
      * Name of the field that contains the label of the node. Terms are not
      * tokenized.
      */
-    public static final String LABEL = "_:LABEL";
+    public static final String LABEL = "_:LABEL".intern();
 
     /**
-     * Prefix for all field names that hold multi valued properties.
+     * Name of the field that contains the names of multi-valued properties that
+     * hold more than one value. Terms are not tokenized and not stored, only
+     * indexed.
      */
-    public static final String MVP_PREFIX = "MVP:";
+    public static final String MVP = "_:MVP".intern();
+
+    /**
+     * Name of the field that contains all values of properties that are indexed
+     * as is without tokenizing. Terms are prefixed with the property name.
+     */
+    public static final String PROPERTIES = "_:PROPERTIES".intern();
+
+    /**
+     * Returns a named value for use as a term in the index. The named
+     * value is of the form: <code>fieldName</code> + '\uFFFF' + value
+     *
+     * @param fieldName the field name.
+     * @param value the value.
+     * @return value prefixed with field name.
+     */
+    public static String createNamedValue(String fieldName, String value) {
+        return fieldName + '\uFFFF' + value;
+    }
+
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java Fri Apr 15 02:34:14 2005
@@ -227,21 +227,17 @@
     }
 
     public Object visit(NotQueryNode node, Object data) {
-        BooleanQuery notQuery = new BooleanQuery();
-        try {
-            // first select any node
-            notQuery.add(new MatchAllQuery(primaryType.toJCRName(nsMappings)),
-                    false, false);
-        } catch (NoPrefixDeclaredException e) {
-            // will never happen, prefixes are created when unknown
-        }
         Object[] result = node.acceptOperands(this, null);
+        if (result.length == 0) {
+            return data;
+        }
+        // join the results
+        BooleanQuery b = new BooleanQuery();
         for (int i = 0; i < result.length; i++) {
-            Query operand = (Query) result[i];
-            // then prohibit the nodes from the not clause
-            notQuery.add(operand, false, true);
+            b.add((Query) result[i], false, false);
         }
-        return notQuery;
+        // negate
+        return new NotQuery(b);
     }
 
     public Object visit(ExactQueryNode node, Object data) {
@@ -253,7 +249,7 @@
         } catch (NoPrefixDeclaredException e) {
             // will never happen, prefixes are created when unknown
         }
-        return new TermQuery(new Term(field, value));
+        return new TermQuery(new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, value)));
     }
 
     public Object visit(NodeTypeQueryNode node, Object data) {
@@ -291,11 +287,15 @@
             // exception occured
             return new BooleanQuery();
         } else if (values.size() == 1) {
-            return new TermQuery(new Term(field, (String) values.get(0)));
+            Term t = new Term(FieldNames.PROPERTIES,
+                    FieldNames.createNamedValue(field, (String) values.get(0)));
+            return new TermQuery(t);
         } else {
             BooleanQuery b = new BooleanQuery();
             for (Iterator it = values.iterator(); it.hasNext();) {
-                b.add(new TermQuery(new Term(field, (String) it.next())), false, false);
+                Term t = new Term(FieldNames.PROPERTIES,
+                        FieldNames.createNamedValue(field, (String) it.next()));
+                b.add(new TermQuery(t), false, false);
             }
             return b;
         }
@@ -458,7 +458,12 @@
                     andQuery.add(subQuery, true, false);
                 } else {
                     // todo this will traverse the whole index, optimize!
-                    Query subQuery = new MatchAllQuery(FieldNames.UUID);
+                    Query subQuery = null;
+                    try {
+                        subQuery = new MatchAllQuery(primaryType.toJCRName(nsMappings));
+                    } catch (NoPrefixDeclaredException e) {
+                        // will never happen, prefixes are created when unknown
+                    }
                     context = new DescendantSelfAxisQuery(context, subQuery);
                     andQuery.add(new ChildAxisQuery(sharedItemMgr, context, null, node.getIndex()), true, false);
                 }
@@ -530,19 +535,8 @@
         }
 
         String field = "";
-        String primaryTypeField = "";
-        String mvpField = "";
         try {
             field = node.getProperty().toJCRName(nsMappings);
-            primaryTypeField = primaryType.toJCRName(nsMappings);
-            StringBuffer tmp = new StringBuffer();
-            tmp.append(nsMappings.getPrefix(node.getProperty().getNamespaceURI()));
-            tmp.append(':').append(FieldNames.MVP_PREFIX);
-            tmp.append(node.getProperty().getLocalName());
-            mvpField = tmp.toString();
-        } catch (NamespaceException e) {
-            // should never happen
-            exceptions.add(e);
         } catch (NoPrefixDeclaredException e) {
             // should never happen
             exceptions.add(e);
@@ -550,82 +544,54 @@
 
         switch (node.getOperation()) {
             case QueryConstants.OPERATION_EQ_VALUE:      // =
-                if (stringValues.length == 1) {
-                    query = new TermQuery(new Term(field, stringValues[0]));
-                } else {
-                    BooleanQuery or = new BooleanQuery();
-                    for (int i = 0; i < stringValues.length; i++) {
-                        or.add(new TermQuery(new Term(field, stringValues[i])), false, false);
-                    }
-                    query = or;
-                }
-                break;
-            case QueryConstants.OPERATION_EQ_GENERAL:    // =
-                // search in single and multi valued properties
+            case QueryConstants.OPERATION_EQ_GENERAL:
                 BooleanQuery or = new BooleanQuery();
                 for (int i = 0; i < stringValues.length; i++) {
-                    or.add(new TermQuery(new Term(field, stringValues[i])), false, false);
-                    or.add(new TermQuery(new Term(mvpField, stringValues[i])), false, false);
+                    or.add(new TermQuery(new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]))), false, false);
                 }
                 query = or;
-                break;
-            case QueryConstants.OPERATION_GE_VALUE:      // >=
-                if (stringValues.length == 1) {
-                    query = new RangeQuery(new Term(field, stringValues[0]), null, true);
-                } else {
-                    or = new BooleanQuery();
-                    for (int i = 0; i < stringValues.length; i++) {
-                        or.add(new RangeQuery(new Term(field, stringValues[i]), null, true), false, false);
-                    }
-                    query = or;
+                if (node.getOperation() == QueryConstants.OPERATION_EQ_VALUE) {
+                    query = createSingleValueConstraint(or, field);
                 }
                 break;
-            case QueryConstants.OPERATION_GE_GENERAL:   // >=
-                // search in single and multi valued properties
+            case QueryConstants.OPERATION_GE_VALUE:      // >=
+            case QueryConstants.OPERATION_GE_GENERAL:
                 or = new BooleanQuery();
                 for (int i = 0; i < stringValues.length; i++) {
-                    or.add(new RangeQuery(new Term(field, stringValues[i]), null, true), false, false);
-                    or.add(new RangeQuery(new Term(mvpField, stringValues[i]), null, true), false, false);
+                    Term lower = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]));
+                    Term upper = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, "\uFFFF"));
+                    or.add(new RangeQuery(lower, upper, true), false, false);
                 }
                 query = or;
-                break;
-            case QueryConstants.OPERATION_GT_VALUE:      // >
-                if (stringValues.length == 1) {
-                    query = new RangeQuery(new Term(field, stringValues[0]), null, false);
-                } else {
-                    or = new BooleanQuery();
-                    for (int i = 0; i < stringValues.length; i++) {
-                        or.add(new RangeQuery(new Term(field, stringValues[i]), null, false), false, false);
-                    }
-                    query = or;
+                if (node.getOperation() == QueryConstants.OPERATION_GE_VALUE) {
+                    query = createSingleValueConstraint(or, field);
                 }
                 break;
-            case QueryConstants.OPERATION_GT_GENERAL:      // >
+            case QueryConstants.OPERATION_GT_VALUE:      // >
+            case QueryConstants.OPERATION_GT_GENERAL:
                 or = new BooleanQuery();
                 for (int i = 0; i < stringValues.length; i++) {
-                    or.add(new RangeQuery(new Term(field, stringValues[i]), null, false), false, false);
-                    or.add(new RangeQuery(new Term(mvpField, stringValues[i]), null, false), false, false);
+                    Term lower = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]));
+                    Term upper = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, "\uFFFF"));
+                    or.add(new RangeQuery(lower, upper, false), false, false);
                 }
                 query = or;
-                break;
-            case QueryConstants.OPERATION_LE_VALUE:      // <=
-                if (stringValues.length == 1) {
-                    query = new RangeQuery(null, new Term(field, stringValues[0]), true);
-                } else {
-                    or = new BooleanQuery();
-                    for (int i = 0; i < stringValues.length; i++) {
-                        or.add(new RangeQuery(null, new Term(field, stringValues[i]), true), false, false);
-                    }
-                    query = or;
+                if (node.getOperation() == QueryConstants.OPERATION_GT_VALUE) {
+                    query = createSingleValueConstraint(or, field);
                 }
                 break;
+            case QueryConstants.OPERATION_LE_VALUE:      // <=
             case QueryConstants.OPERATION_LE_GENERAL:      // <=
                 or = new BooleanQuery();
                 for (int i = 0; i < stringValues.length; i++) {
-                    or.add(new RangeQuery(null, new Term(field, stringValues[i]), true), false, false);
-                    or.add(new RangeQuery(null, new Term(mvpField, stringValues[i]), true), false, false);
+                    Term lower = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, ""));
+                    Term upper = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]));
+                    or.add(new RangeQuery(lower, upper, true), false, false);
                 }
                 query = or;
+                if (node.getOperation() == QueryConstants.OPERATION_LE_VALUE) {
+                    query = createSingleValueConstraint(query, field);
+                }
                 break;
             case QueryConstants.OPERATION_LIKE:          // LIKE
                 // the like operation always has one string value.
@@ -633,52 +599,60 @@
                 if (stringValues[0].equals("%")) {
                     query = new MatchAllQuery(field);
                 } else {
-                    query = new WildcardQuery(new Term(field, stringValues[0]));
+                    Term t = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[0]));
+                    query = new WildcardQuery(t);
                 }
                 break;
             case QueryConstants.OPERATION_LT_VALUE:      // <
-                if (stringValues.length == 1) {
-                    query = new RangeQuery(null, new Term(field, stringValues[0]), false);
-                } else {
-                    or = new BooleanQuery();
-                    for (int i = 0; i < stringValues.length; i++) {
-                        or.add(new RangeQuery(null, new Term(field, stringValues[i]), false), false, false);
-                    }
-                    query = or;
-                }
-                break;
-            case QueryConstants.OPERATION_LT_GENERAL:      // <
+            case QueryConstants.OPERATION_LT_GENERAL:
                 or = new BooleanQuery();
                 for (int i = 0; i < stringValues.length; i++) {
-                    or.add(new RangeQuery(null, new Term(field, stringValues[i]), false), false, false);
-                    or.add(new RangeQuery(null, new Term(mvpField, stringValues[i]), false), false, false);
+                    Term lower = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, ""));
+                    Term upper = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]));
+                    or.add(new RangeQuery(lower, upper, false), false, false);
                 }
                 query = or;
+                if (node.getOperation() == QueryConstants.OPERATION_LT_VALUE) {
+                    query = createSingleValueConstraint(or, field);
+                }
                 break;
             case QueryConstants.OPERATION_NE_VALUE:      // !=
+                // match nodes with property 'field' that includes svp and mvp
                 BooleanQuery notQuery = new BooleanQuery();
                 notQuery.add(new MatchAllQuery(field), false, false);
+                // exclude all nodes where 'field' has the term in question
                 for (int i = 0; i < stringValues.length; i++) {
-                    notQuery.add(new TermQuery(new Term(field, stringValues[i])), false, true);
+                    Term t = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]));
+                    notQuery.add(new TermQuery(t), false, true);
                 }
+                // and exclude all nodes where 'field' is multi valued
+                notQuery.add(new TermQuery(new Term(FieldNames.MVP, field)), false, true);
                 query = notQuery;
                 break;
             case QueryConstants.OPERATION_NE_GENERAL:    // !=
-                // search in single and multi valued properties
+                // that's:
+                // all nodes with property 'field'
+                // minus the nodes that have a single property 'field' that is
+                //    not equal to term in question
+                // minus the nodes that have a multi-valued property 'field' and
+                //    all values are equal to term in question
                 notQuery = new BooleanQuery();
                 notQuery.add(new MatchAllQuery(field), false, false);
-                notQuery.add(new MatchAllQuery(mvpField), false, false);
                 for (int i = 0; i < stringValues.length; i++) {
-                    notQuery.add(new TermQuery(new Term(field, stringValues[i])), false, true);
-                    notQuery.add(new TermQuery(new Term(mvpField, stringValues[i])), false, true);
+                    // exclude the nodes that have the term and are single valued
+                    Term t = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i]));
+                    Query svp = new NotQuery(new TermQuery(new Term(FieldNames.MVP, field)));
+                    BooleanQuery and = new BooleanQuery();
+                    and.add(new TermQuery(t), true, false);
+                    and.add(svp, true, false);
+                    notQuery.add(and, false, true);
                 }
+                // todo above also excludes multi-valued properties that contain
+                //      multiple instances of only stringValues. e.g. text={foo, foo}
                 query = notQuery;
                 break;
             case QueryConstants.OPERATION_NULL:
-                notQuery = new BooleanQuery();
-                notQuery.add(new MatchAllQuery(primaryTypeField), false, false);
-                notQuery.add(new MatchAllQuery(field), false, true);
-                query = notQuery;
+                query = new NotQuery(new MatchAllQuery(field));
                 break;
             case QueryConstants.OPERATION_NOT_NULL:
                 query = new MatchAllQuery(field);
@@ -695,7 +669,30 @@
     }
 
     //---------------------------< internal >-----------------------------------
-    
+
+    /**
+     * Wraps a constraint query around <code>q</code> that limits the nodes to
+     * those where <code>propName</code> is the name of a single value property
+     * on the node instance.
+     * @param q the query to wrap.
+     * @param propName the name of a property that only has one value.
+     * @return the wrapped query <code>q</code>.
+     */
+    private Query createSingleValueConstraint(Query q, String propName) {
+        // get nodes with multi-values in propName
+        Query mvp = new TermQuery(new Term(FieldNames.MVP, propName));
+        // now negate, that gives the nodes that have propName as single
+        // values but also all others
+        Query svp = new NotQuery(mvp);
+        // now join the two, which will result in those nodes where propName
+        // only contains a single value. This works because q already restricts
+        // the result to those nodes that have a property propName
+        BooleanQuery and = new BooleanQuery();
+        and.add(q, true, false);
+        and.add(svp, true, false);
+        return and;
+    }
+
     /**
      * Returns an array of String values to be used as a term to lookup the search index
      * for a String <code>literal</code> of a certain property name. This method

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MatchAllScorer.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MatchAllScorer.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MatchAllScorer.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MatchAllScorer.java Fri Apr 15 02:34:14 2005
@@ -148,8 +148,10 @@
         // we match all terms
         TermEnum terms = null;
         try {
-            terms = reader.terms(new Term(field, ""));
-            while (terms.term() != null && terms.term().field() == field) {
+            terms = reader.terms(new Term(FieldNames.PROPERTIES, field));
+            while (terms.term() != null
+                    && terms.term().field() == FieldNames.PROPERTIES
+                    && terms.term().text().startsWith(field)) {
                 TermDocs termDocs = null;
                 try {
                     termDocs = reader.termDocs(terms.term());

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MultiIndex.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MultiIndex.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MultiIndex.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/MultiIndex.java Fri Apr 15 02:34:14 2005
@@ -294,7 +294,7 @@
                 readers[i] = ((PersistentIndex) indexes.get(i)).getIndexReader();
             }
             readers[readers.length - 1] = volatileIndex.getIndexReader();
-            multiReader = new MultiReader(readers);
+            multiReader = new CachingMultiReader(readers);
         }
         return multiReader;
     }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NodeIndexer.java Fri Apr 15 02:34:14 2005
@@ -161,21 +161,12 @@
             try {
                 PropertyState propState = (PropertyState) stateProvider.getItemState(id);
                 InternalValue[] values = propState.getValues();
-                if (propState.isMultiValued()) {
-                    // multi valued
-                    if (values.length == 1) {
-                        // also index as if single value property
-                        addValue(doc, values[0], propState.getName(), false);
-                    }
-                    for (int i = 0; i < values.length; i++) {
-                        addValue(doc, values[i], propState.getName(), true);
-                    }
-                } else {
-                    // single value
-                    // do we have a value at all?
-                    if (values.length == 1) {
-                        addValue(doc, values[0], propState.getName(), false);
-                    }
+                for (int i = 0; i < values.length; i++) {
+                    addValue(doc, values[i], propState.getName());
+                }
+                if (values.length > 1) {
+                    // real multi-valued
+                    addMVPName(doc, propState.getName());
                 }
             } catch (NoSuchItemStateException e) {
                 throw new RepositoryException("Error while indexing node: " + node.getUUID(), e);
@@ -187,26 +178,32 @@
     }
 
     /**
+     * Adds a {@link FieldNames#MVP} field to <code>doc</code> with the resolved
+     * <code>name</code> using the internal search index namespace mapping.
+     * @param doc the lucene document.
+     * @param name the name of the multi-value property.
+     */
+    private void addMVPName(Document doc, QName name) {
+        try {
+            String propName = name.toJCRName(mappings);
+            doc.add(new Field(FieldNames.MVP, propName, false, true, false));
+        } catch (NoPrefixDeclaredException e) {
+            // will never happen, prefixes are created dynamically
+        }
+    }
+
+    /**
      * Adds a value to the lucene Document.
      *
      * @param doc   the document.
      * @param value the internal jackrabbit value.
      * @param name  the name of the property.
-     * @param multiValued if <code>true</code> the value is treated as a
-     *   multivalued.
      */
-    private void addValue(Document doc, InternalValue value, QName name, boolean multiValued) {
+    private void addValue(Document doc, InternalValue value, QName name) {
         String fieldName = name.getLocalName();
         try {
-            StringBuffer tmp = new StringBuffer();
-            tmp.append(mappings.getPrefix(name.getNamespaceURI()));
-            tmp.append(':');
-            if (multiValued) {
-                tmp.append(FieldNames.MVP_PREFIX);
-            }
-            tmp.append(name.getLocalName());
-            fieldName = tmp.toString();
-        } catch (NamespaceException e) {
+            fieldName = name.toJCRName(mappings);
+        } catch (NoPrefixDeclaredException e) {
             // will never happen
         }
         Object internalValue = value.internalValue();
@@ -296,8 +293,8 @@
      * @param internalValue The value for the field to add to the document.
      */
     protected void addBooleanValue(Document doc, String fieldName, Object internalValue) {
-        doc.add(new Field(fieldName,
-            internalValue.toString(),
+        doc.add(new Field(FieldNames.PROPERTIES,
+            FieldNames.createNamedValue(fieldName, internalValue.toString()),
             false,
             true,
             false));
@@ -314,8 +311,8 @@
      */
     protected void addCalendarValue(Document doc, String fieldName, Object internalValue) {
         long millis = ((Calendar) internalValue).getTimeInMillis();
-        doc.add(new Field(fieldName,
-                DateField.timeToString(millis),
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, DateField.timeToString(millis)),
                 false,
                 true,
                 false));
@@ -332,8 +329,8 @@
      */
     protected void addDoubleValue(Document doc, String fieldName, Object internalValue) {
         double doubleVal = ((Double) internalValue).doubleValue();
-        doc.add(new Field(fieldName,
-                DoubleField.doubleToString(doubleVal),
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, DoubleField.doubleToString(doubleVal)),
                 false,
                 true,
                 false));
@@ -350,8 +347,8 @@
      */
     protected void addLongValue(Document doc, String fieldName, Object internalValue) {
         long longVal = ((Long) internalValue).longValue();
-        doc.add(new Field(fieldName,
-                LongField.longToString(longVal),
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, LongField.longToString(longVal)),
                 false,
                 true,
                 false));
@@ -368,8 +365,8 @@
      */
     protected void addReferenceValue(Document doc, String fieldName, Object internalValue) {
         String uuid = internalValue.toString();
-        doc.add(new Field(fieldName,
-                uuid,
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, uuid),
                 true, // store
                 true,
                 false));
@@ -392,8 +389,8 @@
         } catch (NoPrefixDeclaredException e) {
             // will never happen
         }
-        doc.add(new Field(fieldName,
-                pathString,
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, pathString),
                 false,
                 true,
                 false));
@@ -411,8 +408,8 @@
         String stringValue = String.valueOf(internalValue);
 
         // simple String
-        doc.add(new Field(fieldName,
-                stringValue,
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, stringValue),
                 false,
                 true,
                 false));
@@ -452,8 +449,8 @@
         } catch (NamespaceException e) {
             // will never happen
         }
-        doc.add(new Field(fieldName,
-                normValue,
+        doc.add(new Field(FieldNames.PROPERTIES,
+                FieldNames.createNamedValue(fieldName, normValue),
                 false,
                 true,
                 false));

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NotQuery.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NotQuery.java?view=auto&rev=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NotQuery.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NotQuery.java Fri Apr 15 02:34:14 2005
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.search.lucene;
+
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Similarity;
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+/**
+ * Implements a query that negates documents of a context query. Documents
+ * that matched the context query will not match the <code>NotQuery</code> and
+ * Documents that did not match the context query will be selected by this
+ * <code>NotQuery</code>.
+ */
+class NotQuery extends Query {
+
+    /**
+     * The context query to invert.
+     */
+    private final Query context;
+
+    /**
+     * The context scorer to invert.
+     */
+    private Scorer contextScorer;
+
+    /**
+     * Creates a new <code>NotQuery</code>.
+     * @param context the context query.
+     */
+    NotQuery(Query context) {
+        this.context = context;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Weight createWeight(Searcher searcher) {
+        return new NotQueryWeight(searcher);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString(String field) {
+        return "NotQuery";
+    }
+
+    /**
+     * Implements a weight for this <code>NotQuery</code>.
+     */
+    private class NotQueryWeight implements Weight {
+
+        /**
+         * The searcher to access the index.
+         */
+        private final Searcher searcher;
+
+        /**
+         * Creates a new NotQueryWeight with a searcher.
+         * @param searcher the searcher.
+         */
+        NotQueryWeight(Searcher searcher) {
+            this.searcher = searcher;
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public Query getQuery() {
+            return NotQuery.this;
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public float getValue() {
+            return 1.0f;
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public float sumOfSquaredWeights() throws IOException {
+            return 1.0f;
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public void normalize(float norm) {
+        }
+
+        /**
+         * @inheritDoc
+         */
+        public Scorer scorer(IndexReader reader) throws IOException {
+            contextScorer = context.weight(searcher).scorer(reader);
+            return new NotQueryScorer(reader);
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public Explanation explain(IndexReader reader, int doc) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Implements a scorer that inverts the document matches of the context
+     * scorer.
+     */
+    private class NotQueryScorer extends Scorer {
+
+        /**
+         * The index reader.
+         */
+        private final IndexReader reader;
+
+        /**
+         * Current document number.
+         */
+        private int docNo = -1;
+
+        /**
+         * Current document number of the context scorer;
+         */
+        private int contextNo = -1;
+
+        /**
+         * Creates a new scorer
+         * @param reader
+         */
+        NotQueryScorer(IndexReader reader) {
+            super(Similarity.getDefault());
+            this.reader = reader;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean next() throws IOException {
+            if (docNo == -1) {
+                // get first doc of context scorer
+                if (contextScorer.next()) {
+                    contextNo = contextScorer.doc();
+                }
+            }
+            // move to next candidate
+            do {
+                docNo++;
+            } while (reader.isDeleted(docNo) && docNo < reader.maxDoc());
+
+            // check with contextScorer
+            while (contextNo != -1 && contextNo == docNo) {
+                docNo++;
+                if (contextScorer.next()) {
+                    contextNo = contextScorer.doc();
+                } else {
+                    contextNo = -1;
+                }
+            }
+            return docNo < reader.maxDoc();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public int doc() {
+            return docNo;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public float score() throws IOException {
+            return 1.0f;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean skipTo(int target) throws IOException {
+            if (contextNo != -1 && contextNo < target) {
+                if (contextScorer.skipTo(target)) {
+                    contextNo = contextScorer.doc();
+                } else {
+                    contextNo = -1;
+                }
+            }
+            docNo = target - 1;
+            return next();
+        }
+
+        /**
+         * @throws UnsupportedOperationException always
+         */
+        public Explanation explain(int doc) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/NotQuery.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/PersistentIndex.java Fri Apr 15 02:34:14 2005
@@ -126,4 +126,12 @@
     String getName() {
         return name;
     }
+
+    /**
+     * Always returns <code>true</code>.
+     * @return <code>true</code>.
+     */
+    protected boolean useCachingReader() {
+        return true;
+    }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java Fri Apr 15 02:34:14 2005
@@ -124,7 +124,7 @@
 
         OrderQueryNode orderNode = root.getOrderNode();
 
-        OrderQueryNode.OrderSpec[] orderSpecs = null;
+        OrderQueryNode.OrderSpec[] orderSpecs;
         if (orderNode != null) {
             orderSpecs = orderNode.getOrderSpecs();
         } else {
@@ -178,8 +178,7 @@
                 ntName[0] = Constants.NT_BASE;
             }
             NodeTypeImpl nt = session.getNodeTypeManager().getNodeType(ntName[0]);
-            PropertyDefinition[] propDefs =
-                    (PropertyDefinition[]) nt.getPropertyDefinitions();
+            PropertyDefinition[] propDefs = nt.getPropertyDefinitions();
             List tmp = new ArrayList();
             for (int i = 0; i < propDefs.length; i++) {
                 if (!propDefs[i].isMultiple()) {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SearchIndex.java Fri Apr 15 02:34:14 2005
@@ -200,7 +200,7 @@
                     } catch (NoPrefixDeclaredException e) {
                         // will never happen
                     }
-                    sortFields[i] = new SortField(prop, SortField.STRING, !orderSpecs[i]);
+                    sortFields[i] = new SortField(prop, SharedFieldSortComparator.PROPERTIES, !orderSpecs[i]);
                 }
             }
 

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldCache.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldCache.java?view=auto&rev=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldCache.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldCache.java Fri Apr 15 02:34:14 2005
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.search.lucene;
+
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.SortComparator;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.index.Term;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.HashMap;
+
+/**
+ * Implements a variant of the lucene class <code>org.apache.lucene.search.FieldCacheImpl</code>.
+ * The lucene FieldCache class has some sort of support for custom comparators
+ * but it only works on the basis of a field name. There is no further control
+ * over the terms to iterate, that's why we use our own implementation.
+ */
+class SharedFieldCache {
+
+    /**
+     * Reference to the single instance of <code>SharedFieldCache</code>.
+     */
+    public static final SharedFieldCache INSTANCE = new SharedFieldCache();
+
+    /**
+     * The internal cache. Maps Entry to array of interpreted term values.
+     */
+    private final Map cache = new WeakHashMap();
+
+    /**
+     * Private constructor.
+     */
+    private SharedFieldCache() {
+    }
+
+    /**
+     * Creates a <code>StringIndex</code> for a <code>field</code> and a term
+     * <code>prefix</code>. The term prefix acts as the property name for the
+     * shared <code>field</code>.
+     * <p/>
+     * This method is an adapted version of: <code>org.apache.lucene.search.FieldCacheImpl.getStringIndex()</code>
+     *
+     * @param reader     the <code>IndexReader</code>.
+     * @param field      name of the shared field.
+     * @param prefix     the property name, will be used as term prefix.
+     * @param comparator the sort comparator instance.
+     * @return a StringIndex that contains the field values and order
+     *         information.
+     * @throws IOException if an error occurs while reading from the index.
+     */
+    public FieldCache.StringIndex getStringIndex(IndexReader reader,
+                                                 String field,
+                                                 String prefix,
+                                                 SortComparator comparator)
+            throws IOException {
+        field = field.intern();
+        FieldCache.StringIndex ret = lookup(reader, field, prefix, comparator);
+        if (ret == null) {
+            final int[] retArray = new int[reader.maxDoc()];
+            String[] mterms = new String[reader.maxDoc() + 1];
+            if (retArray.length > 0) {
+                TermDocs termDocs = reader.termDocs();
+                TermEnum termEnum = reader.terms(new Term(field, prefix));
+                int t = 0;  // current term number
+
+                // an entry for documents that have no terms in this field
+                // should a document with no terms be at top or bottom?
+                // this puts them at the top - if it is changed, FieldDocSortedHitQueue
+                // needs to change as well.
+                mterms[t++] = null;
+
+                try {
+                    if (termEnum.term() == null) {
+                        throw new RuntimeException("no terms in field " + field);
+                    }
+                    do {
+                        Term term = termEnum.term();
+                        if (term.field() != field || !term.text().startsWith(prefix)) {
+                            break;
+                        }
+
+                        // store term text
+                        // we expect that there is at most one term per document
+                        if (t >= mterms.length) {
+                            throw new RuntimeException("there are more terms than documents in field \"" + field + "\"");
+                        }
+                        mterms[t] = term.text();
+
+                        termDocs.seek(termEnum);
+                        while (termDocs.next()) {
+                            retArray[termDocs.doc()] = t;
+                        }
+
+                        t++;
+                    } while (termEnum.next());
+                } finally {
+                    termDocs.close();
+                    termEnum.close();
+                }
+
+                if (t == 0) {
+                    // if there are no terms, make the term array
+                    // have a single null entry
+                    mterms = new String[1];
+                } else if (t < mterms.length) {
+                    // if there are less terms than documents,
+                    // trim off the dead array space
+                    String[] terms = new String[t];
+                    System.arraycopy(mterms, 0, terms, 0, t);
+                    mterms = terms;
+                }
+            }
+            FieldCache.StringIndex value = new FieldCache.StringIndex(retArray, mterms);
+            store(reader, field, prefix, comparator, value);
+            return value;
+        }
+        return ret;
+    }
+
+    /**
+     * See if a <code>StringIndex</code> object is in the cache.
+     */
+    FieldCache.StringIndex lookup(IndexReader reader, String field,
+                                  String prefix, SortComparator comparer) {
+        Key key = new Key(field, prefix, comparer);
+        synchronized (this) {
+            HashMap readerCache = (HashMap) cache.get(reader);
+            if (readerCache == null) {
+                return null;
+            }
+            return (FieldCache.StringIndex) readerCache.get(key);
+        }
+    }
+
+    /**
+     * Put a <code>StringIndex</code> <code>value</code> to cache.
+     */
+    Object store(IndexReader reader, String field, String prefix,
+                 SortComparator comparer, FieldCache.StringIndex value) {
+        Key key = new Key(field, prefix, comparer);
+        synchronized (this) {
+            HashMap readerCache = (HashMap) cache.get(reader);
+            if (readerCache == null) {
+                readerCache = new HashMap();
+                cache.put(reader, readerCache);
+            }
+            return readerCache.put(key, value);
+        }
+    }
+
+    /**
+     * A compound <code>Key</code> that consist of <code>field</code>
+     * <code>prefix</code> and <code>comparator</code>.
+     */
+    static class Key {
+
+        private final String field;
+        private final String prefix;
+        private final SortComparator comparator;
+
+        /**
+         * Creates <code>Key</code> for StringIndex lookup.
+         */
+        Key(String field, String prefix, SortComparator comparator) {
+            this.field = field.intern();
+            this.prefix = prefix.intern();
+            this.comparator = comparator;
+        }
+
+        /**
+         * Returns <code>true</code> if <code>o</code> is a <code>Key</code>
+         * instance and refers to the same field, prefix and comparator object.
+         */
+        public boolean equals(Object o) {
+            if (o instanceof Key) {
+                Key other = (Key) o;
+                return other.field == field
+                        && other.prefix == prefix
+                        && other.comparator.equals(comparator);
+            }
+            return false;
+        }
+
+        /**
+         * Composes a hashcode based on the field, prefix and comparator.
+         */
+        public int hashCode() {
+            return field.hashCode() ^ prefix.hashCode() ^ comparator.hashCode();
+        }
+    }
+
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldSortComparator.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldSortComparator.java?view=auto&rev=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldSortComparator.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldSortComparator.java Fri Apr 15 02:34:14 2005
@@ -0,0 +1,78 @@
+package org.apache.jackrabbit.core.search.lucene;
+
+import org.apache.lucene.search.SortComparator;
+import org.apache.lucene.search.ScoreDocComparator;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+/**
+ * Implements a <code>SortComparator</code> which knows how to sort on a lucene
+ * field that contains values for multiple properties.
+ */
+class SharedFieldSortComparator extends SortComparator {
+
+    /**
+     * A <code>SharedFieldSortComparator</code> that is based on
+     * {@link FieldNames#PROPERTIES}.
+     */
+    static final SortComparator PROPERTIES = new SharedFieldSortComparator(FieldNames.PROPERTIES);
+
+    /**
+     * The name of the shared field in the lucene index.
+     */
+    private final String field;
+
+    /**
+     * Creates a new <code>SharedFieldSortComparator</code> for a given shared
+     * field.
+     * @param fieldname the shared field.
+     */
+    public SharedFieldSortComparator(String fieldname) {
+        this.field = fieldname;
+    }
+
+    /**
+     * Creates a new <code>ScoreDocComparator</code> for an embedded
+     * <code>propertyName</code> and a <code>reader</code>.
+     * @param reader the index reader.
+     * @param propertyName the name of the property to sort.
+     * @return a <code>ScoreDocComparator</code> for the
+     * @throws IOException
+     */
+    public ScoreDocComparator newComparator(IndexReader reader, String propertyName)
+            throws IOException {
+        // get the StringIndex for propertyName
+        final FieldCache.StringIndex index
+                = SharedFieldCache.INSTANCE.getStringIndex(reader, field,
+                        propertyName, SharedFieldSortComparator.this);
+
+        return new ScoreDocComparator() {
+            public final int compare (final ScoreDoc i, final ScoreDoc j) {
+              final int fi = index.order[i.doc];
+              final int fj = index.order[j.doc];
+              if (fi < fj) return -1;
+              if (fi > fj) return 1;
+              return 0;
+            }
+
+            public Comparable sortValue (final ScoreDoc i) {
+              return index.lookup[index.order[i.doc]];
+            }
+
+            public int sortType() {
+                return SortField.CUSTOM;
+            }
+        };
+    }
+
+    /**
+     * @throws UnsupportedOperationException always.
+     */
+    protected Comparable getComparable(String termtext) {
+        throw new UnsupportedOperationException();
+    }
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/SharedFieldSortComparator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SimpleQueryTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SimpleQueryTest.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SimpleQueryTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SimpleQueryTest.java Fri Apr 15 02:34:14 2005
@@ -356,50 +356,72 @@
                 "and jcr:path LIKE '" + testRoot + "/%'";
         Query q = superuser.getWorkspace().getQueryManager().createQuery(sql, Query.SQL);
         QueryResult result = q.execute();
-        checkResult(result, 3); // foo, bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
 
         String xpath = "/" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text = 'foo']";
         q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
         result = q.execute();
-        checkResult(result, 3); // foo, bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
 
         xpath = "/jcr:root" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text = 'foo']";
         q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
         result = q.execute();
-        checkResult(result, 3); // foo, bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
+
+        //----------------------------------------------------------------------
 
         // the text = 'foo' is now also a general comparison
         sql = "SELECT * FROM nt:unstructured WHERE text = 'foo' " +
                 "and jcr:path LIKE '" + testRoot + "/%'";
         q = superuser.getWorkspace().getQueryManager().createQuery(sql, Query.SQL);
         result = q.execute();
-        checkResult(result, 3); // foo, bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
 
         xpath = "/" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text eq 'foo']";
         q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
         result = q.execute();
-        checkResult(result, 2); // bar, bla
+        checkResult(result, new Node[]{bar, bla});
 
         xpath = "/jcr:root" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text eq 'foo']";
         q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
         result = q.execute();
-        checkResult(result, 2); // bar, bla
+        checkResult(result, new Node[]{bar, bla});
+
+        //----------------------------------------------------------------------
 
         sql = "SELECT * FROM nt:unstructured WHERE 'bar' NOT IN text " +
                 "and jcr:path LIKE '" + testRoot + "/%'";
         q = superuser.getWorkspace().getQueryManager().createQuery(sql, Query.SQL);
         result = q.execute();
-        checkResult(result, 2); // bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
 
         xpath = "/" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text != 'bar']";
         q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
         result = q.execute();
-        checkResult(result, 2); // bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
 
         xpath = "/jcr:root" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text != 'bar']";
         q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
         result = q.execute();
-        checkResult(result, 2); // bar, bla
+        checkResult(result, new Node[]{foo, bar, bla});
+
+        //----------------------------------------------------------------------
+
+        sql = "SELECT * FROM nt:unstructured WHERE 'foo' NOT IN text " +
+                "and jcr:path LIKE '" + testRoot + "/%'";
+        q = superuser.getWorkspace().getQueryManager().createQuery(sql, Query.SQL);
+        result = q.execute();
+        checkResult(result, new Node[]{foo, blu});
+
+        xpath = "/" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text != 'foo']";
+        q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
+        result = q.execute();
+        checkResult(result, new Node[]{foo, blu});
+
+        xpath = "/jcr:root" + testRoot + "/*[@jcr:primaryType='nt:unstructured' and @text != 'foo']";
+        q = superuser.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH);
+        result = q.execute();
+        checkResult(result, new Node[]{foo, blu});
     }
 
 }

Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/GetPersistentQueryPathLevel1Test.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/GetPersistentQueryPathLevel1Test.java?view=diff&r1=161424&r2=161425
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/GetPersistentQueryPathLevel1Test.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/GetPersistentQueryPathLevel1Test.java Fri Apr 15 02:34:14 2005
@@ -58,7 +58,7 @@
      * Tests if a non-persistent query throws an {@link ItemNotFoundException}
      * when {@link Query#getStoredQueryPath()} is called.
      */
-    public void getStoredQueryPath() throws RepositoryException {
+    public void testGetStoredQueryPath() throws RepositoryException {
         String statement = "/" + jcrRoot;
         Query q = session.getWorkspace().getQueryManager().createQuery(statement, Query.XPATH);
         try {



Mime
View raw message