1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-29 13:49:28 +08:00
Aleksandar Novaković 58cdf2cddc MF-312 - Implement basic MongoDB reader (#344)
* Add mongodb reader service

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Add tests for mongodb reader service

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Add documentation for mongodb reader service

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Fix test function name

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Update comment in docker-compose for mongodb-reader service

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>
2018-08-08 13:38:34 +02:00

197 lines
4.4 KiB
Go

package dropcollopt
import (
"reflect"
"github.com/mongodb/mongo-go-driver/core/option"
"github.com/mongodb/mongo-go-driver/core/session"
)
var dropCollBundle = new(DropCollBundle)
// DropColl represents all possible params for the dropColl() function.
type DropColl interface {
dropColl()
}
// DropCollOption represents the options for the dropColl() function.
type DropCollOption interface {
DropColl
ConvertDropCollOption() option.DropCollectionsOptioner
}
// DropCollSession is the session for the DropColl() function.
type DropCollSession interface {
DropColl
ConvertDropCollSession() *session.Client
}
// DropCollBundle is a bundle of DropColl options
type DropCollBundle struct {
option DropColl
next *DropCollBundle
}
// Implement the DropColl interface
func (lcb *DropCollBundle) dropColl() {}
// ConvertDropCollOption implements the DropColl interface
func (lcb *DropCollBundle) ConvertDropCollOption() option.DropCollectionsOptioner {
return nil
}
// BundleDropColl bundles DropColl options
func BundleDropColl(opts ...DropColl) *DropCollBundle {
head := dropCollBundle
for _, opt := range opts {
newBundle := DropCollBundle{
option: opt,
next: head,
}
head = &newBundle
}
return head
}
// Unbundle transforms a bundle into a slice of options, optionally deduplicating.
func (lcb *DropCollBundle) Unbundle(deduplicate bool) ([]option.DropCollectionsOptioner, *session.Client, error) {
options, sess, err := lcb.unbundle()
if err != nil {
return nil, nil, err
}
if !deduplicate {
return options, sess, nil
}
// iterate backwards and make dedup slice
optionsSet := make(map[reflect.Type]struct{})
for i := len(options) - 1; i >= 0; i-- {
currOption := options[i]
optionType := reflect.TypeOf(currOption)
if _, ok := optionsSet[optionType]; ok {
// option already found
options = append(options[:i], options[i+1:]...)
continue
}
optionsSet[optionType] = struct{}{}
}
return options, sess, nil
}
// Calculates the total length of a bundle, accounting for nested bundles.
func (lcb *DropCollBundle) bundleLength() int {
if lcb == nil {
return 0
}
bundleLen := 0
for ; lcb != nil; lcb = lcb.next {
if lcb.option == nil {
continue
}
if converted, ok := lcb.option.(*DropCollBundle); ok {
// nested bundle
bundleLen += converted.bundleLength()
continue
}
if _, ok := lcb.option.(DropCollSessionOpt); !ok {
bundleLen++
}
}
return bundleLen
}
// Helper that recursively unwraps bundle into slice of options
func (lcb *DropCollBundle) unbundle() ([]option.DropCollectionsOptioner, *session.Client, error) {
if lcb == nil {
return nil, nil, nil
}
var sess *session.Client
listLen := lcb.bundleLength()
options := make([]option.DropCollectionsOptioner, listLen)
index := listLen - 1
for listHead := lcb; listHead != nil; listHead = listHead.next {
if listHead.option == nil {
continue
}
// if the current option is a nested bundle, Unbundle it and add its options to the current array
if converted, ok := listHead.option.(*DropCollBundle); ok {
nestedOptions, s, err := converted.unbundle()
if err != nil {
return nil, nil, err
}
if s != nil && sess == nil {
sess = s
}
// where to start inserting nested options
startIndex := index - len(nestedOptions) + 1
// add nested options in order
for _, nestedOp := range nestedOptions {
options[startIndex] = nestedOp
startIndex++
}
index -= len(nestedOptions)
continue
}
switch t := listHead.option.(type) {
case DropCollOption:
options[index] = t.ConvertDropCollOption()
index--
case DropCollSession:
if sess == nil {
sess = t.ConvertDropCollSession()
}
}
}
return options, sess, nil
}
// String implements the Stringer interface
func (lcb *DropCollBundle) String() string {
if lcb == nil {
return ""
}
str := ""
for head := lcb; head != nil && head.option != nil; head = head.next {
if converted, ok := head.option.(*DropCollBundle); ok {
str += converted.String()
continue
}
if conv, ok := head.option.(DropCollOption); !ok {
str += conv.ConvertDropCollOption().String() + "\n"
}
}
return str
}
// DropCollSessionOpt is a dropColl session option.
type DropCollSessionOpt struct{}
func (DropCollSessionOpt) dropColl() {}
// ConvertDropCollSession implements the DropCollSession interface.
func (DropCollSessionOpt) ConvertDropCollSession() *session.Client {
return nil
}