Categorygithub.com/linuxluigi/gogm/v2
modulepackage
2.3.10
Repository: https://github.com/linuxluigi/gogm.git
Documentation: pkg.go.dev

# README

Go Report Card Actions Status GoDoc

GoGM Golang Object Graph Mapper v2

Installation

go get github.com/mindstand/gogm/v2

Note: Do not use -u when installing. If you do, gogm will not compile.

This is caused by a dependency being updated to go1.18 making it incompatable with go1.17. In a future update, gogm will be updated to go1.18 eliminating this issue.

Features

  • Struct Mapping through the gogm struct decorator
  • Full support for ACID transactions
  • Underlying connection pooling
  • Support for HA Casual Clusters using bolt+routing through the Official Neo4j Go Driver
  • Custom queries in addition to built in functionality
  • Builder pattern cypher queries using MindStand's cypher dsl package
  • CLI to generate link and unlink functions for gogm structs.
  • Multi database support with Neo4j v4

What's new in V2

  • GoGM is an object now! This means you can have multiple instances of GoGM at a time
  • OpenTracing and Context Support
  • Driver has been updated from v1.6 to v4
  • Log interface, so anyone can use the logger of their choice instead of being forced to use logrus
  • Primary Key strategies to use any type of primary key. GoGM is no longer UUID only!
  • TLS now supported

| Note: GoGM v1 has been deprecated.

Usage

Primary Key Strategy

Primary key strategies allow more customization over primary keys. A strategy is provided to gogm on initialization.
Built in primary key strategies are:

  • gogm.DefaultPrimaryKeyStrategy -- just use the graph id from neo4j as the primary key
  • gogm.UUIDPrimaryKeyStrategy -- uuid's as primary keys
// Example of the internal UUID strategy
PrimaryKeyStrategy{
	// StrategyName is the name to reference the strategy
    StrategyName: "UUID",
    // DBName is the name of the field in the database
    DBName:       "uuid",
    // FieldName is the name of the field in go, this will validate to make sure all pk's use the same field name
    FieldName:    "UUID",
    // Type is the reflect type of the primary key, in this case it's a string but it can be any primitive
    Type:         reflect.TypeOf(""),
    // GenIDFunc defines how new ids are generated, in this case we're using googles uuid library
    GenIDFunc: func() (id interface{}) {
        return uuid.New().String()
    },
}

Load Strategy

Load strategies allow control over the queries generated by Load operations. Different strategies change the size of the queries sent to the database as well as the amount of work the database has to do. A load strategy is provided to gomg on initialization.

The defined load strategies are:

  • gogm.PATH_LOAD_STRATEGY -- Use cypher path queries to generate simple queries for load operations.
  • gogm.SCHEMA_LOAD_STRATEGY -- Leverage the GoGM schema to generate more complex queries for load operations which results in less work for the database.

Depending on your use case, PATH_LOAD_STRATEGY may result in higher latency.

Struct Configuration

text notates deprecation

Decorators that can be used

  • name=<name> -- used to set the field name that will show up in neo4j.
  • relationship=<edge_name> -- used to set the name of the edge on that field.
  • direction=<INCOMING|OUTGOING|BOTH|NONE> -- used to specify direction of that edge field.
  • index -- marks field to have an index applied to it.
  • unique -- marks field to have unique constraint.
  • pk=<strategy_name> -- marks field as a primary key and specifies which pk strategy to use. Can only have one pk, composite pk's are not supported.
  • properties -- marks that field is using a map. GoGM only supports properties fields of map[string]interface{}, map[string]<primitive>, map[string][]<primitive> and []<primitive>
  • - -- marks that field will be ignored by the ogm

Not on relationship member variables

All relationships must be defined as either a pointer to a struct or a slice of struct pointers *SomeStruct or []*SomeStruct

Use ; as delimiter between decorator tags.

Ex.

type TdString string

type MyNeo4jObject struct {
  // provides required node field
  // use gogm.BaseUUIDNode if you want to use UUIDs
  gogm.BaseNode

  Field string `gogm:"name=field"`
  Props map[string]interface{} `gogm:"properties;name=props"` //note that this would show up as `props.<key>` in neo4j
  IgnoreMe bool `gogm="-"`
  UniqueTypeDef TdString `gogm:"name=unique_type_def"`
  Relation *SomeOtherStruct `gogm="relationship=SOME_STRUCT;direction=OUTGOING"`
  ManyRelation []*SomeStruct `gogm="relationship=MANY;direction=INCOMING"`
}

GOGM Usage

package main

import (
	"github.com/mindstand/gogm/v2"
	"time"
)

type tdString string
type tdInt int

//structs for the example (can also be found in decoder_test.go)
type VertexA struct {
	// provides required node fields
	gogm.BaseNode

    TestField         string                `gogm:"name=test_field"`
	TestTypeDefString tdString          `gogm:"name=test_type_def_string"`
	TestTypeDefInt    tdInt             `gogm:"name=test_type_def_int"`
	MapProperty       map[string]string `gogm:"name=map_property;properties"`
	SliceProperty     []string          `gogm:"name=slice_property;properties"`
    SingleA           *VertexB          `gogm:"direction=incoming;relationship=test_rel"`
	ManyA             []*VertexB        `gogm:"direction=incoming;relationship=testm2o"`
	MultiA            []*VertexB        `gogm:"direction=incoming;relationship=multib"`
	SingleSpecA       *EdgeC            `gogm:"direction=outgoing;relationship=special_single"`
	MultiSpecA        []*EdgeC          `gogm:"direction=outgoing;relationship=special_multi"`
}

type VertexB struct {
	// provides required node fields
	gogm.BaseNode

	TestField  string     `gogm:"name=test_field"`
	TestTime   time.Time  `gogm:"name=test_time"`
	Single     *VertexA   `gogm:"direction=outgoing;relationship=test_rel"`
	ManyB      *VertexA   `gogm:"direction=outgoing;relationship=testm2o"`
	Multi      []*VertexA `gogm:"direction=outgoing;relationship=multib"`
	SingleSpec *EdgeC     `gogm:"direction=incoming;relationship=special_single"`
	MultiSpec  []*EdgeC   `gogm:"direction=incoming;relationship=special_multi"`
}

type EdgeC struct {
	// provides required node fields
	gogm.BaseNode

	Start *VertexA
	End   *VertexB
	Test  string `gogm:"name=test"`
}

func main() {
	// define your configuration
	config := gogm.Config{
		Host:                      "0.0.0.0",
		Port:                      7687,
		// deprecated in favor of protocol
	    // IsCluster:                 false,
	    Protocol:                  "neo4j", //also supports neo4j+s, neo4j+ssc, bolt, bolt+s and bolt+ssc
	    // Specify CA Public Key when using +ssc or +s
	    CAFileLocation: "my-ca-public.crt",
		Username:                  "neo4j",
		Password:                  "password",
		PoolSize:                  50,
		IndexStrategy:             gogm.VALIDATE_INDEX, //other options are ASSERT_INDEX and IGNORE_INDEX
		TargetDbs:                 nil,
		// default logger wraps the go "log" package, implement the Logger interface from gogm to use your own logger
		Logger:             gogm.GetDefaultLogger(),
		// define the log level
		LogLevel:           "DEBUG",
		// enable neo4j go driver to log
		EnableDriverLogs:   false,
		// enable gogm to log params in cypher queries. WARNING THIS IS A SECURITY RISK! Only use this when debugging
		EnableLogParams:    false,
		// enable open tracing. Ensure contexts have spans already. GoGM does not make root spans, only child spans
		OpentracingEnabled: false,
		// specify the method gogm will use to generate Load queries
		LoadStrategy: gogm.PATH_LOAD_STRATEGY // set to SCHEMA_LOAD_STRATEGY for schema-aware queries which may reduce load on the database
	}

	// register all vertices and edges
	// this is so that GoGM doesn't have to do reflect processing of each edge in real time
	// use nil or gogm.DefaultPrimaryKeyStrategy if you only want graph ids
	// we are using the default key strategy since our vertices are using BaseNode
	_gogm, err := gogm.New(&config, gogm.DefaultPrimaryKeyStrategy, &VertexA{}, &VertexB{}, &EdgeC{})
	if err != nil {
		panic(err)
	}

	//param is readonly, we're going to make stuff so we're going to do read write
	sess, err := _gogm.NewSessionV2(gogm.SessionConfig{AccessMode: gogm.AccessModeWrite})
	if err != nil {
		panic(err)
	}

	//close the session
	defer sess.Close()

	aVal := &VertexA{
		TestField: "woo neo4j",
	}

	bVal := &VertexB{
		TestTime: time.Now().UTC(),
	}

	//set bi directional pointer
	bVal.Single = aVal
	aVal.SingleA = bVal

	err = sess.SaveDepth(context.Background(), aVal, 2)
	if err != nil {
		panic(err)
	}

	//load the object we just made (save will set the uuid)
	var readin VertexA
	err = sess.Load(context.Background(), &readin, aVal.UUID)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%+v", readin)
}


Migrating from V1 to V2

Initialization

Initialization in gogm v1

config := gogm.Config{
    IndexStrategy: gogm.VALIDATE_INDEX, //other options are ASSERT_INDEX and IGNORE_INDEX
    PoolSize:      50,
    Port:          7687,
    IsCluster:     false, //tells it whether or not to use `bolt+routing`
    Host:          "0.0.0.0",
    Password:      "password",
    Username:      "neo4j",
}

err := gogm.Init(&config, &VertexA{}, &VertexB{}, &EdgeC{})
if err != nil {
    panic(err)
}

Equivalent in GoGM v2

// define your configuration
config := gogm.Config{
    IndexStrategy: gogm.VALIDATE_INDEX, //other options are ASSERT_INDEX and IGNORE_INDEX
    PoolSize:      50,
    Port:          7687,
    IsCluster:     false, //tells it whether or not to use `bolt+routing`
    Host:          "0.0.0.0",
    Password:      "password",
    Username:      "neo4j",
}

	// register all vertices and edges
	// this is so that GoGM doesn't have to do reflect processing of each edge in real time
	// use nil or gogm.DefaultPrimaryKeyStrategy if you only want graph ids
	_gogm, err := gogm.New(&config, gogm.UUIDPrimaryKeyStrategy, &VertexA{}, &VertexB{}, &EdgeC{})
	if err != nil {
		panic(err)
	}
	
	gogm.SetGlobalGoGM(_gogm)
Note that we call gogm.SetGloablGogm so that we can still access it from a package level

Create a session

Creating a session in GoGM v1

sess, err := gogm.NewSession(false)
Note this still works in v2, its using the global gogm to create the session. Also note this is making an instance of the deprecated ISession

Equivalent in GoGM v2

// this would also work with a local instance of gogm (localGogm.NewSessionV2)
sess, err := gogm.G().NewSessionV2(gogm.SessionConfig{AccessMode: gogm.AccessModeWrite})

Summary

  • Minimal change requires creating a global gogm, everything else should still work with ISession (gogm v1 session object)
  • ISession is now deprecated but still supported
  • SessionV2 is the new standard

GoGM CLI

CLI Installation

go install github.com/mindstand/gogm/cmd/gogmcli@latest

CLI Usage

NAME:
   gogmcli - used for neo4j operations from gogm schema

USAGE:
   gogmcli [global options] command [command options] [arguments...]

VERSION:
   2.1.1

COMMANDS:
   generate, g, gen  to generate link and unlink functions for nodes
   help, h           Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --debug, -d    execute in debug mode (default: false)
   --help, -h     show help (default: false)
   --version, -v  print the version (default: false)

Inspiration

Inspiration came from the Java OGM implementation by Neo4j.

Road Map

  • Schema Migration
  • Errors overhaul using go 1.13 error wrapping

How you can help

  • Report Bugs
  • Fix bugs
  • Contribute (refer to contribute.md)

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
Code generated by GoGM 2.1.1.

# Functions

G returns the global instance of gogm.
No description provided by the author
New returns an instance of gogm mapTypes requires pointers of the types to map and will error out if pointers are not provided.
NewContext returns an instance of gogm but also takes in a context since NewContext creates a driver instance and reaches out to the database.
NewInvalidDecoratorConfigError creates an InvalidDecoratorConfigError structure.
NewInvalidStructConfigError creates an InvalidStructConfigError structure.
uses global gogm Deprecated: Gogm.NewSession instead.
Deprecated: Gogm.NewSessionWithConfig instead.
PathLoadStrategyEdgeConstraint is similar to load many, but requires that it is related to another node via some edge.
PathLoadStrategyMany loads many using path strategy.
PathLoadStrategyOne loads one object using path strategy.
SchemaLoadStrategyMany loads many using schema strategy.
SchemaLoadStrategyOne loads one object using schema strategy.
SetGlobalGogm sets the global instance of gogm.

# Constants

No description provided by the author
No description provided by the author
ASSERT_INDEX ensures that all indices are set and sets them if they are not there.
IGNORE_INDEX skips the index step of setup.
Side of relationship can point to 0+ other nodes.
PathLoadStrategy uses cypher path.
SchemaLoadStrategy generates queries specifically from generated schema.
Side of relationship can only point to 0 or 1 other nodes.
VALIDATE_INDEX ensures that all indices are set.

# Variables

No description provided by the author
ErrConfiguration is returned for configuration errors.
ErrConnection is returned for connection related errors.
ErrInternal is returned for general internal gogm errors.
ErrInvalidParams is returned when params to a function are invalid.
ErrNotFound is returned when gogm is unable to find data.
ErrTransaction is returned for errors related to gogm transactions.
ErrValidation is returned when there is a validation error.
No description provided by the author

# Structs

BaseNode contains fields that ALL GoGM nodes are required to have.
No description provided by the author
Config defines parameters for creating a GoGM object.
Gogm defines an instance of the GoGM OGM with a configuration and mapped types.
InvalidDecoratorConfigError defines an error for a malformed struct tag.
InvalidStructConfigError defines an error for a malformed gogm structure.
Pagination is used to control the pagination behavior of `LoadAllDepthFilterPagination``.
No description provided by the author
RelationConfig specifies how relationships are loaded.
Deprecated: Session will be removed in a later release in favor of SessionV2.
No description provided by the author

# Interfaces

Edge specifies required functions for special edge nodes.
ISession: V1 session object for ogm interactions Deprecated: use SessionV2 instead.
ITransaction specifies functions for Neo4j ACID transactions Deprecated: Use TransactionV2 instead.
No description provided by the author
session version 2 is experimental to start trying breaking changes.
TransactionV2 specifies functions for Neo4j ACID transactions.

# Type aliases

No description provided by the author
IndexStrategy defines the different index approaches.
Specifies query based load strategy.
Specifies Type of testRelationship.
No description provided by the author
No description provided by the author