Post data

edit

The low level client allows you to post a string or byte[] array directly. On top of this, if you pass a collection of string or object they will be serialized using Elasticsearch’s special bulk/multi format.

Implicit Conversion

edit

Even though the argument for PostData on the low level client takes a PostData, You can rely on implicit conversion to abstract the notion of PostData for the most common two use cases:

  • A string
  • A byte[] array

Let’s demonstrate each with some assertive examples

PostData fromString = @string;
PostData fromByteArray = bytes;

fromByteArray.WrittenBytes.Should().BeSameAs(bytes); 

WrittenBytes will always be set if it originated from byte[]

The Type property is representative of the original type from which post data is constructed

fromString.Type.Should().Be(PostType.LiteralString);
fromByteArray.Type.Should().Be(PostType.ByteArray);

and passing a PostData instance to a method that accepts PostData as an argument does not wrap it again

fromString = MethodThatAcceptsPostData(fromString);
fromByteArray = MethodThatAcceptsPostData(fromByteArray);

fromString.Type.Should().Be(PostType.LiteralString);
fromByteArray.Type.Should().Be(PostType.ByteArray);

Other types of PostData

edit

You can also pass the following objects directly to the low level client.

  • A Serializable object
  • A collection of object as multi line json
  • A collection of string as multi line json

Let’s demonstrate how to use the static helper on PostData for these:

PostData fromObject = PostData.Serializable(@object);
PostData fromListOfString = PostData.MultiJson(collectionOfStrings);
PostData fromListOfObject = PostData.MultiJson(collectionOfObjects);

The Type property is representative of the original type from which post data is constructed

fromListOfString.Type.Should().Be(PostType.EnumerableOfString);
fromListOfObject.Type.Should().Be(PostType.EnumerableOfObject);
fromObject.Type.Should().Be(PostType.Serializable);

and passing a PostData instance to a method that accepts PostData as an argument does not wrap it again

fromListOfString = MethodThatAcceptsPostData(fromListOfString);
fromListOfObject = MethodThatAcceptsPostData(fromListOfObject);
fromObject = MethodThatAcceptsPostData(fromObject);

fromListOfString.Type.Should().Be(PostType.EnumerableOfString);
fromListOfObject.Type.Should().Be(PostType.EnumerableOfObject);
fromObject.Type.Should().Be(PostType.Serializable);

Each of the implicitly converted types behaves slightly differently.

For string, the UTF-8 bytes are sent in the request and the WrittenBytes property is assigned the bytes

await Post(() => @string, writes: Utf8Bytes(@string), writtenBytesIsSet: true, settings: settings);

Similarly, for byte[], the bytes are sent verbatim and the WrittenBytes property is assigned the bytes

await Post(() => bytes, writes: bytes, writtenBytesIsSet: true, settings: settings);

On platforms that support ReadOnlyMemory<byte> you can use PostData.ReadOnlyMemory to pass this directly

await Post(() => PostData.ReadOnlyMemory(bytes.AsMemory()), writes: bytes, writtenBytesIsSet: false, settings: settings);

When passing a collection of string, the client assumes that it’s a collection of valid serialized json, so joins each with newline feeds, ensuring there is a trailing linefeed. As with string and byte[], the WrittenBytes property is assigned the UTF-8 bytes of the collection of strings if DisableDirectStreaming is set on ConnectionConfiguration

await Post(() => PostData.MultiJson(collectionOfStrings), writes: utf8BytesOfListOfStrings, writtenBytesIsSet: false, settings: settings);

When passing a collection of object, the client assumes that it’s a collection of objects that needs to be serialized individually to json and joined with newline feeds. As with the collection of strings, the client ensures that there is a trailing linefeed.

await Post(() => PostData.MultiJson(collectionOfObjects), writes: utf8BytesOfCollectionOfObjects, writtenBytesIsSet: false, settings: settings);

In all other cases, Post data is serialized as is and WrittenBytes is not assigned

await Post(() => PostData.Serializable(@object), writes: utf8ObjectBytes, writtenBytesIsSet: false, settings: settings);

If you want even more control over how your data is written to the stream consider PostData.StreamHandler which allows you to inject your own writer routines

var streamHandler = PostData.StreamHandler(bytes,
    (b, s) => s.Write(b.AsSpan()),
    async (b, s, ctx) => await s.WriteAsync(b.AsMemory(), ctx)
);
await Post(() => streamHandler, writes: bytes, writtenBytesIsSet: false, settings: settings);

Forcing WrittenBytes to be set

edit

If you want to maintain a copy of the request that went out, you can set DisableDirectStreaming on ConnectionConfiguration. In doing so, the serialized bytes are first written to a private MemoryStream so that the client can get hold of the serialized bytes

settings = new ConnectionConfiguration().DisableDirectStreaming();

await Post(() => PostData.MultiJson(collectionOfObjects), writes: utf8BytesOfCollectionOfObjects, writtenBytesIsSet: true, settings: settings);

await Post(() => PostData.MultiJson(collectionOfStrings), writes: utf8BytesOfListOfStrings, writtenBytesIsSet: true, settings: settings);

await Post(() => PostData.ReadOnlyMemory(bytes.AsMemory()), writes: bytes, writtenBytesIsSet: true, settings: settings);

await Post(() => streamHandler, writes: bytes, writtenBytesIsSet: true, settings: settings);

This behavior can also be observed when serializing a simple object using DisableDirectStreaming enabled

await Post(() => PostData.Serializable(@object), writes: utf8ObjectBytes, writtenBytesIsSet: true, settings: settings);