From 9ec2ddd757590c3b85b45b84d82e92bff6ae9008 Mon Sep 17 00:00:00 2001 From: Marty Schoch Date: Mon, 19 Sep 2016 21:12:36 -0400 Subject: [PATCH] initial refactor of query into separate package --- .gitignore | 1 + cmd/bleve/cmd/create.go | 7 +- cmd/bleve/cmd/query.go | 13 +- config.go | 5 - error.go | 32 +- examples_test.go | 50 +- index_test.go | 7 +- query.go | 492 +++++++----------- search.go | 9 +- search/query/query.go | 347 ++++++++++++ .../query/query_bool_field.go | 20 +- .../query/query_boolean.go | 54 +- .../query/query_conjunction.go | 24 +- .../query/query_date_range.go | 33 +- .../query/query_disjunction.go | 35 +- query_docid.go => search/query/query_docid.go | 20 +- query_fuzzy.go => search/query/query_fuzzy.go | 28 +- query_match.go => search/query/query_match.go | 46 +- .../query/query_match_all.go | 22 +- .../query/query_match_none.go | 22 +- .../query/query_match_phrase.go | 20 +- .../query/query_numeric_range.go | 26 +- .../query/query_phrase.go | 29 +- .../query/query_prefix.go | 20 +- .../query/query_regexp.go | 22 +- .../query/query_string.go | 20 +- query_string.y => search/query/query_string.y | 2 +- .../query/query_string.y.go | 2 +- .../query/query_string_lex.go | 2 +- .../query/query_string_parser.go | 4 +- .../query/query_string_parser_test.go | 100 ++-- query_term.go => search/query/query_term.go | 20 +- query_test.go => search/query/query_test.go | 48 +- .../query/query_wildcard.go | 22 +- 34 files changed, 888 insertions(+), 716 deletions(-) create mode 100644 search/query/query.go rename query_bool_field.go => search/query/query_bool_field.go (76%) rename query_boolean.go => search/query/query_boolean.go (78%) rename query_conjunction.go => search/query/query_conjunction.go (78%) rename query_date_range.go => search/query/query_date_range.go (78%) rename query_disjunction.go => search/query/query_disjunction.go (73%) rename query_docid.go => search/query/query_docid.go (76%) rename query_fuzzy.go => search/query/query_fuzzy.go (76%) rename query_match.go => search/query/query_match.go (81%) rename query_match_all.go => search/query/query_match_all.go (73%) rename query_match_none.go => search/query/query_match_none.go (73%) rename query_match_phrase.go => search/query/query_match_phrase.go (86%) rename query_numeric_range.go => search/query/query_numeric_range.go (78%) rename query_phrase.go => search/query/query_phrase.go (78%) rename query_prefix.go => search/query/query_prefix.go (77%) rename query_regexp.go => search/query/query_regexp.go (81%) rename query_string.go => search/query/query_string.go (75%) rename query_string.y => search/query/query_string.y (99%) rename query_string.y.go => search/query/query_string.y.go (99%) rename query_string_lex.go => search/query/query_string_lex.go (99%) rename query_string_parser.go => search/query/query_string_parser.go (98%) rename query_string_parser_test.go => search/query/query_string_parser_test.go (83%) rename query_term.go => search/query/query_term.go (78%) rename query_test.go => search/query/query_test.go (89%) rename query_wildcard.go => search/query/query_wildcard.go (81%) diff --git a/.gitignore b/.gitignore index 923b4605..ab7a1e21 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ query_string.y.go.tmp vendor/** !vendor/manifest /y.output +/search/query/y.output *.test tags diff --git a/cmd/bleve/cmd/create.go b/cmd/bleve/cmd/create.go index 96a516bb..e7b5c993 100644 --- a/cmd/bleve/cmd/create.go +++ b/cmd/bleve/cmd/create.go @@ -20,6 +20,7 @@ import ( "io/ioutil" "github.com/blevesearch/bleve" + "github.com/blevesearch/bleve/mapping" "github.com/spf13/cobra" ) @@ -38,7 +39,7 @@ var createCmd = &cobra.Command{ return nil }, RunE: func(cmd *cobra.Command, args []string) error { - var mapping *bleve.IndexMapping + var mapping mapping.IndexMapping var err error mapping, err = buildMapping() if err != nil { @@ -53,8 +54,8 @@ var createCmd = &cobra.Command{ }, } -func buildMapping() (*bleve.IndexMapping, error) { - mapping := bleve.NewIndexMapping() +func buildMapping() (mapping.IndexMapping, error) { + mapping := mapping.NewIndexMapping() if mappingPath != "" { mappingBytes, err := ioutil.ReadFile(mappingPath) if err != nil { diff --git a/cmd/bleve/cmd/query.go b/cmd/bleve/cmd/query.go index 3dd47762..6033d5a5 100644 --- a/cmd/bleve/cmd/query.go +++ b/cmd/bleve/cmd/query.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/blevesearch/bleve" + "github.com/blevesearch/bleve/search/query" "github.com/spf13/cobra" ) @@ -55,27 +56,27 @@ var queryCmd = &cobra.Command{ }, } -func buildQuery(args []string) bleve.Query { - var query bleve.Query +func buildQuery(args []string) query.Query { + var q query.Query switch qtype { case "prefix": pquery := bleve.NewPrefixQuery(strings.Join(args[1:], " ")) if qfield != "" { pquery.SetField(qfield) } - query = pquery + q = pquery case "term": pquery := bleve.NewTermQuery(strings.Join(args[1:], " ")) if qfield != "" { pquery.SetField(qfield) } - query = pquery + q = pquery default: // build a search with the provided parameters queryString := strings.Join(args[1:], " ") - query = bleve.NewQueryStringQuery(queryString) + q = bleve.NewQueryStringQuery(queryString) } - return query + return q } func init() { diff --git a/config.go b/config.go index 2a4476c6..69ac5cd2 100644 --- a/config.go +++ b/config.go @@ -15,7 +15,6 @@ import ( "log" "time" - "github.com/blevesearch/bleve/analysis/datetime_parsers/datetime_optional" "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/index/store/gtreap" "github.com/blevesearch/bleve/index/upside_down" @@ -31,7 +30,6 @@ type configuration struct { DefaultKVStore string DefaultMemKVStore string DefaultIndexType string - QueryDateTimeParser string SlowSearchLogThreshold time.Duration analysisQueue *index.AnalysisQueue } @@ -68,9 +66,6 @@ func init() { // default index Config.DefaultIndexType = upside_down.Name - // default query date time parser - Config.QueryDateTimeParser = datetime_optional.Name - bootDuration := time.Since(bootStart) bleveExpVar.Add("bootDuration", int64(bootDuration)) indexStats = NewIndexStats() diff --git a/error.go b/error.go index e2708e1d..c3a11531 100644 --- a/error.go +++ b/error.go @@ -15,11 +15,6 @@ const ( ErrorIndexPathDoesNotExist ErrorIndexMetaMissing ErrorIndexMetaCorrupt - ErrorDisjunctionFewerThanMinClauses - ErrorBooleanQueryNeedsMustOrShouldOrNotMust - ErrorNumericQueryNoBounds - ErrorPhraseQueryNoTerms - ErrorUnknownQueryType ErrorUnknownStorageType ErrorIndexClosed ErrorAliasMulti @@ -38,20 +33,15 @@ func (e Error) Error() string { } var errorMessages = map[Error]string{ - ErrorIndexPathExists: "cannot create new index, path already exists", - ErrorIndexPathDoesNotExist: "cannot open index, path does not exist", - ErrorIndexMetaMissing: "cannot open index, metadata missing", - ErrorIndexMetaCorrupt: "cannot open index, metadata corrupt", - ErrorDisjunctionFewerThanMinClauses: "disjunction query has fewer than the minimum number of clauses to satisfy", - ErrorBooleanQueryNeedsMustOrShouldOrNotMust: "boolean query must contain at least one must or should or not must clause", - ErrorNumericQueryNoBounds: "numeric range query must specify min or max", - ErrorPhraseQueryNoTerms: "phrase query must contain at least one term", - ErrorUnknownQueryType: "unknown query type", - ErrorUnknownStorageType: "unknown storage type", - ErrorIndexClosed: "index is closed", - ErrorAliasMulti: "cannot perform single index operation on multiple index alias", - ErrorAliasEmpty: "cannot perform operation on empty alias", - ErrorUnknownIndexType: "unknown index type", - ErrorEmptyID: "document ID cannot be empty", - ErrorIndexReadInconsistency: "index read inconsistency detected", + ErrorIndexPathExists: "cannot create new index, path already exists", + ErrorIndexPathDoesNotExist: "cannot open index, path does not exist", + ErrorIndexMetaMissing: "cannot open index, metadata missing", + ErrorIndexMetaCorrupt: "cannot open index, metadata corrupt", + ErrorUnknownStorageType: "unknown storage type", + ErrorIndexClosed: "index is closed", + ErrorAliasMulti: "cannot perform single index operation on multiple index alias", + ErrorAliasEmpty: "cannot perform operation on empty alias", + ErrorUnknownIndexType: "unknown index type", + ErrorEmptyID: "document ID cannot be empty", + ErrorIndexReadInconsistency: "index read inconsistency detected", } diff --git a/examples_test.go b/examples_test.go index 82ac57fa..f74a1826 100644 --- a/examples_test.go +++ b/examples_test.go @@ -18,6 +18,7 @@ import ( "github.com/blevesearch/bleve/mapping" "github.com/blevesearch/bleve/search" "github.com/blevesearch/bleve/search/highlight/highlighters/ansi" + "github.com/blevesearch/bleve/search/query" ) var indexMapping mapping.IndexMapping @@ -372,11 +373,11 @@ func ExampleNewSearchRequest() { } func ExampleNewBooleanQuery() { - must := make([]Query, 1) - mustNot := make([]Query, 1) - must[0] = NewMatchQuery("one") - mustNot[0] = NewMatchQuery("great") - query := NewBooleanQuery(must, nil, mustNot) + must := NewMatchQuery("one") + mustNot := NewMatchQuery("great") + query := NewBooleanQuery() + query.AddMust(must) + query.AddMustNot(mustNot) searchRequest := NewSearchRequest(query) searchResults, err := example_index.Search(searchRequest) if err != nil { @@ -388,27 +389,10 @@ func ExampleNewBooleanQuery() { // document id 1 } -func ExampleNewBooleanQueryMinShould() { - should := make([]Query, 2) - should[0] = NewMatchQuery("great") - should[1] = NewMatchQuery("one") - query := NewBooleanQueryMinShould(nil, should, nil, float64(2)) - searchRequest := NewSearchRequest(query) - searchResults, err := example_index.Search(searchRequest) - if err != nil { - panic(err) - } - - fmt.Println(searchResults.Hits[0].ID) - // Output: - // document id 2 -} - func ExampleNewConjunctionQuery() { - conjuncts := make([]Query, 2) - conjuncts[0] = NewMatchQuery("great") - conjuncts[1] = NewMatchQuery("one") - query := NewConjunctionQuery(conjuncts) + conjunct1 := NewMatchQuery("great") + conjunct2 := NewMatchQuery("one") + query := NewConjunctionQuery(conjunct1, conjunct2) searchRequest := NewSearchRequest(query) searchResults, err := example_index.Search(searchRequest) if err != nil { @@ -421,7 +405,7 @@ func ExampleNewConjunctionQuery() { } func ExampleNewMatchQueryOperator() { - query := NewMatchQueryOperator("great one", MatchQueryOperatorAnd) + query := NewMatchQueryOperator("great one", query.MatchQueryOperatorAnd) searchRequest := NewSearchRequest(query) searchResults, err := example_index.Search(searchRequest) if err != nil { @@ -434,10 +418,9 @@ func ExampleNewMatchQueryOperator() { } func ExampleNewDisjunctionQuery() { - disjuncts := make([]Query, 2) - disjuncts[0] = NewMatchQuery("great") - disjuncts[1] = NewMatchQuery("named") - query := NewDisjunctionQuery(disjuncts) + disjunct1 := NewMatchQuery("great") + disjunct2 := NewMatchQuery("named") + query := NewDisjunctionQuery(disjunct1, disjunct2) searchRequest := NewSearchRequest(query) searchResults, err := example_index.Search(searchRequest) if err != nil { @@ -450,10 +433,9 @@ func ExampleNewDisjunctionQuery() { } func ExampleNewDisjunctionQueryMin() { - disjuncts := make([]Query, 2) - disjuncts[0] = NewMatchQuery("great") - disjuncts[1] = NewMatchQuery("named") - query := NewDisjunctionQueryMin(disjuncts, float64(2)) + disjunct1 := NewMatchQuery("great") + disjunct2 := NewMatchQuery("named") + query := NewDisjunctionQueryMin(2, disjunct1, disjunct2) searchRequest := NewSearchRequest(query) searchResults, err := example_index.Search(searchRequest) if err != nil { diff --git a/index_test.go b/index_test.go index 05341f99..58feb305 100644 --- a/index_test.go +++ b/index_test.go @@ -31,6 +31,7 @@ import ( "github.com/blevesearch/bleve/index/store/null" "github.com/blevesearch/bleve/mapping" "github.com/blevesearch/bleve/search" + "github.com/blevesearch/bleve/search/query" ) func TestCrud(t *testing.T) { @@ -346,7 +347,7 @@ func TestClosedIndex(t *testing.T) { } type slowQuery struct { - actual Query + actual query.Query delay time.Duration } @@ -354,7 +355,7 @@ func (s *slowQuery) Boost() float64 { return s.actual.Boost() } -func (s *slowQuery) SetBoost(b float64) Query { +func (s *slowQuery) SetBoost(b float64) query.Query { return s.actual.SetBoost(b) } @@ -362,7 +363,7 @@ func (s *slowQuery) Field() string { return s.actual.Field() } -func (s *slowQuery) SetField(f string) Query { +func (s *slowQuery) SetField(f string) query.Query { return s.actual.SetField(f) } diff --git a/query.go b/query.go index 3af5f451..364c5a05 100644 --- a/query.go +++ b/query.go @@ -9,329 +9,185 @@ package bleve -import ( - "encoding/json" - "fmt" +import "github.com/blevesearch/bleve/search/query" - "github.com/blevesearch/bleve/index" - "github.com/blevesearch/bleve/mapping" - "github.com/blevesearch/bleve/search" -) - -// A Query represents a description of the type -// and parameters for a query into the index. -type Query interface { - Boost() float64 - SetBoost(b float64) Query - Field() string - SetField(f string) Query - Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) - Validate() error +// NewBoolFieldQuery creates a new Query for boolean fields +func NewBoolFieldQuery(val bool) *query.BoolFieldQuery { + return query.NewBoolFieldQuery(val) } -// ParseQuery deserializes a JSON representation of -// a Query object. -func ParseQuery(input []byte) (Query, error) { - var tmp map[string]interface{} - err := json.Unmarshal(input, &tmp) - if err != nil { - return nil, err - } - _, isMatchQuery := tmp["match"] - _, hasFuzziness := tmp["fuzziness"] - if hasFuzziness && !isMatchQuery { - var rv fuzzyQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, isTermQuery := tmp["term"] - if isTermQuery { - var rv termQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - if isMatchQuery { - var rv matchQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, isMatchPhraseQuery := tmp["match_phrase"] - if isMatchPhraseQuery { - var rv matchPhraseQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasMust := tmp["must"] - _, hasShould := tmp["should"] - _, hasMustNot := tmp["must_not"] - if hasMust || hasShould || hasMustNot { - var rv booleanQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasTerms := tmp["terms"] - if hasTerms { - var rv phraseQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasConjuncts := tmp["conjuncts"] - if hasConjuncts { - var rv conjunctionQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasDisjuncts := tmp["disjuncts"] - if hasDisjuncts { - var rv disjunctionQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - - _, hasSyntaxQuery := tmp["query"] - if hasSyntaxQuery { - var rv queryStringQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasMin := tmp["min"] - _, hasMax := tmp["max"] - if hasMin || hasMax { - var rv numericRangeQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasStart := tmp["start"] - _, hasEnd := tmp["end"] - if hasStart || hasEnd { - var rv dateRangeQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasPrefix := tmp["prefix"] - if hasPrefix { - var rv prefixQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasRegexp := tmp["regexp"] - if hasRegexp { - var rv regexpQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasWildcard := tmp["wildcard"] - if hasWildcard { - var rv wildcardQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasMatchAll := tmp["match_all"] - if hasMatchAll { - var rv matchAllQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasMatchNone := tmp["match_none"] - if hasMatchNone { - var rv matchNoneQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - _, hasDocIds := tmp["ids"] - if hasDocIds { - var rv docIDQuery - err := json.Unmarshal(input, &rv) - if err != nil { - return nil, err - } - if rv.Boost() == 0 { - rv.SetBoost(1) - } - return &rv, nil - } - return nil, ErrorUnknownQueryType +// NewBooleanQuery creates a compound Query composed +// of several other Query objects. +// These other query objects are added using the +// AddMust() AddShould() and AddMustNot() methods. +// Result documents must satisfy ALL of the +// must Queries. +// Result documents must satisfy NONE of the must not +// Queries. +// Result documents that ALSO satisfy any of the should +// Queries will score higher. +func NewBooleanQuery() *query.BooleanQuery { + return query.NewBooleanQuery(nil, nil, nil) } -// expandQuery traverses the input query tree and returns a new tree where -// query string queries have been expanded into base queries. Returned tree may -// reference queries from the input tree or new queries. -func expandQuery(m mapping.IndexMapping, query Query) (Query, error) { - var expand func(query Query) (Query, error) - var expandSlice func(queries []Query) ([]Query, error) - - expandSlice = func(queries []Query) ([]Query, error) { - expanded := []Query{} - for _, q := range queries { - exp, err := expand(q) - if err != nil { - return nil, err - } - expanded = append(expanded, exp) - } - return expanded, nil - } - - expand = func(query Query) (Query, error) { - switch query.(type) { - case *queryStringQuery: - q := query.(*queryStringQuery) - parsed, err := parseQuerySyntax(q.Query) - if err != nil { - return nil, fmt.Errorf("could not parse '%s': %s", q.Query, err) - } - return expand(parsed) - case *conjunctionQuery: - q := *query.(*conjunctionQuery) - children, err := expandSlice(q.Conjuncts) - if err != nil { - return nil, err - } - q.Conjuncts = children - return &q, nil - case *disjunctionQuery: - q := *query.(*disjunctionQuery) - children, err := expandSlice(q.Disjuncts) - if err != nil { - return nil, err - } - q.Disjuncts = children - return &q, nil - case *booleanQuery: - q := *query.(*booleanQuery) - var err error - q.Must, err = expand(q.Must) - if err != nil { - return nil, err - } - q.Should, err = expand(q.Should) - if err != nil { - return nil, err - } - q.MustNot, err = expand(q.MustNot) - if err != nil { - return nil, err - } - return &q, nil - case *phraseQuery: - q := *query.(*phraseQuery) - children, err := expandSlice(q.termQueries) - if err != nil { - return nil, err - } - q.termQueries = children - return &q, nil - default: - return query, nil - } - } - return expand(query) +// NewConjunctionQuery creates a new compound Query. +// Result documents must satisfy all of the queries. +func NewConjunctionQuery(conjuncts ...query.Query) *query.ConjunctionQuery { + return query.NewConjunctionQuery(conjuncts) } -// DumpQuery returns a string representation of the query tree, where query -// string queries have been expanded into base queries. The output format is -// meant for debugging purpose and may change in the future. -func DumpQuery(m mapping.IndexMapping, query Query) (string, error) { - q, err := expandQuery(m, query) - if err != nil { - return "", err - } - data, err := json.MarshalIndent(q, "", " ") - return string(data), err +// NewDateRangeQuery creates a new Query for ranges +// of date values. +// Date strings are parsed using the DateTimeParser configured in the +// top-level config.QueryDateTimeParser +// Either, but not both endpoints can be nil. +func NewDateRangeQuery(start, end *string) *query.DateRangeQuery { + return query.NewDateRangeQuery(start, end) +} + +// NewDateRangeInclusiveQuery creates a new Query for ranges +// of date values. +// Date strings are parsed using the DateTimeParser configured in the +// top-level config.QueryDateTimeParser +// Either, but not both endpoints can be nil. +// startInclusive and endInclusive control inclusion of the endpoints. +func NewDateRangeInclusiveQuery(start, end *string, startInclusive, endInclusive *bool) *query.DateRangeQuery { + return query.NewDateRangeInclusiveQuery(start, end, startInclusive, endInclusive) +} + +// NewDisjunctionQuery creates a new compound Query. +// Result documents satisfy at least one Query. +func NewDisjunctionQuery(disjuncts ...query.Query) *query.DisjunctionQuery { + return query.NewDisjunctionQuery(disjuncts) +} + +// NewDisjunctionQueryMin creates a new compound Query. +// Result documents satisfy at least min Queries. +func NewDisjunctionQueryMin(min float64, disjuncts ...query.Query) *query.DisjunctionQuery { + return query.NewDisjunctionQueryMin(disjuncts, min) +} + +// NewDocIDQuery creates a new Query object returning indexed documents among +// the specified set. Combine it with ConjunctionQuery to restrict the scope of +// other queries output. +func NewDocIDQuery(ids []string) *query.DocIDQuery { + return query.NewDocIDQuery(ids) +} + +// NewFuzzyQuery creates a new Query which finds +// documents containing terms within a specific +// fuzziness of the specified term. +// The default fuzziness is 2. +// +// The current implementation uses Levenshtein edit +// distance as the fuzziness metric. +func NewFuzzyQuery(term string) *query.FuzzyQuery { + return query.NewFuzzyQuery(term) +} + +// NewMatchAllQuery creates a Query which will +// match all documents in the index. +func NewMatchAllQuery() *query.MatchAllQuery { + return query.NewMatchAllQuery() +} + +// NewMatchNoneQuery creates a Query which will not +// match any documents in the index. +func NewMatchNoneQuery() *query.MatchNoneQuery { + return query.NewMatchNoneQuery() +} + +// NewMatchPhraseQuery creates a new Query object +// for matching phrases in the index. +// An Analyzer is chosen based on the field. +// Input text is analyzed using this analyzer. +// Token terms resulting from this analysis are +// used to build a search phrase. Result documents +// must match this phrase. Queried field must have been indexed with +// IncludeTermVectors set to true. +func NewMatchPhraseQuery(matchPhrase string) *query.MatchPhraseQuery { + return query.NewMatchPhraseQuery(matchPhrase) +} + +// NewMatchQuery creates a Query for matching text. +// An Analyzer is chosen based on the field. +// Input text is analyzed using this analyzer. +// Token terms resulting from this analysis are +// used to perform term searches. Result documents +// must satisfy at least one of these term searches. +func NewMatchQuery(match string) *query.MatchQuery { + return query.NewMatchQuery(match) +} + +// NewMatchQueryOperator creates a Query for matching text. +// An Analyzer is chosen based on the field. +// Input text is analyzed using this analyzer. +// Token terms resulting from this analysis are +// used to perform term searches. Result documents +// must satisfy term searches according to given operator. +func NewMatchQueryOperator(match string, operator query.MatchQueryOperator) *query.MatchQuery { + return query.NewMatchQueryOperator(match, operator) +} + +// NewNumericRangeQuery creates a new Query for ranges +// of numeric values. +// Either, but not both endpoints can be nil. +// The minimum value is inclusive. +// The maximum value is exclusive. +func NewNumericRangeQuery(min, max *float64) *query.NumericRangeQuery { + return query.NewNumericRangeQuery(min, max) +} + +// NewNumericRangeInclusiveQuery creates a new Query for ranges +// of numeric values. +// Either, but not both endpoints can be nil. +// Control endpoint inclusion with inclusiveMin, inclusiveMax. +func NewNumericRangeInclusiveQuery(min, max *float64, minInclusive, maxInclusive *bool) *query.NumericRangeQuery { + return query.NewNumericRangeInclusiveQuery(min, max, minInclusive, maxInclusive) +} + +// NewPhraseQuery creates a new Query for finding +// exact term phrases in the index. +// The provided terms must exist in the correct +// order, at the correct index offsets, in the +// specified field. Queried field must have been indexed with +// IncludeTermVectors set to true. +func NewPhraseQuery(terms []string, field string) *query.PhraseQuery { + return query.NewPhraseQuery(terms, field) +} + +// NewPrefixQuery creates a new Query which finds +// documents containing terms that start with the +// specified prefix. +func NewPrefixQuery(prefix string) *query.PrefixQuery { + return query.NewPrefixQuery(prefix) +} + +// NewRegexpQuery creates a new Query which finds +// documents containing terms that match the +// specified regular expression. +func NewRegexpQuery(regexp string) *query.RegexpQuery { + return query.NewRegexpQuery(regexp) +} + +// NewQueryStringQuery creates a new Query used for +// finding documents that satisfy a query string. The +// query string is a small query language for humans. +func NewQueryStringQuery(q string) *query.QueryStringQuery { + return query.NewQueryStringQuery(q) +} + +// NewTermQuery creates a new Query for finding an +// exact term match in the index. +func NewTermQuery(term string) *query.TermQuery { + return query.NewTermQuery(term) +} + +// NewWildcardQuery creates a new Query which finds +// documents containing terms that match the +// specified wildcard. In the wildcard pattern '*' +// will match any sequence of 0 or more characters, +// and '?' will match any single character. +func NewWildcardQuery(wildcard string) *query.WildcardQuery { + return query.NewWildcardQuery(wildcard) } diff --git a/search.go b/search.go index dc6bc26e..0b2fda6c 100644 --- a/search.go +++ b/search.go @@ -16,6 +16,7 @@ import ( "github.com/blevesearch/bleve/analysis" "github.com/blevesearch/bleve/search" + "github.com/blevesearch/bleve/search/query" ) type numericRange struct { @@ -195,7 +196,7 @@ func (h *HighlightRequest) AddField(field string) { // // A special field named "*" can be used to return all fields. type SearchRequest struct { - Query Query `json:"query"` + Query query.Query `json:"query"` Size int `json:"size"` From int `json:"from"` Highlight *HighlightRequest `json:"highlight"` @@ -274,7 +275,7 @@ func (r *SearchRequest) UnmarshalJSON(input []byte) error { r.Highlight = temp.Highlight r.Fields = temp.Fields r.Facets = temp.Facets - r.Query, err = ParseQuery(temp.Q) + r.Query, err = query.ParseQuery(temp.Q) if err != nil { return err } @@ -293,7 +294,7 @@ func (r *SearchRequest) UnmarshalJSON(input []byte) error { // NewSearchRequest creates a new SearchRequest // for the Query, using default values for all // other search parameters. -func NewSearchRequest(q Query) *SearchRequest { +func NewSearchRequest(q query.Query) *SearchRequest { return NewSearchRequestOptions(q, 10, 0, false) } @@ -301,7 +302,7 @@ func NewSearchRequest(q Query) *SearchRequest { // for the Query, with the requested size, from // and explanation search parameters. // By default results are ordered by score, descending. -func NewSearchRequestOptions(q Query, size, from int, explain bool) *SearchRequest { +func NewSearchRequestOptions(q query.Query, size, from int, explain bool) *SearchRequest { return &SearchRequest{ Query: q, Size: size, diff --git a/search/query/query.go b/search/query/query.go new file mode 100644 index 00000000..01b151d8 --- /dev/null +++ b/search/query/query.go @@ -0,0 +1,347 @@ +// Copyright (c) 2014 Couchbase, Inc. +// Licensed 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 query + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + + "github.com/blevesearch/bleve/index" + "github.com/blevesearch/bleve/mapping" + "github.com/blevesearch/bleve/search" +) + +var logger = log.New(ioutil.Discard, "bleve mapping ", log.LstdFlags) + +// SetLog sets the logger used for logging +// by default log messages are sent to ioutil.Discard +func SetLog(l *log.Logger) { + logger = l +} + +// A Query represents a description of the type +// and parameters for a query into the index. +type Query interface { + Boost() float64 + SetBoost(b float64) Query + Field() string + SetField(f string) Query + Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) + Validate() error +} + +// ParseQuery deserializes a JSON representation of +// a Query object. +func ParseQuery(input []byte) (Query, error) { + var tmp map[string]interface{} + err := json.Unmarshal(input, &tmp) + if err != nil { + return nil, err + } + _, isMatchQuery := tmp["match"] + _, hasFuzziness := tmp["fuzziness"] + if hasFuzziness && !isMatchQuery { + var rv FuzzyQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, isTermQuery := tmp["term"] + if isTermQuery { + var rv TermQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + if isMatchQuery { + var rv MatchQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, isMatchPhraseQuery := tmp["match_phrase"] + if isMatchPhraseQuery { + var rv MatchPhraseQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasMust := tmp["must"] + _, hasShould := tmp["should"] + _, hasMustNot := tmp["must_not"] + if hasMust || hasShould || hasMustNot { + var rv BooleanQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasTerms := tmp["terms"] + if hasTerms { + var rv PhraseQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasConjuncts := tmp["conjuncts"] + if hasConjuncts { + var rv ConjunctionQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasDisjuncts := tmp["disjuncts"] + if hasDisjuncts { + var rv DisjunctionQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + + _, hasSyntaxQuery := tmp["query"] + if hasSyntaxQuery { + var rv QueryStringQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasMin := tmp["min"] + _, hasMax := tmp["max"] + if hasMin || hasMax { + var rv NumericRangeQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasStart := tmp["start"] + _, hasEnd := tmp["end"] + if hasStart || hasEnd { + var rv DateRangeQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasPrefix := tmp["prefix"] + if hasPrefix { + var rv PrefixQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasRegexp := tmp["regexp"] + if hasRegexp { + var rv RegexpQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasWildcard := tmp["wildcard"] + if hasWildcard { + var rv WildcardQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasMatchAll := tmp["match_all"] + if hasMatchAll { + var rv MatchAllQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasMatchNone := tmp["match_none"] + if hasMatchNone { + var rv MatchNoneQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + _, hasDocIds := tmp["ids"] + if hasDocIds { + var rv DocIDQuery + err := json.Unmarshal(input, &rv) + if err != nil { + return nil, err + } + if rv.Boost() == 0 { + rv.SetBoost(1) + } + return &rv, nil + } + return nil, fmt.Errorf("unknown query type") +} + +// expandQuery traverses the input query tree and returns a new tree where +// query string queries have been expanded into base queries. Returned tree may +// reference queries from the input tree or new queries. +func expandQuery(m mapping.IndexMapping, query Query) (Query, error) { + var expand func(query Query) (Query, error) + var expandSlice func(queries []Query) ([]Query, error) + + expandSlice = func(queries []Query) ([]Query, error) { + expanded := []Query{} + for _, q := range queries { + exp, err := expand(q) + if err != nil { + return nil, err + } + expanded = append(expanded, exp) + } + return expanded, nil + } + + expand = func(query Query) (Query, error) { + switch query.(type) { + case *QueryStringQuery: + q := query.(*QueryStringQuery) + parsed, err := parseQuerySyntax(q.Query) + if err != nil { + return nil, fmt.Errorf("could not parse '%s': %s", q.Query, err) + } + return expand(parsed) + case *ConjunctionQuery: + q := *query.(*ConjunctionQuery) + children, err := expandSlice(q.Conjuncts) + if err != nil { + return nil, err + } + q.Conjuncts = children + return &q, nil + case *DisjunctionQuery: + q := *query.(*DisjunctionQuery) + children, err := expandSlice(q.Disjuncts) + if err != nil { + return nil, err + } + q.Disjuncts = children + return &q, nil + case *BooleanQuery: + q := *query.(*BooleanQuery) + var err error + q.Must, err = expand(q.Must) + if err != nil { + return nil, err + } + q.Should, err = expand(q.Should) + if err != nil { + return nil, err + } + q.MustNot, err = expand(q.MustNot) + if err != nil { + return nil, err + } + return &q, nil + case *PhraseQuery: + q := *query.(*PhraseQuery) + children, err := expandSlice(q.termQueries) + if err != nil { + return nil, err + } + q.termQueries = children + return &q, nil + default: + return query, nil + } + } + return expand(query) +} + +// DumpQuery returns a string representation of the query tree, where query +// string queries have been expanded into base queries. The output format is +// meant for debugging purpose and may change in the future. +func DumpQuery(m mapping.IndexMapping, query Query) (string, error) { + q, err := expandQuery(m, query) + if err != nil { + return "", err + } + data, err := json.MarshalIndent(q, "", " ") + return string(data), err +} diff --git a/query_bool_field.go b/search/query/query_bool_field.go similarity index 76% rename from query_bool_field.go rename to search/query/query_bool_field.go index 16545994..66ba5764 100644 --- a/query_bool_field.go +++ b/search/query/query_bool_field.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "github.com/blevesearch/bleve/index" @@ -16,39 +16,39 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type boolFieldQuery struct { +type BoolFieldQuery struct { Bool bool `json:"bool"` FieldVal string `json:"field,omitempty"` BoostVal float64 `json:"boost,omitempty"` } // NewBoolFieldQuery creates a new Query for boolean fields -func NewBoolFieldQuery(val bool) *boolFieldQuery { - return &boolFieldQuery{ +func NewBoolFieldQuery(val bool) *BoolFieldQuery { + return &BoolFieldQuery{ Bool: val, BoostVal: 1.0, } } -func (q *boolFieldQuery) Boost() float64 { +func (q *BoolFieldQuery) Boost() float64 { return q.BoostVal } -func (q *boolFieldQuery) SetBoost(b float64) Query { +func (q *BoolFieldQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *boolFieldQuery) Field() string { +func (q *BoolFieldQuery) Field() string { return q.FieldVal } -func (q *boolFieldQuery) SetField(f string) Query { +func (q *BoolFieldQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *boolFieldQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *BoolFieldQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -60,6 +60,6 @@ func (q *boolFieldQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, e return searchers.NewTermSearcher(i, term, field, q.BoostVal, explain) } -func (q *boolFieldQuery) Validate() error { +func (q *BoolFieldQuery) Validate() error { return nil } diff --git a/query_boolean.go b/search/query/query_boolean.go similarity index 78% rename from query_boolean.go rename to search/query/query_boolean.go index 2fa2186e..6d66db93 100644 --- a/query_boolean.go +++ b/search/query/query_boolean.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" @@ -19,7 +19,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type booleanQuery struct { +type BooleanQuery struct { Must Query `json:"must,omitempty"` Should Query `json:"should,omitempty"` MustNot Query `json:"must_not,omitempty"` @@ -34,7 +34,7 @@ type booleanQuery struct { // Queries. // Result documents that ALSO satisfy any of the should // Queries will score higher. -func NewBooleanQuery(must []Query, should []Query, mustNot []Query) *booleanQuery { +func NewBooleanQuery(must []Query, should []Query, mustNot []Query) *BooleanQuery { return NewBooleanQueryMinShould(must, should, mustNot, 0.0) } @@ -42,9 +42,9 @@ func NewBooleanQuery(must []Query, should []Query, mustNot []Query) *booleanQuer // NewBooleanQuery, only it offers control of the // minimum number of should queries that must be // satisfied. -func NewBooleanQueryMinShould(must []Query, should []Query, mustNot []Query, minShould float64) *booleanQuery { +func NewBooleanQueryMinShould(must []Query, should []Query, mustNot []Query, minShould float64) *BooleanQuery { - rv := booleanQuery{ + rv := BooleanQuery{ BoostVal: 1.0, } if len(must) > 0 { @@ -62,41 +62,47 @@ func NewBooleanQueryMinShould(must []Query, should []Query, mustNot []Query, min // SetMinShould requires that at least minShould of the // should Queries must be satisfied. -func (q *booleanQuery) SetMinShould(minShould float64) { - q.Should.(*disjunctionQuery).SetMin(minShould) +func (q *BooleanQuery) SetMinShould(minShould float64) { + q.Should.(*DisjunctionQuery).SetMin(minShould) } -func (q *booleanQuery) AddMust(m Query) { +func (q *BooleanQuery) AddMust(m ...Query) { if q.Must == nil { q.Must = NewConjunctionQuery([]Query{}) } - q.Must.(*conjunctionQuery).AddQuery(m) + for _, mq := range m { + q.Must.(*ConjunctionQuery).AddQuery(mq) + } } -func (q *booleanQuery) AddShould(m Query) { +func (q *BooleanQuery) AddShould(m ...Query) { if q.Should == nil { q.Should = NewDisjunctionQuery([]Query{}) } - q.Should.(*disjunctionQuery).AddQuery(m) + for _, mq := range m { + q.Should.(*DisjunctionQuery).AddQuery(mq) + } } -func (q *booleanQuery) AddMustNot(m Query) { +func (q *BooleanQuery) AddMustNot(m ...Query) { if q.MustNot == nil { q.MustNot = NewDisjunctionQuery([]Query{}) } - q.MustNot.(*disjunctionQuery).AddQuery(m) + for _, mq := range m { + q.MustNot.(*DisjunctionQuery).AddQuery(mq) + } } -func (q *booleanQuery) Boost() float64 { +func (q *BooleanQuery) Boost() float64 { return q.BoostVal } -func (q *booleanQuery) SetBoost(b float64) Query { +func (q *BooleanQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *booleanQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *BooleanQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { var err error var mustNotSearcher search.Searcher if q.MustNot != nil { @@ -132,7 +138,7 @@ func (q *booleanQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, exp return searchers.NewBooleanSearcher(i, mustSearcher, shouldSearcher, mustNotSearcher, explain) } -func (q *booleanQuery) Validate() error { +func (q *BooleanQuery) Validate() error { if q.Must != nil { err := q.Must.Validate() if err != nil { @@ -152,12 +158,12 @@ func (q *booleanQuery) Validate() error { } } if q.Must == nil && q.Should == nil && q.MustNot == nil { - return ErrorBooleanQueryNeedsMustOrShouldOrNotMust + return fmt.Errorf("boolean query must contain at least one must or should or not must clause") } return nil } -func (q *booleanQuery) UnmarshalJSON(data []byte) error { +func (q *BooleanQuery) UnmarshalJSON(data []byte) error { tmp := struct { Must json.RawMessage `json:"must,omitempty"` Should json.RawMessage `json:"should,omitempty"` @@ -174,7 +180,7 @@ func (q *booleanQuery) UnmarshalJSON(data []byte) error { if err != nil { return err } - _, isConjunctionQuery := q.Must.(*conjunctionQuery) + _, isConjunctionQuery := q.Must.(*ConjunctionQuery) if !isConjunctionQuery { return fmt.Errorf("must clause must be conjunction") } @@ -185,7 +191,7 @@ func (q *booleanQuery) UnmarshalJSON(data []byte) error { if err != nil { return err } - _, isDisjunctionQuery := q.Should.(*disjunctionQuery) + _, isDisjunctionQuery := q.Should.(*DisjunctionQuery) if !isDisjunctionQuery { return fmt.Errorf("should clause must be disjunction") } @@ -196,7 +202,7 @@ func (q *booleanQuery) UnmarshalJSON(data []byte) error { if err != nil { return err } - _, isDisjunctionQuery := q.MustNot.(*disjunctionQuery) + _, isDisjunctionQuery := q.MustNot.(*DisjunctionQuery) if !isDisjunctionQuery { return fmt.Errorf("must not clause must be disjunction") } @@ -209,10 +215,10 @@ func (q *booleanQuery) UnmarshalJSON(data []byte) error { return nil } -func (q *booleanQuery) Field() string { +func (q *BooleanQuery) Field() string { return "" } -func (q *booleanQuery) SetField(f string) Query { +func (q *BooleanQuery) SetField(f string) Query { return q } diff --git a/query_conjunction.go b/search/query/query_conjunction.go similarity index 78% rename from query_conjunction.go rename to search/query/query_conjunction.go index 1c01e668..eb75c2ec 100644 --- a/query_conjunction.go +++ b/search/query/query_conjunction.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" @@ -18,35 +18,35 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type conjunctionQuery struct { +type ConjunctionQuery struct { Conjuncts []Query `json:"conjuncts"` BoostVal float64 `json:"boost,omitempty"` } // NewConjunctionQuery creates a new compound Query. // Result documents must satisfy all of the queries. -func NewConjunctionQuery(conjuncts []Query) *conjunctionQuery { - return &conjunctionQuery{ +func NewConjunctionQuery(conjuncts []Query) *ConjunctionQuery { + return &ConjunctionQuery{ Conjuncts: conjuncts, BoostVal: 1.0, } } -func (q *conjunctionQuery) Boost() float64 { +func (q *ConjunctionQuery) Boost() float64 { return q.BoostVal } -func (q *conjunctionQuery) SetBoost(b float64) Query { +func (q *ConjunctionQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *conjunctionQuery) AddQuery(aq Query) *conjunctionQuery { +func (q *ConjunctionQuery) AddQuery(aq Query) *ConjunctionQuery { q.Conjuncts = append(q.Conjuncts, aq) return q } -func (q *conjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *ConjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { ss := make([]search.Searcher, len(q.Conjuncts)) for in, conjunct := range q.Conjuncts { var err error @@ -58,7 +58,7 @@ func (q *conjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, return searchers.NewConjunctionSearcher(i, ss, explain) } -func (q *conjunctionQuery) Validate() error { +func (q *ConjunctionQuery) Validate() error { for _, q := range q.Conjuncts { err := q.Validate() if err != nil { @@ -68,7 +68,7 @@ func (q *conjunctionQuery) Validate() error { return nil } -func (q *conjunctionQuery) UnmarshalJSON(data []byte) error { +func (q *ConjunctionQuery) UnmarshalJSON(data []byte) error { tmp := struct { Conjuncts []json.RawMessage `json:"conjuncts"` BoostVal float64 `json:"boost,omitempty"` @@ -92,10 +92,10 @@ func (q *conjunctionQuery) UnmarshalJSON(data []byte) error { return nil } -func (q *conjunctionQuery) Field() string { +func (q *ConjunctionQuery) Field() string { return "" } -func (q *conjunctionQuery) SetField(f string) Query { +func (q *ConjunctionQuery) SetField(f string) Query { return q } diff --git a/query_date_range.go b/search/query/query_date_range.go similarity index 78% rename from query_date_range.go rename to search/query/query_date_range.go index 7f84d3dc..c089581b 100644 --- a/query_date_range.go +++ b/search/query/query_date_range.go @@ -7,20 +7,27 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "fmt" "math" + "github.com/blevesearch/bleve/analysis/datetime_parsers/datetime_optional" "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/mapping" "github.com/blevesearch/bleve/numeric_util" + "github.com/blevesearch/bleve/registry" "github.com/blevesearch/bleve/search" "github.com/blevesearch/bleve/search/searchers" ) -type dateRangeQuery struct { +// QueryDateTimeParser controls the default query date time parser +var QueryDateTimeParser = datetime_optional.Name + +var cache = registry.NewCache() + +type DateRangeQuery struct { Start *string `json:"start,omitempty"` End *string `json:"end,omitempty"` InclusiveStart *bool `json:"inclusive_start,omitempty"` @@ -34,7 +41,7 @@ type dateRangeQuery struct { // Date strings are parsed using the DateTimeParser configured in the // top-level config.QueryDateTimeParser // Either, but not both endpoints can be nil. -func NewDateRangeQuery(start, end *string) *dateRangeQuery { +func NewDateRangeQuery(start, end *string) *DateRangeQuery { return NewDateRangeInclusiveQuery(start, end, nil, nil) } @@ -44,8 +51,8 @@ func NewDateRangeQuery(start, end *string) *dateRangeQuery { // top-level config.QueryDateTimeParser // Either, but not both endpoints can be nil. // startInclusive and endInclusive control inclusion of the endpoints. -func NewDateRangeInclusiveQuery(start, end *string, startInclusive, endInclusive *bool) *dateRangeQuery { - return &dateRangeQuery{ +func NewDateRangeInclusiveQuery(start, end *string, startInclusive, endInclusive *bool) *DateRangeQuery { + return &DateRangeQuery{ Start: start, End: end, InclusiveStart: startInclusive, @@ -54,25 +61,25 @@ func NewDateRangeInclusiveQuery(start, end *string, startInclusive, endInclusive } } -func (q *dateRangeQuery) Boost() float64 { +func (q *DateRangeQuery) Boost() float64 { return q.BoostVal } -func (q *dateRangeQuery) SetBoost(b float64) Query { +func (q *DateRangeQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *dateRangeQuery) Field() string { +func (q *DateRangeQuery) Field() string { return q.FieldVal } -func (q *dateRangeQuery) SetField(f string) Query { +func (q *DateRangeQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *dateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *DateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { min, max, err := q.parseEndpoints() if err != nil { @@ -87,8 +94,8 @@ func (q *dateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, e return searchers.NewNumericRangeSearcher(i, min, max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal, explain) } -func (q *dateRangeQuery) parseEndpoints() (*float64, *float64, error) { - dateTimeParser, err := Config.Cache.DateTimeParserNamed(Config.QueryDateTimeParser) +func (q *DateRangeQuery) parseEndpoints() (*float64, *float64, error) { + dateTimeParser, err := cache.DateTimeParserNamed(QueryDateTimeParser) if err != nil { return nil, nil, err } @@ -114,7 +121,7 @@ func (q *dateRangeQuery) parseEndpoints() (*float64, *float64, error) { return &min, &max, nil } -func (q *dateRangeQuery) Validate() error { +func (q *DateRangeQuery) Validate() error { if q.Start == nil && q.Start == q.End { return fmt.Errorf("must specify start or end") } diff --git a/query_disjunction.go b/search/query/query_disjunction.go similarity index 73% rename from query_disjunction.go rename to search/query/query_disjunction.go index be272ea3..57d1fec8 100644 --- a/query_disjunction.go +++ b/search/query/query_disjunction.go @@ -7,10 +7,11 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" + "fmt" "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/mapping" @@ -18,7 +19,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type disjunctionQuery struct { +type DisjunctionQuery struct { Disjuncts []Query `json:"disjuncts"` BoostVal float64 `json:"boost,omitempty"` MinVal float64 `json:"min"` @@ -26,8 +27,8 @@ type disjunctionQuery struct { // NewDisjunctionQuery creates a new compound Query. // Result documents satisfy at least one Query. -func NewDisjunctionQuery(disjuncts []Query) *disjunctionQuery { - return &disjunctionQuery{ +func NewDisjunctionQuery(disjuncts []Query) *DisjunctionQuery { + return &DisjunctionQuery{ Disjuncts: disjuncts, BoostVal: 1.0, } @@ -35,38 +36,38 @@ func NewDisjunctionQuery(disjuncts []Query) *disjunctionQuery { // NewDisjunctionQueryMin creates a new compound Query. // Result documents satisfy at least min Queries. -func NewDisjunctionQueryMin(disjuncts []Query, min float64) *disjunctionQuery { - return &disjunctionQuery{ +func NewDisjunctionQueryMin(disjuncts []Query, min float64) *DisjunctionQuery { + return &DisjunctionQuery{ Disjuncts: disjuncts, BoostVal: 1.0, MinVal: min, } } -func (q *disjunctionQuery) Boost() float64 { +func (q *DisjunctionQuery) Boost() float64 { return q.BoostVal } -func (q *disjunctionQuery) SetBoost(b float64) Query { +func (q *DisjunctionQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *disjunctionQuery) AddQuery(aq Query) Query { +func (q *DisjunctionQuery) AddQuery(aq Query) Query { q.Disjuncts = append(q.Disjuncts, aq) return q } -func (q *disjunctionQuery) Min() float64 { +func (q *DisjunctionQuery) Min() float64 { return q.MinVal } -func (q *disjunctionQuery) SetMin(m float64) Query { +func (q *DisjunctionQuery) SetMin(m float64) Query { q.MinVal = m return q } -func (q *disjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *DisjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { ss := make([]search.Searcher, len(q.Disjuncts)) for in, disjunct := range q.Disjuncts { var err error @@ -78,9 +79,9 @@ func (q *disjunctionQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, return searchers.NewDisjunctionSearcher(i, ss, q.MinVal, explain) } -func (q *disjunctionQuery) Validate() error { +func (q *DisjunctionQuery) Validate() error { if int(q.MinVal) > len(q.Disjuncts) { - return ErrorDisjunctionFewerThanMinClauses + return fmt.Errorf("disjunction query has fewer than the minimum number of clauses to satisfy") } for _, q := range q.Disjuncts { err := q.Validate() @@ -91,7 +92,7 @@ func (q *disjunctionQuery) Validate() error { return nil } -func (q *disjunctionQuery) UnmarshalJSON(data []byte) error { +func (q *DisjunctionQuery) UnmarshalJSON(data []byte) error { tmp := struct { Disjuncts []json.RawMessage `json:"disjuncts"` BoostVal float64 `json:"boost,omitempty"` @@ -117,10 +118,10 @@ func (q *disjunctionQuery) UnmarshalJSON(data []byte) error { return nil } -func (q *disjunctionQuery) Field() string { +func (q *DisjunctionQuery) Field() string { return "" } -func (q *disjunctionQuery) SetField(f string) Query { +func (q *DisjunctionQuery) SetField(f string) Query { return q } diff --git a/query_docid.go b/search/query/query_docid.go similarity index 76% rename from query_docid.go rename to search/query/query_docid.go index 5935eaa9..992d9be3 100644 --- a/query_docid.go +++ b/search/query/query_docid.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "github.com/blevesearch/bleve/index" @@ -16,7 +16,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type docIDQuery struct { +type DocIDQuery struct { IDs []string `json:"ids"` BoostVal float64 `json:"boost,omitempty"` } @@ -24,34 +24,34 @@ type docIDQuery struct { // NewDocIDQuery creates a new Query object returning indexed documents among // the specified set. Combine it with ConjunctionQuery to restrict the scope of // other queries output. -func NewDocIDQuery(ids []string) *docIDQuery { - return &docIDQuery{ +func NewDocIDQuery(ids []string) *DocIDQuery { + return &DocIDQuery{ IDs: ids, BoostVal: 1.0, } } -func (q *docIDQuery) Boost() float64 { +func (q *DocIDQuery) Boost() float64 { return q.BoostVal } -func (q *docIDQuery) SetBoost(b float64) Query { +func (q *DocIDQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *docIDQuery) Field() string { +func (q *DocIDQuery) Field() string { return "" } -func (q *docIDQuery) SetField(f string) Query { +func (q *DocIDQuery) SetField(f string) Query { return q } -func (q *docIDQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *DocIDQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { return searchers.NewDocIDSearcher(i, q.IDs, q.BoostVal, explain) } -func (q *docIDQuery) Validate() error { +func (q *DocIDQuery) Validate() error { return nil } diff --git a/query_fuzzy.go b/search/query/query_fuzzy.go similarity index 76% rename from query_fuzzy.go rename to search/query/query_fuzzy.go index 7644d643..3fcec976 100644 --- a/query_fuzzy.go +++ b/search/query/query_fuzzy.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "github.com/blevesearch/bleve/index" @@ -16,7 +16,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type fuzzyQuery struct { +type FuzzyQuery struct { Term string `json:"term"` PrefixVal int `json:"prefix_length"` FuzzinessVal int `json:"fuzziness"` @@ -31,8 +31,8 @@ type fuzzyQuery struct { // // The current implementation uses Levenshtein edit // distance as the fuzziness metric. -func NewFuzzyQuery(term string) *fuzzyQuery { - return &fuzzyQuery{ +func NewFuzzyQuery(term string) *FuzzyQuery { + return &FuzzyQuery{ Term: term, PrefixVal: 0, FuzzinessVal: 2, @@ -40,43 +40,43 @@ func NewFuzzyQuery(term string) *fuzzyQuery { } } -func (q *fuzzyQuery) Boost() float64 { +func (q *FuzzyQuery) Boost() float64 { return q.BoostVal } -func (q *fuzzyQuery) SetBoost(b float64) Query { +func (q *FuzzyQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *fuzzyQuery) Field() string { +func (q *FuzzyQuery) Field() string { return q.FieldVal } -func (q *fuzzyQuery) SetField(f string) Query { +func (q *FuzzyQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *fuzzyQuery) Fuzziness() int { +func (q *FuzzyQuery) Fuzziness() int { return q.FuzzinessVal } -func (q *fuzzyQuery) SetFuzziness(f int) Query { +func (q *FuzzyQuery) SetFuzziness(f int) Query { q.FuzzinessVal = f return q } -func (q *fuzzyQuery) Prefix() int { +func (q *FuzzyQuery) Prefix() int { return q.PrefixVal } -func (q *fuzzyQuery) SetPrefix(p int) Query { +func (q *FuzzyQuery) SetPrefix(p int) Query { q.PrefixVal = p return q } -func (q *fuzzyQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *FuzzyQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -84,6 +84,6 @@ func (q *fuzzyQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expla return searchers.NewFuzzySearcher(i, q.Term, q.PrefixVal, q.FuzzinessVal, field, q.BoostVal, explain) } -func (q *fuzzyQuery) Validate() error { +func (q *FuzzyQuery) Validate() error { return nil } diff --git a/query_match.go b/search/query/query_match.go similarity index 81% rename from query_match.go rename to search/query/query_match.go index b13083a3..918376df 100644 --- a/query_match.go +++ b/search/query/query_match.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" @@ -18,7 +18,7 @@ import ( "github.com/blevesearch/bleve/search" ) -type matchQuery struct { +type MatchQuery struct { Match string `json:"match"` FieldVal string `json:"field,omitempty"` Analyzer string `json:"analyzer,omitempty"` @@ -63,90 +63,84 @@ func (o *MatchQueryOperator) UnmarshalJSON(data []byte) error { *o = MatchQueryOperatorAnd return nil default: - return matchQueryOperatorUnmarshalError(operatorString) + return fmt.Errorf("cannot unmarshal match operator '%s' from JSON", o) } } -type matchQueryOperatorUnmarshalError string - -func (e matchQueryOperatorUnmarshalError) Error() string { - return fmt.Sprintf("cannot unmarshal match operator '%s' from JSON", e) -} - // NewMatchQuery creates a Query for matching text. // An Analyzer is chosen based on the field. // Input text is analyzed using this analyzer. // Token terms resulting from this analysis are // used to perform term searches. Result documents // must satisfy at least one of these term searches. -func NewMatchQuery(match string) *matchQuery { - return &matchQuery{ +func NewMatchQuery(match string) *MatchQuery { + return &MatchQuery{ Match: match, BoostVal: 1.0, OperatorVal: MatchQueryOperatorOr, } } -// NewMatchQuery creates a Query for matching text. +// NewMatchQueryOperator creates a Query for matching text. // An Analyzer is chosen based on the field. // Input text is analyzed using this analyzer. // Token terms resulting from this analysis are // used to perform term searches. Result documents // must satisfy term searches according to given operator. -func NewMatchQueryOperator(match string, operator MatchQueryOperator) *matchQuery { - return &matchQuery{ +func NewMatchQueryOperator(match string, operator MatchQueryOperator) *MatchQuery { + return &MatchQuery{ Match: match, BoostVal: 1.0, OperatorVal: operator, } } -func (q *matchQuery) Boost() float64 { +func (q *MatchQuery) Boost() float64 { return q.BoostVal } -func (q *matchQuery) SetBoost(b float64) Query { +func (q *MatchQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *matchQuery) Field() string { +func (q *MatchQuery) Field() string { return q.FieldVal } -func (q *matchQuery) SetField(f string) Query { +func (q *MatchQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *matchQuery) Fuzziness() int { +func (q *MatchQuery) Fuzziness() int { return q.FuzzinessVal } -func (q *matchQuery) SetFuzziness(f int) Query { +func (q *MatchQuery) SetFuzziness(f int) Query { q.FuzzinessVal = f return q } -func (q *matchQuery) Prefix() int { +func (q *MatchQuery) Prefix() int { return q.PrefixVal } -func (q *matchQuery) SetPrefix(p int) Query { +func (q *MatchQuery) SetPrefix(p int) Query { q.PrefixVal = p return q } -func (q *matchQuery) Operator() MatchQueryOperator { +func (q *MatchQuery) Operator() MatchQueryOperator { return q.OperatorVal } -func (q *matchQuery) SetOperator(operator MatchQueryOperator) Query { +func (q *MatchQuery) SetOperator(operator MatchQueryOperator) Query { q.OperatorVal = operator return q } -func (q *matchQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { @@ -207,6 +201,6 @@ func (q *matchQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expla return noneQuery.Searcher(i, m, explain) } -func (q *matchQuery) Validate() error { +func (q *MatchQuery) Validate() error { return nil } diff --git a/query_match_all.go b/search/query/query_match_all.go similarity index 73% rename from query_match_all.go rename to search/query/query_match_all.go index 2ae2acb3..18ad9c4c 100644 --- a/query_match_all.go +++ b/search/query/query_match_all.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" @@ -18,44 +18,44 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type matchAllQuery struct { +type MatchAllQuery struct { BoostVal float64 `json:"boost,omitempty"` } // NewMatchAllQuery creates a Query which will // match all documents in the index. -func NewMatchAllQuery() *matchAllQuery { - return &matchAllQuery{ +func NewMatchAllQuery() *MatchAllQuery { + return &MatchAllQuery{ BoostVal: 1.0, } } -func (q *matchAllQuery) Boost() float64 { +func (q *MatchAllQuery) Boost() float64 { return q.BoostVal } -func (q *matchAllQuery) SetBoost(b float64) Query { +func (q *MatchAllQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *matchAllQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchAllQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { return searchers.NewMatchAllSearcher(i, q.BoostVal, explain) } -func (q *matchAllQuery) Validate() error { +func (q *MatchAllQuery) Validate() error { return nil } -func (q *matchAllQuery) Field() string { +func (q *MatchAllQuery) Field() string { return "" } -func (q *matchAllQuery) SetField(f string) Query { +func (q *MatchAllQuery) SetField(f string) Query { return q } -func (q *matchAllQuery) MarshalJSON() ([]byte, error) { +func (q *MatchAllQuery) MarshalJSON() ([]byte, error) { tmp := map[string]interface{}{ "boost": q.BoostVal, "match_all": map[string]interface{}{}, diff --git a/query_match_none.go b/search/query/query_match_none.go similarity index 73% rename from query_match_none.go rename to search/query/query_match_none.go index 14fddab5..743d2453 100644 --- a/query_match_none.go +++ b/search/query/query_match_none.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" @@ -18,44 +18,44 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type matchNoneQuery struct { +type MatchNoneQuery struct { BoostVal float64 `json:"boost,omitempty"` } // NewMatchNoneQuery creates a Query which will not // match any documents in the index. -func NewMatchNoneQuery() *matchNoneQuery { - return &matchNoneQuery{ +func NewMatchNoneQuery() *MatchNoneQuery { + return &MatchNoneQuery{ BoostVal: 1.0, } } -func (q *matchNoneQuery) Boost() float64 { +func (q *MatchNoneQuery) Boost() float64 { return q.BoostVal } -func (q *matchNoneQuery) SetBoost(b float64) Query { +func (q *MatchNoneQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *matchNoneQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchNoneQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { return searchers.NewMatchNoneSearcher(i) } -func (q *matchNoneQuery) Validate() error { +func (q *MatchNoneQuery) Validate() error { return nil } -func (q *matchNoneQuery) Field() string { +func (q *MatchNoneQuery) Field() string { return "" } -func (q *matchNoneQuery) SetField(f string) Query { +func (q *MatchNoneQuery) SetField(f string) Query { return q } -func (q *matchNoneQuery) MarshalJSON() ([]byte, error) { +func (q *MatchNoneQuery) MarshalJSON() ([]byte, error) { tmp := map[string]interface{}{ "boost": q.BoostVal, "match_none": map[string]interface{}{}, diff --git a/query_match_phrase.go b/search/query/query_match_phrase.go similarity index 86% rename from query_match_phrase.go rename to search/query/query_match_phrase.go index 1d588628..ca6e48f1 100644 --- a/query_match_phrase.go +++ b/search/query/query_match_phrase.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "fmt" @@ -18,7 +18,7 @@ import ( "github.com/blevesearch/bleve/search" ) -type matchPhraseQuery struct { +type MatchPhraseQuery struct { MatchPhrase string `json:"match_phrase"` FieldVal string `json:"field,omitempty"` Analyzer string `json:"analyzer,omitempty"` @@ -33,32 +33,32 @@ type matchPhraseQuery struct { // used to build a search phrase. Result documents // must match this phrase. Queried field must have been indexed with // IncludeTermVectors set to true. -func NewMatchPhraseQuery(matchPhrase string) *matchPhraseQuery { - return &matchPhraseQuery{ +func NewMatchPhraseQuery(matchPhrase string) *MatchPhraseQuery { + return &MatchPhraseQuery{ MatchPhrase: matchPhrase, BoostVal: 1.0, } } -func (q *matchPhraseQuery) Boost() float64 { +func (q *MatchPhraseQuery) Boost() float64 { return q.BoostVal } -func (q *matchPhraseQuery) SetBoost(b float64) Query { +func (q *MatchPhraseQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *matchPhraseQuery) Field() string { +func (q *MatchPhraseQuery) Field() string { return q.FieldVal } -func (q *matchPhraseQuery) SetField(f string) Query { +func (q *MatchPhraseQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *matchPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *MatchPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -111,6 +111,6 @@ func tokenStreamToPhrase(tokens analysis.TokenStream) []string { return nil } -func (q *matchPhraseQuery) Validate() error { +func (q *MatchPhraseQuery) Validate() error { return nil } diff --git a/query_numeric_range.go b/search/query/query_numeric_range.go similarity index 78% rename from query_numeric_range.go rename to search/query/query_numeric_range.go index 973b9cb3..1acfa4fc 100644 --- a/query_numeric_range.go +++ b/search/query/query_numeric_range.go @@ -7,16 +7,18 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( + "fmt" + "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/mapping" "github.com/blevesearch/bleve/search" "github.com/blevesearch/bleve/search/searchers" ) -type numericRangeQuery struct { +type NumericRangeQuery struct { Min *float64 `json:"min,omitempty"` Max *float64 `json:"max,omitempty"` InclusiveMin *bool `json:"inclusive_min,omitempty"` @@ -30,7 +32,7 @@ type numericRangeQuery struct { // Either, but not both endpoints can be nil. // The minimum value is inclusive. // The maximum value is exclusive. -func NewNumericRangeQuery(min, max *float64) *numericRangeQuery { +func NewNumericRangeQuery(min, max *float64) *NumericRangeQuery { return NewNumericRangeInclusiveQuery(min, max, nil, nil) } @@ -38,8 +40,8 @@ func NewNumericRangeQuery(min, max *float64) *numericRangeQuery { // of numeric values. // Either, but not both endpoints can be nil. // Control endpoint inclusion with inclusiveMin, inclusiveMax. -func NewNumericRangeInclusiveQuery(min, max *float64, minInclusive, maxInclusive *bool) *numericRangeQuery { - return &numericRangeQuery{ +func NewNumericRangeInclusiveQuery(min, max *float64, minInclusive, maxInclusive *bool) *NumericRangeQuery { + return &NumericRangeQuery{ Min: min, Max: max, InclusiveMin: minInclusive, @@ -48,25 +50,25 @@ func NewNumericRangeInclusiveQuery(min, max *float64, minInclusive, maxInclusive } } -func (q *numericRangeQuery) Boost() float64 { +func (q *NumericRangeQuery) Boost() float64 { return q.BoostVal } -func (q *numericRangeQuery) SetBoost(b float64) Query { +func (q *NumericRangeQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *numericRangeQuery) Field() string { +func (q *NumericRangeQuery) Field() string { return q.FieldVal } -func (q *numericRangeQuery) SetField(f string) Query { +func (q *NumericRangeQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *numericRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *NumericRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -74,9 +76,9 @@ func (q *numericRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping return searchers.NewNumericRangeSearcher(i, q.Min, q.Max, q.InclusiveMin, q.InclusiveMax, field, q.BoostVal, explain) } -func (q *numericRangeQuery) Validate() error { +func (q *NumericRangeQuery) Validate() error { if q.Min == nil && q.Min == q.Max { - return ErrorNumericQueryNoBounds + return fmt.Errorf("numeric range query must specify min or max") } return nil } diff --git a/query_phrase.go b/search/query/query_phrase.go similarity index 78% rename from query_phrase.go rename to search/query/query_phrase.go index 9f8df326..5c64c87e 100644 --- a/query_phrase.go +++ b/search/query/query_phrase.go @@ -7,10 +7,11 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "encoding/json" + "fmt" "github.com/blevesearch/bleve/index" "github.com/blevesearch/bleve/mapping" @@ -18,7 +19,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type phraseQuery struct { +type PhraseQuery struct { Terms []string `json:"terms"` FieldVal string `json:"field,omitempty"` BoostVal float64 `json:"boost,omitempty"` @@ -31,14 +32,14 @@ type phraseQuery struct { // order, at the correct index offsets, in the // specified field. Queried field must have been indexed with // IncludeTermVectors set to true. -func NewPhraseQuery(terms []string, field string) *phraseQuery { +func NewPhraseQuery(terms []string, field string) *PhraseQuery { termQueries := make([]Query, 0) for _, term := range terms { if term != "" { termQueries = append(termQueries, NewTermQuery(term).SetField(field)) } } - return &phraseQuery{ + return &PhraseQuery{ Terms: terms, FieldVal: field, BoostVal: 1.0, @@ -46,16 +47,16 @@ func NewPhraseQuery(terms []string, field string) *phraseQuery { } } -func (q *phraseQuery) Boost() float64 { +func (q *PhraseQuery) Boost() float64 { return q.BoostVal } -func (q *phraseQuery) SetBoost(b float64) Query { +func (q *PhraseQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *phraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *PhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { conjunctionQuery := NewConjunctionQuery(q.termQueries) conjunctionSearcher, err := conjunctionQuery.Searcher(i, m, explain) @@ -65,15 +66,15 @@ func (q *phraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expl return searchers.NewPhraseSearcher(i, conjunctionSearcher.(*searchers.ConjunctionSearcher), q.Terms) } -func (q *phraseQuery) Validate() error { +func (q *PhraseQuery) Validate() error { if len(q.termQueries) < 1 { - return ErrorPhraseQueryNoTerms + return fmt.Errorf("phrase query must contain at least one term") } return nil } -func (q *phraseQuery) UnmarshalJSON(data []byte) error { - type _phraseQuery phraseQuery +func (q *PhraseQuery) UnmarshalJSON(data []byte) error { + type _phraseQuery PhraseQuery tmp := _phraseQuery{} err := json.Unmarshal(data, &tmp) if err != nil { @@ -87,15 +88,15 @@ func (q *phraseQuery) UnmarshalJSON(data []byte) error { } q.termQueries = make([]Query, len(q.Terms)) for i, term := range q.Terms { - q.termQueries[i] = &termQuery{Term: term, FieldVal: q.FieldVal, BoostVal: q.BoostVal} + q.termQueries[i] = &TermQuery{Term: term, FieldVal: q.FieldVal, BoostVal: q.BoostVal} } return nil } -func (q *phraseQuery) Field() string { +func (q *PhraseQuery) Field() string { return "" } -func (q *phraseQuery) SetField(f string) Query { +func (q *PhraseQuery) SetField(f string) Query { return q } diff --git a/query_prefix.go b/search/query/query_prefix.go similarity index 77% rename from query_prefix.go rename to search/query/query_prefix.go index a3710a1c..8621451a 100644 --- a/query_prefix.go +++ b/search/query/query_prefix.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "github.com/blevesearch/bleve/index" @@ -16,7 +16,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type prefixQuery struct { +type PrefixQuery struct { Prefix string `json:"prefix"` FieldVal string `json:"field,omitempty"` BoostVal float64 `json:"boost,omitempty"` @@ -25,32 +25,32 @@ type prefixQuery struct { // NewPrefixQuery creates a new Query which finds // documents containing terms that start with the // specified prefix. -func NewPrefixQuery(prefix string) *prefixQuery { - return &prefixQuery{ +func NewPrefixQuery(prefix string) *PrefixQuery { + return &PrefixQuery{ Prefix: prefix, BoostVal: 1.0, } } -func (q *prefixQuery) Boost() float64 { +func (q *PrefixQuery) Boost() float64 { return q.BoostVal } -func (q *prefixQuery) SetBoost(b float64) Query { +func (q *PrefixQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *prefixQuery) Field() string { +func (q *PrefixQuery) Field() string { return q.FieldVal } -func (q *prefixQuery) SetField(f string) Query { +func (q *PrefixQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *prefixQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *PrefixQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -58,6 +58,6 @@ func (q *prefixQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expl return searchers.NewTermPrefixSearcher(i, q.Prefix, field, q.BoostVal, explain) } -func (q *prefixQuery) Validate() error { +func (q *PrefixQuery) Validate() error { return nil } diff --git a/query_regexp.go b/search/query/query_regexp.go similarity index 81% rename from query_regexp.go rename to search/query/query_regexp.go index 89e0e7c4..820da39c 100644 --- a/query_regexp.go +++ b/search/query/query_regexp.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "regexp" @@ -19,7 +19,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type regexpQuery struct { +type RegexpQuery struct { Regexp string `json:"regexp"` FieldVal string `json:"field,omitempty"` BoostVal float64 `json:"boost,omitempty"` @@ -29,32 +29,32 @@ type regexpQuery struct { // NewRegexpQuery creates a new Query which finds // documents containing terms that match the // specified regular expression. -func NewRegexpQuery(regexp string) *regexpQuery { - return ®expQuery{ +func NewRegexpQuery(regexp string) *RegexpQuery { + return &RegexpQuery{ Regexp: regexp, BoostVal: 1.0, } } -func (q *regexpQuery) Boost() float64 { +func (q *RegexpQuery) Boost() float64 { return q.BoostVal } -func (q *regexpQuery) SetBoost(b float64) Query { +func (q *RegexpQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *regexpQuery) Field() string { +func (q *RegexpQuery) Field() string { return q.FieldVal } -func (q *regexpQuery) SetField(f string) Query { +func (q *RegexpQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *regexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *RegexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -67,11 +67,11 @@ func (q *regexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, expl return searchers.NewRegexpSearcher(i, q.compiled, field, q.BoostVal, explain) } -func (q *regexpQuery) Validate() error { +func (q *RegexpQuery) Validate() error { return q.compile() } -func (q *regexpQuery) compile() error { +func (q *RegexpQuery) compile() error { if q.compiled == nil { // require that pattern be anchored to start and end of term actualRegexp := q.Regexp diff --git a/query_string.go b/search/query/query_string.go similarity index 75% rename from query_string.go rename to search/query/query_string.go index eed878b5..037e44cd 100644 --- a/query_string.go +++ b/search/query/query_string.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "github.com/blevesearch/bleve/index" @@ -15,7 +15,7 @@ import ( "github.com/blevesearch/bleve/search" ) -type queryStringQuery struct { +type QueryStringQuery struct { Query string `json:"query"` BoostVal float64 `json:"boost,omitempty"` } @@ -23,23 +23,23 @@ type queryStringQuery struct { // NewQueryStringQuery creates a new Query used for // finding documents that satisfy a query string. The // query string is a small query language for humans. -func NewQueryStringQuery(query string) *queryStringQuery { - return &queryStringQuery{ +func NewQueryStringQuery(query string) *QueryStringQuery { + return &QueryStringQuery{ Query: query, BoostVal: 1.0, } } -func (q *queryStringQuery) Boost() float64 { +func (q *QueryStringQuery) Boost() float64 { return q.BoostVal } -func (q *queryStringQuery) SetBoost(b float64) Query { +func (q *QueryStringQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *queryStringQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *QueryStringQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { newQuery, err := parseQuerySyntax(q.Query) if err != nil { return nil, err @@ -47,7 +47,7 @@ func (q *queryStringQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, return newQuery.Searcher(i, m, explain) } -func (q *queryStringQuery) Validate() error { +func (q *QueryStringQuery) Validate() error { newQuery, err := parseQuerySyntax(q.Query) if err != nil { return err @@ -55,10 +55,10 @@ func (q *queryStringQuery) Validate() error { return newQuery.Validate() } -func (q *queryStringQuery) Field() string { +func (q *QueryStringQuery) Field() string { return "" } -func (q *queryStringQuery) SetField(f string) Query { +func (q *QueryStringQuery) SetField(f string) Query { return q } diff --git a/query_string.y b/search/query/query_string.y similarity index 99% rename from query_string.y rename to search/query/query_string.y index 4849ac38..48fb40d6 100644 --- a/query_string.y +++ b/search/query/query_string.y @@ -1,5 +1,5 @@ %{ -package bleve +package query import ( "fmt" "strconv" diff --git a/query_string.y.go b/search/query/query_string.y.go similarity index 99% rename from query_string.y.go rename to search/query/query_string.y.go index df75cd07..7f423919 100644 --- a/query_string.y.go +++ b/search/query/query_string.y.go @@ -1,4 +1,4 @@ -package bleve +package query import __yyfmt__ "fmt" diff --git a/query_string_lex.go b/search/query/query_string_lex.go similarity index 99% rename from query_string_lex.go rename to search/query/query_string_lex.go index 8bd4a015..8955c6ef 100644 --- a/query_string_lex.go +++ b/search/query/query_string_lex.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "bufio" diff --git a/query_string_parser.go b/search/query/query_string_parser.go similarity index 98% rename from query_string_parser.go rename to search/query/query_string_parser.go index f9875df0..2b44c23b 100644 --- a/query_string_parser.go +++ b/search/query/query_string_parser.go @@ -15,7 +15,7 @@ // using -i.tmp works on both, at the expense of having to remove // the unsightly .tmp files -package bleve +package query import ( "fmt" @@ -55,7 +55,7 @@ const ( type lexerWrapper struct { lex yyLexer errs []string - query *booleanQuery + query *BooleanQuery } func newLexerWrapper(lex yyLexer) *lexerWrapper { diff --git a/query_string_parser_test.go b/search/query/query_string_parser_test.go similarity index 83% rename from query_string_parser_test.go rename to search/query/query_string_parser_test.go index 68a4b27d..e978f0e2 100644 --- a/query_string_parser_test.go +++ b/search/query/query_string_parser_test.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "reflect" @@ -29,7 +29,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }{ { input: "test", - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -39,7 +39,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `"test phrase 1"`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -49,7 +49,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: "field:test", - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -60,7 +60,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // - is allowed inside a term, just not the start { input: "field:t-est", - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -71,7 +71,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // + is allowed inside a term, just not the start { input: "field:t+est", - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -82,7 +82,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // > is allowed inside a term, just not the start { input: "field:t>est", - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -93,7 +93,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // < is allowed inside a term, just not the start { input: "field:t5`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -300,7 +300,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:>=5`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -310,7 +310,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:<5`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -320,7 +320,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:<=5`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -330,7 +330,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:>"2006-01-02T15:04:05Z07:00"`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -340,7 +340,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:>="2006-01-02T15:04:05Z07:00"`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -350,7 +350,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:<"2006-01-02T15:04:05Z07:00"`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -360,7 +360,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `field:<="2006-01-02T15:04:05Z07:00"`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -370,7 +370,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `/mar.*ty/`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -380,7 +380,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `name:/mar.*ty/`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -390,7 +390,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `mart*`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -400,7 +400,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `name:mart*`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -414,7 +414,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // escape : as field delimeter { input: `name\:marty`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -425,7 +425,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // first colon delimiter, second escaped { input: `name:marty\:couchbase`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -436,7 +436,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // escape space, single arguemnt to match query { input: `marty\ couchbase`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -447,7 +447,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // escape leading plus, not a must clause { input: `\+marty`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -458,7 +458,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // escape leading minus, not a must not clause { input: `\-marty`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -469,7 +469,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // escape quote inside of phrase { input: `"what does \"quote\" mean"`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -480,7 +480,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // escaping an unsupported character retains backslash { input: `can\ i\ escap\e`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -491,7 +491,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // leading spaces { input: ` what`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -502,7 +502,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // no boost value defaults to 1 { input: `term^`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -514,7 +514,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { // but contains escape and ends up as string { input: `3.0\:`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -524,7 +524,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { }, { input: `3.0\a`, - mapping: NewIndexMapping(), + mapping: mapping.NewIndexMapping(), result: NewBooleanQuery( nil, []Query{ @@ -547,7 +547,7 @@ func TestQuerySyntaxParserValid(t *testing.T) { } if !reflect.DeepEqual(q, test.result) { t.Errorf("Expected %#v, got %#v: for %s", test.result, q, test.input) - t.Errorf("Expected %#v, got %#v: for %s", test.result.(*booleanQuery).Should.(*disjunctionQuery).Disjuncts[0], q.(*booleanQuery).Should.(*disjunctionQuery).Disjuncts[0], test.input) + t.Errorf("Expected %#v, got %#v: for %s", test.result.(*BooleanQuery).Should.(*DisjunctionQuery).Disjuncts[0], q.(*BooleanQuery).Should.(*DisjunctionQuery).Disjuncts[0], test.input) } } } diff --git a/query_term.go b/search/query/query_term.go similarity index 78% rename from query_term.go rename to search/query/query_term.go index 683cdf1e..bb961856 100644 --- a/query_term.go +++ b/search/query/query_term.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "github.com/blevesearch/bleve/index" @@ -16,7 +16,7 @@ import ( "github.com/blevesearch/bleve/search/searchers" ) -type termQuery struct { +type TermQuery struct { Term string `json:"term"` FieldVal string `json:"field,omitempty"` BoostVal float64 `json:"boost,omitempty"` @@ -24,32 +24,32 @@ type termQuery struct { // NewTermQuery creates a new Query for finding an // exact term match in the index. -func NewTermQuery(term string) *termQuery { - return &termQuery{ +func NewTermQuery(term string) *TermQuery { + return &TermQuery{ Term: term, BoostVal: 1.0, } } -func (q *termQuery) Boost() float64 { +func (q *TermQuery) Boost() float64 { return q.BoostVal } -func (q *termQuery) SetBoost(b float64) Query { +func (q *TermQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *termQuery) Field() string { +func (q *TermQuery) Field() string { return q.FieldVal } -func (q *termQuery) SetField(f string) Query { +func (q *TermQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *termQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *TermQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -57,6 +57,6 @@ func (q *termQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explai return searchers.NewTermSearcher(i, q.Term, field, q.BoostVal, explain) } -func (q *termQuery) Validate() error { +func (q *TermQuery) Validate() error { return nil } diff --git a/query_test.go b/search/query/query_test.go similarity index 89% rename from query_test.go rename to search/query/query_test.go index e7b39876..f9e06420 100644 --- a/query_test.go +++ b/search/query/query_test.go @@ -7,12 +7,14 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "reflect" "strings" "testing" + + "github.com/blevesearch/bleve/mapping" ) var minNum = 5.1 @@ -24,7 +26,7 @@ func TestParseQuery(t *testing.T) { tests := []struct { input []byte output Query - err error + err bool }{ { input: []byte(`{"term":"water","field":"desc"}`), @@ -49,7 +51,7 @@ func TestParseQuery(t *testing.T) { { input: []byte(`{"match":"beer","field":"desc","operator":"does not exist"}`), output: nil, - err: matchQueryOperatorUnmarshalError("does not exist"), + err: true, }, { input: []byte(`{"match_phrase":"light beer","field":"desc"}`), @@ -98,18 +100,14 @@ func TestParseQuery(t *testing.T) { { input: []byte(`{"madeitup":"queryhere"}`), output: nil, - err: ErrorUnknownQueryType, + err: true, }, } for i, test := range tests { actual, err := ParseQuery(test.input) - if err != nil && test.err == nil { + if err != nil && test.err == false { t.Errorf("error %v for %d", err, i) - } else if test.err != nil { - if !reflect.DeepEqual(err, test.err) { - t.Errorf("expected error: %#v, got: %#v", test.err, err) - } } if !reflect.DeepEqual(test.output, actual) { @@ -160,90 +158,77 @@ func TestSetGetField(t *testing.T) { func TestQueryValidate(t *testing.T) { tests := []struct { query Query - err error + err bool }{ { query: NewTermQuery("water").SetField("desc"), - err: nil, }, { query: NewMatchQuery("beer").SetField("desc"), - err: nil, }, { query: NewMatchPhraseQuery("light beer").SetField("desc"), - err: nil, }, { query: NewNumericRangeQuery(&minNum, &maxNum).SetField("desc"), - err: nil, }, { query: NewNumericRangeQuery(nil, nil).SetField("desc"), - err: ErrorNumericQueryNoBounds, + err: true, }, { query: NewDateRangeQuery(&startDate, &endDate).SetField("desc"), - err: nil, }, { query: NewPrefixQuery("budwei").SetField("desc"), - err: nil, }, { query: NewQueryStringQuery(`+beer "light beer" -devon`), - err: nil, }, { query: NewPhraseQuery([]string{"watered", "down"}, "desc"), - err: nil, }, { query: NewPhraseQuery([]string{}, "field"), - err: ErrorPhraseQueryNoTerms, + err: true, }, { query: NewMatchNoneQuery().SetBoost(25), - err: nil, }, { query: NewMatchAllQuery().SetBoost(25), - err: nil, }, { query: NewBooleanQuery( []Query{NewMatchQuery("beer").SetField("desc")}, []Query{NewMatchQuery("water").SetField("desc")}, []Query{NewMatchQuery("devon").SetField("desc")}), - err: nil, }, { query: NewBooleanQuery( nil, nil, []Query{NewMatchQuery("devon").SetField("desc")}), - err: nil, }, { query: NewBooleanQuery( []Query{}, []Query{}, []Query{NewMatchQuery("devon").SetField("desc")}), - err: nil, }, { query: NewBooleanQuery( nil, nil, nil), - err: ErrorBooleanQueryNeedsMustOrShouldOrNotMust, + err: true, }, { query: NewBooleanQuery( []Query{}, []Query{}, []Query{}), - err: ErrorBooleanQueryNeedsMustOrShouldOrNotMust, + err: true, }, { query: NewBooleanQueryMinShould( @@ -251,24 +236,25 @@ func TestQueryValidate(t *testing.T) { []Query{NewMatchQuery("water").SetField("desc")}, []Query{NewMatchQuery("devon").SetField("desc")}, 2.0), - err: ErrorDisjunctionFewerThanMinClauses, + err: true, }, { query: NewDocIDQuery(nil).SetBoost(25), - err: nil, }, } for _, test := range tests { actual := test.query.Validate() - if !reflect.DeepEqual(actual, test.err) { + if actual != nil && !test.err { + t.Errorf("expected no error: %#v got %#v", test.err, actual) + } else if actual == nil && test.err { t.Errorf("expected error: %#v got %#v", test.err, actual) } } } func TestDumpQuery(t *testing.T) { - mapping := NewIndexMapping() + mapping := mapping.NewIndexMapping() q := NewQueryStringQuery("+water -light beer") s, err := DumpQuery(mapping, q) if err != nil { diff --git a/query_wildcard.go b/search/query/query_wildcard.go similarity index 81% rename from query_wildcard.go rename to search/query/query_wildcard.go index 7b713454..50409492 100644 --- a/query_wildcard.go +++ b/search/query/query_wildcard.go @@ -7,7 +7,7 @@ // either express or implied. See the License for the specific language governing permissions // and limitations under the License. -package bleve +package query import ( "regexp" @@ -38,7 +38,7 @@ var wildcardRegexpReplacer = strings.NewReplacer( "*", ".*", "?", ".") -type wildcardQuery struct { +type WildcardQuery struct { Wildcard string `json:"wildcard"` FieldVal string `json:"field,omitempty"` BoostVal float64 `json:"boost,omitempty"` @@ -50,32 +50,32 @@ type wildcardQuery struct { // specified wildcard. In the wildcard pattern '*' // will match any sequence of 0 or more characters, // and '?' will match any single character. -func NewWildcardQuery(wildcard string) *wildcardQuery { - return &wildcardQuery{ +func NewWildcardQuery(wildcard string) *WildcardQuery { + return &WildcardQuery{ Wildcard: wildcard, BoostVal: 1.0, } } -func (q *wildcardQuery) Boost() float64 { +func (q *WildcardQuery) Boost() float64 { return q.BoostVal } -func (q *wildcardQuery) SetBoost(b float64) Query { +func (q *WildcardQuery) SetBoost(b float64) Query { q.BoostVal = b return q } -func (q *wildcardQuery) Field() string { +func (q *WildcardQuery) Field() string { return q.FieldVal } -func (q *wildcardQuery) SetField(f string) Query { +func (q *WildcardQuery) SetField(f string) Query { q.FieldVal = f return q } -func (q *wildcardQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { +func (q *WildcardQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) { field := q.FieldVal if q.FieldVal == "" { field = m.DefaultSearchField() @@ -91,13 +91,13 @@ func (q *wildcardQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, ex return searchers.NewRegexpSearcher(i, q.compiled, field, q.BoostVal, explain) } -func (q *wildcardQuery) Validate() error { +func (q *WildcardQuery) Validate() error { var err error q.compiled, err = q.convertToRegexp() return err } -func (q *wildcardQuery) convertToRegexp() (*regexp.Regexp, error) { +func (q *WildcardQuery) convertToRegexp() (*regexp.Regexp, error) { regexpString := "^" + wildcardRegexpReplacer.Replace(q.Wildcard) + "$" return regexp.Compile(regexpString) }