Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java (revision 1644617) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java (working copy) @@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd; +import org.apache.jackrabbit.oak.query.fulltext.FullTextContains; import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression; import org.apache.jackrabbit.oak.query.fulltext.FullTextOr; import org.apache.jackrabbit.oak.query.fulltext.FullTextTerm; @@ -112,6 +113,11 @@ composite.set(false); ft.accept(new FullTextVisitor() { + + @Override + public boolean visit(FullTextContains contains) { + return contains.getBase().accept(this); + } @Override public boolean visit(FullTextTerm term) { @@ -163,6 +169,11 @@ } final AtomicReference result = new AtomicReference(); constraint.accept(new FullTextVisitor() { + + @Override + public boolean visit(FullTextContains contains) { + return contains.getBase().accept(this); + } @Override public boolean visit(FullTextTerm term) { @@ -216,6 +227,11 @@ final String path) { constraint.accept(new FullTextVisitor() { + + @Override + public boolean visit(FullTextContains contains) { + return contains.getBase().accept(this); + } @Override public boolean visit(FullTextTerm term) { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java (revision 1644617) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java (working copy) @@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.query.fulltext.FullTextContains; import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression; import org.apache.jackrabbit.oak.query.fulltext.FullTextParser; import org.apache.jackrabbit.oak.query.index.FilterImpl; @@ -138,7 +139,9 @@ p = PathUtils.concat(relativePath, p); } String p2 = normalizePropertyName(p); - return FullTextParser.parse(p2, v.getValue(Type.STRING)); + String rawText = v.getValue(Type.STRING); + FullTextExpression e = FullTextParser.parse(p2, rawText); + return new FullTextContains(p2, rawText, e); } catch (ParseException e) { throw new IllegalArgumentException("Invalid expression: " + fullTextSearchExpression, e); } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextContains.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextContains.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextContains.java (working copy) @@ -0,0 +1,78 @@ +/* + * 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.oak.query.fulltext; + +/** + * A group of full-text expressions that reflects a "contains(...)" expression, + * and allows to access the original (unparsed) full text term. + */ +public class FullTextContains extends FullTextExpression { + + private final String propertyName; + private final String rawText; + private final FullTextExpression base; + + public FullTextContains(String propertyName, String rawText, FullTextExpression base) { + this.propertyName = propertyName; + this.rawText = rawText; + this.base = base; + } + + @Override + public int getPrecedence() { + return base.getPrecedence(); + } + + @Override + public boolean evaluate(String value) { + return base.evaluate(value); + } + + @Override + FullTextExpression simplify() { + FullTextExpression s = base.simplify(); + if (s == base) { + return this; + } + return new FullTextContains(propertyName, rawText, s); + } + + @Override + public String toString() { + return base.toString(); + } + + @Override + public boolean accept(FullTextVisitor v) { + return v.visit(this); + } + + public FullTextExpression getBase() { + return base; + } + + public String getPropertyName() { + return propertyName; + } + + public String getRawText() { + return rawText; + } + +} Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextVisitor.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextVisitor.java (revision 1644617) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/fulltext/FullTextVisitor.java (working copy) @@ -24,6 +24,14 @@ * likely visit(FullTextTerm). */ public interface FullTextVisitor { + + /** + * Visit an "contains" expression. + * + * @param contains the "contains" expression + * @return true if visiting should continue + */ + boolean visit(FullTextContains contains); /** * Visit an "and" expression. @@ -55,6 +63,11 @@ public abstract static class FullTextVisitorBase implements FullTextVisitor { @Override + public boolean visit(FullTextContains contains) { + return contains.getBase().accept(this); + } + + @Override public boolean visit(FullTextAnd and) { for (FullTextExpression e : and.list) { if (!e.accept(this)) { @@ -73,7 +86,7 @@ } return true; } - + } } Index: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java =================================================================== --- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java (revision 1644617) +++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java (working copy) @@ -61,6 +61,7 @@ import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.QueryImpl; import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd; +import org.apache.jackrabbit.oak.query.fulltext.FullTextContains; import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression; import org.apache.jackrabbit.oak.query.fulltext.FullTextOr; import org.apache.jackrabbit.oak.query.fulltext.FullTextTerm; @@ -701,6 +702,11 @@ // (a "non-local return") final AtomicReference result = new AtomicReference(); ft.accept(new FullTextVisitor() { + + @Override + public boolean visit(FullTextContains contains) { + return contains.getBase().accept(this); + } @Override public boolean visit(FullTextOr or) { @@ -734,19 +740,22 @@ @Override public boolean visit(FullTextTerm term) { - String p = term.getPropertyName(); + return visitTerm(term.getPropertyName(), term.getText(), term.getBoost(), term.isNot()); + } + + private boolean visitTerm(String propertyName, String text, String boost, boolean not) { + String p = propertyName; if (p != null && p.indexOf('/') >= 0) { p = getName(p); } - Query q = tokenToQuery(term.getText(), p, analyzer, reader); + Query q = tokenToQuery(text, p, analyzer, reader); if (q == null) { return false; } - String boost = term.getBoost(); if (boost != null) { q.setBoost(Float.parseFloat(boost)); } - if (term.isNot()) { + if (not) { BooleanQuery bq = new BooleanQuery(); bq.add(q, MUST_NOT); result.set(bq); Index: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java =================================================================== --- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java (revision 1644617) +++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java (working copy) @@ -45,6 +45,7 @@ import org.apache.jackrabbit.oak.query.QueryEngineSettings; import org.apache.jackrabbit.oak.query.QueryImpl; import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd; +import org.apache.jackrabbit.oak.query.fulltext.FullTextContains; import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression; import org.apache.jackrabbit.oak.query.fulltext.FullTextOr; import org.apache.jackrabbit.oak.query.fulltext.FullTextTerm; @@ -99,6 +100,7 @@ import static org.apache.jackrabbit.oak.api.Type.LONG; import static org.apache.jackrabbit.oak.api.Type.STRING; import static org.apache.jackrabbit.oak.commons.PathUtils.denotesRoot; +import static org.apache.jackrabbit.oak.commons.PathUtils.getName; import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath; import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.PATH; import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION; @@ -802,6 +804,12 @@ // (a "non-local return") final AtomicReference result = new AtomicReference(); ft.accept(new FullTextVisitor() { + + @Override + public boolean visit(FullTextContains contains) { + visitTerm(contains.getPropertyName(), contains.getRawText(), null, false); + return true; + } @Override public boolean visit(FullTextOr or) { @@ -832,22 +840,25 @@ result.set(q); return true; } - + @Override public boolean visit(FullTextTerm term) { - String p = term.getPropertyName(); - if (p != null) { - p = getLuceneFieldName(p, pr); + return visitTerm(term.getPropertyName(), term.getText(), term.getBoost(), term.isNot()); + } + + private boolean visitTerm(String propertyName, String text, String boost, boolean not) { + String p = propertyName; + if (p != null && p.indexOf('/') >= 0) { + p = getName(p); } - Query q = tokenToQuery(term.getText(), p, analyzer, reader); + Query q = tokenToQuery(text, p, analyzer, reader); if (q == null) { return false; } - String boost = term.getBoost(); if (boost != null) { q.setBoost(Float.parseFloat(boost)); } - if (term.isNot()) { + if (not) { BooleanQuery bq = new BooleanQuery(); bq.add(q, MUST_NOT); result.set(bq); Index: oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java =================================================================== --- oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java (revision 1644617) +++ oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java (working copy) @@ -92,6 +92,22 @@ .with(new NodeTypeIndexProvider()) .createContentRepository(); } + +// @Test +// public void fulltextSearchWithCustomAnalyzer() throws Exception{ +// Tree idx = createFulltextIndex(root.getTree("/"), "test"); +// TestUtil.useV2(idx); +// +// Tree anl = idx.addChild(ANALYZERS).addChild(ANL_DEFAULT); +// anl.addChild(ANL_TOKENIZER).setProperty(ANL_NAME, "whitespace"); +// anl.addChild(ANL_FILTERS).addChild("stop"); +// +// Tree test = root.getTree("/").addChild("test"); +// test.setProperty("foo", "fox jumping"); +// root.commit(); +// +// assertQuery("select * from [nt:base] where CONTAINS(*, 'fox was jumping')", asList("/test")); +// } @Test public void indexSelection() throws Exception {