github.com/dgraph-io/dgraph@v1.2.8/types/sort.go (about)

     1  /*
     2   * Copyright 2016-2018 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package types
    18  
    19  import (
    20  	"sort"
    21  	"time"
    22  
    23  	"github.com/dgraph-io/dgraph/protos/pb"
    24  	"github.com/dgraph-io/dgraph/x"
    25  	"github.com/pkg/errors"
    26  	"golang.org/x/text/collate"
    27  	"golang.org/x/text/language"
    28  )
    29  
    30  type sortBase struct {
    31  	values [][]Val // Each uid could have multiple values which we need to sort it by.
    32  	desc   []bool  // Sort orders for different values.
    33  	ul     *[]uint64
    34  	o      []*pb.Facets
    35  	cl     *collate.Collator // Compares Unicode strings according to the given collation order.
    36  }
    37  
    38  // Len returns size of vector.
    39  // skipcq: CRT-P0003
    40  func (s sortBase) Len() int { return len(s.values) }
    41  
    42  // Swap swaps two elements.
    43  // skipcq: CRT-P0003
    44  func (s sortBase) Swap(i, j int) {
    45  	s.values[i], s.values[j] = s.values[j], s.values[i]
    46  	(*s.ul)[i], (*s.ul)[j] = (*s.ul)[j], (*s.ul)[i]
    47  	if s.o != nil {
    48  		s.o[i], s.o[j] = s.o[j], s.o[i]
    49  	}
    50  }
    51  
    52  type byValue struct{ sortBase }
    53  
    54  // Less compares two elements
    55  // skipcq: CRT-P0003
    56  func (s byValue) Less(i, j int) bool {
    57  	first, second := s.values[i], s.values[j]
    58  	if len(first) == 0 || len(second) == 0 {
    59  		return false
    60  	}
    61  	for vidx := range first {
    62  		// Null value is considered greatest hence comes at first place while doing descending sort
    63  		// and at last place while doing ascending sort.
    64  		if first[vidx].Value == nil {
    65  			return s.desc[vidx]
    66  		}
    67  
    68  		if second[vidx].Value == nil {
    69  			return !s.desc[vidx]
    70  		}
    71  
    72  		// We have to look at next value to decide.
    73  		if eq := equal(first[vidx], second[vidx]); eq {
    74  			continue
    75  		}
    76  
    77  		// Its either less or greater.
    78  		less := less(first[vidx], second[vidx], s.cl)
    79  		if s.desc[vidx] {
    80  			return !less
    81  		}
    82  		return less
    83  	}
    84  	return false
    85  }
    86  
    87  // SortWithFacet sorts the given array in-place and considers the given facets to calculate
    88  // the proper ordering.
    89  func SortWithFacet(v [][]Val, ul *[]uint64, l []*pb.Facets, desc []bool, lang string) error {
    90  	if len(v) == 0 || len(v[0]) == 0 {
    91  		return nil
    92  	}
    93  
    94  	typ := v[0][0].Tid
    95  	switch typ {
    96  	case DateTimeID, IntID, FloatID, StringID, DefaultID:
    97  		// Don't do anything, we can sort values of this type.
    98  	default:
    99  		return errors.Errorf("Value of type: %s isn't sortable", typ.Name())
   100  	}
   101  
   102  	var cl *collate.Collator
   103  	if lang != "" {
   104  		// Collator is nil if we are unable to parse the language.
   105  		// We default to bytewise comparison in that case.
   106  		if langTag, err := language.Parse(lang); err == nil {
   107  			cl = collate.New(langTag)
   108  		}
   109  	}
   110  
   111  	b := sortBase{v, desc, ul, l, cl}
   112  	toBeSorted := byValue{b}
   113  	sort.Sort(toBeSorted)
   114  	return nil
   115  }
   116  
   117  // Sort sorts the given array in-place.
   118  func Sort(v [][]Val, ul *[]uint64, desc []bool, lang string) error {
   119  	return SortWithFacet(v, ul, nil, desc, lang)
   120  }
   121  
   122  // Less returns true if a is strictly less than b.
   123  func Less(a, b Val) (bool, error) {
   124  	if a.Tid != b.Tid {
   125  		return false, errors.Errorf("Arguments of different type can not be compared.")
   126  	}
   127  	typ := a.Tid
   128  	switch typ {
   129  	case DateTimeID, UidID, IntID, FloatID, StringID, DefaultID:
   130  		// Don't do anything, we can sort values of this type.
   131  	default:
   132  		return false, errors.Errorf("Compare not supported for type: %v", a.Tid)
   133  	}
   134  	return less(a, b, nil), nil
   135  }
   136  
   137  func less(a, b Val, cl *collate.Collator) bool {
   138  	if a.Tid != b.Tid {
   139  		return mismatchedLess(a, b)
   140  	}
   141  	switch a.Tid {
   142  	case DateTimeID:
   143  		return a.Value.(time.Time).Before(b.Value.(time.Time))
   144  	case IntID:
   145  		return (a.Value.(int64)) < (b.Value.(int64))
   146  	case FloatID:
   147  		return (a.Value.(float64)) < (b.Value.(float64))
   148  	case UidID:
   149  		return (a.Value.(uint64) < b.Value.(uint64))
   150  	case StringID, DefaultID:
   151  		// Use language comparator.
   152  		if cl != nil {
   153  			return cl.CompareString(a.Safe().(string), b.Safe().(string)) < 0
   154  		}
   155  		return (a.Safe().(string)) < (b.Safe().(string))
   156  	}
   157  	return false
   158  }
   159  
   160  func mismatchedLess(a, b Val) bool {
   161  	x.AssertTrue(a.Tid != b.Tid)
   162  	if (a.Tid != IntID && a.Tid != FloatID) || (b.Tid != IntID && b.Tid != FloatID) {
   163  		// Non-float/int are sorted arbitrarily by type.
   164  		return a.Tid < b.Tid
   165  	}
   166  
   167  	// Floats and ints can be sorted together in a sensible way. The approach
   168  	// here isn't 100% correct, and will be wrong when dealing with ints and
   169  	// floats close to each other and greater in magnitude than 1<<53 (the
   170  	// point at which consecutive floats are more than 1 apart).
   171  	if a.Tid == FloatID {
   172  		return a.Value.(float64) < float64(b.Value.(int64))
   173  	}
   174  	x.AssertTrue(b.Tid == FloatID)
   175  	return float64(a.Value.(int64)) < b.Value.(float64)
   176  }
   177  
   178  // Equal returns true if a is equal to b.
   179  func Equal(a, b Val) (bool, error) {
   180  	if a.Tid != b.Tid {
   181  		return false, errors.Errorf("Arguments of different type can not be compared.")
   182  	}
   183  	typ := a.Tid
   184  	switch typ {
   185  	case DateTimeID, IntID, FloatID, StringID, DefaultID, BoolID:
   186  		// Don't do anything, we can sort values of this type.
   187  	default:
   188  		return false, errors.Errorf("Equal not supported for type: %v", a.Tid)
   189  	}
   190  	return equal(a, b), nil
   191  }
   192  
   193  func equal(a, b Val) bool {
   194  	if a.Tid != b.Tid {
   195  		return false
   196  	}
   197  	switch a.Tid {
   198  	case DateTimeID:
   199  		aVal, aOk := a.Value.(time.Time)
   200  		bVal, bOk := b.Value.(time.Time)
   201  		return aOk && bOk && aVal.Equal(bVal)
   202  	case IntID:
   203  		aVal, aOk := a.Value.(int64)
   204  		bVal, bOk := b.Value.(int64)
   205  		return aOk && bOk && aVal == bVal
   206  	case FloatID:
   207  		aVal, aOk := a.Value.(float64)
   208  		bVal, bOk := b.Value.(float64)
   209  		return aOk && bOk && aVal == bVal
   210  	case StringID, DefaultID:
   211  		aVal, aOk := a.Value.(string)
   212  		bVal, bOk := b.Value.(string)
   213  		return aOk && bOk && aVal == bVal
   214  	case BoolID:
   215  		aVal, aOk := a.Value.(bool)
   216  		bVal, bOk := b.Value.(bool)
   217  		return aOk && bOk && aVal == bVal
   218  	}
   219  	return false
   220  }