Skip to content

Commit f1c3eb5

Browse files
Merge pull request #5 from chenmingyong0423/feture/enhancement
finder: add Distinct and DistinctWithParse methods
2 parents 435a8a5 + 4d98897 commit f1c3eb5

File tree

4 files changed

+479
-3
lines changed

4 files changed

+479
-3
lines changed

finder/finder.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type iFinder[T any] interface {
2727
FindOne(ctx context.Context, opts ...*options.FindOneOptions) (*T, error)
2828
Find(ctx context.Context, opts ...*options.FindOptions) ([]*T, error)
2929
Count(ctx context.Context, opts ...*options.CountOptions) (int64, error)
30+
Distinct(ctx context.Context, fieldName string, opts ...*options.DistinctOptions) ([]any, error)
31+
DistinctWithParse(ctx context.Context, fieldName string, result any, opts ...*options.DistinctOptions) error
3032
}
3133

3234
func NewFinder[T any](collection *mongo.Collection) *Finder[T] {
@@ -76,3 +78,27 @@ func (f *Finder[T]) Count(ctx context.Context, opts ...*options.CountOptions) (i
7678
}
7779
return cnt, nil
7880
}
81+
82+
func (f *Finder[T]) Distinct(ctx context.Context, fieldName string, opts ...*options.DistinctOptions) ([]any, error) {
83+
return f.collection.Distinct(ctx, fieldName, f.filter, opts...)
84+
}
85+
86+
// DistinctWithParse is used to parse the result of Distinct
87+
// result must be a pointer
88+
func (f *Finder[T]) DistinctWithParse(ctx context.Context, fieldName string, result any, opts ...*options.DistinctOptions) error {
89+
docs, err := f.collection.Distinct(ctx, fieldName, f.filter, opts...)
90+
if err != nil {
91+
return err
92+
}
93+
94+
valueType, valueBytes, err := bson.MarshalValue(docs)
95+
if err != nil {
96+
return err
97+
}
98+
rawValue := bson.RawValue{Type: valueType, Value: valueBytes}
99+
err = rawValue.Unmarshal(result)
100+
if err != nil {
101+
return err
102+
}
103+
return nil
104+
}

finder/finder_e2e_test.go

Lines changed: 297 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ package finder
1818

1919
import (
2020
"context"
21+
"errors"
2122
"testing"
2223

24+
"github.com/chenmingyong0423/go-mongox/pkg/utils"
25+
"github.com/stretchr/testify/require"
26+
2327
"github.com/chenmingyong0423/go-mongox/bsonx"
2428

2529
"github.com/chenmingyong0423/go-mongox/builder/query"
@@ -39,9 +43,8 @@ func getCollection(t *testing.T) *mongo.Collection {
3943
Password: "test",
4044
AuthSource: "db-test",
4145
}))
42-
assert.NoError(t, err)
43-
assert.NoError(t, client.Ping(context.Background(), readpref.Primary()))
44-
46+
require.NoError(t, err)
47+
require.NoError(t, client.Ping(context.Background(), readpref.Primary()))
4548
return client.Database("db-test").Collection("test_user")
4649
}
4750

@@ -583,3 +586,294 @@ func TestFinder_e2e_Count(t *testing.T) {
583586
})
584587
}
585588
}
589+
590+
func TestFinder_e2e_Distinct(t *testing.T) {
591+
collection := getCollection(t)
592+
finder := NewFinder[types.TestUser](collection)
593+
594+
testCases := []struct {
595+
name string
596+
before func(ctx context.Context, t *testing.T)
597+
after func(ctx context.Context, t *testing.T)
598+
599+
fieldName string
600+
filter any
601+
opts []*options.DistinctOptions
602+
603+
ctx context.Context
604+
want []any
605+
wantErr assert.ErrorAssertionFunc
606+
}{
607+
{
608+
name: "nil filter error",
609+
before: func(_ context.Context, _ *testing.T) {},
610+
after: func(_ context.Context, _ *testing.T) {},
611+
612+
filter: "name",
613+
ctx: context.Background(),
614+
want: nil,
615+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
616+
if err == nil {
617+
t.Errorf("expected error, got nil")
618+
return false
619+
}
620+
return errors.Is(err, mongo.ErrNilDocument)
621+
},
622+
},
623+
{
624+
name: "returns empty documents",
625+
before: func(ctx context.Context, t *testing.T) {},
626+
after: func(ctx context.Context, t *testing.T) {},
627+
filter: bson.D{},
628+
fieldName: "name",
629+
ctx: context.Background(),
630+
want: []any{},
631+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
632+
if err != nil {
633+
t.Errorf("expected nil, got error: %v", err)
634+
return false
635+
}
636+
return true
637+
},
638+
},
639+
{
640+
name: "returns all documents",
641+
before: func(ctx context.Context, t *testing.T) {
642+
insertManyResult, err := collection.InsertMany(ctx, utils.ToAnySlice([]*types.TestUser{
643+
{
644+
Id: "1",
645+
Name: "chenmingyong",
646+
Age: 24,
647+
},
648+
{
649+
Id: "2",
650+
Name: "burt",
651+
Age: 45,
652+
},
653+
}...))
654+
require.NoError(t, err)
655+
assert.ElementsMatch(t, []string{"1", "2"}, insertManyResult.InsertedIDs)
656+
},
657+
after: func(ctx context.Context, t *testing.T) {
658+
deleteResult, err := collection.DeleteMany(ctx, query.In("_id", "1", "2"))
659+
assert.NoError(t, err)
660+
assert.Equal(t, int64(2), deleteResult.DeletedCount)
661+
},
662+
filter: bson.D{},
663+
fieldName: "name",
664+
ctx: context.Background(),
665+
want: []any{
666+
"chenmingyong",
667+
"burt",
668+
},
669+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
670+
if err != nil {
671+
t.Errorf("expected nil, got error: %v", err)
672+
return false
673+
}
674+
return true
675+
},
676+
},
677+
{
678+
name: "name distinct",
679+
before: func(ctx context.Context, t *testing.T) {
680+
insertManyResult, err := collection.InsertMany(ctx, utils.ToAnySlice([]*types.TestUser{
681+
{
682+
Id: "1",
683+
Name: "chenmingyong",
684+
Age: 24,
685+
},
686+
{
687+
Id: "2",
688+
Name: "chenmingyong",
689+
Age: 25,
690+
},
691+
{
692+
Id: "3",
693+
Name: "burt",
694+
Age: 26,
695+
},
696+
}...))
697+
require.NoError(t, err)
698+
assert.ElementsMatch(t, []string{"1", "2", "3"}, insertManyResult.InsertedIDs)
699+
},
700+
after: func(ctx context.Context, t *testing.T) {
701+
deleteResult, err := collection.DeleteMany(ctx, query.In("_id", "1", "2", "3"))
702+
assert.NoError(t, err)
703+
assert.Equal(t, int64(3), deleteResult.DeletedCount)
704+
},
705+
filter: bson.D{},
706+
fieldName: "name",
707+
ctx: context.Background(),
708+
want: []any{
709+
"chenmingyong",
710+
"burt",
711+
},
712+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
713+
if err != nil {
714+
t.Errorf("expected nil, got error: %v", err)
715+
return false
716+
}
717+
return true
718+
},
719+
},
720+
}
721+
for _, tc := range testCases {
722+
t.Run(tc.name, func(t *testing.T) {
723+
tc.before(tc.ctx, t)
724+
result, err := finder.Filter(tc.filter).Distinct(tc.ctx, tc.fieldName, tc.opts...)
725+
tc.after(tc.ctx, t)
726+
if !tc.wantErr(t, err) {
727+
return
728+
}
729+
assert.ElementsMatch(t, tc.want, result)
730+
})
731+
}
732+
}
733+
734+
func TestFinder_e2e_DistinctWithParse(t *testing.T) {
735+
collection := getCollection(t)
736+
finder := NewFinder[types.TestUser](collection)
737+
738+
testCases := []struct {
739+
name string
740+
before func(ctx context.Context, t *testing.T)
741+
after func(ctx context.Context, t *testing.T)
742+
743+
fieldName string
744+
filter any
745+
result []string
746+
opts []*options.DistinctOptions
747+
748+
ctx context.Context
749+
want []string
750+
wantErr assert.ErrorAssertionFunc
751+
}{
752+
{
753+
name: "nil filter error",
754+
before: func(_ context.Context, _ *testing.T) {},
755+
after: func(_ context.Context, _ *testing.T) {},
756+
757+
filter: "name",
758+
ctx: context.Background(),
759+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
760+
if err == nil {
761+
t.Errorf("expected error, got nil")
762+
return false
763+
}
764+
return errors.Is(err, mongo.ErrNilDocument)
765+
},
766+
},
767+
{
768+
name: "returns empty documents",
769+
before: func(ctx context.Context, t *testing.T) {},
770+
after: func(ctx context.Context, t *testing.T) {},
771+
filter: bson.D{},
772+
fieldName: "name",
773+
ctx: context.Background(),
774+
result: []string{},
775+
want: []string{},
776+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
777+
if err != nil {
778+
t.Errorf("expected nil, got error: %v", err)
779+
return false
780+
}
781+
return true
782+
},
783+
},
784+
{
785+
name: "returns all documents",
786+
before: func(ctx context.Context, t *testing.T) {
787+
insertManyResult, err := collection.InsertMany(ctx, utils.ToAnySlice([]*types.TestUser{
788+
{
789+
Id: "1",
790+
Name: "chenmingyong",
791+
Age: 24,
792+
},
793+
{
794+
Id: "2",
795+
Name: "burt",
796+
Age: 45,
797+
},
798+
}...))
799+
require.NoError(t, err)
800+
assert.ElementsMatch(t, []string{"1", "2"}, insertManyResult.InsertedIDs)
801+
},
802+
after: func(ctx context.Context, t *testing.T) {
803+
deleteResult, err := collection.DeleteMany(ctx, query.In("_id", "1", "2"))
804+
assert.NoError(t, err)
805+
assert.Equal(t, int64(2), deleteResult.DeletedCount)
806+
},
807+
filter: bson.D{},
808+
fieldName: "name",
809+
ctx: context.Background(),
810+
result: []string{},
811+
want: []string{
812+
"chenmingyong",
813+
"burt",
814+
},
815+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
816+
if err != nil {
817+
t.Errorf("expected nil, got error: %v", err)
818+
return false
819+
}
820+
return true
821+
},
822+
},
823+
{
824+
name: "name distinct",
825+
before: func(ctx context.Context, t *testing.T) {
826+
insertManyResult, err := collection.InsertMany(ctx, utils.ToAnySlice([]*types.TestUser{
827+
{
828+
Id: "1",
829+
Name: "chenmingyong",
830+
Age: 24,
831+
},
832+
{
833+
Id: "2",
834+
Name: "chenmingyong",
835+
Age: 25,
836+
},
837+
{
838+
Id: "3",
839+
Name: "burt",
840+
Age: 26,
841+
},
842+
}...))
843+
require.NoError(t, err)
844+
assert.ElementsMatch(t, []string{"1", "2", "3"}, insertManyResult.InsertedIDs)
845+
},
846+
after: func(ctx context.Context, t *testing.T) {
847+
deleteResult, err := collection.DeleteMany(ctx, query.In("_id", "1", "2", "3"))
848+
assert.NoError(t, err)
849+
assert.Equal(t, int64(3), deleteResult.DeletedCount)
850+
},
851+
filter: bson.D{},
852+
fieldName: "name",
853+
ctx: context.Background(),
854+
result: []string{},
855+
want: []string{
856+
"chenmingyong",
857+
"burt",
858+
},
859+
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
860+
if err != nil {
861+
t.Errorf("expected nil, got error: %v", err)
862+
return false
863+
}
864+
return true
865+
},
866+
},
867+
}
868+
for _, tc := range testCases {
869+
t.Run(tc.name, func(t *testing.T) {
870+
tc.before(tc.ctx, t)
871+
err := finder.Filter(tc.filter).DistinctWithParse(tc.ctx, tc.fieldName, &tc.result, tc.opts...)
872+
tc.after(tc.ctx, t)
873+
if !tc.wantErr(t, err) {
874+
return
875+
}
876+
assert.ElementsMatch(t, tc.want, tc.result)
877+
})
878+
}
879+
}

0 commit comments

Comments
 (0)