Skip to content

Commit

Permalink
Add basic GraphBinary support
Browse files Browse the repository at this point in the history
Only RelationIdentifier and Text predicates are supported right now.
Geoshapes need to wait for JanusGraph 1.0.0 because of
JanusGraph/janusgraph#2856.

I also changed the API of the JanusGraphClientBuilder a bit to be more
independent of GraphSON and to be more in line with Gremlin.NET. It now
simply takes an `IMessageSerializer`.

GraphSON now uses JanusGraph predicates by default. So, users need to
change that on their own if they want to connect with a JanusGraph
Server < 0.6.0.
That should be OK however given that this contribution includes breaking
changes any way and it makes it easier to remove this functionality in
an upcoming version completely.

This also includes an update of Gremlin.Net to 3.5.2 as we need
TINKERPOP-2646[1] for this change.

Fixes #82

[1]: https://issues.apache.org/jira/browse/TINKERPOP-2646

Signed-off-by: Florian Hockmann <[email protected]>
  • Loading branch information
FlorianHockmann committed Feb 1, 2022
1 parent ee8038b commit 8e7a9b8
Show file tree
Hide file tree
Showing 31 changed files with 1,028 additions and 97 deletions.
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,51 @@ await g.V().Has("demigod", "name", "hercules").OutE("battled")
.Has("place", Geoshape.Point(38.1f, 23.7f)).Count().Promise(t => t.Next());
```

Only the point Geoshape is supported right now.
Only the `Point` Geoshape is supported right now.

## Version Compatibility

The lowest supported JanusGraph version is 0.3.0.
The following table shows the supported JanusGraph versions for each version
of JanusGraph.Net:

| JanusGraph.Net | JanusGraph |
|---|---|
| 0.1.z | 0.3.z |
| 0.2.z | 0.4.z, 0.5.z |
| 0.3.z | 0.4.z, 0.5.z, 0.6.z |
| JanusGraph.Net | JanusGraph |
| -------------- | ---------------------- |
| 0.1.z | 0.3.z |
| 0.2.z | 0.4.z, 0.5.z |
| 0.3.z | 0.4.z, 0.5.z, 0.6.z |
| 0.4.z | (0.4.z, 0.5.z,)* 0.6.z |

While it should also be possible to use JanusGraph.Net with other versions of
JanusGraph than mentioned here, compatibility is not tested and some
functionality (like added Gremlin steps) will not work as it is not supported
yet in case of an older JanusGraph version or was removed in a newer JanusGraph
version.

\* JanusGraph.Net 0.4 still supports older versions of JanusGraph, but the
`janusGraphPredicates` flag needs to be set to `false` in order to be able to
use JanusGraph's Text predicates.

## Serialization Formats

JanusGraph.Net supports GraphSON 3 as well as GraphBinary. GraphSON 3 is used
by default. GraphBinary can be configured like this:

```c#
var client = JanusGraphClientBuilder.BuildClientForServer(new GremlinServer("localhost", 8182))
.WithSerializer(new GraphBinaryMessageSerializer(JanusGraphTypeSerializerRegistry.Instance)).Create();
```

Note that support for GraphBinary was only added in JanusGraph 0.6.0. So, the
server needs to be at least on that version.

Not all of the JanusGraph-specific types are already supported by both formats:

| Format | RelationIdentifier | Text predicates | Geoshapes | Geo predicates |
| ----------- | ------------------ | --------------- | --------- | -------------- |
| GraphSON3 | x | x | `Point` | - |
| GraphBinary | x | x | - | - |

## Community

JanusGraph.Net uses the same communication channels as JanusGraph in general.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#region License

/*
* Copyright 2021 JanusGraph.Net Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#endregion

using System;
using Gremlin.Net.Structure.IO.GraphBinary;
using Gremlin.Net.Structure.IO.GraphBinary.Types;
using JanusGraph.Net.IO.GraphBinary.Types;

namespace JanusGraph.Net.IO.GraphBinary
{
/// <summary>
/// Provides GraphBinary serializers for different types, including JanusGraph specific types.
/// </summary>
public static class JanusGraphTypeSerializerRegistry
{
/// <summary>
/// Provides a default <see cref="TypeSerializerRegistry" /> instance with JanusGraph types already registered.
/// </summary>
public static readonly TypeSerializerRegistry Instance = Build().Create();

private static Builder Build() => new Builder();

/// <summary>
/// Builds a <see cref="TypeSerializerRegistry" /> with serializers for JanusGraph types already registered.
/// </summary>
public class Builder
{
private readonly TypeSerializerRegistry.Builder _builder = TypeSerializerRegistry.Build();

internal Builder()
{
_builder.AddCustomType(typeof(RelationIdentifier), new RelationIdentifierSerializer());
_builder.AddCustomType(typeof(JanusGraphP), new JanusGraphPSerializer());
}

/// <summary>
/// Adds a serializer for a custom type.
/// </summary>
/// <param name="type">The custom type supported by the serializer.</param>
/// <param name="serializer">The serializer for the custom type.</param>
public Builder AddCustomType(Type type, CustomTypeSerializer serializer)
{
_builder.AddCustomType(type, serializer);
return this;
}

/// <summary>
/// Creates the <see cref="TypeSerializerRegistry"/>.
/// </summary>
public TypeSerializerRegistry Create() => _builder.Create();
}
}
}
55 changes: 55 additions & 0 deletions src/JanusGraph.Net/IO/GraphBinary/Types/GraphBinaryType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#region License

/*
* Copyright 2021 JanusGraph.Net Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#endregion

namespace JanusGraph.Net.IO.GraphBinary.Types
{
/// <summary>
/// Represents a JanusGraph GraphBinary type with its type information needed for serialization.
/// </summary>
public class GraphBinaryType
{
internal static readonly GraphBinaryType RelationIdentifier =
new GraphBinaryType(0x1001, "janusgraph.RelationIdentifier");

internal static readonly GraphBinaryType JanusGraphP =
new GraphBinaryType(0x1002, "janusgraph.P");

/// <summary>
/// Initializes a new instance of the <see cref="GraphBinaryType" /> class.
/// </summary>
/// <param name="typeId">The JanusGraph internal id of the type.</param>
/// <param name="typeName">The name of the type.</param>
public GraphBinaryType(int typeId, string typeName)
{
TypeId = typeId;
TypeName = typeName;
}

/// <summary>
/// Gets the JanusGraph internal id of the type.
/// </summary>
public int TypeId { get; }

/// <summary>
/// Gets the name of the type.
/// </summary>
public string TypeName { get; }
}
}
47 changes: 47 additions & 0 deletions src/JanusGraph.Net/IO/GraphBinary/Types/JanusGraphPSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#region License

/*
* Copyright 2021 JanusGraph.Net Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#endregion

using System.IO;
using System.Threading.Tasks;
using Gremlin.Net.Structure.IO.GraphBinary;

namespace JanusGraph.Net.IO.GraphBinary.Types
{
internal class JanusGraphPSerializer : JanusGraphTypeSerializer<JanusGraphP>
{
public JanusGraphPSerializer() : base(GraphBinaryType.JanusGraphP)
{
}

protected override async Task WriteNonNullableValueAsync(JanusGraphP value, Stream stream,
GraphBinaryWriter writer)
{
await writer.WriteValueAsync(value.OperatorName, stream, false).ConfigureAwait(false);
await writer.WriteAsync(value.Value, stream).ConfigureAwait(false);
}

protected override async Task<JanusGraphP> ReadNonNullableValueAsync(Stream stream, GraphBinaryReader reader)
{
var operatorName = (string)await reader.ReadValueAsync<string>(stream, false).ConfigureAwait(false);
var value = await reader.ReadAsync(stream).ConfigureAwait(false);
return new JanusGraphP(operatorName, value);
}
}
}
123 changes: 123 additions & 0 deletions src/JanusGraph.Net/IO/GraphBinary/Types/JanusGraphTypeSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#region License

/*
* Copyright 2021 JanusGraph.Net Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#endregion

using System.IO;
using System.Threading.Tasks;
using Gremlin.Net.Structure.IO.GraphBinary;
using Gremlin.Net.Structure.IO.GraphBinary.Types;

namespace JanusGraph.Net.IO.GraphBinary.Types
{
/// <summary>
/// Base class for GraphBinary serializers of JanusGraph types.
/// </summary>
/// <typeparam name="T">The JanusGraph type to be serialized.</typeparam>
public abstract class JanusGraphTypeSerializer<T> : CustomTypeSerializer
{
private readonly GraphBinaryType _type;

/// <summary>
/// Initializes a new instance of the <see cref="JanusGraphTypeSerializer{T}" /> class.
/// </summary>
/// <param name="type">The GraphBinaryType for this serializer.</param>
protected JanusGraphTypeSerializer(GraphBinaryType type)
{
_type = type;
}

/// <inheritdoc />
public override async Task WriteAsync(object value, Stream stream, GraphBinaryWriter writer)
{
await stream.WriteIntAsync(_type.TypeId).ConfigureAwait(false);

await WriteValueAsync(value, stream, writer, true).ConfigureAwait(false);
}

/// <inheritdoc />
public override async Task WriteValueAsync(object value, Stream stream, GraphBinaryWriter writer, bool nullable)
{
if (value == null)
{
if (!nullable)
{
throw new IOException("Unexpected null value when nullable is false");
}

await writer.WriteValueFlagNullAsync(stream).ConfigureAwait(false);
return;
}

if (nullable)
{
await writer.WriteValueFlagNoneAsync(stream).ConfigureAwait(false);
}

await WriteNonNullableValueAsync((T) value, stream, writer).ConfigureAwait(false);
}

/// <summary>
/// Writes a non-nullable value without including type information.
/// </summary>
/// <param name="value">The value to write.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="writer">A <see cref="GraphBinaryWriter"/> that can be used to write nested values.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
protected abstract Task WriteNonNullableValueAsync(T value, Stream stream, GraphBinaryWriter writer);

/// <inheritdoc />
public override async Task<object> ReadAsync(Stream stream, GraphBinaryReader reader)
{
var customTypeInfo = await stream.ReadIntAsync().ConfigureAwait(false);
if (customTypeInfo != _type.TypeId)
{
throw new IOException(
$"Custom type info {customTypeInfo} doesn't match expected type info {_type.TypeId}");
}

return await ReadValueAsync(stream, reader, true).ConfigureAwait(false);
}

/// <inheritdoc />
public override async Task<object> ReadValueAsync(Stream stream, GraphBinaryReader reader, bool nullable)
{
if (nullable)
{
var valueFlag = await stream.ReadByteAsync().ConfigureAwait(false);
if ((valueFlag & 1) == 1)
{
return null;
}
}

return await ReadNonNullableValueAsync(stream, reader).ConfigureAwait(false);
}

/// <summary>
/// Reads a non-nullable value from the stream.
/// </summary>
/// <param name="stream">The GraphBinary data to parse.</param>
/// <param name="reader">A <see cref="GraphBinaryReader"/> that can be used to read nested values.</param>
/// <returns>The read value.</returns>
protected abstract Task<T> ReadNonNullableValueAsync(Stream stream, GraphBinaryReader reader);

/// <inheritdoc />
public override string TypeName => _type.TypeName;
}
}
Loading

0 comments on commit 8e7a9b8

Please sign in to comment.