{"id":17330538,"url":"https://github.com/sewenew/redis-protobuf","last_synced_at":"2025-08-20T14:31:52.866Z","repository":{"id":37395550,"uuid":"185817279","full_name":"sewenew/redis-protobuf","owner":"sewenew","description":"Redis module for reading and writing Protobuf messages","archived":false,"fork":false,"pushed_at":"2022-06-13T14:05:00.000Z","size":599,"stargazers_count":204,"open_issues_count":9,"forks_count":22,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-12-10T02:10:38.808Z","etag":null,"topics":["protobuf","redis","redis-module","redis-protobuf"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sewenew.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-09T14:38:30.000Z","updated_at":"2024-11-22T12:16:34.000Z","dependencies_parsed_at":"2022-08-08T20:15:25.231Z","dependency_job_id":null,"html_url":"https://github.com/sewenew/redis-protobuf","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sewenew%2Fredis-protobuf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sewenew%2Fredis-protobuf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sewenew%2Fredis-protobuf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sewenew%2Fredis-protobuf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sewenew","download_url":"https://codeload.github.com/sewenew/redis-protobuf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230431103,"owners_count":18224655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["protobuf","redis","redis-module","redis-protobuf"],"created_at":"2024-10-15T14:51:33.677Z","updated_at":"2024-12-19T12:10:33.350Z","avatar_url":"https://github.com/sewenew.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"# redis-protobuf\n\n[中文交流群](http://github.com/sewenew/redis-protobuf/blob/master/Chinese.md)\n\n- [Overview](#overview)\n    - [Motivation](#motivation)\n- [Installation](#installation)\n    - [Run redis-protobuf With Docker](#run-redis-protobuf-with-docker)\n    - [Install redis-protobuf With Source Code](#install-redis-protobuf-with-source-code)\n    - [Load redis-protobuf](#load-redis-protobuf)\n- [Getting Started](#getting-started)\n    - [redis-cli](#redis-cli)\n    - [C++ Client](#c-client)\n    - [Python Client](#python-client)\n- [Commands](#commands)\n    - [Path](#path)\n    - [PB.SET](#pbset)\n    - [PB.GET](#pbget)\n    - [PB.DEL](#pbdel)\n    - [PB.APPEND](#pbappend)\n    - [PB.LEN](#pblen)\n    - [PB.CLEAR](#pbclear)\n    - [PB.MERGE](#pbmerge)\n    - [PB.TYPE](#pbtype)\n    - [PB.SCHEMA](#pbschema)\n- [Author](#author)\n\n## Overview\n\nThis is a [Redis Module](https://redis.io/topics/modules-intro) for reading and writing protobuf messages (only support Protobuf version 3, i.e. `syntax=\"proto3\";`).\n\n**NOTE**: In order to use this module, you should read the [Protobuf doc](https://developers.google.com/protocol-buffers) to learn how to define a protobuf message.\n\n**NOTE**: I'm not a native speaker. So if the documentation is unclear, please feel free to open an issue or pull request. I'll response ASAP.\n\n### Motivation\n\nRedis supports multiple data structures, such as string, list, and hash. In order to keep things simple, Redis doesn't support nested data structures, e.g. you cannot have a list whose element is a list. However, sometimes nested data structures are useful, and [RedisJSON](https://github.com/RedisJSON/RedisJSON) is an option. With *RedisJSON*, you can save JSON object into Redis, and read and write JSON fields with that module. Since JSON can be nested, you can define your own nested data structure.\n\nIn fact, *redis-protobuf* is inspired by *RedisJSON*.\n\nBoth JSON and Protobuf are methods for serializing structured data. You can find many articles comparing these methods. Basically, Protobuf is faster and smaller than JSON, while JSON is more human friendly, since the former is binary while the latter is text. Also, you can convert a Protobuf to JSON and vice versa.\n\nIn order to make the nested data structure fast and memory efficient, I wrote this *redis-protobuf* module. With this module, you can define a Protobuf message, and *redis-protobuf* will use reflection to read and write message fields.\n\n## Installation\n\n### Run redis-protobuf With Docker\n\nRun the following command to start *redis-protobuf* with Docker.\n\n```\ndocker run -p 6379:6379 sewenew/redis-protobuf:latest\n```\n\nIn this case, Docker runs Redis with a *redis.conf* file located at */usr/lib/redis/conf/redis.conf*. Also, you should put your *.proto* files in */usr/lib/redis/proto* directory. However, by default, the Docker image ships with an [example.proto](https://github.com/sewenew/redis-protobuf/blob/master/docker/example.proto) file, so that you can run the following examples without creating extra *.proto* files.\n\nAfter running the Docker image, you can go to the [Getting Started section](#getting-started) to see how to run *redis-protobuf* commands.\n\n### Install redis-protobuf With Source Code\n\nYou can also install redis-protobuf with source code.\n\n#### Install Protobuf\n\nFirst of all, you need to install Protobuf-C++. However, [the offical release](https://github.com/protocolbuffers/protobuf) doesn't expose reflection API for fields of map type (see [this issue](https://github.com/protocolbuffers/protobuf/issues/1322) for detail). So I modified the code to expose some internal interfaces. I published the modified code with *redis-protobuf* release, and you can download it from [here](https://github.com/sewenew/redis-protobuf/releases/download/0.0.1/protobuf-3.8.0-map-reflection.tar.gz). Also, you can check the *CHANGES-BY-SEWENEW.md* file for detail on what I've modifed.\n\nInstall modified Protobuf with the following commands:\n\n```\ncurl -L -k https://github.com/sewenew/redis-protobuf/releases/download/0.0.1/protobuf-3.8.0-map-reflection.tar.gz -o protobuf-3.8.0-map-reflection.tar.gz\n\ntar xfz protobuf-3.8.0-map-reflection.tar.gz\n\ncd protobuf-3.8.0-map-reflection\n\n./configure \"CFLAGS=-fPIC\" \"CXXFLAGS=-fPIC\"\n\nmake -j 4\n\nmake install\n```\n\nIf you want to install Protobuf at a non-default location, you can specify the `--prefix=/path/to/install/location` option.\n\n```\n./configure \"CFLAGS=-fPIC\" \"CXXFLAGS=-fPIC\" --prefix=/usr\n```\n\n**NOTE**: You must specify the `\"CFLAGS=-fPIC\" \"CXXFLAGS=-fPIC\"` options when compiling Protobuf.\n\n#### Install redis-protobuf\n\n*redis-protobuf* is built with [CMAKE](https://cmake.org).\n\n```\ngit clone https://github.com/sewenew/redis-protobuf.git\n\ncd redis-protobuf\n\nmkdir compile\n\ncd compile\n\ncmake ..\n\nmake\n```\n\nIf Protobuf is installed at non-default location, you should use `CMAKE_PREFIX_PATH` to specify the installation path of Protobuf.\n\n```\ncmake -DCMAKE_PREFIX_PATH=/path/to/Protobuf ..\n```\n\nWhen `make` is done, you should find *libredis-protobuf.so* (or *libredis-protobuf.dylib* on MacOS) under the *redis-protobuf/compile* directory.\n\n### Load redis-protobuf\n\nRedis Module is supported since Redis 4.0, so you must install Redis 4.0 or above.\n\nIn order to load *redis-protobuf*, you need to modify the *redis.conf* file to add the `loadmodule` directive:\n\n```\nloadmodule /path/to/libredis-protobuf.so --dir proto-directory\n```\n\n*proto-directory* is the directory where your *.proto* files located. You must ensure that the directory exists and put your *.proto* files in this directory, so that *redis-protobuf* can load these *.proto* files dynamically. Also, if you want to use the standard *.proto* files, such as *google/protobuf/timestamp.proto*, *google/protobuf/struct.proto*, you also need to copy [these](https://github.com/sewenew/redis-protobuf/tree/master/docker/google/protobuf) standard proto files to *proto-directory*. Check [this](https://github.com/sewenew/redis-protobuf/issues/13) and [this](https://github.com/sewenew/redis-protobuf/issues/14) issues for detail.\n\nNow, you can start your Redis instance:\n\n```\nredis-server /path/to/redis.conf\n```\n\nIf Redis loads the module successfully, you can get the following message from the log:\n\n```\nModule 'PB' loaded from /path/to/libredis-protobuf.so\n```\n\n**NOTE**: If any of the given *.proto* file is invalid, Redis fails to load the module.\n\n#### Possible Errors\n\nIf Redis fails to load *redis-protobuf*, and print the following error message:\n\n```\nundefined symbol: deflateInit2_\n```\n\nYou can try [this solution](https://github.com/sewenew/redis-protobuf/issues/16).\n\n#### redis-protobuf Options\n\n## Getting Started\n\nAfter [loading the module](#load-redis-protobuf), you can use any Redis client to send *redis-protobuf* [commands](#Commands).\n\nWe'll use the following *.proto* file as example, unless otherwise stated. In order to test examples in this doc, you need to put the following *.proto* file in the *proto-directory*.\n\n**NOTE**: The [Docker image](https://cloud.docker.com/repository/docker/sewenew/redis-protobuf) also ships with this *.proto* file. So you can run the following examples with Docker too.\n\n```\nsyntax = \"proto3\";\n\nmessage SubMsg {\n    string s = 1;\n    int32 i = 2;\n}\n\nmessage Msg {\n    int32 i = 1;\n    SubMsg sub = 2;\n    repeated int32 arr = 3;\n}\n```\n\nAs we mentioned before, *redis-protobuf* only supports Protobuf version 3. So you must put `syntax = \"proto3\";` at the beginning of your *.proto* file.\n\n### redis-cli\n\nThe following examples use the offical Redis client, i.e. *redis-cli*, to send *redis-protobuf* commands.\n\nList module info:\n\n```\n127.0.0.1:6379\u003e MODULE LIST\n1) 1) \"name\"\n   2) \"PB\"\n   3) \"ver\"\n   4) (integer) 1\n```\n\nSet message:\n\n```\n127.0.0.1:6379\u003e PB.SET key Msg '{\"i\" : 1, \"sub\" : {\"s\" : \"string\", \"i\" : 2}, \"arr\" : [1, 2, 3]}'\n(integer) 1\n```\n\n**NOTE**: As we mentioned before, Protobuf is not human friendly. So *redis-protobuf* also supports setting JSON string as value, and the module will convert the JSON string to Protobuf message automatically. Check the [C++ client section](c-client) to see an example of setting binary string as value.\n\nGet message:\n\n```\n127.0.0.1:6379\u003e PB.GET key --FORMAT JSON Msg\n\"{\\\"i\\\":1,\\\"sub\\\":{\\\"s\\\":\\\"string\\\",\\\"i\\\":2},\\\"arr\\\":[1,2,3]}\"\n```\n\nSet fields:\n\n```\n127.0.0.1:6379\u003e PB.SET key Msg /i 10\n(integer) 1\n127.0.0.1:6379\u003e PB.SET key Msg /sub/s redis-protobuf\n(integer) 1\n127.0.0.1:6379\u003e PB.SET key Msg /arr/0 2\n(integer) 1\n```\n\nGet fields:\n\n```\n127.0.0.1:6379\u003e PB.GET key Msg /i\n(integer) 10\n127.0.0.1:6379\u003e PB.GET key Msg /sub/s\n\"redis-protobuf\"\n127.0.0.1:6379\u003e PB.GET key Msg /arr/0\n(integer) 2\n127.0.0.1:6379\u003e PB.GET key --FORMAT JSON Msg /sub\n\"{\\\"s\\\":\\\"redis-protobuf\\\",\\\"i\\\":2}\"\n```\n\nDelete message:\n\n```\n127.0.0.1:6379\u003e PB.DEL key Msg\n(integer) 1\n```\n\n### C++ Client\n\nIf you are using C++, you can use [redis-plus-plus](https://github.com/sewenew/redis-plus-plus) to send *redis-protobuf* commands:\n\n```C++\ntry {\n    auto redis = Redis(\"tcp://127.0.0.1\");\n\n    // Set Protobuf message.\n    Msg msg;\n    msg.set_i(1);\n    auto *sub = msg.mutable_sub();\n    sub-\u003eset_s(\"string\");\n    sub-\u003eset_i(2);\n    msg.add_arr(1);\n    msg.add_arr(2);\n    msg.add_arr(3);\n\n    // Serialize Protobuf message.\n    std::string s;\n    if (!msg.SerializeToString(\u0026s)) {\n        throw Error(\"failed to serialize protobuf message\");\n    }\n\n    // Set value with the serialized message.\n    assert(redis.command\u003clong long\u003e(\"PB.SET\", \"key\", \"Msg\", s) == 1);\n\n    // Get the message in binary format.\n    s = redis.command\u003cstd::string\u003e(\"PB.GET\", \"key\", \"--FORMAT\", \"BINARY\", \"Msg\");\n\n    // Create a new message with the binary string.\n    Msg msg_new;\n    if (!msg_new.ParseFromString(s)) {\n        throw Error(\"failed to parse string to protobuf message\");\n    }\n\n    // Set and get a message field.\n    assert(redis.command\u003clong long\u003e(\"PB.SET\", \"key\", \"Msg\", \"/i\", 10) == 1);\n    assert(redis.command\u003clong long\u003e(\"PB.GET\", \"key\", \"Msg\", \"/i\") == 10);\n\n    // Set and get a nested message field.\n    assert(redis.command\u003clong long\u003e(\"PB.SET\", \"key\", \"Msg\", \"/sub/s\", \"redis-protobuf\") == 1);\n    assert(redis.command\u003cstd::string\u003e(\"PB.GET\", \"key\", \"Msg\", \"/sub/s\") == \"redis-protobuf\");\n\n    // Delete the message.\n    assert(redis.command\u003clong long\u003e(\"PB.DEL\", \"key\", \"Msg\") == 1);\n} catch (const Error \u0026e) {\n    // Error handling\n}\n```\n\n### Python Client\n\nIf you are using Python, you can use [redis-py](https://github.com/andymccurdy/redis-py) to send *redis-protobuf* commands:\n\n```\n\u003e\u003e\u003e import redis\n\u003e\u003e\u003e r = redis.StrictRedis(host='localhost', port=6379, db=0)\n\u003e\u003e\u003e r.execute_command('PB.SET', 'key', 'Msg', '{\"i\" : 1, \"sub\" : {\"s\" : \"string\", \"i\" : 2}, \"arr\" : [1, 2, 3]}')\n1\n\u003e\u003e\u003e r.execute_command('PB.GET', 'key', '--FORMAT', 'BINARY', 'Msg')\nb'\\x08\\x01\\x12\\n\\n\\x06string\\x10\\x02\\x1a\\x03\\x01\\x02\\x03'\n\u003e\u003e\u003e r.execute_command('PB.GET', 'key', '--FORMAT', 'JSON', 'Msg')\nb'{\"i\":1,\"sub\":{\"s\":\"string\",\"i\":2},\"arr\":[1,2,3]}'\n\u003e\u003e\u003e r.execute_command('PB.SET', 'key', 'Msg', '/i', 2)\n1\n\u003e\u003e\u003e r.execute_command('PB.GET', 'key', 'Msg', '/i')\n2\n\u003e\u003e\u003e r.execute_command('PB.SET', 'key', 'Msg', '/sub/s', 'redis-protobuf')\n1\n\u003e\u003e\u003e r.execute_command('PB.GET', 'key', 'Msg', '/sub/s')\nb'redis-protobuf'\n\u003e\u003e\u003e r.execute_command('PB.SET', 'key', 'Msg', '/arr/0', 100)\n1\n\u003e\u003e\u003e r.execute_command('PB.GET', 'key', 'Msg', '/arr/0')\n100\n```\n\n## Commands\n\n### Type and Path\n\nMost commands have *type* and *path* as arguments, which specifies the message type and field. We use *JSON Pointer*: [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) to specify the *path*. I'll use the following *.proto* file as an example to show you how to specify *type* and *path*.\n\n**NOTE**: *path* IS CASE SENSITIVE.\n\n```\nsyntax = \"proto3\";\n\npackage redis.pb;\n\nmessage SubMsg {\n    string s = 1;\n}\n\nmessage Msg {\n    int32 i = 1;\n    SubMsg sub = 2;\n    repeated string str_arr = 3;\n    repeated SubMsg msg_arr = 4;\n    map\u003cstring, SubMsg\u003e m = 5;\n};\n```\n\n#### Type\n\n*Type* is the type name of a protobuf message. If you specify package name in the *.proto* definition, e.g. `package redis.pb`, you must also specify the pacakge name as a part of the type name, separated with a dot, i.e. '.'. For example, we can use the following to specify the type the message:\n\n```\nredis.pb.Msg\n```\n\nHowever, if you don't specify package name in the *.proto* definition, you can use the message type directly without any prefix.\n\n### Path\n\nWith JSON Pointer, you can use `/` to address the field (and nested field) of a message.\n\n```\n/i\n\n/sub\n\n/sub/s\n```\n\nIf the field is an array, you can use array index (seperated by '/'), i.e. `/i`, to specify the ith element. If the element is of message type, you can use '/' to specify the field of the element:\n\n```\n/str_arr/2\n\n/msg_arr/1/s\n```\n\nThe index is 0-based, and if the index is out-of-range, *redis-protobuf* will reply with an error.\n\nIf the field is a map, you can use a key (seperated by '/'), i.e. `/key`, to specify the corresponding value. If the value of message type, again, you can use a '/' to specify the field of the value:\n\n```\n/m/key\n\n/m/key/s\n```\n\n### PB.SET\n\n#### Syntax\n\n```\nPB.SET key [--NX | --XX] [--EX seconds | --PX milliseconds] type [path] value\n```\n\n- If the *path* is omitted, set the whole message with the given *value*.\n- Otherwise, set the corresponding field with the given *value*.\n\n**NOTE**: Since Protobuf fields are optional, when setting the whole message, you can only set parts of fields of this message. The following example only sets `/i` field, and leave other fields unset.\n\n```\n127.0.0.1:6379\u003e PB.SET key Msg '{\"i\" : 1}'\n(integer) 1\n```\n\nProtobuf message has pre-defined schema, so the *value* should match the type of the corresponding field.\n\n- If the field is of integer type, i.e. `int32`, `int64`, `uint32`, `uint64`, `sint32`, `sint64`, `fixed32`, `fixed64`, `sfixed32`, `sfixed64`, the *value* string should be converted to the corresponding integer.\n- If the field is of floating-point type, i.e. `float`, `double`, the *value* string should be converted to the corresponding floating-point number.\n- If the field is of boolean type, i.e. `bool`, the *value* string should be *true* or *false*, or an integer (`0` is `false`, none `0` is `true`).\n- If the field is of string type, i.e. `string`, `byte`, the *value* string can be any binary string.\n- If the field is of enum type, i.e. `enum`, the *value* string should be converted to an integer.\n- If the field is of message type, the *value* string should be a binary string that serialized from the corresponding Protobuf message, or a JSON string that can be converted to the corresponding Protobuf message.\n\n#### Options\n\n- **--NX**: Only set the key if it doesn't exist.\n- **--XX**: Only set the key if it already exists.\n- **--EX seconds**: Set the key with the specified expiration in seconds.\n- **--PX milliseconds**: Set the key with the specified expiration in milliseconds.\n\n#### Return Value\n\nInteger reply: 1 if set successfully. 0, otherwise, e.g. option *--NX* has been set, while the key already exists.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- The field specified by *path*, doesn't exist.\n- *type* doesn't match the type of the message saved in *key*, i.e. try to overwrite a *key*, in which the message is of a different type. See the examples part for an example.\n- *value* doesn't match the type of the field.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.SET key Msg '{\"i\" : 1, \"sub\" : {\"s\" : \"string\", \"i\" : 2}, \"arr\" : [1, 2, 3]}'\n(integer) 1\n127.0.0.1:6379\u003e PB.SET key Msg /i 10\n(integer) 1\n127.0.0.1:6379\u003e PB.SET key Msg /sub/s redis-protobuf\n(integer) 1\n127.0.0.1:6379\u003e PB.SET key Msg /arr/0 2\n(integer) 1\n127.0.0.1:6379\u003e PB.SET key SubMsg '{\"s\" : \"hello\"}'\n(error) ERR type mismatch\n```\n\n### PB.GET\n\n#### Syntax\n\n```\nPB.GET key [--FORMAT BINARY|JSON] type [path]\n```\n\n- If *path* is omitted, return the whole message in *key*.\n- Otherwise, return the value of that field.\n\n#### Options\n\n- **--FORMAT**: If the field at *path* is of message type, this option specifies the format of the return value. If the field is of other types, this option is ignored.\n    - **BINARY**: return the value as a binary string by serializing the Protobuf message.\n    - **JSON**: return the value as a JSON string by converting the Protobuf message to JSON.\n\n#### Return Value\n\nReply type is depends on the type of the field at *path*.\n\n- Integer reply: if the field is of integer or enum type.\n- Bulk string reply: if the field is of string or message type.\n- Simple string reply: if the field is of boolean or floating-point type.\n- Array reply: if the field is repeated or map type.\n- Nil reply: if *key* doesn't exist.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- If the field specified by *path*, doesn't exist.\n- If the specified type doesn't match the type of the message saved in *key*.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.GET key --FORMAT BINARY Msg\n\"\\b\\n\\x12\\x12\\n\\x0eredis-protobuf\\x10\\x02\\x1a\\x03\\x02\\x02\\x03\"\n127.0.0.1:6379\u003e PB.GET key --FORMAT JSON Msg\n\"{\\\"i\\\":10,\\\"sub\\\":{\\\"s\\\":\\\"redis-protobuf\\\",\\\"i\\\":2},\\\"arr\\\":[2,2,3]}\"\n127.0.0.1:6379\u003e PB.GET key Msg /i\n(integer) 10\n127.0.0.1:6379\u003e PB.GET key --FORMAT JSON Msg /sub\n\"{\\\"s\\\":\\\"redis-protobuf\\\",\\\"i\\\":2}\"\n127.0.0.1:6379\u003e PB.GET key Msg /arr/0\n(integer) 2\n127.0.0.1:6379\u003e PB.GET key Msg /arr\n1) (integer) 2\n2) (integer) 2\n3) (integer) 3\n```\n\n### PB.DEL\n\n#### Syntax\n\n```\nPB.DEL key type [path]\n```\n\n- If *path* specifies an array element, e.g. `/arr/0`, delete the corresponding element from the array.\n- If *path* specifies a map value, e.g. `/m/key`, delete the corresponding key-value pair from the map.\n- If *path* is omitted, delete the key.\n\n#### Return Value\n\nInteger reply: 1 if *key* exists, 0 othewise.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- The field specified by *path*, doesn't exist\n- The field is not an array element or a map value.\n- The specifies *type* doesn't match the type of the message saved in *key*.\n\n#### Time Complexity\n\n- Delete array element: O(N), and N is the length of the array.\n- Delete map element: O(1)\n- Delete message: O(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.DEL key Msg /arr/0\n(integer) 1\n127.0.0.1:6379\u003e PB.DEL key Msg\n(integer) 1\n```\n\n### PB.APPEND\n\n#### Syntax\n\n```\nPB.APPEND key type path element [element, element...]\n```\n\n- If the field at *path* is a string, append *value* string to the field.\n- If the field at *path* is an array, append the *value* as an element to the array.\n\nIf *key* doesn't exist, create an empty message, and do the append operation to the new message.\n\n#### Return Value\n\nInteger reply: The length of the string or the size of the array after the append operation.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- The field specified by *path*, doesn't exist.\n- The field at *path* is not a string or array.\n\n#### Time Complexity\n\nAmortized O(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e pb.append key Msg /sub/s WithTail\n(integer) 14\n127.0.0.1:6379\u003e pb.append key Msg /arr 4\n(integer) 4\n```\n\n### PB.LEN\n\n#### Syntax\n\n```\nPB.LEN key type [path]\n```\n\n- If the field at *path* is a string, return the length of the string.\n- If the field at *path* is an array, return the size of the array.\n- If the field at *path* is a map, return the size of the map.\n- If the field at *path* is a message, return the length of the serialized binary string of the message.\n- If *path* is omitted, return the length of the serialized binary string of whole message.\n\n#### Return Value\n\nInteger reply: 0 if *key* doesn't exist. Otherwise, the length of the string/array/map/message.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- *path* doesn't exist.\n- The field at *path* is not a string/array/map/message.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.LEN key Msg\n(integer) 28\n127.0.0.1:6379\u003e PB.LEN key Msg /sub/s\n(integer) 14\n127.0.0.1:6379\u003e PB.LEN key Msg /arr\n(integer) 4\n```\n\n### PB.CLEAR\n\n#### Syntax\n\n```\nPB.CLEAR key type [path]\n```\n\n- If *path* specifies a message type, clear the message in *key*.\n- If *path* specifies a field, clear the field.\n- If *path* is omitted, clear the whole message, NOT delete!\n\nPlease check the Protubuf doc for the definition of **clear**.\n\n#### Return Value\n\nInteger reply: 1 if the *key* exists, 0 otherwise.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- The specified *type* doesn't match the type of the message saved in *key*.\n- *path* doesn't exist.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.CLEAR key Msg\n(integer) 1\n127.0.0.1:6379\u003e PB.CLEAR key Msg /arr\n(integer) 1\n127.0.0.1:6379\u003e PB.CLEAR non-exist-key Msg\n(integer) 0\n```\n\n### PB.MERGE\n\n#### Syntax\n\n```\nPB.MERGE key type [path] value\n```\n\n- If *path* specifies a field, merge the *value* into the field.\n- If *key* doesn't exist, this command behaves as *PB.SET*.\n- If *path* is omitted, parse *value* to a message, and merge it into the message in *key*.\n\nPlease check the Protubuf doc for the definition of **merge**.\n\n#### Return Value\n\nInteger reply: 1 if the *key* exists, 0 otherwise.\n\n#### Error\n\nReturn an error reply in the following cases:\n\n- The specified *type*, doesn't match the type of the message saved in *key*.\n- *path* doesn't exist.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n```\n\n### PB.TYPE\n\n#### Syntax\n\n```\nPB.TYPE key\n```\n\nGet the message type of message in *key*.\n\n#### Return Value\n\n- Simple string reply: The Protobuf message type, if *key* exists.\n- Nil reply: If *key* doesn't exist.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.TYPE key\nMsg\n```\n\n### PB.SCHEMA\n\n#### Syntax\n\n```\nPB.SCHEMA type\n```\n\nGet the schema of the given Protobuf message *type*.\n\n#### Return Value\n\n- Bulk string reply: The schema of the given *type*.\n- Nil reply: If the *type* doesn't exist.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.SCHEMA Msg\n\"message Msg {\\n  int32 i = 1;\\n  SubMsg sub = 2;\\n  repeated int32 arr = 3;\\n}\\n\"\n```\n\n### PB.IMPORT\n\n#### Syntax\n\n```\nPB.IMPORT filename content\n```\n\nImport a protobuf file asynchronously. If the file has been imported successfully, the file will be persisted in `proto-directory`.\n\n**NOTE**:\n\n- Since this command runs asynchronously, you need to use [PB.LASTIMPORT](#pblastimport) to check the importing result.\n- If a file has already been imported, you cannot re-import it with this command, i.e. you cannot update an already imported proto file. Instead, you need to update the file on disk, and restart Redis server.\n\n#### Return Value\n\n- Simple string reply: \"OK\"\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.IMPORT test.proto 'syntax=\"proto3\"; message M { int32 i = 1; }'\nOK\n```\n\n### PB.LASTIMPORT\n\n#### Syntax\n\n```\nPB.LASTIMPORT\n```\n\nGet the importing result of all imported proto files since last call to this command.\n\nSince `PB.IMPORT` runs asynchronously, the importing result will be recorded. When `PB.LASTIMPORT` is called, all these records will be returned to client, and these records will be cleared on the server side. So if you call `PB.LASTIMPORT` twice, without calling `PB.IMPORT` between these two calls of `PB.LASTIMPORT`, the second calls will return empty array reply.\n\n#### Return Value\n\n- Array reply: Status of last imported protobuf files. For each file, if it's imported successfully, the status is \"OK\". Otherwise, the status is an error message.\n\n#### Time Complexity\n\nO(1)\n\n#### Examples\n\n```\n127.0.0.1:6379\u003e PB.LASTIMPORT\n1) 1) \"msg1.proto\"\n   2) \"ERR failed to load msg1.proto\\nerror:...\n2) 1) \"msg2.proto\"\n   2) \"OK\"\n```\n\n## Author\n\n*redis-protobuf* is written by [sewenew](https://github.com/sewenew), who is also active on [StackOverflow](https://stackoverflow.com/users/5384363/for-stack).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsewenew%2Fredis-protobuf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsewenew%2Fredis-protobuf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsewenew%2Fredis-protobuf/lists"}