From 6e5079a74584134b1bed7518e6eb9b17f82617cc Mon Sep 17 00:00:00 2001 From: Han Xiao Date: Wed, 5 Jan 2022 19:24:27 +0100 Subject: [PATCH] docs(array): add doc for array serialization --- docarray/array/mixins/io/binary.py | 16 +- docs/fundamentals/document/serialization.md | 72 +++--- docs/fundamentals/documentarray/construct.md | 54 +++- .../documentarray/images/benchmark-size.svg | 1 + .../documentarray/images/benchmark-time.svg | 1 + .../documentarray/images/da-push.png | Bin 0 -> 32235 bytes .../documentarray/serialization.md | 236 +++++++++++++++++- tests/unit/array/test_from_to_bytes.py | 4 +- 8 files changed, 339 insertions(+), 45 deletions(-) create mode 100644 docs/fundamentals/documentarray/images/benchmark-size.svg create mode 100644 docs/fundamentals/documentarray/images/benchmark-time.svg create mode 100644 docs/fundamentals/documentarray/images/da-push.png diff --git a/docarray/array/mixins/io/binary.py b/docarray/array/mixins/io/binary.py index 316e66fabdf..8cf760de65a 100644 --- a/docarray/array/mixins/io/binary.py +++ b/docarray/array/mixins/io/binary.py @@ -18,7 +18,7 @@ class BinaryIOMixin: def load_binary( cls: Type['T'], file: Union[str, BinaryIO, bytes], - protocol: str = 'pickle-once', + protocol: str = 'pickle-array', compress: Optional[str] = None, ) -> 'T': """Load array elements from a LZ4-compressed binary file. @@ -45,14 +45,14 @@ def load_binary( d = decompress_bytes(d, algorithm=compress) compress = None - if protocol == 'protobuf-once': + if protocol == 'protobuf-array': from ....proto.docarray_pb2 import DocumentArrayProto dap = DocumentArrayProto() dap.ParseFromString(d) return cls.from_protobuf(dap) - elif protocol == 'pickle-once': + elif protocol == 'pickle-array': return pickle.loads(d) else: _len = len(random_uuid().bytes) @@ -66,7 +66,7 @@ def load_binary( def from_bytes( cls: Type['T'], data: bytes, - protocol: str = 'pickle-once', + protocol: str = 'pickle-array', compress: Optional[str] = None, ) -> 'T': return cls.load_binary(data, protocol=protocol, compress=compress) @@ -74,7 +74,7 @@ def from_bytes( def save_binary( self, file: Union[str, BinaryIO], - protocol: str = 'pickle-once', + protocol: str = 'pickle-array', compress: Optional[str] = None, ) -> None: """Save array elements into a LZ4 compressed binary file. @@ -98,7 +98,7 @@ def save_binary( def to_bytes( self, - protocol: str = 'pickle-once', + protocol: str = 'pickle-array', compress: Optional[str] = None, _file_ctx: Optional[BinaryIO] = None, ) -> bytes: @@ -121,9 +121,9 @@ def to_bytes( fc = f compress = None with fc: - if protocol == 'protobuf-once': + if protocol == 'protobuf-array': f.write(self.to_protobuf().SerializePartialToString()) - elif protocol == 'pickle-once': + elif protocol == 'pickle-array': f.write(pickle.dumps(self)) else: for d in self: diff --git a/docs/fundamentals/document/serialization.md b/docs/fundamentals/document/serialization.md index 7982da094b6..16ee8c38513 100644 --- a/docs/fundamentals/document/serialization.md +++ b/docs/fundamentals/document/serialization.md @@ -4,7 +4,7 @@ DocArray is designed to be "ready-to-wire": it assumes you always want to send/receive Document over network across microservices. Hence, serialization of Document is important. This chapter introduces multiple serialization methods of a single Document. ```{tip} -One should use DocumentArray for serializing multiple Documents, instead of looping over Documents one by one. The former is much faster and yield more compact serialization. +One should use {ref}`DocumentArray for serializing multiple Documents`, instead of looping over Documents one by one. The former is much faster and yield more compact serialization. ``` @@ -47,38 +47,7 @@ print(d_as_json, d) ``` - -## From/to dict - -```{important} -This feature requires `protobuf` dependency. You can do `pip install docarray[full]` to install it. -``` - -You can serialize a Document as a Python `dict` via {meth}`~docarray.document.mixins.porting.PortingMixin.to_dict`, and then read from it via {meth}`~docarray.document.mixins.porting.PortingMixin.from_dict`. - -```python -from docarray import Document -import numpy as np - -d_as_dict = Document(text='hello, world', embedding=np.array([1, 2, 3])).to_dict() - -d = Document.from_dict(d_as_dict) - -print(d_as_dict, d) -``` - -```text -{'id': 'b29d39066d5611ec87661e008a366d49', 'text': 'hello, world', 'mime_type': 'text/plain', 'embedding': {'dense': {'buffer': 'AQAAAAAAAAACAAAAAAAAAAMAAAAAAAAA', 'shape': [3], 'dtype': ' -``` - -```{note} -Note that the result dict is very "stricted" in the sense that all fields and values boil down to very basic data type such as `int`, `float`, `string`. This behavior is designed due to the "serialization to `dict`" is often an intermediate step of serializing into JSON/YAML. Hence all values in `dict` must be schema-friendly. After all, a Python `dict` object means nothing if you are not working in Python. - -You can use `to_dict(strict=False)` to override this behavior. This will preserve the original Python data type of every value, which may not be JSON-friendly. But hey, you want it. -``` - +(doc-in-bytes)= ## From/to bytes ```{important} @@ -127,6 +96,43 @@ Note that when deserializing from a non-default binary serialization, you need t d = Document.from_bytes(d_bytes, protocol='protobuf', compress='gzip') ``` +```{tip} +If you go with default `protcol` and `compress` settings, you can simply use `bytes(d)`, which is more Pythonic. +``` + + +## From/to dict + +```{important} +This feature requires `protobuf` dependency. You can do `pip install docarray[full]` to install it. +``` + +You can serialize a Document as a Python `dict` via {meth}`~docarray.document.mixins.porting.PortingMixin.to_dict`, and then read from it via {meth}`~docarray.document.mixins.porting.PortingMixin.from_dict`. + +```python +from docarray import Document +import numpy as np + +d_as_dict = Document(text='hello, world', embedding=np.array([1, 2, 3])).to_dict() + +d = Document.from_dict(d_as_dict) + +print(d_as_dict, d) +``` + +```text +{'id': 'b29d39066d5611ec87661e008a366d49', 'text': 'hello, world', 'mime_type': 'text/plain', 'embedding': {'dense': {'buffer': 'AQAAAAAAAAACAAAAAAAAAAMAAAAAAAAA', 'shape': [3], 'dtype': ' +``` + +(strict-arg-explain)= +```{note} +Note that the result dict is very "stricted" in the sense that all fields and values boil down to very basic data type such as `int`, `float`, `string`. This behavior is designed due to the "serialization to `dict`" is often an intermediate step of serializing into JSON/YAML. Hence all values in `dict` must be schema-friendly. After all, a Python `dict` object means nothing if you are not working in Python. + +You can use `to_dict(strict=False)` to override this behavior. This will preserve the original Python data type of every value, which may not be JSON-friendly. But hey, you want it. +``` + ## From/to Protobuf ```{important} diff --git a/docs/fundamentals/documentarray/construct.md b/docs/fundamentals/documentarray/construct.md index f14156584c1..dfca91b7e90 100644 --- a/docs/fundamentals/documentarray/construct.md +++ b/docs/fundamentals/documentarray/construct.md @@ -6,11 +6,62 @@ ```python from docarray import DocumentArray +da = DocumentArray() +``` + +```text + +``` + +Now you can use list-like interfaces such as `.append()` and `.extend()` as you would add elements to a Python List. + +```python +da.append(Document(text='hello world!')) +da.extend([Document(text='hello'), Document(text='world!')]) +``` + +```text + +``` + +Directly printing a DocumentArray does not show you too much useful information, you can use {meth}`~docarray.array.mixins.plot.PlotMixin.summary`. + +```{important} +This feature requires `rich` dependency. You can do `pip install docarray[full]` to install it. +``` + +```python +da.summary() +``` + +```text + Documents Summary + + Length 3 + Homogenous Documents True + Common Attributes ('id', 'mime_type', 'text') + + Attributes Summary + + Attribute Data type #Unique values Has empty value + ────────────────────────────────────────────────────────── + id ('str',) 3 False + mime_type ('str',) 1 False + text ('str',) 3 False +``` + +## Construct with empty Documents + +Like `numpy.zeros()`, you can quickly build a DocumentArray with only empty Documents: + +```python +from docarray import DocumentArray + da = DocumentArray.empty(10) ``` ```text - + ``` ## Construct from list-like objects @@ -41,6 +92,7 @@ da = DocumentArray((Document() for _ in range(10))) ``` ```` + As DocumentArray itself is also a "list-like object that yields `Document`", you can also construct DocumentArray from another DocumentArray: ```python diff --git a/docs/fundamentals/documentarray/images/benchmark-size.svg b/docs/fundamentals/documentarray/images/benchmark-size.svg new file mode 100644 index 00000000000..4031944ff9d --- /dev/null +++ b/docs/fundamentals/documentarray/images/benchmark-size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/fundamentals/documentarray/images/benchmark-time.svg b/docs/fundamentals/documentarray/images/benchmark-time.svg new file mode 100644 index 00000000000..129b4a982bf --- /dev/null +++ b/docs/fundamentals/documentarray/images/benchmark-time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/fundamentals/documentarray/images/da-push.png b/docs/fundamentals/documentarray/images/da-push.png new file mode 100644 index 0000000000000000000000000000000000000000..82e564faa7af11c7c19eaa996129545a5295ac27 GIT binary patch literal 32235 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

@v9&9B3!|1!b`K!An2{_}29pj?O!S${z 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 literal 0 HcmV?d00001 diff --git a/docs/fundamentals/documentarray/serialization.md b/docs/fundamentals/documentarray/serialization.md index dce862a5b81..0ac446c6208 100644 --- a/docs/fundamentals/documentarray/serialization.md +++ b/docs/fundamentals/documentarray/serialization.md @@ -1,13 +1,247 @@ +(docarray-serialization)= # Serialization DocArray is designed to be "ready-to-wire" at anytime. Serialization is important. DocumentArray provides multiple serialization methods that allows one transfer DocumentArray object over network and across different microservices. +- JSON string: `.from_json()`/`.to_json()` +- Bytes (compressed): `.from_bytes()`/`.to_bytes()` +- Protobuf Message: `.from_protobuf()`/`.to_protobuf()` +- Python List: `.from_list()`/`.to_list()` +- Pandas Dataframe: `.from_dataframe()`/`.to_dataframe()` +- Cloud: `.push()`/`.pull()` + ## From/to JSON +```{important} +This feature requires `protobuf` dependency. You can do `pip install docarray[full]` to install it. +``` + +```python +from docarray import DocumentArray, Document + +da = DocumentArray([Document(text='hello'), Document(text='world')]) +da.to_json() +``` + +```text +[{"id": "72db9a7e6e3211ec97f51e008a366d49", "text": "hello", "mime_type": "text/plain"}, {"id": "72db9cb86e3211ec97f51e008a366d49", "text": "world", "mime_type": "text/plain"}] +``` + + +```python +da_r = DocumentArray.from_json(da.to_json()) + +da_r.summary() +``` + +```text + Documents Summary + + Length 2 + Homogenous Documents True + Common Attributes ('id', 'mime_type', 'text') + + Attributes Summary + + Attribute Data type #Unique values Has empty value + ────────────────────────────────────────────────────────── + id ('str',) 2 False + mime_type ('str',) 1 False + text ('str',) 2 False + +``` + + ## From/to bytes +```{important} +Depending on your values of `protocol` and `compress` arguments, this feature may require `protobuf` and `lz4` dependencies. You can do `pip install docarray[full]` to install it. +``` + +Serialization into bytes often yield more compact representation than in JSON. Similar to {ref}`the Document serialization`, DocumentArray can be serialized with different `protocol` and `compress` combinations. In its most simple form, + +```python +from docarray import DocumentArray, Document + +da = DocumentArray([Document(text='hello'), Document(text='world')]) +da.to_bytes() +``` + +```text +b'\x80\x03cdocarray.array.document\nDocumentArray\nq\x00)\x81q\x01}q\x02(X\x05\x00\x00\x00_dataq\x03]q\x04(cdocarray.document\nDocument\nq\x05) ... +``` + +```python +da_r = DocumentArray.from_bytes(da.to_bytes()) + +da_r.summary() +``` + +```text + Documents Summary + + Length 2 + Homogenous Documents True + Common Attributes ('id', 'mime_type', 'text') + + Attributes Summary + + Attribute Data type #Unique values Has empty value + ────────────────────────────────────────────────────────── + id ('str',) 2 False + mime_type ('str',) 1 False + text ('str',) 2 False +``` + +```{tip} +If you go with default `protcol` and `compress` settings, you can simply use `bytes(da)`, which is more Pythonic. +``` + +The table below summarize the supported serialization protocols and compressions: + +| `protocol=...` | Description | Remarks | +|--------------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| +| `pickle-array` (default) | Serialize the whole array in one-shot using Python `pickle` | Often fastest. Not portable to other languages. Insecure in production. | +| `protobuf-array` | Serialize the whole array using [`DocumentArrayProto`](../../../proto/#docarray.DocumentArrayProto). | Portable to other languages if they implement `DocumentArrayProto`. 2GB max-size (pre-compression) restriction by Protobuf. | +| `pickle` | Serialize elements one-by-one using Python `pickle`. | Allow streaming. Not portable to other languages. Insecure in production. | +| `protobuf` | Serialize elements one-by-one using [`DocumentProto`](../../../proto/#docarray.DocumentProto). | Allow streaming. Portable to other languages if they implement `DocumentProto`. No max-size restriction | + +For compressions, the following algorithms are supported: `lz4`, `bz2`, `lzma`, `zlib`, `gzip`. The most frequently used ones are `lz4` (fastest) and `gzip` (most widely used). + +If you specified non-default `protocol` and `compress` in {meth}`~docarray.array.mixins.io.binary.BinaryIOMixin.to_bytes`, you will need to specify the same in {meth}`~docarray.array.mixins.io.binary.BinaryIOMixin.from_bytes`. + +Depending on the use cases, you can choose the one works best for you. Here is a benchmark on serializing a DocumentArray with one million near-empty Documents (i.e. init with `DocumentArray.empty(...)` where each Document has only `id`). + +```{figure} images/benchmark-size.svg +``` + +```{figure} images/benchmark-time.svg +``` + +The benchmark was conducted [on the codebase of Jan. 5, 2022](https://github.com/jina-ai/docarray/tree/a56067e486d2318e05bcf6088bd1436040107ad2). + +Depending on how you want to interpret the results, the figures above can be an over-estimation/under-estimation of the serialization latency: one may argue that near-empty Documents are not realistic, but serializing a DocumentArray with one million Documents is also unreal. In practice, DocumentArray passing across microservices are relatively small, say at thousands, for better overlapping the network latency and computational overhead. + + +### Wire format of `pickle` and `protobuf` + +When set `protocol=pickle` or `protobuf`, the result binary string looks like the following: + +```text +----------------------------------------------------------------------------------- +| Delimiter | doc1.to_bytes() | Delimiter | doc2.to_bytes() | Delimiter | ... +----------------------------------------------------------------------------------- + | | + | | + | | + Fixed-length | + | + Variable-length +``` + +Here `Delimiter` is a 16-bytes separator such as `b'g\x81\xcc\x1c\x0f\x93L\xed\xa2\xb0s)\x9c\xf9\xf6\xf2'` used for setting the boundary of each Document's serialization. Given a `to_bytes(protocol='pickle/protobuf')` binary string, once we know the first 16 bytes, the boundary is clear. Consequently, one can leverage this format to stream Documents, drop, skip, or early-stop, etc. + ## From/to Protobuf +Serializing to Protobuf Message is less frequently used, unless you are using Python Protobuf API. Nonetheless, you can use {meth}`~docarray.array.mixins.io.binary.BinaryIOMixin.from_protobuf` and {meth}`~docarray.array.mixins.io.binary.BinaryIOMixin.to_protobuf` to get a Protobuf Message object in Python. + +```python +from docarray import DocumentArray, Document + +da = DocumentArray([Document(text='hello'), Document(text='world')]) +da.to_bytes() +``` + +```text +docs { + id: "2571b8b66e4d11ec9f271e008a366d49" + text: "hello" + mime_type: "text/plain" +} +docs { + id: "2571ba466e4d11ec9f271e008a366d49" + text: "world" + mime_type: "text/plain" +} +``` + ## From/to list -## From/to dataframe \ No newline at end of file +Serializing to/from Python list is less frequently used for the same reason as `Document.to_dict()`: it is often an intermediate step of serializing to JSON. You can do: + +```python +from docarray import DocumentArray, Document + +da = DocumentArray([Document(text='hello'), Document(text='world')]) +da.to_list() +``` + +```text +[{'id': 'ae55782a6e4d11ec803c1e008a366d49', 'text': 'hello', 'mime_type': 'text/plain'}, {'id': 'ae557a146e4d11ec803c1e008a366d49', 'text': 'world', 'mime_type': 'text/plain'}] +``` + +There is an argument `strict` shares {ref}`the same semantic` as in `Document.to_dict()`. + +## From/to dataframe + +```{important} +This feature requires `pandas` dependency. You can do `pip install docarray[full]` to install it. +``` + +One can convert between a DocumentArray object and a `pandas.dataframe` object. + +```python +from docarray import DocumentArray, Document + +da = DocumentArray([Document(text='hello'), Document(text='world')]) +da.to_dataframe() +``` + +```text + id text mime_type +0 43cb93b26e4e11ec8b731e008a366d49 hello text/plain +1 43cb95746e4e11ec8b731e008a366d49 world text/plain +``` + +To build a DocumentArray from dataframe, + +```python +df = ... +da = DocumentArray.from_dataframe(df) +``` + +## From/to cloud + +```{important} +This feature requires `rich` and `requests` dependency. You can do `pip install docarray[full]` to install it. +``` + +{meth}`~docarray.array.mixins.io.pushpull.PushPullMixin.push` and {meth}`~docarray.array.mixins.io.pushpull.PushPullMixin.pull` allows you to share a DocumentArray object across machines. + +Considering you are working on a GPU machine via Google Colab/Jupyter. After preprocessing and embedding, you got everything you need in a DocumentArray. You can easily transfer it to the local laptop via: + +```python +from docarray import DocumentArray + +da = DocumentArray(...) # heavylifting, processing, GPU task, ... +da.push(token='myda123') +``` + +```{figure} images/da-push.png +``` + +Then on your local laptop, simply + +```python +from docarray import DocumentArray + +da = DocumentArray.pull(token='myda123') +``` + +Now you can continue the work at local, analyzing `da` or visualizing it. Your friends & colleagues who know the token `myda123` can also pull that DocumentArray. It's useful when you want to quickly share the results with your colleagues & friends. + +For more information of this feature, please refer to {class}`~jina.types.arrays.mixins.io.pushpull.PushPullMixin`. + +```{danger} +The lifetime of the storage is not promised at the momennt: could be a day, could be a week. Do not use it for persistence in production. Only consider this as temporary transmission or a clipboard. +``` \ No newline at end of file diff --git a/tests/unit/array/test_from_to_bytes.py b/tests/unit/array/test_from_to_bytes.py index b1599cc3a45..f64eec6f30a 100644 --- a/tests/unit/array/test_from_to_bytes.py +++ b/tests/unit/array/test_from_to_bytes.py @@ -28,7 +28,7 @@ def get_ndarrays_for_ravel(): @pytest.mark.parametrize('ndarray_val, is_sparse', get_ndarrays_for_ravel()) @pytest.mark.parametrize('target_da', [DocumentArray.empty(100), random_docs(100)]) @pytest.mark.parametrize( - 'protocol', ['protobuf', 'protobuf-once', 'pickle', 'pickle-once'] + 'protocol', ['protobuf', 'protobuf-array', 'pickle', 'pickle-array'] ) @pytest.mark.parametrize('compress', ['lz4', 'bz2', 'lzma', 'zlib', 'gzip', None]) def test_to_from_bytes(target_da, protocol, compress, ndarray_val, is_sparse): @@ -54,7 +54,7 @@ def test_to_from_bytes(target_da, protocol, compress, ndarray_val, is_sparse): @pytest.mark.parametrize('target_da', [DocumentArray.empty(100), random_docs(100)]) @pytest.mark.parametrize( - 'protocol', ['protobuf', 'protobuf-once', 'pickle', 'pickle-once'] + 'protocol', ['protobuf', 'protobuf-array', 'pickle', 'pickle-array'] ) @pytest.mark.parametrize('compress', ['lz4', 'bz2', 'lzma', 'zlib', 'gzip', None]) def test_save_bytes(target_da, protocol, compress, tmpfile):