Skip to content

Commit

Permalink
fix: 修复服务列表、引用套餐、脚本标签列表排序
Browse files Browse the repository at this point in the history
  • Loading branch information
Ambition9186 committed Jul 5, 2024
1 parent 7ae74de commit 12a9552
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 1 deletion.
7 changes: 7 additions & 0 deletions bcs-services/bcs-bscp/cmd/config-server/service/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package service

import (
"context"
"sort"
"strconv"
"strings"

Expand All @@ -31,6 +32,7 @@ import (
pbapp "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/core/app"
pbds "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/data-service"
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/rest/view/webannotation"
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/runtime/natsort"
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/space"
)

Expand Down Expand Up @@ -325,10 +327,15 @@ func (s *Service) ListAppsBySpaceRest(ctx context.Context,
}
}

sort.SliceStable(rp.Details, func(i, j int) bool {
return natsort.NaturalLess(rp.Details[i].Spec.Name, rp.Details[j].Spec.Name)
})

resp := &pbcs.ListAppsResp{
Count: rp.Count,
Details: rp.Details,
}

return resp, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
pbatb "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/core/app-template-binding"
pbtset "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/core/template-set"
pbds "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/data-service"
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/runtime/natsort"
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/tools"
)

Expand Down Expand Up @@ -327,6 +328,14 @@ func (s *Service) ListAppBoundTmplRevisions(ctx context.Context, req *pbcs.ListA
details = append(details, group)
}

// 自然排序
sort.SliceStable(details, func(i, j int) bool {
if details[i].TemplateSpaceName == details[j].TemplateSpaceName {
return natsort.NaturalLess(details[i].TemplateSetName, details[j].TemplateSetName)
}
return natsort.NaturalLess(details[i].TemplateSpaceName, details[j].TemplateSpaceName)
})

resp := &pbcs.ListAppBoundTmplRevisionsResp{
Details: details,
}
Expand Down Expand Up @@ -487,6 +496,13 @@ func (s *Service) ListReleasedAppBoundTmplRevisions(ctx context.Context,
details = append(details, group)
}

sort.SliceStable(details, func(i, j int) bool {
if details[i].TemplateSpaceName == details[j].TemplateSpaceName {
return natsort.NaturalLess(details[i].TemplateSetName, details[j].TemplateSetName)
}
return natsort.NaturalLess(details[i].TemplateSpaceName, details[j].TemplateSpaceName)
})

resp := &pbcs.ListReleasedAppBoundTmplRevisionsResp{
Details: details,
}
Expand Down
6 changes: 6 additions & 0 deletions bcs-services/bcs-bscp/cmd/config-server/service/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package service
import (
"context"
"errors"
"sort"
"sync"

"golang.org/x/sync/errgroup"
Expand All @@ -29,6 +30,7 @@ import (
pbbase "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/core/base"
pbhook "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/core/hook"
pbds "github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/protocol/data-service"
"github.com/TencentBlueKing/bk-bcs/bcs-services/bcs-bscp/pkg/runtime/natsort"
)

// CreateHook create a hook
Expand Down Expand Up @@ -269,6 +271,10 @@ func (s *Service) ListHookTags(ctx context.Context, req *pbcs.ListHookTagsReq) (
return nil, err
}

sort.SliceStable(ht.Details, func(i, j int) bool {
return natsort.NaturalLess(ht.Details[i].Tag, ht.Details[j].Tag)
})

resp := &pbcs.ListHookTagsResp{
Details: ht.Details,
}
Expand Down
2 changes: 1 addition & 1 deletion bcs-services/bcs-bscp/pkg/dal/dao/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (dao *hookDao) ListWithRefer(kit *kit.Kit, opt *types.ListHooksWithReferOpt
q := dao.genQ.Hook.WithContext(kit.Ctx).Where(h.BizID.Eq(opt.BizID)).Order(h.Name)

if opt.Name != "" {
q = q.Where(h.Name.Regexp("(?i)" + opt.Name))
q = q.Where(h.Name.Like(opt.Name))
}
if opt.Tag != "" {
q = q.Where(rawgen.Cond(datatypes.JSONArrayQuery("tags").Contains(opt.Tag))...)
Expand Down
89 changes: 89 additions & 0 deletions bcs-services/bcs-bscp/pkg/runtime/natsort/natsort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Tencent is pleased to support the open source community by making Blueking Container Service available.
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* 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 natsort Natural sorting
package natsort

// Natural implements sort.Interface to sort strings in natural order. This
// means that e.g. "abc2" < "abc12".
//
// Non-digit sequences and numbers are compared separately. The former are
// compared bytewise, while digits are compared numerically (except that
// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
//
// Limitation: only ASCII digits (0-9) are considered.
type Natural []string

func (n Natural) Len() int { return len(n) }
func (n Natural) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n Natural) Less(i, j int) bool { return NaturalLess(n[i], n[j]) }

func isDigit(b byte) bool { return '0' <= b && b <= '9' }

// NaturalLess compares two strings using natural ordering. This means that e.g.
// "abc2" < "abc12".
//
// Non-digit sequences and numbers are compared separately. The former are
// compared bytewise, while digits are compared numerically (except that
// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
//
// Limitation: only ASCII digits (0-9) are considered.
func NaturalLess(str1, str2 string) bool {
idx1, idx2 := 0, 0
for idx1 < len(str1) && idx2 < len(str2) {
c1, c2 := str1[idx1], str2[idx2]
dig1, dig2 := isDigit(c1), isDigit(c2)
switch {
case dig1 != dig2: // Digits before other characters.
return dig1 // True if LHS is a digit, false if the RHS is one.
case !dig1: // && !dig2, because dig1 == dig2
// UTF-8 compares bytewise-lexicographically, no need to decode
// codepoints.
if c1 != c2 {
return c1 < c2
}
idx1++
idx2++
default: // Digits
// Eat zeros.
for ; idx1 < len(str1) && str1[idx1] == '0'; idx1++ {
}
for ; idx2 < len(str2) && str2[idx2] == '0'; idx2++ {
}
// Eat all digits.
nonZero1, nonZero2 := idx1, idx2
for ; idx1 < len(str1) && isDigit(str1[idx1]); idx1++ {
}
for ; idx2 < len(str2) && isDigit(str2[idx2]); idx2++ {
}
// If lengths of numbers with non-zero prefix differ, the shorter
// one is less.
if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 {
return len1 < len2
}
// If they're equally long, string comparison is correct.
if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 {
return nr1 < nr2
}
// Otherwise, the one with less zeros is less.
// Because everything up to the number is equal, comparing the index
// after the zeros is sufficient.
if nonZero1 != nonZero2 {
return nonZero1 < nonZero2
}
}
// They're identical so far, so continue comparing.
}
// So far they are identical. At least one is ended. If the other continues,
// it sorts last.
return len(str1) < len(str2)
}
195 changes: 195 additions & 0 deletions bcs-services/bcs-bscp/pkg/runtime/natsort/natsort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Tencent is pleased to support the open source community by making Blueking Container Service available.
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* 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 natsort

import (
"math/rand"
"reflect"
"sort"
"strconv"
"testing"
)

func TestStringSort(t *testing.T) {
want := []string{
"ab", "abc1",
"abc01", "abc2",
"abc5", "abc10",
}
got := []string{
"abc5", "abc1",
"abc01", "ab",
"abc10", "abc2",
}
sort.Sort(Natural(got))
if !reflect.DeepEqual(want, got) {
t.Errorf("Error: sort failed, expected: %#q, got: %#q", want, got)
}
}

func TestNaturalLess(t *testing.T) {
testset := []struct {
s1, s2 string
less bool
}{
{"0", "00", true},
{"aa", "ab", true},
{"ab", "abc", true},
{"abc", "ad", true},
{"ab1", "ab2", true},
{"ab1c", "ab1c", false},
{"ab12", "abc", true},
{"ab2a", "ab10", true},
{"a0001", "a0000001", true},
{"a10", "abcdefgh2", true},
{"аб2аб", "аб10аб", true},
{"2аб", "3аб", true},
//
{"a1b", "a01b", true},
{"ab01b", "ab010b", true},
{"a01b001", "a001b01", true},
{"a1", "a1x", true},
{"1ax", "1b", true},
//
{"082", "83", true},
{"9a", "083a", true},
}
for _, v := range testset {
if got := NaturalLess(v.s1, v.s2); got != v.less {
t.Errorf("Compared %#q to %#q: expected %v, got %v",
v.s1, v.s2, v.less, got)
}
// If A < B, then B < A must be false.
// The same cannot be said if !(A < B),
// because A might be equal to B
if v.less {
if v.s1 != v.s2 && NaturalLess(v.s2, v.s1) {
t.Errorf("Reverse-compared %#q to %#q: expected false, got true",
v.s2, v.s1)
}
}
}
}

// Use a regular string sort.
// As this does not perform a natural sort,
// this is not directly comparable with the other sorts.
// It is only here for a sense of scale.
func BenchmarkStdStringSort(b *testing.B) {
set := testSet(300)
arr := make([]string, len(set[0]))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, list := range set {
b.StopTimer()
copy(arr, list)
b.StartTimer()

sort.Strings(arr)
}
}
}

// Natural sort order.
func BenchmarkNaturalStringSort(b *testing.B) {
set := testSet(300)
arr := make([]string, len(set[0]))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, list := range set {
// Resetting the test set to be unsorted does not count.
b.StopTimer()
copy(arr, list)
b.StartTimer()

sort.Sort(Natural(arr))
}
}
}

func BenchmarkStdStringLess(b *testing.B) {
set := testSet(300)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := range set[0] {
k := (j + 1) % len(set[0])
_ = set[0][j] < set[0][k]
}
}
}

func BenchmarkNaturalLess(b *testing.B) {
set := testSet(300)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := range set[0] {
k := (j + 1) % len(set[0])
_ = NaturalLess(set[0][j], set[0][k])
}
}
}

// Get 1000 arrays of 10000-string-arrays (less if -short is specified).
func testSet(seed int) [][]string {
gen := &generator{
src: rand.New(rand.NewSource(
int64(seed),
)),
}
n := 1000
if testing.Short() {
n = 1
}
set := make([][]string, n)
for i := range set {
strings := make([]string, 10000)
for idx := range strings {
// Generate a random string
strings[idx] = gen.NextString()
}
set[i] = strings
}
return set
}

type generator struct {
src *rand.Rand
}

func (g *generator) NextInt(max int) int {
return g.src.Intn(max)
}

// Gets random random-length alphanumeric string.
func (g *generator) NextString() (str string) {
// Random-length 3-8 chars part
strlen := g.src.Intn(6) + 3
// Random-length 1-3 num
numlen := g.src.Intn(3) + 1
// Random position for num in string
numpos := g.src.Intn(strlen + 1)
// Generate the number
var num string
for i := 0; i < numlen; i++ {
num += strconv.Itoa(g.src.Intn(10))
}
// Put it all together
for i := 0; i < strlen+1; i++ {
if i == numpos {
str += num
} else {
str += string('a' + rune(g.src.Intn(16)))
}
}
return str
}

0 comments on commit 12a9552

Please sign in to comment.