git.zd.zone/hrpc/hrpc@v0.0.12/database/dgraph/dgraph.go (about)

     1  package dgraph
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  
     7  	"git.zd.zone/hrpc/hrpc/database"
     8  	"github.com/dgraph-io/dgo/v210"
     9  	"github.com/dgraph-io/dgo/v210/protos/api"
    10  	"google.golang.org/grpc"
    11  	"google.golang.org/grpc/credentials/insecure"
    12  )
    13  
    14  // Proxy represents a collection of methods to operate dGraph
    15  type Proxy interface {
    16  	// Alter can be used to set schema.
    17  	// For example:
    18  	// 	op := &api.Operation{
    19  	//		Schema: 		 `name: string @index(exact) .`,
    20  	//		RunInBackground: true
    21  	//	}
    22  	Alter(ctx context.Context, op *api.Operation) error
    23  	// Transaction should be called normally to run a Mutation or Upsert(Query + Mutation).
    24  	// For example:
    25  	// 	(Mutation)
    26  	//		type Person struct {
    27  	//			Name string `json:"name,omitempty"`
    28  	//		}
    29  	//		p := Persion{Name: "xxx"}
    30  	//		b, err := json.Marshal(p)
    31  	//		mu := &api.Mutation{
    32  	//			SetJson: b,
    33  	//		}
    34  	//		req := &api.Request{CommitNow: true, Mutations: []*api.Mutation{mu}}
    35  	//		rsp, err := Transaction(ctx, req)
    36  	//	(Query + Mutation) WITHOUT conditions
    37  	//		q := `query {user as var(func: eq(email, "wrong_email@example.com"))}`
    38  	//		mu := &api.Mutation{SetNquads: []byte(`uid(user) <email> "correct_email@example.com" .`)}
    39  	//		req := &api.Request{Query: q, Mutations: []*api.Mutation{mu}, CommitNow: true}
    40  	//		rsp, err := Transaction(ctx, req)
    41  	//	(Query + Mutation) WITH conditions
    42  	//		q := `query {user as var(func: eq(email, "wrong_email@example.com"))}`
    43  	//		mu := &api.Mutation{
    44  	//			Cond: `@if(eq(len(user), 1))`,
    45  	//			SetNquads: []byte(`uid(user) <email> "correct_email@example.com" .`),
    46  	//		}
    47  	//		req := &api.Request{Query: q, Mutations: []*api.Mutation{mu}, CommitNow: true}
    48  	//		rsp, err := Transaction(ctx, req)
    49  	Transaction(ctx context.Context, req *api.Request) ([]byte, error)
    50  	// Query can be used in two ways, including general query and query with variables.
    51  	// It is useful to increase read speed.
    52  	// the param of `vars` can be nil if you do not want to pass variables to the Query statements.
    53  	Query(ctx context.Context, q string, vars map[string]string) ([]byte, error)
    54  }
    55  
    56  var dg *dGraph
    57  
    58  type dGraph struct {
    59  	client *dgo.Dgraph
    60  	conns  []*grpc.ClientConn
    61  
    62  	option Option
    63  }
    64  
    65  func New() *dGraph {
    66  	if dg != nil {
    67  		dg.Destory()
    68  	}
    69  	dg = &dGraph{}
    70  	return dg
    71  }
    72  
    73  // ------------ for implemation database.Database START-------- //
    74  
    75  func (d *dGraph) Load(src []byte) error {
    76  	if err := json.Unmarshal(src, &d.option); err != nil {
    77  		return err
    78  	}
    79  	return nil
    80  }
    81  func (d *dGraph) Connect() error {
    82  	d.Destory()
    83  
    84  	var clients []api.DgraphClient
    85  	for _, target := range d.option.Targets {
    86  		c, err := grpc.Dial(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
    87  		if err != nil {
    88  			return err
    89  		}
    90  		d.conns = append(d.conns, c)
    91  		clients = append(clients, api.NewDgraphClient(c))
    92  	}
    93  	d.client = dgo.NewDgraphClient(clients...)
    94  	if d.option.Credential.User != "" && d.option.Credential.Password != "" {
    95  		if d.option.Credential.Namespace == 0 {
    96  			d.client.Login(
    97  				context.Background(),
    98  				d.option.Credential.User,
    99  				d.option.Credential.Password,
   100  			)
   101  		} else {
   102  			// SHOULD ONLY valid for Dgraph v21.03 or above
   103  			d.client.LoginIntoNamespace(
   104  				context.Background(),
   105  				d.option.Credential.User,
   106  				d.option.Credential.Password,
   107  				d.option.Credential.Namespace,
   108  			)
   109  		}
   110  	}
   111  	return nil
   112  }
   113  func (d *dGraph) Name() string {
   114  	return "dgraph"
   115  }
   116  func (d *dGraph) Destory() {
   117  	for _, c := range d.conns {
   118  		c.Close()
   119  	}
   120  }
   121  
   122  // ------------ for implemation database.Database END-------- //
   123  
   124  func (d *dGraph) Alter(ctx context.Context, op *api.Operation) error {
   125  	return dg.client.Alter(ctx, op)
   126  }
   127  
   128  func (d *dGraph) Transaction(ctx context.Context, req *api.Request) ([]byte, error) {
   129  	txn := dg.client.NewTxn()
   130  	defer txn.Discard(ctx)
   131  
   132  	rsp, err := txn.Do(ctx, req)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	if err := txn.Commit(ctx); err != nil {
   137  		return nil, err
   138  	}
   139  	return rsp.GetJson(), nil
   140  }
   141  
   142  func (d *dGraph) Query(ctx context.Context, q string, vars map[string]string) ([]byte, error) {
   143  	txn := dg.client.NewReadOnlyTxn()
   144  	defer txn.Discard(ctx)
   145  
   146  	if vars != nil {
   147  		rsp, err := txn.QueryWithVars(ctx, q, vars)
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  		return rsp.GetJson(), nil
   152  	}
   153  
   154  	rsp, err := txn.Query(ctx, q)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return rsp.GetJson(), nil
   159  }
   160  
   161  // Client returns the handler to operate mysql if success
   162  func Client() Proxy {
   163  	return dg
   164  }
   165  
   166  var _ database.Database = (*dGraph)(nil)