|
| 1 | +/* |
| 2 | +Licensed to the Apache Software Foundation (ASF) under one |
| 3 | +or more contributor license agreements. See the NOTICE file |
| 4 | +distributed with this work for additional information |
| 5 | +regarding copyright ownership. The ASF licenses this file |
| 6 | +to you under the Apache License, Version 2.0 (the |
| 7 | +"License"); you may not use this file except in compliance |
| 8 | +with the License. You may obtain a copy of the License at |
| 9 | +
|
| 10 | +http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +
|
| 12 | +Unless required by applicable law or agreed to in writing, |
| 13 | +software distributed under the License is distributed on an |
| 14 | +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | +KIND, either express or implied. See the License for the |
| 16 | +specific language governing permissions and limitations |
| 17 | +under the License. |
| 18 | +*/ |
| 19 | + |
| 20 | +package gremlingo |
| 21 | + |
| 22 | +import "sync" |
| 23 | + |
| 24 | +// SerializeRequest serializes a complete request with bytecode into GraphBinary format. |
| 25 | +// This function enables developers to implement alternative transport protocols (e.g., gRPC, HTTP/2) |
| 26 | +// while maintaining full compatibility with the Gremlin traversal API. |
| 27 | +// |
| 28 | +// The function creates a proper Gremlin request that includes the bytecode, serializes it using |
| 29 | +// the GraphBinary serializer, and returns the raw bytes that can be transmitted over any transport. |
| 30 | +// |
| 31 | +// Parameters: |
| 32 | +// - bytecode: The Gremlin bytecode to serialize |
| 33 | +// - traversalSource: The name of the traversal source (typically "g") |
| 34 | +// - sessionId: The session ID for stateful sessions (empty string for stateless) |
| 35 | +// |
| 36 | +// Returns: |
| 37 | +// - []byte: The serialized request in GraphBinary format |
| 38 | +// - error: Any error encountered during serialization |
| 39 | +// |
| 40 | +// Example usage: |
| 41 | +// |
| 42 | +// bytecode := &Bytecode{...} |
| 43 | +// bytes, err := SerializeRequest(bytecode, "g", "") |
| 44 | +// if err != nil { |
| 45 | +// return err |
| 46 | +// } |
| 47 | +// // Send bytes over custom transport (gRPC, HTTP/2, etc.) |
| 48 | +func SerializeRequest(bytecode *Bytecode, traversalSource, sessionId string) ([]byte, error) { |
| 49 | + // Use the existing makeBytecodeRequest function from request.go |
| 50 | + request := makeBytecodeRequest(bytecode, traversalSource, sessionId) |
| 51 | + |
| 52 | + // Serialize the request using GraphBinary serializer |
| 53 | + serializer := graphBinarySerializer{} |
| 54 | + return serializer.serializeMessage(&request) |
| 55 | +} |
| 56 | + |
| 57 | +// SerializeBytecode serializes bytecode into GraphBinary format using the default traversal source "g". |
| 58 | +// This is a convenience wrapper around SerializeRequest for backward compatibility. |
| 59 | +// |
| 60 | +// Parameters: |
| 61 | +// - bytecode: The Gremlin bytecode to serialize |
| 62 | +// |
| 63 | +// Returns: |
| 64 | +// - []byte: The serialized request in GraphBinary format |
| 65 | +// - error: Any error encountered during serialization |
| 66 | +func SerializeBytecode(bytecode *Bytecode) ([]byte, error) { |
| 67 | + return SerializeRequest(bytecode, "g", "") |
| 68 | +} |
| 69 | + |
| 70 | +// SerializeStringQuery serializes a string-based Gremlin query into GraphBinary format. |
| 71 | +// This function enables sending raw Gremlin query strings over custom transport protocols. |
| 72 | +// |
| 73 | +// The function creates a proper Gremlin request that includes the query string, serializes it |
| 74 | +// using the GraphBinary serializer, and returns the raw bytes. |
| 75 | +// |
| 76 | +// Parameters: |
| 77 | +// - query: The Gremlin query string to serialize |
| 78 | +// - traversalSource: The name of the traversal source (typically "g") |
| 79 | +// - sessionId: The session ID for stateful sessions (empty string for stateless) |
| 80 | +// - requestOptions: Options for the request (bindings, timeout, etc.) |
| 81 | +// |
| 82 | +// Returns: |
| 83 | +// - []byte: The serialized request in GraphBinary format |
| 84 | +// - error: Any error encountered during serialization |
| 85 | +// |
| 86 | +// Example usage: |
| 87 | +// |
| 88 | +// bytes, err := SerializeStringQuery("g.V().count()", "g", "", RequestOptions{}) |
| 89 | +// if err != nil { |
| 90 | +// return err |
| 91 | +// } |
| 92 | +// // Send bytes over custom transport |
| 93 | +func SerializeStringQuery(query string, traversalSource string, sessionId string, requestOptions RequestOptions) ([]byte, error) { |
| 94 | + request := makeStringRequest(query, traversalSource, sessionId, requestOptions) |
| 95 | + serializer := graphBinarySerializer{} |
| 96 | + return serializer.serializeMessage(&request) |
| 97 | +} |
| 98 | + |
| 99 | +// DeserializeResult deserializes a response message from GraphBinary format into a Result. |
| 100 | +// This function enables receiving and processing responses from custom transport protocols. |
| 101 | +// |
| 102 | +// The function takes raw bytes received from a transport, deserializes them using the GraphBinary |
| 103 | +// deserializer, and returns a Result object containing the response data. |
| 104 | +// |
| 105 | +// Parameters: |
| 106 | +// - data: The response bytes in GraphBinary format |
| 107 | +// |
| 108 | +// Returns: |
| 109 | +// - *Result: The deserialized result containing response data |
| 110 | +// - error: Any error encountered during deserialization |
| 111 | +// |
| 112 | +// Example usage: |
| 113 | +// |
| 114 | +// // Receive bytes from custom transport |
| 115 | +// result, err := DeserializeResult(responseBytes) |
| 116 | +// if err != nil { |
| 117 | +// return err |
| 118 | +// } |
| 119 | +// value := result.Data |
| 120 | +func DeserializeResult(data []byte) (*Result, error) { |
| 121 | + serializer := graphBinarySerializer{} |
| 122 | + resp, err := serializer.deserializeMessage(data) |
| 123 | + if err != nil { |
| 124 | + return nil, err |
| 125 | + } |
| 126 | + |
| 127 | + result := &Result{ |
| 128 | + Data: resp.responseResult.data, |
| 129 | + } |
| 130 | + return result, nil |
| 131 | +} |
| 132 | + |
| 133 | +// NewResultSet creates a new ResultSet from a slice of Result objects. |
| 134 | +// This function enables custom transport implementations to create ResultSets from |
| 135 | +// results collected via alternative protocols. |
| 136 | +// |
| 137 | +// The function creates a channel-based ResultSet, pre-populates it with the provided results, |
| 138 | +// and closes the channel to indicate completion. |
| 139 | +// |
| 140 | +// Parameters: |
| 141 | +// - results: A slice of Result objects to include in the ResultSet |
| 142 | +// |
| 143 | +// Returns: |
| 144 | +// - ResultSet: A ResultSet containing all the provided results |
| 145 | +// |
| 146 | +// Example usage: |
| 147 | +// |
| 148 | +// var results []*Result |
| 149 | +// // Collect results from custom transport |
| 150 | +// for _, responseBytes := range responses { |
| 151 | +// result, _ := DeserializeResult(responseBytes) |
| 152 | +// results = append(results, result) |
| 153 | +// } |
| 154 | +// resultSet := NewResultSet(results) |
| 155 | +// allResults, _ := resultSet.All() |
| 156 | +func NewResultSet(results []*Result) ResultSet { |
| 157 | + // Create a channel-based result set with capacity for all results |
| 158 | + channelSize := len(results) |
| 159 | + if channelSize == 0 { |
| 160 | + channelSize = 1 // Ensure at least size 1 |
| 161 | + } |
| 162 | + rs := newChannelResultSetCapacity("", &synchronizedMap{make(map[string]ResultSet), sync.Mutex{}}, channelSize).(*channelResultSet) |
| 163 | + |
| 164 | + // Add all results to the channel |
| 165 | + for _, result := range results { |
| 166 | + rs.channel <- result |
| 167 | + } |
| 168 | + |
| 169 | + // Close the channel to indicate no more results |
| 170 | + rs.channelMutex.Lock() |
| 171 | + rs.closed = true |
| 172 | + close(rs.channel) |
| 173 | + rs.channelMutex.Unlock() |
| 174 | + |
| 175 | + return rs |
| 176 | +} |
0 commit comments