support JSON marshal/unmarshal of search request sort
The syntax used is an array of strings. The strings "_id" and "_score" are special and reserved to mean sorting on the document id and score repsectively. All other strings refer to the literal field name with that value. If the string is prefixed with "-" the order of that sort is descending, without it, it defaults to ascending. Examples: "sort":["-abv","-_score"] This will sort results in decreasing order of the "abv" field. Results which have the same value of the "abv" field will then be sorted by their score, also decreasing. If no value for "sort" is provided in the search request the default soring is the same as before, which is decreasing score.
This commit is contained in:
parent
be56380833
commit
0d873916f0
|
@ -238,7 +238,7 @@ func (r *SearchRequest) UnmarshalJSON(input []byte) error {
|
|||
Fields []string `json:"fields"`
|
||||
Facets FacetsRequest `json:"facets"`
|
||||
Explain bool `json:"explain"`
|
||||
Sort search.SortOrder `json:"sort"`
|
||||
Sort []json.RawMessage `json:"sort"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal(input, &temp)
|
||||
|
@ -253,6 +253,11 @@ func (r *SearchRequest) UnmarshalJSON(input []byte) error {
|
|||
}
|
||||
if temp.Sort == nil {
|
||||
r.Sort = search.SortOrder{&search.SortScore{Descending: true}}
|
||||
} else {
|
||||
r.Sort, err = search.ParseSortOrder(temp.Sort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
r.From = temp.From
|
||||
r.Explain = temp.Explain
|
||||
|
|
|
@ -136,59 +136,3 @@ type Searcher interface {
|
|||
type SearchContext struct {
|
||||
DocumentMatchPool *DocumentMatchPool
|
||||
}
|
||||
|
||||
type SearchSort interface {
|
||||
Compare(a, b *DocumentMatch) int
|
||||
|
||||
RequiresDocID() bool
|
||||
RequiresScoring() bool
|
||||
RequiresStoredFields() []string
|
||||
}
|
||||
|
||||
type SortOrder []SearchSort
|
||||
|
||||
func (so SortOrder) Compare(i, j *DocumentMatch) int {
|
||||
// compare the documents on all search sorts until a differences is found
|
||||
for _, soi := range so {
|
||||
c := soi.Compare(i, j)
|
||||
if c == 0 {
|
||||
continue
|
||||
}
|
||||
return c
|
||||
}
|
||||
// if they are the same at this point, impose order based on index natural sort order
|
||||
if i.HitNumber == j.HitNumber {
|
||||
return 0
|
||||
} else if i.HitNumber > j.HitNumber {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (so SortOrder) RequiresScore() bool {
|
||||
rv := false
|
||||
for _, soi := range so {
|
||||
if soi.RequiresScoring() {
|
||||
rv = true
|
||||
}
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func (so SortOrder) RequiresDocID() bool {
|
||||
rv := false
|
||||
for _, soi := range so {
|
||||
if soi.RequiresDocID() {
|
||||
rv = true
|
||||
}
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func (so SortOrder) RequiredStoredFields() []string {
|
||||
var rv []string
|
||||
for _, soi := range so {
|
||||
rv = append(rv, soi.RequiresStoredFields()...)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
|
120
search/sort.go
120
search/sort.go
|
@ -9,7 +9,104 @@
|
|||
|
||||
package search
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SearchSort interface {
|
||||
Compare(a, b *DocumentMatch) int
|
||||
|
||||
RequiresDocID() bool
|
||||
RequiresScoring() bool
|
||||
RequiresStoredFields() []string
|
||||
}
|
||||
|
||||
func ParseSearchSort(input json.RawMessage) (SearchSort, error) {
|
||||
var tmp string
|
||||
err := json.Unmarshal(input, &tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
descending := false
|
||||
if strings.HasPrefix(tmp, "-") {
|
||||
descending = true
|
||||
tmp = tmp[1:]
|
||||
}
|
||||
if tmp == "_id" {
|
||||
return &SortDocID{
|
||||
Descending: descending,
|
||||
}, nil
|
||||
} else if tmp == "_score" {
|
||||
return &SortScore{
|
||||
Descending: descending,
|
||||
}, nil
|
||||
}
|
||||
return &SortStoredField{
|
||||
Field: tmp,
|
||||
Descending: descending,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ParseSortOrder(in []json.RawMessage) (SortOrder, error) {
|
||||
rv := make(SortOrder, 0, len(in))
|
||||
for _, i := range in {
|
||||
ss, err := ParseSearchSort(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rv = append(rv, ss)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type SortOrder []SearchSort
|
||||
|
||||
func (so SortOrder) Compare(i, j *DocumentMatch) int {
|
||||
// compare the documents on all search sorts until a differences is found
|
||||
for _, soi := range so {
|
||||
c := soi.Compare(i, j)
|
||||
if c == 0 {
|
||||
continue
|
||||
}
|
||||
return c
|
||||
}
|
||||
// if they are the same at this point, impose order based on index natural sort order
|
||||
if i.HitNumber == j.HitNumber {
|
||||
return 0
|
||||
} else if i.HitNumber > j.HitNumber {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (so SortOrder) RequiresScore() bool {
|
||||
rv := false
|
||||
for _, soi := range so {
|
||||
if soi.RequiresScoring() {
|
||||
rv = true
|
||||
}
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func (so SortOrder) RequiresDocID() bool {
|
||||
rv := false
|
||||
for _, soi := range so {
|
||||
if soi.RequiresDocID() {
|
||||
rv = true
|
||||
}
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func (so SortOrder) RequiredStoredFields() []string {
|
||||
var rv []string
|
||||
for _, soi := range so {
|
||||
rv = append(rv, soi.RequiresStoredFields()...)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
// SortStoredField will sort results by the value of a stored field
|
||||
type SortStoredField struct {
|
||||
|
@ -31,6 +128,13 @@ func (s *SortStoredField) RequiresScoring() bool { return false }
|
|||
// RequiresStoredFields says this SearchStore requires the specified stored field
|
||||
func (s *SortStoredField) RequiresStoredFields() []string { return []string{s.Field} }
|
||||
|
||||
func (s *SortStoredField) MarshalJSON() ([]byte, error) {
|
||||
if s.Descending {
|
||||
return json.Marshal("-" + s.Field)
|
||||
}
|
||||
return json.Marshal(s.Field)
|
||||
}
|
||||
|
||||
// SortDocID will sort results by the document identifier
|
||||
type SortDocID struct {
|
||||
Descending bool
|
||||
|
@ -53,6 +157,13 @@ func (s *SortDocID) RequiresScoring() bool { return false }
|
|||
// RequiresStoredFields says this SearchStore does not require any stored fields
|
||||
func (s *SortDocID) RequiresStoredFields() []string { return nil }
|
||||
|
||||
func (s *SortDocID) MarshalJSON() ([]byte, error) {
|
||||
if s.Descending {
|
||||
return json.Marshal("-_id")
|
||||
}
|
||||
return json.Marshal("_id")
|
||||
}
|
||||
|
||||
// SortScore will sort results by the document match score
|
||||
type SortScore struct {
|
||||
Descending bool
|
||||
|
@ -76,3 +187,10 @@ func (s *SortScore) RequiresScoring() bool { return true }
|
|||
|
||||
// RequiresStoredFields says this SearchStore does not require any store fields
|
||||
func (s *SortScore) RequiresStoredFields() []string { return nil }
|
||||
|
||||
func (s *SortScore) MarshalJSON() ([]byte, error) {
|
||||
if s.Descending {
|
||||
return json.Marshal("-_score")
|
||||
}
|
||||
return json.Marshal("_score")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue