Skip to content

Instantly share code, notes, and snippets.

@Zenithar
Last active March 5, 2025 07:01
Show Gist options
  • Save Zenithar/e81a0ff9371ecdaa763419eb0b44ad4f to your computer and use it in GitHub Desktop.
Save Zenithar/e81a0ff9371ecdaa763419eb0b44ad4f to your computer and use it in GitHub Desktop.
Custom JanusGraph specific type handler for TinkerPop Gremlin Go driver.
package gremlin
import (
"encoding/binary"
"errors"
"fmt"
gremlingo "github.com/apache/tinkerpop/gremlin-go/v3/driver"
)
const (
valueFlagNone byte = 0
)
func readTemp(data *[]byte, i *int, len int) *[]byte {
tmp := make([]byte, len)
for j := 0; j < len; j++ {
tmp[j] = (*data)[j+*i]
}
*i += len
return &tmp
}
func readUint32Safe(data *[]byte, i *int) uint32 {
return binary.BigEndian.Uint32(*readTemp(data, i, 4))
}
func readByteSafe(data *[]byte, i *int) byte {
*i++
return (*data)[*i-1]
}
func readLongSafe(data *[]byte, i *int) int64 {
return int64(binary.BigEndian.Uint64(*readTemp(data, i, 8)))
}
func readString(data *[]byte, i *int) (interface{}, error) {
sz := int(readUint32Safe(data, i))
if sz == 0 {
return "", nil
}
*i += sz
return string((*data)[*i-sz : *i]), nil
}
// janusgraphRelationIdentifierReader reads a JanusGraph relation identifier from the given byte slice.
// It expects the data to be in a specific format and returns a map containing the relation identifier details.
//
// The format is as follows:
// - 4 bytes: custom data type (0x1001)
// - 1 byte: value flag (0x00)
// - 1 byte: outVertexId type (0x00 for long, 0x01 for string)
// - if 0x00: 8 bytes: outVertexId (long)
// - if 0x01: 4 bytes: outVertexId length, followed by outVertexId string
// - 8 bytes: typeId (long)
// - 8 bytes: relationId (long)
// - 1 byte: inVertexId type (0x00 for long, 0x01 for string)
// - if 0x00: 8 bytes: inVertexId (long)
// - if 0x01: 4 bytes: inVertexId length, followed by inVertexId string
//
// https://github.com/JanusGraph/janusgraph-python/blob/cd812fc0614047d25a2cabd143902806a74a8465/janusgraph_python/structure/io/graphbinaryV1.py#L133
func janusgraphRelationIdentifierReader(data *[]byte, i *int) (interface{}, error) {
const relationIdentifierType = 0x1001
const (
longMarker = 0
stringMarker = 1
)
// expect type code
customDataTyp := readUint32Safe(data, i)
if customDataTyp != relationIdentifierType {
return nil, fmt.Errorf("unknown type code. got 0x%x, expected 0x%x", customDataTyp, relationIdentifierType)
}
// value flag, expect this to be non-nullable
if readByteSafe(data, i) != valueFlagNone {
return nil, errors.New("expected non-null value")
}
// outVertexId
var (
outVertexID any
err error
)
if readByteSafe(data, i) == stringMarker {
outVertexID, err = readString(data, i)
if err != nil {
return nil, fmt.Errorf("unable to read outVertexId: %w", err)
}
} else {
outVertexID = readLongSafe(data, i)
}
typeID := readLongSafe(data, i)
// relationID
relationID := readLongSafe(data, i)
// inVertexId
var inVertexID any
if readByteSafe(data, i) == stringMarker {
inVertexID, err = readString(data, i)
if err != nil {
return nil, fmt.Errorf("unable to read inVertexId: %w", err)
}
} else {
inVertexID = readLongSafe(data, i)
}
return map[string]any{
"outVertexId": outVertexID,
"typeId": typeID,
"relationId": relationID,
"inVertexId": inVertexID,
}, nil
}
func init() {
// https://issues.apache.org/jira/browse/TINKERPOP-2802
gremlingo.RegisterCustomTypeReader("janusgraph.RelationIdentifier", janusgraphRelationIdentifierReader)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment