Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java (working copy) @@ -63,6 +63,7 @@ import org.apache.jackrabbit.oak.query.ast.SimilarImpl; import org.apache.jackrabbit.oak.query.ast.SourceImpl; import org.apache.jackrabbit.oak.query.ast.SpellcheckImpl; +import org.apache.jackrabbit.oak.query.ast.SuggestImpl; import org.apache.jackrabbit.oak.query.ast.UpperCaseImpl; import org.apache.jackrabbit.oak.query.index.FilterImpl; import org.apache.jackrabbit.oak.query.index.TraversingIndex; @@ -107,6 +108,11 @@ */ public static final String REP_SPELLCHECK = "rep:spellcheck()"; + /** + * The "rep:suggest" pseudo-property. + */ + public static final String REP_SUGGEST = "rep:suggest()"; + private static final Logger LOG = LoggerFactory.getLogger(QueryImpl.class); SourceImpl source; @@ -251,6 +257,13 @@ } @Override + public boolean visit(SuggestImpl node) { + node.setQuery(query); + node.bindSelector(source); + return super.visit(node); + } + + @Override public boolean visit(FullTextSearchScoreImpl node) { node.setQuery(query); node.bindSelector(source); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java (working copy) @@ -566,6 +566,15 @@ selectorName = getOnlySelectorName(); } c = factory.spellcheck(selectorName, parseStaticOperand()); + } else if ("SUGGEST".equalsIgnoreCase(functionName)) { + String selectorName; + if (currentTokenType == IDENTIFIER) { + selectorName = readName(); + read(","); + } else { + selectorName = getOnlySelectorName(); + } + c = factory.suggest(selectorName, parseStaticOperand()); } else { return null; } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java (working copy) @@ -161,4 +161,7 @@ return new SpellcheckImpl(selectorName, expression); } + public ConstraintImpl suggest(String selectorName, StaticOperandImpl expression) { + return new SuggestImpl(selectorName, expression); + } } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitor.java (working copy) @@ -85,4 +85,5 @@ boolean visit(SpellcheckImpl node); + boolean visit(SuggestImpl suggest); } \ No newline at end of file Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstVisitorBase.java (working copy) @@ -93,6 +93,15 @@ } /** + * Calls accept on the static operand in the suggest search constraint. + */ + @Override + public boolean visit(SuggestImpl node) { + node.getExpression().accept(this); + return true; + } + + /** * Calls accept on the two sources and the join condition in the join node. */ @Override Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (working copy) @@ -635,7 +635,11 @@ } else if (oakPropertyName.equals(QueryImpl.REP_EXCERPT)) { result = currentRow.getValue(QueryImpl.REP_EXCERPT); } else if (oakPropertyName.equals(QueryImpl.REP_SPELLCHECK)) { + // TODO : filter spellcheck corrections by ACLs ? result = currentRow.getValue(QueryImpl.REP_SPELLCHECK); + } else if (oakPropertyName.equals(QueryImpl.REP_SUGGEST)) { + // TODO : filter suggestions by ACLs + result = currentRow.getValue(QueryImpl.REP_SUGGEST); } else { result = PropertyValues.create(t.getProperty(oakPropertyName)); } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SuggestImpl.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SuggestImpl.java (revision 0) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SuggestImpl.java (working copy) @@ -0,0 +1,112 @@ +/* + * 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.ast; + +import java.util.Collections; +import java.util.Set; + +import org.apache.jackrabbit.oak.api.PropertyValue; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.query.index.FilterImpl; +import org.apache.jackrabbit.oak.spi.query.PropertyValues; +import org.apache.jackrabbit.oak.spi.query.QueryIndex.FulltextQueryIndex; + +/** + * Support for "suggest(...) + */ +public class SuggestImpl extends ConstraintImpl { + + public static final String NATIVE_LUCENE_LANGUAGE = "lucene"; + + public static final String SUGGEST_PREFIX = "suggest?term="; + + private final String selectorName; + private final StaticOperandImpl expression; + private SelectorImpl selector; + + SuggestImpl(String selectorName, StaticOperandImpl expression) { + this.selectorName = selectorName; + this.expression = expression; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("suggest("); + builder.append(quote(selectorName)); + builder.append(", "); + builder.append(getExpression()); + builder.append(')'); + return builder.toString(); + } + + @Override + public boolean evaluate() { + // disable evaluation if a fulltext index is used, + // and because we don't know how to process native + // conditions + if (!(selector.getIndex() instanceof FulltextQueryIndex)) { + throw new IllegalArgumentException("No full-text index was found that can process the condition " + toString()); + } + // we assume the index only returns the requested entries + return true; + } + + @Override + public Set getPropertyExistenceConditions() { + return Collections.emptySet(); + } + + @Override + public void restrict(FilterImpl f) { + if (f.getSelector().equals(selector)) { + PropertyValue p = expression.currentValue(); + String term = p.getValue(Type.STRING); + String query = SUGGEST_PREFIX + term; + PropertyValue v = PropertyValues.newString(query); + f.restrictProperty(NativeFunctionImpl.NATIVE_PREFIX + NATIVE_LUCENE_LANGUAGE, Operator.EQUAL, v); + } + } + + @Override + public void restrictPushDown(SelectorImpl s) { + if (s.equals(selector)) { + selector.restrictSelector(this); + } + } + + @Override + public Set getSelectors() { + return Collections.emptySet(); + } + + @Override + boolean accept(AstVisitor v) { + return v.visit(this); + } + + public void bindSelector(SourceImpl source) { + selector = source.getExistingSelector(selectorName); + } + + public StaticOperandImpl getExpression() { + return expression; + } + +} \ No newline at end of file Property changes on: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SuggestImpl.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/Expression.java (working copy) @@ -578,6 +578,37 @@ } /** + * A rep:suggest condition. + */ + static class Suggest extends Expression { + + final Expression term; + + Suggest(Expression term) { + this.term = term; + } + + @Override + public String toString() { + StringBuilder buff = new StringBuilder("suggest("); + buff.append(term); + buff.append(')'); + return buff.toString(); + } + + @Override + boolean isCondition() { + return true; + } + + @Override + boolean isName() { + return false; + } + + } + + /** * A function call. */ static class Function extends Expression { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java (revision 1656768) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/xpath/XPathToSQL2Converter.java (working copy) @@ -215,6 +215,10 @@ read(")"); Expression.Property p = new Expression.Property(currentSelector, "rep:spellcheck()", false); statement.addSelectColumn(p); + } else if (readIf("rep:suggest")) { + readExcerpt(); + Expression.Property p = new Expression.Property(currentSelector, "rep:suggest()", false); + statement.addSelectColumn(p); } } while (readIf("|")); read(")"); @@ -631,9 +635,13 @@ Expression term = parseExpression(); read(")"); return new Expression.Spellcheck(term); + } else if ("rep:suggest".equals(functionName)) { + Expression term = parseExpression(); + read(")"); + return new Expression.Suggest(term); } else { throw getSyntaxError("jcr:like | jcr:contains | jcr:score | xs:dateTime | " + - "fn:lower-case | fn:upper-case | fn:name | rep:similar | rep:spellcheck"); + "fn:lower-case | fn:upper-case | fn:name | rep:similar | rep:spellcheck | rep:suggest"); } }