2014-07-30 18:30:38 +02:00
|
|
|
// 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 bleve
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
2014-08-14 03:14:47 +02:00
|
|
|
"github.com/couchbaselabs/bleve/analysis"
|
2014-07-30 18:30:38 +02:00
|
|
|
"github.com/couchbaselabs/bleve/search"
|
|
|
|
)
|
|
|
|
|
2014-08-11 17:03:29 +02:00
|
|
|
type NumericRange struct {
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
Min *float64 `json:"min,omitempty"`
|
|
|
|
Max *float64 `json:"max,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type DateTimeRange struct {
|
2014-08-14 03:14:47 +02:00
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
Start time.Time `json:"start,omitempty"`
|
|
|
|
End time.Time `json:"end,omitempty"`
|
|
|
|
startString *string
|
|
|
|
endString *string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dr *DateTimeRange) ParseDates(dateTimeParser analysis.DateTimeParser) {
|
|
|
|
if dr.Start.IsZero() && dr.startString != nil {
|
|
|
|
start, err := dateTimeParser.ParseDateTime(*dr.startString)
|
|
|
|
if err == nil {
|
|
|
|
dr.Start = start
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if dr.End.IsZero() && dr.endString != nil {
|
|
|
|
end, err := dateTimeParser.ParseDateTime(*dr.endString)
|
|
|
|
if err == nil {
|
|
|
|
dr.End = end
|
|
|
|
}
|
|
|
|
}
|
2014-08-11 17:03:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (dr *DateTimeRange) UnmarshalJSON(input []byte) error {
|
|
|
|
var temp struct {
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
Start *string `json:"start,omitempty"`
|
|
|
|
End *string `json:"end,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err := json.Unmarshal(input, &temp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
dr.Name = temp.Name
|
|
|
|
if temp.Start != nil {
|
2014-08-14 03:14:47 +02:00
|
|
|
dr.startString = temp.Start
|
2014-08-11 17:03:29 +02:00
|
|
|
}
|
|
|
|
if temp.End != nil {
|
2014-08-14 03:14:47 +02:00
|
|
|
dr.endString = temp.End
|
2014-08-11 17:03:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type FacetRequest struct {
|
|
|
|
Size int
|
|
|
|
Field string
|
|
|
|
NumericRanges []*NumericRange `json:"numeric_ranges,omitempty"`
|
|
|
|
DateTimeRanges []*DateTimeRange `json:"date_ranges,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFacetRequest(field string, size int) *FacetRequest {
|
|
|
|
return &FacetRequest{
|
|
|
|
Field: field,
|
|
|
|
Size: size,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (fr *FacetRequest) AddDateTimeRange(name string, start, end time.Time) {
|
|
|
|
if fr.DateTimeRanges == nil {
|
|
|
|
fr.DateTimeRanges = make([]*DateTimeRange, 0, 1)
|
|
|
|
}
|
|
|
|
fr.DateTimeRanges = append(fr.DateTimeRanges, &DateTimeRange{Name: name, Start: start, End: end})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (fr *FacetRequest) AddNumericRange(name string, min, max *float64) {
|
|
|
|
if fr.NumericRanges == nil {
|
|
|
|
fr.NumericRanges = make([]*NumericRange, 0, 1)
|
|
|
|
}
|
|
|
|
fr.NumericRanges = append(fr.NumericRanges, &NumericRange{Name: name, Min: min, Max: max})
|
|
|
|
}
|
|
|
|
|
|
|
|
type FacetsRequest map[string]*FacetRequest
|
|
|
|
|
2014-07-30 18:30:38 +02:00
|
|
|
type HighlightRequest struct {
|
|
|
|
Style *string `json:"style"`
|
|
|
|
Fields []string `json:"fields"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewHighlight() *HighlightRequest {
|
|
|
|
return &HighlightRequest{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewHighlightWithStyle(style string) *HighlightRequest {
|
|
|
|
return &HighlightRequest{
|
|
|
|
Style: &style,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type SearchRequest struct {
|
|
|
|
Query Query `json:"query"`
|
|
|
|
Size int `json:"size"`
|
|
|
|
From int `json:"from"`
|
|
|
|
Highlight *HighlightRequest `json:"highlight"`
|
2014-08-06 19:52:20 +02:00
|
|
|
Fields []string `json:"fields"`
|
2014-08-11 17:03:29 +02:00
|
|
|
Facets FacetsRequest `json:"facets"`
|
2014-07-30 18:30:38 +02:00
|
|
|
Explain bool `json:"explain"`
|
|
|
|
}
|
|
|
|
|
2014-08-11 17:03:29 +02:00
|
|
|
func (r *SearchRequest) AddFacet(facetName string, f *FacetRequest) {
|
|
|
|
if r.Facets == nil {
|
|
|
|
r.Facets = make(FacetsRequest, 1)
|
|
|
|
}
|
|
|
|
r.Facets[facetName] = f
|
|
|
|
}
|
|
|
|
|
2014-07-30 18:30:38 +02:00
|
|
|
func (r *SearchRequest) UnmarshalJSON(input []byte) error {
|
|
|
|
var temp struct {
|
|
|
|
Q json.RawMessage `json:"query"`
|
|
|
|
Size int `json:"size"`
|
|
|
|
From int `json:"from"`
|
|
|
|
Highlight *HighlightRequest `json:"highlight"`
|
2014-08-06 19:52:20 +02:00
|
|
|
Fields []string `json:"fields"`
|
2014-08-11 17:03:29 +02:00
|
|
|
Facets FacetsRequest `json:"facets"`
|
2014-07-30 18:30:38 +02:00
|
|
|
Explain bool `json:"explain"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err := json.Unmarshal(input, &temp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
r.Size = temp.Size
|
|
|
|
r.From = temp.From
|
|
|
|
r.Explain = temp.Explain
|
|
|
|
r.Highlight = temp.Highlight
|
2014-08-06 19:52:20 +02:00
|
|
|
r.Fields = temp.Fields
|
2014-08-11 17:03:29 +02:00
|
|
|
r.Facets = temp.Facets
|
2014-07-30 18:30:38 +02:00
|
|
|
r.Query, err = ParseQuery(temp.Q)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Size <= 0 {
|
|
|
|
r.Size = 10
|
|
|
|
}
|
|
|
|
if r.From <= 0 {
|
|
|
|
r.From = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-07-31 16:58:20 +02:00
|
|
|
func NewSearchRequest(q Query) *SearchRequest {
|
|
|
|
return NewSearchRequestOptions(q, 10, 0, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSearchRequestOptions(q Query, size, from int, explain bool) *SearchRequest {
|
2014-07-30 18:30:38 +02:00
|
|
|
return &SearchRequest{
|
|
|
|
Query: q,
|
|
|
|
Size: size,
|
|
|
|
From: from,
|
|
|
|
Explain: explain,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type SearchResult struct {
|
|
|
|
Request *SearchRequest `json:"request"`
|
|
|
|
Hits search.DocumentMatchCollection `json:"hits"`
|
|
|
|
Total uint64 `json:"total_hits"`
|
|
|
|
MaxScore float64 `json:"max_score"`
|
|
|
|
Took time.Duration `json:"took"`
|
2014-08-11 17:03:29 +02:00
|
|
|
Facets search.FacetResults `json:"facets"`
|
2014-07-30 18:30:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sr *SearchResult) String() string {
|
|
|
|
rv := ""
|
|
|
|
if len(sr.Hits) > 0 {
|
|
|
|
rv = fmt.Sprintf("%d matches, showing %d through %d, took %s\n", sr.Total, sr.Request.From+1, sr.Request.From+len(sr.Hits), sr.Took)
|
|
|
|
for i, hit := range sr.Hits {
|
|
|
|
rv += fmt.Sprintf("%5d. %s (%f)\n", i+sr.Request.From+1, hit.ID, hit.Score)
|
|
|
|
for fragmentField, fragments := range hit.Fragments {
|
|
|
|
rv += fmt.Sprintf("\t%s\n", fragmentField)
|
|
|
|
for _, fragment := range fragments {
|
|
|
|
rv += fmt.Sprintf("\t\t%s\n", fragment)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rv = "No matches"
|
|
|
|
}
|
|
|
|
return rv
|
|
|
|
}
|