0
0
bleve/search/sort.go

197 lines
5.0 KiB
Go
Raw Normal View History

// 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 search
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 {
Field string
Descending bool
}
// Compare orders DocumentMatch instances by stored field values
func (s *SortStoredField) Compare(i, j *DocumentMatch) int {
return i.Document.CompareFieldsNamed(j.Document, s.Field, s.Descending)
}
// RequiresDocID says this SearchSort does not require the DocID be loaded
func (s *SortStoredField) RequiresDocID() bool { return false }
// RequiresScoring says this SearchStore does not require scoring
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
}
// Compare orders DocumentMatch instances by document identifiers
func (s *SortDocID) Compare(i, j *DocumentMatch) int {
if s.Descending {
return strings.Compare(j.ID, i.ID)
}
return strings.Compare(i.ID, j.ID)
}
// RequiresDocID says this SearchSort does require the DocID be loaded
func (s *SortDocID) RequiresDocID() bool { return true }
// RequiresScoring says this SearchStore does not require scoring
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
}
// Compare orders DocumentMatch instances by computed scores
func (s *SortScore) Compare(i, j *DocumentMatch) int {
if i.Score == j.Score {
return 0
} else if (i.Score < j.Score && !s.Descending) || (j.Score < i.Score && s.Descending) {
return -1
}
return 1
}
// RequiresDocID says this SearchSort does not require the DocID be loaded
func (s *SortScore) RequiresDocID() bool { return false }
// RequiresScoring says this SearchStore does require scoring
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")
}