mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-24 15:55:55 +00:00
#594 - ElasticSearch - multi match support
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -292,6 +293,11 @@ public interface ExpressionFactory {
|
||||
*/
|
||||
Expression textMatch(String propertyName, String search, Match options);
|
||||
|
||||
/**
|
||||
* Create a Text Multi match expression (currently doc store/Elastic only).
|
||||
*/
|
||||
Expression textMultiMatch(String query, MultiMatch options);
|
||||
|
||||
/**
|
||||
* And - join two expressions with a logical and.
|
||||
*/
|
||||
@@ -334,4 +340,5 @@ public interface ExpressionFactory {
|
||||
* </p>
|
||||
*/
|
||||
<T> TextJunction<T> textJunction(Query<T> query, TextExpressionList<T> parent, TextJunction.Type type);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
|
||||
/**
|
||||
* An list of Full text query expressions.
|
||||
@@ -26,6 +27,16 @@ public interface TextExpressionList<T> extends ExpressionList<T> {
|
||||
*/
|
||||
TextExpressionList<T> match(String propertyName, String search, Match options);
|
||||
|
||||
/**
|
||||
* Add a multi-match expression.
|
||||
*/
|
||||
TextExpressionList<T> multiMatch(String search, String... properties);
|
||||
|
||||
/**
|
||||
* Add a multi-match expression using options.
|
||||
*/
|
||||
TextExpressionList<T> multiMatch(String search, MultiMatch options);
|
||||
|
||||
/**
|
||||
* Start a list of expressions that will be joined by MUST.
|
||||
*/
|
||||
@@ -55,4 +66,5 @@ public interface TextExpressionList<T> extends ExpressionList<T> {
|
||||
* End the list of MUST NOT expressions.
|
||||
*/
|
||||
TextExpressionList<T> endMustNot();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.avaje.ebean.search;
|
||||
|
||||
/**
|
||||
* Options for the text match expression.
|
||||
*/
|
||||
public abstract class BaseMatch {
|
||||
|
||||
protected boolean and;
|
||||
|
||||
protected String analyzer;
|
||||
|
||||
protected double boost;
|
||||
|
||||
protected String minShouldMatch;
|
||||
|
||||
protected int maxExpansions;
|
||||
|
||||
protected String zeroTerms;
|
||||
|
||||
protected double cutoffFrequency;
|
||||
|
||||
protected String fuzziness;
|
||||
|
||||
protected int prefixLength;
|
||||
|
||||
protected String rewrite;
|
||||
|
||||
/**
|
||||
* Return true if using the AND operator otherwise using the OR operator.
|
||||
*/
|
||||
public boolean isAnd() {
|
||||
return and;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the boost.
|
||||
*/
|
||||
public double getBoost() {
|
||||
return boost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum should match.
|
||||
*/
|
||||
public String getMinShouldMatch() {
|
||||
return minShouldMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the zero terms option.
|
||||
*/
|
||||
public String getZeroTerms() {
|
||||
return zeroTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cutoff frequency.
|
||||
*/
|
||||
public double getCutoffFrequency() {
|
||||
return cutoffFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the max expansions.
|
||||
*/
|
||||
public int getMaxExpansions() {
|
||||
return maxExpansions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the analyzer.
|
||||
*/
|
||||
public String getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fuzziness.
|
||||
*/
|
||||
public String getFuzziness() {
|
||||
return fuzziness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prefix length.
|
||||
*/
|
||||
public int getPrefixLength() {
|
||||
return prefixLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rewrite option.
|
||||
*/
|
||||
public String getRewrite() {
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
protected boolean baseEquals(BaseMatch baseMatch) {
|
||||
|
||||
if (and != baseMatch.and) return false;
|
||||
if (Double.compare(baseMatch.boost, boost) != 0) return false;
|
||||
if (maxExpansions != baseMatch.maxExpansions) return false;
|
||||
if (Double.compare(baseMatch.cutoffFrequency, cutoffFrequency) != 0) return false;
|
||||
if (prefixLength != baseMatch.prefixLength) return false;
|
||||
if (analyzer != null ? !analyzer.equals(baseMatch.analyzer) : baseMatch.analyzer != null) return false;
|
||||
if (minShouldMatch != null ? !minShouldMatch.equals(baseMatch.minShouldMatch) : baseMatch.minShouldMatch != null) return false;
|
||||
if (zeroTerms != null ? !zeroTerms.equals(baseMatch.zeroTerms) : baseMatch.zeroTerms != null) return false;
|
||||
if (fuzziness != null ? !fuzziness.equals(baseMatch.fuzziness) : baseMatch.fuzziness != null) return false;
|
||||
return rewrite != null ? rewrite.equals(baseMatch.rewrite) : baseMatch.rewrite == null;
|
||||
}
|
||||
|
||||
protected int baseHashCode() {
|
||||
|
||||
int result;
|
||||
long temp;
|
||||
result = (and ? 1 : 0);
|
||||
result = 31 * result + (analyzer != null ? analyzer.hashCode() : 0);
|
||||
temp = Double.doubleToLongBits(boost);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (minShouldMatch != null ? minShouldMatch.hashCode() : 0);
|
||||
result = 31 * result + maxExpansions;
|
||||
result = 31 * result + (zeroTerms != null ? zeroTerms.hashCode() : 0);
|
||||
temp = Double.doubleToLongBits(cutoffFrequency);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (fuzziness != null ? fuzziness.hashCode() : 0);
|
||||
result = 31 * result + prefixLength;
|
||||
result = 31 * result + (rewrite != null ? rewrite.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -3,26 +3,12 @@ package com.avaje.ebean.search;
|
||||
/**
|
||||
* Options for the text match expression.
|
||||
*/
|
||||
public class Match {
|
||||
|
||||
protected boolean and;
|
||||
|
||||
protected double boost;
|
||||
|
||||
protected String minShouldMatch;
|
||||
|
||||
protected String zeroTerms;
|
||||
|
||||
protected double cutoffFrequency;
|
||||
|
||||
protected String analyzer;
|
||||
public class Match extends BaseMatch {
|
||||
|
||||
protected boolean phrase;
|
||||
|
||||
protected boolean phrasePrefix;
|
||||
|
||||
protected int maxExpansions;
|
||||
|
||||
/**
|
||||
* Create and return Match options using AND operator.
|
||||
*/
|
||||
@@ -37,38 +23,6 @@ public class Match {
|
||||
return new Match().opOr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the AND operator (rather than OR).
|
||||
*/
|
||||
public Match opAnd() {
|
||||
and = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the OR operator (rather than AND).
|
||||
*/
|
||||
public Match opOr() {
|
||||
and = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match value.
|
||||
*/
|
||||
public Match minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boost.
|
||||
*/
|
||||
public Match boost(double boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to be a "Phrase" type expression.
|
||||
*/
|
||||
@@ -85,6 +39,22 @@ public class Match {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the AND operator (rather than OR).
|
||||
*/
|
||||
public Match opAnd() {
|
||||
and = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the OR operator (rather than AND).
|
||||
*/
|
||||
public Match opOr() {
|
||||
and = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the zero terms.
|
||||
*/
|
||||
@@ -118,50 +88,43 @@ public class Match {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if using the AND operator otherwise using the OR operator.
|
||||
* Set the boost.
|
||||
*/
|
||||
public boolean isAnd() {
|
||||
return and;
|
||||
public Match boost(double boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the boost.
|
||||
* Set the rewrite to use.
|
||||
*/
|
||||
public double getBoost() {
|
||||
return boost;
|
||||
public Match minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum should match.
|
||||
* Set the rewrite to use.
|
||||
*/
|
||||
public String getMinShouldMatch() {
|
||||
return minShouldMatch;
|
||||
}
|
||||
|
||||
public String getZeroTerms() {
|
||||
return zeroTerms;
|
||||
}
|
||||
|
||||
public double getCutoffFrequency() {
|
||||
return cutoffFrequency;
|
||||
public Match rewrite(String rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a phrase query.
|
||||
*/
|
||||
public boolean isPhrase() {
|
||||
return phrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a phrase prefix query.
|
||||
*/
|
||||
public boolean isPhrasePrefix() {
|
||||
return phrasePrefix;
|
||||
}
|
||||
|
||||
public int getMaxExpansions() {
|
||||
return maxExpansions;
|
||||
}
|
||||
|
||||
public String getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -169,33 +132,16 @@ public class Match {
|
||||
|
||||
Match match = (Match) o;
|
||||
|
||||
if (and != match.and) return false;
|
||||
if (Double.compare(match.boost, boost) != 0) return false;
|
||||
if (Double.compare(match.cutoffFrequency, cutoffFrequency) != 0) return false;
|
||||
if (phrase != match.phrase) return false;
|
||||
if (phrasePrefix != match.phrasePrefix) return false;
|
||||
if (maxExpansions != match.maxExpansions) return false;
|
||||
if (minShouldMatch != null ? !minShouldMatch.equals(match.minShouldMatch) : match.minShouldMatch != null)
|
||||
return false;
|
||||
if (zeroTerms != null ? !zeroTerms.equals(match.zeroTerms) : match.zeroTerms != null) return false;
|
||||
return analyzer != null ? analyzer.equals(match.analyzer) : match.analyzer == null;
|
||||
return baseEquals(match);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = (and ? 1 : 0);
|
||||
temp = Double.doubleToLongBits(boost);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (minShouldMatch != null ? minShouldMatch.hashCode() : 0);
|
||||
result = 31 * result + (zeroTerms != null ? zeroTerms.hashCode() : 0);
|
||||
temp = Double.doubleToLongBits(cutoffFrequency);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (analyzer != null ? analyzer.hashCode() : 0);
|
||||
result = 31 * result + (phrase ? 1 : 0);
|
||||
int result = (phrase ? 1 : 0);
|
||||
result = 31 * result + (phrasePrefix ? 1 : 0);
|
||||
result = 31 * result + maxExpansions;
|
||||
result = 31 * result + baseHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.avaje.ebean.search;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Options for the text match expression.
|
||||
*/
|
||||
public class MultiMatch extends BaseMatch {
|
||||
|
||||
/**
|
||||
* The MultiMatch type.
|
||||
*/
|
||||
public enum Type {
|
||||
BEST_FIELDS,
|
||||
MOST_FIELDS,
|
||||
CROSS_FIELDS,
|
||||
PHRASE,
|
||||
PHRASE_PREFIX
|
||||
}
|
||||
|
||||
protected Type type = Type.BEST_FIELDS;
|
||||
|
||||
protected String[] fields;
|
||||
|
||||
protected double tieBreaker;
|
||||
|
||||
/**
|
||||
* Create and return Match options using AND operator.
|
||||
*/
|
||||
public static MultiMatch fields(String... fields) {
|
||||
return new MultiMatch(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return Match options using OR operator.
|
||||
*/
|
||||
public static MultiMatch OR() {
|
||||
return new MultiMatch().opOr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with a set of fields.
|
||||
*/
|
||||
public MultiMatch(String... fields) {
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of query.
|
||||
*/
|
||||
public MultiMatch type(Type type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tieBreaker to use.
|
||||
*/
|
||||
public MultiMatch tieBreaker(double tieBreaker) {
|
||||
this.tieBreaker = tieBreaker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the AND operator (rather than OR).
|
||||
*/
|
||||
public MultiMatch opAnd() {
|
||||
and = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the OR operator (rather than AND).
|
||||
*/
|
||||
public MultiMatch opOr() {
|
||||
and = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum should match value.
|
||||
*/
|
||||
public MultiMatch minShouldMatch(String minShouldMatch) {
|
||||
this.minShouldMatch = minShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boost.
|
||||
*/
|
||||
public MultiMatch boost(double boost) {
|
||||
this.boost = boost;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the zero terms.
|
||||
*/
|
||||
public MultiMatch zeroTerms(String zeroTerms) {
|
||||
this.zeroTerms = zeroTerms;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cutoff frequency.
|
||||
*/
|
||||
public MultiMatch cutoffFrequency(double cutoffFrequency) {
|
||||
this.cutoffFrequency = cutoffFrequency;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max expansions (for phrase prefix only).
|
||||
*/
|
||||
public MultiMatch maxExpansions(int maxExpansions) {
|
||||
this.maxExpansions = maxExpansions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Analyzer to use for this expression.
|
||||
*/
|
||||
public MultiMatch analyzer(String analyzer) {
|
||||
this.analyzer = analyzer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rewrite to use.
|
||||
*/
|
||||
public MultiMatch rewrite(String rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type.
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fields to search.
|
||||
*/
|
||||
public String[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tie breaker.
|
||||
*/
|
||||
public double getTieBreaker() {
|
||||
return tieBreaker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MultiMatch that = (MultiMatch) o;
|
||||
|
||||
if (Double.compare(that.tieBreaker, tieBreaker) != 0) return false;
|
||||
if (type != that.type) return false;
|
||||
if (fields.length != that.fields.length) return false;
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
if (!fields[i].equals(that.fields[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return baseEquals(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = type.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(fields);
|
||||
temp = Double.doubleToLongBits(tieBreaker);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + baseHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.avaje.ebean.Query;
|
||||
import com.avaje.ebean.TextExpressionList;
|
||||
import com.avaje.ebean.TextJunction;
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiTextJunction;
|
||||
|
||||
@@ -47,6 +48,16 @@ class DTextJunction<T> extends JunctionExpression<T> implements SpiTextJunction<
|
||||
return exprList.match(propertyName, search, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextExpressionList<T> multiMatch(String query, String... properties) {
|
||||
return exprList.multiMatch(query, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextExpressionList<T> multiMatch(String query, MultiMatch options) {
|
||||
return exprList.multiMatch(query, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextJunction<T> must() {
|
||||
return exprList.must();
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.avaje.ebean.TextExpressionList;
|
||||
import com.avaje.ebean.TextJunction;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
|
||||
@@ -44,6 +45,11 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
return new TextMatchExpression(propertyName, search, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression textMultiMatch(String query, MultiMatch options) {
|
||||
return new TextMultiMatchExpression(query, options);
|
||||
}
|
||||
|
||||
public Expression jsonExists(String propertyName, String path) {
|
||||
return new JsonPathExpression(propertyName, path, Op.EXISTS, null);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
import com.avaje.ebean.*;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.ManyWhereJoins;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -864,6 +865,17 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T>, SpiTextEx
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextExpressionList<T> multiMatch(String query, String... fields) {
|
||||
return multiMatch(query, new MultiMatch().fields(fields));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextExpressionList<T> multiMatch(String query, MultiMatch options) {
|
||||
add(expr.textMultiMatch(query, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextJunction<T> must() {
|
||||
return textJunction(TextJunction.Type.MUST);
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.expression;
|
||||
import com.avaje.ebean.LikeType;
|
||||
import com.avaje.ebean.TextJunction;
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -98,6 +99,11 @@ public interface DocQueryContext {
|
||||
*/
|
||||
void writeMatch(String propName, String search, Match options) throws IOException;
|
||||
|
||||
/**
|
||||
* Write a Multi-match expression.
|
||||
*/
|
||||
void writeMultiMatch(String search, MultiMatch options) throws IOException;
|
||||
|
||||
/**
|
||||
* Start a Bool which may contain some of Must, Must Not, Should.
|
||||
*/
|
||||
@@ -117,4 +123,5 @@ public interface DocQueryContext {
|
||||
* End the Bool group.
|
||||
*/
|
||||
void endBoolGroup() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.search.BaseMatch;
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
@@ -44,9 +45,9 @@ public class TextMatchExpression extends AbstractExpression {
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(TextMatchExpression.class).add(propName).add(search);
|
||||
if (options != null) {
|
||||
builder.add(options.isAnd());
|
||||
builder.add(options.getBoost());
|
||||
builder.add(options.getMinShouldMatch());
|
||||
builder.add(options.isPhrase());
|
||||
builder.add(options.isPhrasePrefix());
|
||||
addHash(builder, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,4 +73,20 @@ public class TextMatchExpression extends AbstractExpression {
|
||||
TextMatchExpression that = (TextMatchExpression) other;
|
||||
return search.equals(that.search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the hash to the builder for the base/common options.
|
||||
*/
|
||||
public static void addHash(HashQueryPlanBuilder builder, BaseMatch options) {
|
||||
builder.add(options.isAnd());
|
||||
builder.add(options.getAnalyzer());
|
||||
builder.add(options.getBoost());
|
||||
builder.add(options.getCutoffFrequency());
|
||||
builder.add(options.getFuzziness());
|
||||
builder.add(options.getMaxExpansions());
|
||||
builder.add(options.getMinShouldMatch());
|
||||
builder.add(options.getPrefixLength());
|
||||
builder.add(options.getRewrite());
|
||||
builder.add(options.getZeroTerms());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import com.avaje.ebeaninternal.api.SpiExpression;
|
||||
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Full text Multi-Match expression.
|
||||
*/
|
||||
public class TextMultiMatchExpression extends AbstractExpression {
|
||||
|
||||
private final String search;
|
||||
|
||||
private final MultiMatch options;
|
||||
|
||||
public TextMultiMatchExpression(String search, MultiMatch options) {
|
||||
super(null);
|
||||
this.search = search;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDocQuery(DocQueryContext context) throws IOException {
|
||||
context.writeMultiMatch(search, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
throw new IllegalStateException("Not implemented - DocStore/Elastic only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
throw new IllegalStateException("Not implemented - DocStore/Elastic only");
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the type and propertyName.
|
||||
*/
|
||||
@Override
|
||||
public void queryPlanHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(TextMultiMatchExpression.class).add(search);
|
||||
builder.add(options.getType());
|
||||
builder.add(options.getTieBreaker());
|
||||
String[] fields = options.getFields();
|
||||
builder.add(fields.length);
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
builder.add(fields[i]);
|
||||
}
|
||||
TextMatchExpression.addHash(builder, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int queryBindHash() {
|
||||
return search.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByPlan(SpiExpression other) {
|
||||
if (!(other instanceof TextMultiMatchExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TextMultiMatchExpression that = (TextMultiMatchExpression) other;
|
||||
return this.search.equals(that.search)
|
||||
&& this.options == null ? that.options == null : options.equals(that.options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameByBind(SpiExpression other) {
|
||||
TextMultiMatchExpression that = (TextMultiMatchExpression) other;
|
||||
return search.equals(that.search);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebean;
|
||||
|
||||
|
||||
import com.avaje.ebean.search.Match;
|
||||
import com.avaje.ebean.search.MultiMatch;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -59,4 +60,21 @@ public class TextExpressionListTest {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void syntax_multiMatch() {
|
||||
|
||||
Ebean.find(Order.class)
|
||||
.text()
|
||||
.multiMatch("Will Smith", "title", "*name");
|
||||
|
||||
MultiMatch match = MultiMatch.fields("title", "*name")
|
||||
.opAnd()
|
||||
.type(MultiMatch.Type.PHRASE_PREFIX);
|
||||
|
||||
Ebean.find(Order.class)
|
||||
.text()
|
||||
.multiMatch("Will Smith", match);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user