Browse Source

initial refactor of query into separate package

Marty Schoch 3 years ago
parent
commit
9ec2ddd757

+ 1 - 0
.gitignore

@@ -14,5 +14,6 @@ query_string.y.go.tmp
 vendor/**
 !vendor/manifest
 /y.output
+/search/query/y.output
 *.test
 tags

+ 4 - 3
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 {

+ 7 - 6
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() {

+ 0 - 5
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()

+ 11 - 21
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",
 }

+ 16 - 34
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 {

+ 4 - 3
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)
 }
 

+ 170 - 314
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"
-)
+// NewBoolFieldQuery creates a new Query for boolean fields
+func NewBoolFieldQuery(val bool) *query.BoolFieldQuery {
+	return query.NewBoolFieldQuery(val)
+}
+
+// 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)
+}
+
+// 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)
+}
+
+// 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)
+}
 
-// 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
+// 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)
 }
 
-// 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
-	}
+// 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)
+}
 
-	_, 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
+// 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)
 }
 
-// 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)
+// 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)
+}
 
-	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
-	}
+// 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)
+}
 
-	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)
+// NewTermQuery creates a new Query for finding an
+// exact term match in the index.
+func NewTermQuery(term string) *query.TermQuery {
+	return query.NewTermQuery(term)
 }
 
-// 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
+// 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)
 }

+ 5 - 4
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,

+ 347 - 0
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
+}

+ 10 - 10
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
 }

+ 30 - 24
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
 }

+ 12 - 12
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
 }

+ 20 - 13
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")
 	}

+ 18 - 17
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
 }

+ 10 - 10
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
 }

+ 14 - 14
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
 }

+ 20 - 26
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
 }

+ 11 - 11
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{}{},

+ 11 - 11
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{}{},

+ 10 - 10
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
 }

+ 14 - 12
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
 }

+ 15 - 14
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
 }

+ 10 - 10
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
 }

+ 11 - 11
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 &regexpQuery{
+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

+ 10 - 10
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
 }

+ 1 - 1
query_string.y

@@ -1,5 +1,5 @@
 %{
-package bleve
+package query
 import (
 	"fmt"
 	"strconv"

+ 1 - 1
query_string.y.go

@@ -1,4 +1,4 @@
-package bleve
+package query
 
 import __yyfmt__ "fmt"
 

+ 1 - 1
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"

+ 2 - 2
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 {

+ 50 - 50
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