Index: src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (revision 1002043) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (working copy) @@ -69,6 +69,7 @@ import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.BooleanClause.Occur; @@ -265,7 +266,28 @@ Object[] result = node.acceptOperands(this, null); for (Object aResult : result) { Query operand = (Query) aResult; - orQuery.add(operand, Occur.SHOULD); + if (operand instanceof BooleanQuery) { + // check if the clauses are all optional, then + // we can collapse into the the enclosing orQuery + boolean hasNonOptional = false; + for (BooleanClause clause : ((BooleanQuery) operand).getClauses()) { + if (clause.isProhibited() || clause.isRequired()) { + hasNonOptional = true; + break; + } + } + if (hasNonOptional) { + // cannot collapse + orQuery.add(operand, Occur.SHOULD); + } else { + // collapse + for (BooleanClause clause : ((BooleanQuery) operand).getClauses()) { + orQuery.add(clause); + } + } + } else { + orQuery.add(operand, Occur.SHOULD); + } } return orQuery; } Index: src/test/java/org/apache/jackrabbit/core/query/FulltextQueryTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/query/FulltextQueryTest.java (revision 1002043) +++ src/test/java/org/apache/jackrabbit/core/query/FulltextQueryTest.java (working copy) @@ -17,6 +17,9 @@ package org.apache.jackrabbit.core.query; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; import javax.jcr.Node; import javax.jcr.RepositoryException; @@ -24,7 +27,10 @@ import javax.jcr.query.InvalidQueryException; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; +import javax.jcr.query.Row; +import org.apache.jackrabbit.commons.iterator.RowIterable; + /** * Performs tests with the CONTAINS function. */ @@ -267,6 +273,46 @@ executeContainsQuery("foo:bar", "foo:bar", true); } + public void testMultipleOrExpressions() throws RepositoryException { + Node n = testRootNode.addNode("node1"); + n.setProperty("prop1", "foo"); + n.setProperty("prop2", "bar"); + n.setProperty("prop3", "baz"); + + n = testRootNode.addNode("node2"); + n.setProperty("prop1", "bar"); + n.setProperty("prop2", "foo"); + n.setProperty("prop3", "baz"); + + n = testRootNode.addNode("node3"); + n.setProperty("prop1", "bar"); + n.setProperty("prop2", "baz"); + n.setProperty("prop3", "foo"); + + superuser.save(); + + List r1 = new ArrayList(); + QueryResult result = qm.createQuery(testPath + "/*[jcr:contains(@prop1, 'foo') or jcr:contains(@prop2, 'foo') or jcr:contains(@prop3, 'foo')] order by @jcr:score descending", Query.XPATH).execute(); + for (Row r : new RowIterable(result.getRows())) { + r1.add(r.getPath() + ":" + (int) (r.getScore() * 1000)); + } + + List r2 = new ArrayList(); + result = qm.createQuery(testPath + "/*[jcr:contains(@prop3, 'foo') or jcr:contains(@prop1, 'foo') or jcr:contains(@prop2, 'foo')] order by @jcr:score descending", Query.XPATH).execute(); + for (Row r : new RowIterable(result.getRows())) { + r2.add(r.getPath() + ":" + (int) (r.getScore() * 1000)); + } + + List r3 = new ArrayList(); + result = qm.createQuery(testPath + "/*[jcr:contains(@prop2, 'foo') or jcr:contains(@prop3, 'foo') or jcr:contains(@prop1, 'foo')] order by @jcr:score descending", Query.XPATH).execute(); + for (Row r : new RowIterable(result.getRows())) { + r3.add(r.getPath() + ":" + (int) (r.getScore() * 1000)); + } + + assertEquals(r1, r2); + assertEquals(r1, r3); + } + /** * Executes a query and checks if the query matched the test node. *