Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 修复服务列表、引用套餐、脚本标签列表排序 #3341

Merged
merged 1 commit into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading