diff --git a/mongoext/pipeline.go b/mongoext/pipeline.go new file mode 100644 index 0000000..3e430d2 --- /dev/null +++ b/mongoext/pipeline.go @@ -0,0 +1,49 @@ +package mongoext + +import ( + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +// FixTextSearchPipeline moves {$match:{$text:{$search}}} entries to the front of the pipeline (otherwise its an mongo error) +func FixTextSearchPipeline(pipeline mongo.Pipeline) mongo.Pipeline { + + dget := func(v bson.D, k string) (bson.M, bool) { + for _, e := range v { + if e.Key == k { + if mv, ok := e.Value.(bson.M); ok { + return mv, true + } + } + } + return nil, false + } + mget := func(v bson.M, k string) (bson.M, bool) { + for ekey, eval := range v { + if ekey == k { + if mv, ok := eval.(bson.M); ok { + return mv, true + } + } + } + return nil, false + } + + result := make([]bson.D, 0, len(pipeline)) + + for _, entry := range pipeline { + + if v0, ok := dget(entry, "$match"); ok { + if v1, ok := mget(v0, "$text"); ok { + if _, ok := v1["$search"]; ok { + result = append([]bson.D{entry}, result...) + continue + } + } + } + + result = append(result, entry) + } + + return result +} diff --git a/mongoext/projections.go b/mongoext/projections.go new file mode 100644 index 0000000..a8b123f --- /dev/null +++ b/mongoext/projections.go @@ -0,0 +1,30 @@ +package mongoext + +import ( + "go.mongodb.org/mongo-driver/bson" + "reflect" + "strings" +) + +// ProjectionFromStruct automatically generated a mongodb projection for a struct +// This way you can pretty much always write +// `options.FindOne().SetProjection(mongoutils.ProjectionFromStruct(...your_model...))` +// to only get the data from mongodb that you will actually use in the later decode step +func ProjectionFromStruct(obj interface{}) bson.M { + v := reflect.ValueOf(obj) + t := v.Type() + + result := bson.M{} + + for i := 0; i < v.NumField(); i++ { + tag := t.Field(i).Tag.Get("bson") + if tag == "" { + continue + } + tag = strings.Split(tag, ",")[0] + + result[tag] = 1 + } + + return result +} diff --git a/rfctime/rfc3339.go b/rfctime/rfc3339.go index 33427dd..a05b0d8 100644 --- a/rfctime/rfc3339.go +++ b/rfctime/rfc3339.go @@ -71,6 +71,7 @@ func (t *RFC3339Time) UnmarshalText(data []byte) error { func (t *RFC3339Time) UnmarshalBSONValue(bt bsontype.Type, data []byte) error { if bt == bsontype.Null { // we can't set nil in UnmarshalBSONValue (so we use default(struct)) + // Use mongoext.CreateGoExtBsonRegistry if you need to unmarsh pointer values // https://stackoverflow.com/questions/75167597 // https://jira.mongodb.org/browse/GODRIVER-2252 *t = RFC3339Time{} diff --git a/rfctime/rfc3339Nano.go b/rfctime/rfc3339Nano.go index 6bc9256..f0a4d90 100644 --- a/rfctime/rfc3339Nano.go +++ b/rfctime/rfc3339Nano.go @@ -71,6 +71,7 @@ func (t *RFC3339NanoTime) UnmarshalText(data []byte) error { func (t *RFC3339NanoTime) UnmarshalBSONValue(bt bsontype.Type, data []byte) error { if bt == bsontype.Null { // we can't set nil in UnmarshalBSONValue (so we use default(struct)) + // Use mongoext.CreateGoExtBsonRegistry if you need to unmarsh pointer values // https://stackoverflow.com/questions/75167597 // https://jira.mongodb.org/browse/GODRIVER-2252 *t = RFC3339NanoTime{}