You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
7.6 KiB
275 lines
7.6 KiB
// Copyright 2012-present Oliver Eilhard. All rights reserved. |
|
// Use of this source code is governed by a MIT-license. |
|
// See http://olivere.mit-license.org/license.txt for details. |
|
|
|
package elastic |
|
|
|
import ( |
|
"fmt" |
|
"strings" |
|
) |
|
|
|
// MultiMatchQuery builds on the MatchQuery to allow multi-field queries. |
|
// |
|
// For more details, see |
|
// https://www.elastic.co/guide/en/elasticsearch/reference/5.2/query-dsl-multi-match-query.html |
|
type MultiMatchQuery struct { |
|
text interface{} |
|
fields []string |
|
fieldBoosts map[string]*float64 |
|
typ string // best_fields, boolean, most_fields, cross_fields, phrase, phrase_prefix |
|
operator string // AND or OR |
|
analyzer string |
|
boost *float64 |
|
slop *int |
|
fuzziness string |
|
prefixLength *int |
|
maxExpansions *int |
|
minimumShouldMatch string |
|
rewrite string |
|
fuzzyRewrite string |
|
tieBreaker *float64 |
|
lenient *bool |
|
cutoffFrequency *float64 |
|
zeroTermsQuery string |
|
queryName string |
|
} |
|
|
|
// MultiMatchQuery creates and initializes a new MultiMatchQuery. |
|
func NewMultiMatchQuery(text interface{}, fields ...string) *MultiMatchQuery { |
|
q := &MultiMatchQuery{ |
|
text: text, |
|
fields: make([]string, 0), |
|
fieldBoosts: make(map[string]*float64), |
|
} |
|
q.fields = append(q.fields, fields...) |
|
return q |
|
} |
|
|
|
// Field adds a field to run the multi match against. |
|
func (q *MultiMatchQuery) Field(field string) *MultiMatchQuery { |
|
q.fields = append(q.fields, field) |
|
return q |
|
} |
|
|
|
// FieldWithBoost adds a field to run the multi match against with a specific boost. |
|
func (q *MultiMatchQuery) FieldWithBoost(field string, boost float64) *MultiMatchQuery { |
|
q.fields = append(q.fields, field) |
|
q.fieldBoosts[field] = &boost |
|
return q |
|
} |
|
|
|
// Type can be "best_fields", "boolean", "most_fields", "cross_fields", |
|
// "phrase", or "phrase_prefix". |
|
func (q *MultiMatchQuery) Type(typ string) *MultiMatchQuery { |
|
var zero = float64(0.0) |
|
var one = float64(1.0) |
|
|
|
switch strings.ToLower(typ) { |
|
default: // best_fields / boolean |
|
q.typ = "best_fields" |
|
q.tieBreaker = &zero |
|
case "most_fields": |
|
q.typ = "most_fields" |
|
q.tieBreaker = &one |
|
case "cross_fields": |
|
q.typ = "cross_fields" |
|
q.tieBreaker = &zero |
|
case "phrase": |
|
q.typ = "phrase" |
|
q.tieBreaker = &zero |
|
case "phrase_prefix": |
|
q.typ = "phrase_prefix" |
|
q.tieBreaker = &zero |
|
} |
|
return q |
|
} |
|
|
|
// Operator sets the operator to use when using boolean query. |
|
// It can be either AND or OR (default). |
|
func (q *MultiMatchQuery) Operator(operator string) *MultiMatchQuery { |
|
q.operator = operator |
|
return q |
|
} |
|
|
|
// Analyzer sets the analyzer to use explicitly. It defaults to use explicit |
|
// mapping config for the field, or, if not set, the default search analyzer. |
|
func (q *MultiMatchQuery) Analyzer(analyzer string) *MultiMatchQuery { |
|
q.analyzer = analyzer |
|
return q |
|
} |
|
|
|
// Boost sets the boost for this query. |
|
func (q *MultiMatchQuery) Boost(boost float64) *MultiMatchQuery { |
|
q.boost = &boost |
|
return q |
|
} |
|
|
|
// Slop sets the phrase slop if evaluated to a phrase query type. |
|
func (q *MultiMatchQuery) Slop(slop int) *MultiMatchQuery { |
|
q.slop = &slop |
|
return q |
|
} |
|
|
|
// Fuzziness sets the fuzziness used when evaluated to a fuzzy query type. |
|
// It defaults to "AUTO". |
|
func (q *MultiMatchQuery) Fuzziness(fuzziness string) *MultiMatchQuery { |
|
q.fuzziness = fuzziness |
|
return q |
|
} |
|
|
|
// PrefixLength for the fuzzy process. |
|
func (q *MultiMatchQuery) PrefixLength(prefixLength int) *MultiMatchQuery { |
|
q.prefixLength = &prefixLength |
|
return q |
|
} |
|
|
|
// MaxExpansions is the number of term expansions to use when using fuzzy |
|
// or prefix type query. It defaults to unbounded so it's recommended |
|
// to set it to a reasonable value for faster execution. |
|
func (q *MultiMatchQuery) MaxExpansions(maxExpansions int) *MultiMatchQuery { |
|
q.maxExpansions = &maxExpansions |
|
return q |
|
} |
|
|
|
// MinimumShouldMatch represents the minimum number of optional should clauses |
|
// to match. |
|
func (q *MultiMatchQuery) MinimumShouldMatch(minimumShouldMatch string) *MultiMatchQuery { |
|
q.minimumShouldMatch = minimumShouldMatch |
|
return q |
|
} |
|
|
|
func (q *MultiMatchQuery) Rewrite(rewrite string) *MultiMatchQuery { |
|
q.rewrite = rewrite |
|
return q |
|
} |
|
|
|
func (q *MultiMatchQuery) FuzzyRewrite(fuzzyRewrite string) *MultiMatchQuery { |
|
q.fuzzyRewrite = fuzzyRewrite |
|
return q |
|
} |
|
|
|
// TieBreaker for "best-match" disjunction queries (OR queries). |
|
// The tie breaker capability allows documents that match more than one |
|
// query clause (in this case on more than one field) to be scored better |
|
// than documents that match only the best of the fields, without confusing |
|
// this with the better case of two distinct matches in the multiple fields. |
|
// |
|
// A tie-breaker value of 1.0 is interpreted as a signal to score queries as |
|
// "most-match" queries where all matching query clauses are considered for scoring. |
|
func (q *MultiMatchQuery) TieBreaker(tieBreaker float64) *MultiMatchQuery { |
|
q.tieBreaker = &tieBreaker |
|
return q |
|
} |
|
|
|
// Lenient indicates whether format based failures will be ignored. |
|
func (q *MultiMatchQuery) Lenient(lenient bool) *MultiMatchQuery { |
|
q.lenient = &lenient |
|
return q |
|
} |
|
|
|
// CutoffFrequency sets a cutoff value in [0..1] (or absolute number >=1) |
|
// representing the maximum threshold of a terms document frequency to be |
|
// considered a low frequency term. |
|
func (q *MultiMatchQuery) CutoffFrequency(cutoff float64) *MultiMatchQuery { |
|
q.cutoffFrequency = &cutoff |
|
return q |
|
} |
|
|
|
// ZeroTermsQuery can be "all" or "none". |
|
func (q *MultiMatchQuery) ZeroTermsQuery(zeroTermsQuery string) *MultiMatchQuery { |
|
q.zeroTermsQuery = zeroTermsQuery |
|
return q |
|
} |
|
|
|
// QueryName sets the query name for the filter that can be used when |
|
// searching for matched filters per hit. |
|
func (q *MultiMatchQuery) QueryName(queryName string) *MultiMatchQuery { |
|
q.queryName = queryName |
|
return q |
|
} |
|
|
|
// Source returns JSON for the query. |
|
func (q *MultiMatchQuery) Source() (interface{}, error) { |
|
// |
|
// { |
|
// "multi_match" : { |
|
// "query" : "this is a test", |
|
// "fields" : [ "subject", "message" ] |
|
// } |
|
// } |
|
|
|
source := make(map[string]interface{}) |
|
|
|
multiMatch := make(map[string]interface{}) |
|
source["multi_match"] = multiMatch |
|
|
|
multiMatch["query"] = q.text |
|
|
|
if len(q.fields) > 0 { |
|
var fields []string |
|
for _, field := range q.fields { |
|
if boost, found := q.fieldBoosts[field]; found { |
|
if boost != nil { |
|
fields = append(fields, fmt.Sprintf("%s^%f", field, *boost)) |
|
} else { |
|
fields = append(fields, field) |
|
} |
|
} else { |
|
fields = append(fields, field) |
|
} |
|
} |
|
multiMatch["fields"] = fields |
|
} |
|
|
|
if q.typ != "" { |
|
multiMatch["type"] = q.typ |
|
} |
|
|
|
if q.operator != "" { |
|
multiMatch["operator"] = q.operator |
|
} |
|
if q.analyzer != "" { |
|
multiMatch["analyzer"] = q.analyzer |
|
} |
|
if q.boost != nil { |
|
multiMatch["boost"] = *q.boost |
|
} |
|
if q.slop != nil { |
|
multiMatch["slop"] = *q.slop |
|
} |
|
if q.fuzziness != "" { |
|
multiMatch["fuzziness"] = q.fuzziness |
|
} |
|
if q.prefixLength != nil { |
|
multiMatch["prefix_length"] = *q.prefixLength |
|
} |
|
if q.maxExpansions != nil { |
|
multiMatch["max_expansions"] = *q.maxExpansions |
|
} |
|
if q.minimumShouldMatch != "" { |
|
multiMatch["minimum_should_match"] = q.minimumShouldMatch |
|
} |
|
if q.rewrite != "" { |
|
multiMatch["rewrite"] = q.rewrite |
|
} |
|
if q.fuzzyRewrite != "" { |
|
multiMatch["fuzzy_rewrite"] = q.fuzzyRewrite |
|
} |
|
if q.tieBreaker != nil { |
|
multiMatch["tie_breaker"] = *q.tieBreaker |
|
} |
|
if q.lenient != nil { |
|
multiMatch["lenient"] = *q.lenient |
|
} |
|
if q.cutoffFrequency != nil { |
|
multiMatch["cutoff_frequency"] = *q.cutoffFrequency |
|
} |
|
if q.zeroTermsQuery != "" { |
|
multiMatch["zero_terms_query"] = q.zeroTermsQuery |
|
} |
|
if q.queryName != "" { |
|
multiMatch["_name"] = q.queryName |
|
} |
|
return source, nil |
|
}
|
|
|