Git Product home page Git Product logo

flatbson's Introduction

FlatBSON

Build status API reference codecov

FlatBSON recursively flattens a Go struct using its BSON tags.

It is particularly useful for partially updating embedded Mongo documents.

For example, to update a User's Address.Visited field, first call flatbson.Flatten with the parent struct:

type User struct {
  ID      bson.ObjectID `bson:"_id,omitempty"`
  Name    string        `bson:"name,omitempty"`
  Address Address       `bson:"address,omitempty"`
}

type Address struct {
  Street    string    `bson:"street,omitempty"`
  City      string    `bson:"city,omitempty"`
  State     string    `bson:"state,omitempty"`
  VisitedAt time.Time `bson:"visitedAt,omitempty"`
}

flatbson.Flatten(User{Address: {VisitedAt: time.Now().UTC()}})

// Result:
// map[string]interface{}{"address.visitedAt": time.Time{...}}

Passing the result to coll.UpdateOne updates only the address.VisitedAt field instead of overwriting the entire address embedded document. See this blog post for more information.

The complete documentation is available on Godoc.

How to Install

go get -v github.com/chidiwilliams/flatbson

flatbson's People

Contributors

chidiwilliams avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

flatbson's Issues

Cannot create field in element - nil

Hello, great library thanks for that :)

What about this case, if you have object like this

{
verifyToken: {
  code: string,
  expiresAt: date,
}
}

If verify token is pre filled with data, it works fine, but if verifyToken is nil there is an error like this

multiple write errors: [{write errors: [{Cannot create field 'code' in element {forgotPasswordToken: null}}]}, {<nil>}]

Consider embedding this code to official mongodb codebase

Hi, first of all, really excellent project. Thank you for solving this problem, I honestly don't understand why this is not part of the official bson/mongodb package for go. It was really hard to find it and is my personal opinion it's a fundamental feature to perform updates on mongodb documents in a way that is natural using struct tags. I'm not affiliated in anyway with mongodb but, if you agree, we could give it a try.

Package panics when attempting to flatten struct with unexported fields

Package panics when attempting to flatten struct with unexported fields

go version: go1.13
OS: macOS mojave (10.14.6)

sample code:

package main

import "github.com/chidiwilliams/flatbson"
import "time"
import "fmt"

func main() {
  a := struct {
    B time.Time `bson:"b,omitempty"` // time.Time has 3 unexported fields
  }{time.Now()}
  fmt.Println(flatbson.Flatten(a))
}

expected output:
map[b:2020-05-22 00:20:15.63927 +0100 WAT m=+0.003913472]

actual output:

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

goroutine 1 [running]:
reflect.valueInterface(0x11b6640, 0xc00000c300, 0xab, 0x1, 0x1390240, 0xc000016600)
        /usr/local/go/src/reflect/value.go:1014 +0x1bc
reflect.Value.Interface(...)
        /usr/local/go/src/reflect/value.go:1003
github.com/chidiwilliams/flatbson.flattenFields(0x11f5380, 0xc00000c300, 0x99, 0xc000066930, 0xc00001660a, 0x2, 0x1, 0xc00001660a)
        /Users/oghogho/go/pkg/mod/github.com/chidiwilliams/[email protected]/flatten.go:78 +0x3ce
github.com/chidiwilliams/flatbson.flattenFields(0x11ccbe0, 0xc00000c300, 0x99, 0xc000066930, 0x0, 0x0, 0xbb3ae01, 0xc00009bec8)
        /Users/oghogho/go/pkg/mod/github.com/chidiwilliams/[email protected]/flatten.go:67 +0x249
github.com/chidiwilliams/flatbson.Flatten(0x11ccbe0, 0xc00000c300, 0x11ccbe0, 0xc00000c300, 0x103a067)
        /Users/oghogho/go/pkg/mod/github.com/chidiwilliams/[email protected]/flatten.go:42 +0x10e
main.main()
        /Users/oghogho/Desktop/Softcom/tester/main.go:11 +0x75
exit status 2

Arrays -

I want to make it work with arrays so that this:

type My struct {
  Arr []string `bson:"arr"`
}

func test() {
	my := My{}
	my.Arr = make([]string, 0, 2)
	my.Arr[1] = "test"
	flatbson.Flatten(my)
}

Becomes something like this:

{"arr.1": "test"}

Do you think that would be easy to do?

Thank you!
Tim

Pointer nested pointer serialized to address

type X struct {
	Y  string  `bson:"y,omitempty"`
	Z  string  `bson:"z,omitempty"`
	ZZ *string `bson:"zz,omitempty"`
}
type A struct {
	B *X `bson:"b,omitempty"`
	//F []*X   `bson:"f,omitempty"`
}
zz := "zz"
aaa := X{"aaa", "", &zz}
flattened, err := flatbson.Flatten(A{
	B: &aaa,
})

want result: map[b.y:aaa b.zz:zz]
but actual: map[b.y:aaa b.zz:0xc0002b1610]

Is there a problem with my use?

Package does not have functionality to limit flatten depth

Package does not have functionality to limit flatten depth. i.e specify how deep object should be flattened.

go version: go1.13
OS: macOS mojave (10.14.6)

sample code:

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/chidiwilliams/flatbson"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/bsontype"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type doc struct {
	ID   *primitive.ObjectID `bson:"_id,omitempty"`
	Data *Data               `bson:"data,omitempty"`
}

type Data struct {
	Name string `bson:"name,omitempty"`
	Date *Date  `bson:"date,omitempty"`
}

type obj = map[string]interface{}

func main() {
	client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017/partedit"))

	if err != nil {
		log.Fatalln(err)
	}

	if err := client.Connect(nil); err != nil {
		log.Fatalln(err)
	}

	db := client.Database("partedit")

	col := db.Collection("partials")

	initialDate, updatedDate := time.Date(2020, 5, 22, 0, 0, 0, 0, time.UTC), time.Date(2020, 4, 12, 0, 0, 0, 0, time.UTC)
	doc1 := &doc{
		Data: &Data{
			Name: "Oghogho",
			Date: &Date{initialDate},
		},
	}
	doc2 := &doc{
		Data: &Data{
			Date: &Date{updatedDate},
		},
	}
	out1, out2 := new(doc), new(doc)
	id, err := col.InsertOne(nil, doc1)
	if err != nil {
		log.Fatalln(err)
	}
	res1 := col.FindOne(nil, bson.D{{Key: "_id", Value: id.InsertedID}})
	if err := res1.Decode(out1); err != nil {
		log.Fatalln(err)
	}
	r, _ := json.Marshal(out1.Data)
	fmt.Printf("Inserted document: %s\n", r)
	update, err := flatbson.Flatten(doc2)
	if err != nil {
		log.Fatalln(err)
	}
	res2 := col.FindOneAndUpdate(nil, bson.D{{Key: "_id", Value: id.InsertedID}}, obj{"$set": update}, options.FindOneAndUpdate().SetReturnDocument(options.After))
	if err := res2.Decode(out2); err != nil {
		log.Fatalln(err)
	}
	r, _ = json.Marshal(out2.Data)
	fmt.Printf("Document after update: %s\n", r)
}

type Date struct {
	time.Time
}

func NewDate(t time.Time) *Date {
	return &Date{t}
}

func (d *Date) UnmarshalBSON(data []byte) error {
	val := bson.RawValue{
		Type:  bsontype.DateTime,
		Value: data,
	}
	stamp, ok := val.DateTimeOK()
	if !ok {
		return errors.New("Invalid datetime")
	}

	newTime := time.Unix(stamp/1000, 0)
	d.Time = time.Date(newTime.Year(), newTime.Month(), newTime.Day(), 0, 0, 0, 0, time.UTC)
	return nil
}

func (d *Date) MarshalBSONValue() (bsontype.Type, []byte, error) {
	return bson.MarshalValue(d.Time)
}

func (d *Date) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.Time.Format("2006-01-02"))
}

expected output:

Inserted document: {"Name":"Oghogho","Date":"2020-05-22"}
Document after update: {"Name":"Oghogho","Date":"2020-04-12"}

actual output:

Inserted document: {"Name":"Oghogho","Date":"2020-05-22"}
2020/05/22 11:04:38 (PathNotViable) Cannot create field 'time' in element {date: new Date(1590141878506)}
exit status 1

expected flattened object:
map[data.date:2020-04-12 00:00:00 +0000 UTC]

actual flattened object:
map[data.date.time:2020-04-12 00:00:00 +0000 UTC]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.