Skip to content

Commit

Permalink
Merge branch 'master' into MB-58134
Browse files Browse the repository at this point in the history
  • Loading branch information
CascadingRadium authored Sep 8, 2023
2 parents 274fc9d + 81c11e1 commit e2859c9
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 40 deletions.
19 changes: 16 additions & 3 deletions index_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,10 +531,23 @@ func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr
} else if facetRequest.DateTimeRanges != nil {
// build date range facet
facetBuilder := facet.NewDateTimeFacetBuilder(facetRequest.Field, facetRequest.Size)
dateTimeParser := i.m.DateTimeParserNamed("")
for _, dr := range facetRequest.DateTimeRanges {
start, end := dr.ParseDates(dateTimeParser)
facetBuilder.AddRange(dr.Name, start, end)
dateTimeParserName := defaultDateTimeParser
if dr.DateTimeParser != "" {
dateTimeParserName = dr.DateTimeParser
}
dateTimeParser := i.m.DateTimeParserNamed(dateTimeParserName)
if dateTimeParser == nil {
return nil, fmt.Errorf("no date time parser named `%s` registered", dateTimeParserName)
}
start, end, startLayout, endLayout, err := dr.ParseDates(dateTimeParser)
if err != nil {
return nil, fmt.Errorf("ParseDates err: %v, using date time parser named %s", err, dateTimeParserName)
}
if start.IsZero() && end.IsZero() {
return nil, fmt.Errorf("date range query must specify either start, end or both for date range name '%s'", dr.Name)
}
facetBuilder.AddRange(dr.Name, start, end, startLayout, endLayout)
}
facetsBuilder.Add(facetName, facetBuilder)
} else {
Expand Down
89 changes: 63 additions & 26 deletions search.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,36 +46,44 @@ func init() {
}

type dateTimeRange struct {
Name string `json:"name,omitempty"`
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
startString *string
endString *string
Name string `json:"name,omitempty"`
Start time.Time `json:"start,omitempty"`
End time.Time `json:"end,omitempty"`
DateTimeParser string `json:"datetime_parser,omitempty"`
startString *string
endString *string
}

func (dr *dateTimeRange) ParseDates(dateTimeParser analysis.DateTimeParser) (start, end time.Time) {
func (dr *dateTimeRange) ParseDates(dateTimeParser analysis.DateTimeParser) (start, end time.Time, startLayout, endLayout string, err error) {
start = dr.Start
startLayout = time.RFC3339Nano
if dr.Start.IsZero() && dr.startString != nil {
s, _, err := dateTimeParser.ParseDateTime(*dr.startString)
if err == nil {
start = s
s, layout, parseError := dateTimeParser.ParseDateTime(*dr.startString)
if parseError != nil {
return start, end, startLayout, endLayout, fmt.Errorf("error parsing start date '%s' for date range name '%s': %v", *dr.startString, dr.Name, parseError)
}
start = s
startLayout = layout
}
end = dr.End
endLayout = time.RFC3339Nano
if dr.End.IsZero() && dr.endString != nil {
e, _, err := dateTimeParser.ParseDateTime(*dr.endString)
if err == nil {
end = e
e, layout, parseError := dateTimeParser.ParseDateTime(*dr.endString)
if parseError != nil {
return start, end, startLayout, endLayout, fmt.Errorf("error parsing end date '%s' for date range name '%s': %v", *dr.endString, dr.Name, parseError)
}
end = e
endLayout = layout
}
return start, end
return start, end, startLayout, endLayout, err
}

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"`
Name string `json:"name,omitempty"`
Start *string `json:"start,omitempty"`
End *string `json:"end,omitempty"`
DateTimeParser string `json:"datetime_parser,omitempty"`
}

if err := json.Unmarshal(input, &temp); err != nil {
Expand All @@ -89,22 +97,33 @@ func (dr *dateTimeRange) UnmarshalJSON(input []byte) error {
if temp.End != nil {
dr.endString = temp.End
}
if temp.DateTimeParser != "" {
dr.DateTimeParser = temp.DateTimeParser
}

return nil
}

func (dr *dateTimeRange) MarshalJSON() ([]byte, error) {
rv := map[string]interface{}{
"name": dr.Name,
"start": dr.Start,
"end": dr.End,
"name": dr.Name,
}
if dr.Start.IsZero() && dr.startString != nil {

if !dr.Start.IsZero() {
rv["start"] = dr.Start
} else if dr.startString != nil {
rv["start"] = dr.startString
}
if dr.End.IsZero() && dr.endString != nil {

if !dr.End.IsZero() {
rv["end"] = dr.End
} else if dr.endString != nil {
rv["end"] = dr.endString
}

if dr.DateTimeParser != "" {
rv["datetime_parser"] = dr.DateTimeParser
}
return json.Marshal(rv)
}

Expand Down Expand Up @@ -138,7 +157,7 @@ func (fr *FacetRequest) Validate() error {
nrCount := len(fr.NumericRanges)
drCount := len(fr.DateTimeRanges)
if nrCount > 0 && drCount > 0 {
return fmt.Errorf("facet can only conain numeric ranges or date ranges, not both")
return fmt.Errorf("facet can only contain numeric ranges or date ranges, not both")
}

if nrCount > 0 {
Expand All @@ -164,9 +183,16 @@ func (fr *FacetRequest) Validate() error {
return fmt.Errorf("date ranges contains duplicate name '%s'", dr.Name)
}
drNames[dr.Name] = struct{}{}
start, end := dr.ParseDates(dateTimeParser)
if start.IsZero() && end.IsZero() {
return fmt.Errorf("date range query must specify either start, end or both for range name '%s'", dr.Name)
if dr.DateTimeParser == "" {
// cannot parse the date range dates as the defaultDateTimeParser is overridden
// so perform this validation at query time
start, end, _, _, err := dr.ParseDates(dateTimeParser)
if err != nil {
return fmt.Errorf("ParseDates err: %v, using date time parser named %s", err, defaultDateTimeParser)
}
if start.IsZero() && end.IsZero() {
return fmt.Errorf("date range query must specify either start, end or both for range name '%s'", dr.Name)
}
}
}
}
Expand All @@ -186,7 +212,7 @@ func (fr *FacetRequest) AddDateTimeRange(name string, start, end time.Time) {
}

// AddDateTimeRangeString adds a bucket to a field
// containing date values.
// containing date values. Uses defaultDateTimeParser to parse the date strings.
func (fr *FacetRequest) AddDateTimeRangeString(name string, start, end *string) {
if fr.DateTimeRanges == nil {
fr.DateTimeRanges = make([]*dateTimeRange, 0, 1)
Expand All @@ -195,6 +221,17 @@ func (fr *FacetRequest) AddDateTimeRangeString(name string, start, end *string)
&dateTimeRange{Name: name, startString: start, endString: end})
}

// AddDateTimeRangeString adds a bucket to a field
// containing date values. Uses the specified parser to parse the date strings.
// provided the parser is registered in the index mapping.
func (fr *FacetRequest) AddDateTimeRangeStringWithParser(name string, start, end *string, parser string) {
if fr.DateTimeRanges == nil {
fr.DateTimeRanges = make([]*dateTimeRange, 0, 1)
}
fr.DateTimeRanges = append(fr.DateTimeRanges,
&dateTimeRange{Name: name, startString: start, endString: end, DateTimeParser: parser})
}

// AddNumericRange adds a bucket to a field
// containing numeric values. Documents with a
// numeric value falling into this range are
Expand Down
31 changes: 24 additions & 7 deletions search/facet/facet_builder_datetime.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package facet
import (
"reflect"
"sort"
"strconv"
"time"

"github.com/blevesearch/bleve/v2/numeric"
Expand All @@ -35,8 +36,10 @@ func init() {
}

type dateTimeRange struct {
start time.Time
end time.Time
start time.Time
end time.Time
startLayout string
endLayout string
}

type DateTimeFacetBuilder struct {
Expand Down Expand Up @@ -75,10 +78,12 @@ func (fb *DateTimeFacetBuilder) Size() int {
return sizeInBytes
}

func (fb *DateTimeFacetBuilder) AddRange(name string, start, end time.Time) {
func (fb *DateTimeFacetBuilder) AddRange(name string, start, end time.Time, startLayout string, endLayout string) {
r := dateTimeRange{
start: start,
end: end,
start: start,
end: end,
startLayout: startLayout,
endLayout: endLayout,
}
fb.ranges[name] = &r
}
Expand Down Expand Up @@ -134,11 +139,23 @@ func (fb *DateTimeFacetBuilder) Result() *search.FacetResult {
Count: count,
}
if !dateRange.start.IsZero() {
start := dateRange.start.Format(time.RFC3339Nano)
var start string
if dateRange.startLayout == "" {
// layout not set probably means it is probably a timestamp
start = strconv.FormatInt(dateRange.start.UnixNano(), 10)
} else {
start = dateRange.start.Format(dateRange.startLayout)
}
tf.Start = &start
}
if !dateRange.end.IsZero() {
end := dateRange.end.Format(time.RFC3339Nano)
var end string
if dateRange.endLayout == "" {
// layout not set probably means it is probably a timestamp
end = strconv.FormatInt(dateRange.end.UnixNano(), 10)
} else {
end = dateRange.end.Format(dateRange.endLayout)
}
tf.End = &end
}
rv.DateRanges = append(rv.DateRanges, tf)
Expand Down
Loading

0 comments on commit e2859c9

Please sign in to comment.