Conversation
- Implement updateMany. - Add Update interface with fluent API.
|
This closes #18. |
|
(I assume you meant for |
|
Annnyyyyywayyyyyy so I do like the idea of having offiicial typed and untyped ways of defining your update filters, it's definitely better than just There's just one problem, which is that tables and collections have somewhat different update filters, with collection updates allowing for much more functionality than table updates, so it'd probably be in your best interest to split Now if you'd rather just go ahead and just use the same So if you've read on, humor me for a minute and consider the following: package update
import "encoding/json"
// INTERFACES
type CollectionUpdate interface {
json.Marshaler
isCollectionUpdate() // brand to make the interface more nominal
}
type TableUpdate interface {
json.Marshaler
isTableUpdate() // brand to make the interface more nominal
}
// COLLECTION UPDATE
var _ CollectionUpdate = (*CollectionUpdater)(nil)
func Coll() *CollectionUpdater {
return &CollectionUpdater{ops: make(map[string]map[string]any)}
}
type CollectionUpdater struct {
ops map[string]map[string]any
}
func (u *CollectionUpdater) MarshalJSON() ([]byte, error) {
return json.Marshal(u.ops)
}
func (u *CollectionUpdater) isCollectionUpdate() {
// no-op, just to satisfy the interface
}
// TABLE UPDATE
var _ TableUpdate = (*TableUpdater)(nil)
func Table() *TableUpdater {
return &TableUpdater{ops: make(map[string]map[string]any)}
}
type TableUpdater struct {
ops map[string]map[string]any
}
func (u *TableUpdater) MarshalJSON() ([]byte, error) {
return json.Marshal(u.ops)
}
func (u *TableUpdater) isTableUpdate() {
// no-op, just to satisfy the interface
}
// GENERIC UPDATE
var _ CollectionUpdate = (*U)(nil)
var _ TableUpdate = (*U)(nil)
type U map[string]any
func (u U) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]any(u))
}
func (u U) isCollectionUpdate() {
// no-op, just to satisfy the interfaces
}
func (u U) isTableUpdate() {
// no-op, just to satisfy the interfaces
}package astradb
import (
"github.com/datastax/astra-db-go/update"
)
func CollectionUpdateMany(u update.CollectionUpdate) {
// pretend this actually does something
}
func main() {
// ok
CollectionUpdateMany(update.Coll())
// also ok; less type safe, but can be used for both table and collection updates
CollectionUpdateMany(update.U{})
// Cannot use update.Table() (type *TableUpdater) as the type update.CollectionUpdate
// Type does not implement update.CollectionUpdate as some methods are missing:
// isCollectionUpdate()
CollectionUpdateMany(update.Table())
}My solution takes inspiration from opaque/branded types in TypeScript (which also has structural interfaces) as a way to produce somewhat nominal interfaces.
The idea is that instead of doing
That way, both
|
|
This would unfortunately mean you couldn't really do a direct |
|
In general, you don't need to have nominally different Or of course if you wanted you could do something like type CollectionUpdate interface {
MkCollectionUpdateMarshaler() *json.Marshaler
}or just type CollectionUpdate interface {
MarshalCollectionUpdateToJSON() ([]byte, error)
}to also make the interfaces harder to accidentally mix up but that may take a little extra care and effort to create or use than just having a couple of no-op marker/branding methods like |
|
Hey @toptobes I added more tests and split out collection/table updates. Give this a try and LMK what you think. |
Collection Update Updates
Hey @toptobes check this out. This implements collection updateMany. I also didn't like that the "update" param was of type
anyso I took a stab at helpers for updates, along with unit tests that verify my structs marshal to the JSON from the curl command in the docs.So this:
... is the same as this:
And both properly
json.Marshalinto this example from the docs