func GetTransaction(ctx context.Context) (bool, *Store) {
	if ctx == nil {
		return false, nil
	}
	val, ok := ctx.Value(hasTransaction).(*Store)
	if !ok {
		return false, nil
	}
	return true, val
}
GetTransaction will return whether the context carries a transaction and the
store used to create the transaction.
package coal
import (
	"context"
	"net/url"
	"strings"
	"sync"
	"time"
	"github.com/256dpi/lungo"
	"github.com/256dpi/xo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readconcern"
	"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
func MustConnect(uri string, reporter func(error), opts ...*options.ClientOptions) *Store {
	store, err := Connect(uri, reporter, opts...)
	if err != nil {
		panic(err)
	}
	return store
}
func Connect(uri string, reporter func(error), opts ...*options.ClientOptions) (*Store, error) {
	parsedURL, err := url.Parse(uri)
	if err != nil {
		return nil, xo.W(err)
	}
	defaultDB := strings.Trim(parsedURL.Path, "/")
	opt := options.MergeClientOptions(opts...)
	opt.ApplyURI(uri)
	opt.SetReadConcern(readconcern.Majority())
	opt.SetWriteConcern(writeconcern.New(writeconcern.WMajority()))
	client, err := lungo.Connect(nil, opt)
	if err != nil {
		return nil, xo.W(err)
	}
	err = client.Ping(nil, nil)
	if err != nil {
		return nil, xo.W(err)
	}
	return NewStore(client, defaultDB, nil, reporter), nil
}
func MustOpen(store lungo.Store, defaultDB string, reporter func(error)) *Store {
	s, err := Open(store, defaultDB, reporter)
	if err != nil {
		panic(err)
	}
	return s
}
func Open(store lungo.Store, defaultDB string, reporter func(error)) (*Store, error) {
	if store == nil {
		store = lungo.NewMemoryStore()
	}
	client, engine, err := lungo.Open(nil, lungo.Options{
		Store:          store,
		ExpireInterval: time.Minute,
		ExpireErrors:   reporter,
	})
	if err != nil {
		return nil, xo.W(err)
	}
	return NewStore(client, defaultDB, engine, reporter), nil
}
func NewStore(client lungo.IClient, defaultDB string, engine *lungo.Engine, reporter func(error)) *Store {
	return &Store{
		client:   client,
		defDB:    defaultDB,
		engine:   engine,
		reporter: reporter,
	}
}
type Store struct {
	client   lungo.IClient
	defDB    string
	engine   *lungo.Engine
	reporter func(error)
	colls    sync.Map
	managers sync.Map
}
func (s *Store) Client() lungo.IClient {
	return s.client
}
func (s *Store) Lungo() bool {
	_, ok := s.client.(*lungo.Client)
	return ok
}
func (s *Store) DB() lungo.IDatabase {
	return s.client.Database(s.defDB)
}
func (s *Store) C(model Model) *Collection {
	meta := GetMeta(model)
	val, ok := s.colls.Load(meta)
	if ok {
		return val.(*Collection)
	}
	coll := &Collection{
		coll: s.DB().Collection(meta.Collection),
	}
	s.colls.Store(meta, coll)
	return coll
}
func (s *Store) M(model Model) *Manager {
	meta := GetMeta(model)
	val, ok := s.managers.Load(meta)
	if ok {
		return val.(*Manager)
	}
	manager := &Manager{
		meta:  meta,
		coll:  s.C(model),
		trans: NewTranslator(model),
	}
	s.managers.Store(meta, manager)
	return manager
}
func (s *Store) T(ctx context.Context, readOnly bool, fn func(tc context.Context) error) error {
	if ctx == nil {
		ctx = context.Background()
	}
	if HasTransaction(ctx) {
		return fn(ctx)
	}
	ctx, span := xo.Trace(ctx, "coal/Store.T")
	defer span.End()
	opts := options.Session().
		SetCausalConsistency(true).
		SetDefaultReadConcern(readconcern.Snapshot())
	return xo.W(s.client.UseSessionWithOptions(ctx, opts, func(sc lungo.ISessionContext) error {
		err := sc.StartTransaction()
		if err != nil {
			return xo.W(err)
		}
		err = fn(context.WithValue(sc, hasTransaction, s))
		if err != nil {
			_ = sc.AbortTransaction(sc)
			return xo.W(err)
		}
		if readOnly {
			err = sc.AbortTransaction(sc)
		} else {
			err = sc.CommitTransaction(sc)
		}
		if err != nil {
			return xo.W(err)
		}
		return nil
	}))
}
func (s *Store) Close() error {
	err := s.client.Disconnect(nil)
	if err != nil {
		return xo.W(err)
	}
	if s.engine != nil {
		s.engine.Close()
	}
	return nil
}
type contextKey struct{}
var hasTransaction = contextKey{}