{"id":20661583,"url":"https://github.com/aerospike/aerospike-document-lib","last_synced_at":"2025-04-19T15:25:32.673Z","repository":{"id":40456437,"uuid":"335430769","full_name":"aerospike/aerospike-document-lib","owner":"aerospike","description":"Aerospike Java Document API","archived":false,"fork":false,"pushed_at":"2025-02-22T08:52:34.000Z","size":253,"stargazers_count":11,"open_issues_count":4,"forks_count":3,"subscribers_count":55,"default_branch":"main","last_synced_at":"2025-03-29T09:22:28.135Z","etag":null,"topics":["aerospike","document-api","java","jsonpath"],"latest_commit_sha":null,"homepage":"","language":"Java","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/aerospike.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-02T21:41:14.000Z","updated_at":"2024-07-04T21:18:20.000Z","dependencies_parsed_at":"2024-03-20T16:05:38.389Z","dependency_job_id":null,"html_url":"https://github.com/aerospike/aerospike-document-lib","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerospike%2Faerospike-document-lib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerospike%2Faerospike-document-lib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerospike%2Faerospike-document-lib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aerospike%2Faerospike-document-lib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aerospike","download_url":"https://codeload.github.com/aerospike/aerospike-document-lib/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249725850,"owners_count":21316273,"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":["aerospike","document-api","java","jsonpath"],"created_at":"2024-11-16T19:10:21.959Z","updated_at":"2025-04-19T15:25:32.648Z","avatar_url":"https://github.com/aerospike.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Aerospike Document API\n[![Build project](https://github.com/aerospike/aerospike-document-lib/actions/workflows/build.yml/badge.svg)](https://github.com/aerospike/aerospike-document-lib/actions/workflows/build.yml)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.aerospike/aerospike-document-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.aerospike/aerospike-document-api/)\n[![javadoc](https://javadoc.io/badge2/com.aerospike/aerospike-document-api/javadoc.svg)](https://javadoc.io/doc/com.aerospike/aerospike-document-api)\n\nThis project provides an API for accessing and mutating Aerospike\n[Collection Data Type](https://www.aerospike.com/docs/client/java/index.html) (CDT)\nobjects using [JSONPath](https://goessner.net/articles/JsonPath/) syntax.\nThis effectively provides a document API, with CDT objects used to represent\nJSON documents in the Aerospike database.\n\n### Documentation\n\nThe documentation for this project can be found on [javadoc.io](https://www.javadoc.io/doc/com.aerospike/aerospike-document-api).\n\n### Assumptions\n\n- Familiarity with the Aerospike client for Java (see [Introduction - Java Client](https://www.aerospike.com/docs/client/java/index.html))\n- Some knowledge of the Aerospike CDTs (see reference above)\n\n## Getting Started Blog Posts\n\n 1. [Aerospike Document API](https://medium.com/aerospike-developer-blog/aerospike-document-api-fd8870b4106c?source=friends_link\u0026sk=b733e9fbe5a089ccca4f692e4f429711)\n 2. [Aerospike Document API: JSONPath Queries](https://medium.com/aerospike-developer-blog/aerospike-document-api-jsonpath-queries-bd6260b2d076?source=friends_link\u0026sk=d2c75b3beec691a36aa73513945f22a1)\n\n## Build Instructions\n```sh\nmvn clean package\n```\n\n## Maven Dependency\n\nAdd the Maven dependency:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.aerospike\u003c/groupId\u003e\n  \u003cartifactId\u003eaerospike-document-api\u003c/artifactId\u003e\n  \u003cversion\u003e2.0.3\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Overview\n\nConsider the following JSON:\n\n``` json\n{\n  \"forenames\": [\n    \"Tommy\",\n    \"Lee\"\n  ],\n  \"surname\": \"Jones\",\n  \"date_of_birth\": {\n    \"day\": 15,\n    \"month\": 9,\n    \"year\": 1946\n  },\n  \"selected_filmography\":{\n    \"2012\":[\"Lincoln\",\"Men In Black 3\"],\n    \"2007\":[\"No Country For Old Men\"],\n    \"2002\":[\"Men in Black 2\"],\n    \"1997\":[\"Men in Black\",\"Volcano\"],\n    \"1994\":[\"Natural Born Killers\",\"Cobb\"],\n    \"1991\":[\"JFK\"],\n    \"1980\":[\"Coal Miner's Daughter\",\"Barn Burning\"]\n  },\n  \"imdb_rank\":{\n    \"source\":\"https://www.imdb.com/list/ls050274118/\",\n    \"rank\":51\n  },\n  \"best_films_ranked\": [\n    {\n      \"source\": \"http://www.rottentomatoes.com\",\n      \"films\": [\"The Fugitive\",\"No Country For Old Men\",\"Men In Black\",\"Coal Miner's Daughter\",\"Lincoln\"]\n    },\n    {\n      \"source\":\"https://medium.com/the-greatest-films-according-to-me/10-greatest-films-of-tommy-lee-jones-97426103e3d6\",\n      \"films\":[\"The Three Burials of Melquiades Estrada\",\"The Homesman\",\"No Country for Old Men\",\"In the Valley of Elah\",\"Coal Miner's Daughter\"]\n    }\n  ]\n}\n```\n\n### Instantiating an Aerospike Document Client\n\nThe Aerospike Document Client is instantiated as follows\n* You can create a new AerospikeClient using other constructors - in this example we are using IP and Port only.\n\n``` java\nAerospikeClient client = new AerospikeClient(AEROSPIKE_SERVER_IP, AEROSPIKE_SERVER_PORT);\nAerospikeDocumentClient documentClient = new AerospikeDocumentClient(client);\n```\n\n### Create\n\nWe add the example JSON document to our Aerospike database as follows\n\n``` java\nJsonNode jsonNode = JsonConverters.convertStringToJsonNode(jsonString);\n// For details of Aerospike namespace/set/key see https://www.aerospike.com/docs/architecture/data-model.html\nKey tommyLeeJonesDBKey = new Key(AEROSPIKE_NAMESPACE, AEROSPIKE_SET, \"tommy-lee-jones.json\");\nString documentBinName = \"documentBin\";\ndocumentClient.put(tommyLeeJonesDBKey, documentBinName, jsonNode);\n```\n\n### Insert\n\nWe can add filmography for 2019 using the JSONPath ```$.selected_filmography.2019```\n\n```java\nList\u003cString\u003e _2019Films = new Vector\u003cString\u003e();\n_2019Films.add(\"Ad Astra\");\ndocumentClient.put(tommyLeeJonesDBKey, documentBinName, \"$.selected_filmography.2019\",_2019Films);\n```\n\n### Update\n\nUpdate Jones' IMDB ranking using the JSONPath ```$.imdb_rank.rank```\n\n``` java\ndocumentClient.put(tommyLeeJonesDBKey, documentBinName, \"$.imdb_rank.rank\",45);\n```\n\n### Append\n\nWe can append to 'Rotten Tomatoes' list of best films using the reference ```$.best_films_ranked[0].films```\n\n```java\ndocumentClient.append(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films\",\"Rolling Thunder\");\ndocumentClient.append(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films\",\"The Three Burials\");\n```\n\n### Delete\n\nWe can delete a node e.g. the Medium reviewer's rankings\n\n```java\ndocumentClient.delete(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[1]\");\n```\n\n### Get\n\nWe can find out the name of Jones' best film according to 'Rotten Tomatoes' using the JSONPath ```$.best_films_ranked[0].films[0]```\n\n```java\ndocumentClient.get(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films[0]\");\n```\n\n## JSONPath Queries\n\nJSONPath is a query language for JSON.\nIt supports operators, functions and filters.\n\nConsider the following JSON document\n\n``` json\n{\n  \"store\": {\n    \"book\": [\n      {\n        \"category\": \"reference\",\n        \"author\": \"Nigel Rees\",\n        \"title\": \"Sayings of the Century\",\n        \"price\": 8.95,\n        \"ref\": [1,2]\n      },\n      {\n        \"category\": \"fiction\",\n        \"author\": \"Evelyn Waugh\",\n        \"title\": \"Sword of Honour\",\n        \"price\": 12.99,\n        \"ref\": [2,4,16]\n      },\n      {\n        \"category\": \"fiction\",\n        \"author\": \"Herman Melville\",\n        \"title\": \"Moby Dick\",\n        \"isbn\": \"0-553-21311-3\",\n        \"price\": 8.99,\n        \"ref\": [1,3,5]\n      },\n      {\n        \"category\": \"fiction\",\n        \"author\": \"J. R. R. Tolkien\",\n        \"title\": \"The Lord of the Rings\",\n        \"isbn\": \"0-395-19395-8\",\n        \"price\": 22.99,\n        \"ref\": [1,2,7]\n      }\n    ],\n    \"bicycle\": {\n      \"color\": \"red\",\n      \"price\": 19.95\n    }\n  },\n  \"expensive\": 10\n}\n```\n\n#### Examples\nHere are some examples of JSONPath queries:\n\n```java\n// All things, both books and bicycles\nString jsonPath = \"$.store.*\";\nObject objectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\n\n// The authors of all books\nString jsonPath = \"$.store.book[*].author\";\nObject objectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\n\n// The authors of all books\nString jsonPath = \"$.store.book[*].author\";\nString jsonObject = \"J.K. Rowling\";\n// Modify the authors of all books to \"J.K. Rowling\"\ndocumentClient.put(TEST_AEROSPIKE_KEY, documentBinName, jsonPath, jsonObject);\nObject objectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\n\n// All books with an ISBN number\njsonPath = \"$..book[?(@.isbn)]\";\nobjectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\n\n// All books in store cheaper than 10\njsonPath = \"$.store.book[?(@.price \u003c 10)]\";\nobjectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\n\n// All books matching regex (ignore case)\njsonPath = \"$..book[?(@.author =~ /.*REES/i)]\";\nobjectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\n\n// The price of everything\nString jsonPath = \"$.store..price\";\n// Delete the price field of every object exists in the store\ndocumentClient.delete(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);\nObject objectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, documentBinName, jsonPath);        \n```\n\n## Multiple Document Bins\n\nStarting at version `1.1.0` there is a new feature called multiple document bins.\n\nYou can have multiple documents - each stored in a different bin, all documents have the same structure but not the same data.\n\nExample of a use-case can be storing events, each document contains events for a specific amount of time - for example, a week, and now you \nhave the ability to use Document API operations (including JSONPath queries) on multiple documents (with the same structure) at once\nusing a single Aerospike operate() command under the hood which saves server resources boilerplate code.\n\n### How it looks\n\nConsider the following JSON documents:\n\nevents1.json\n```json\n{\n  \"authentication\": {\n    \"login\": [\n      {\n        \"id\": 1,\n        \"name\": \"John Smith\",\n        \"location\": \"US\",\n        \"date\": \"1.7.2021\",\n        \"device\": \"Computer\",\n        \"os\": \"Windows\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Jonathan Sidwell\",\n        \"location\": \"Israel\",\n        \"date\": \"1.7.2021\",\n        \"device\": \"Mobile\",\n        \"os\": \"Android\"\n      },\n      {\n        \"id\": 3,\n        \"name\": \"Mike Ross\",\n        \"location\": \"US\",\n        \"date\": \"1.7.2021\",\n        \"device\": \"Computer\",\n        \"os\": \"MacOS\"\n      },\n      {\n        \"id\": 4,\n        \"name\": \"Jessica Pearson\",\n        \"location\": \"France\",\n        \"date\": \"1.7.2021\",\n        \"device\": \"Computer\",\n        \"os\": \"Windows\"\n      }\n    ],\n    \"logout\": {\n      \"name\": \"Nathan Levy\",\n      \"datetime\": \"1.7.2021\",\n      \"device\": \"Tablet\",\n      \"ref\": [7,4,2]\n    }\n  },\n  \"like\": 10\n}\n```\n\nevents2.json\n```json\n{\n  \"authentication\": {\n    \"login\": [\n      {\n        \"id\": 21,\n        \"name\": \"Simba Lion\",\n        \"location\": \"Italy\",\n        \"date\": \"2.7.2021\",\n        \"device\": \"Mobile\",\n        \"os\": \"iOS\"\n      },\n      {\n        \"id\": 22,\n        \"name\": \"Sean Cahill\",\n        \"location\": \"US\",\n        \"date\": \"2.7.2021\",\n        \"device\": \"Mobile\",\n        \"os\": \"Android\"\n      },\n      {\n        \"id\": 23,\n        \"name\": \"Forrest Gump\",\n        \"location\": \"Spain\",\n        \"date\": \"2.7.2021\",\n        \"device\": \"Computer\",\n        \"os\": \"Windows\"\n      },\n      {\n        \"id\": 24,\n        \"name\": \"Patrick St. Claire\",\n        \"location\": \"France\",\n        \"date\": \"2.7.2021\",\n        \"device\": \"Mobile\",\n        \"os\": \"iOS\"\n      }\n    ],\n    \"logout\": {\n      \"name\": \"John Snow\",\n      \"datetime\": \"2.7.2021\",\n      \"device\": \"Mobile\",\n      \"ref\": [1,2,3]\n    }\n  },\n  \"like\": 20\n}\n```\n\nWe have 2 documents with the same structure but not the same data that represents events.\n\nDefining a bins list.\n```java\nString documentBinName1 = \"events1Bin\";\nString documentBinName2 = \"events2Bin\";\nList\u003cString\u003e bins = new ArrayList\u003c\u003e();\nbins.add(documentBinName1);\nbins.add(documentBinName2);\n```\n\nExamples:\n```java\n// The names of the users of all logout events from each document\nString jsonPath = \"$.authentication.logout.name\";\nObject objectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, bins, jsonPath);\n\n// Modify the devices of all the authentications (login and logout) to \"Mobile\"\njsonPath = \"$.authentication..device\";\njsonObject = \"Mobile\";\ndocumentClient.put(TEST_AEROSPIKE_KEY, bins, jsonPath, jsonObject);\n\n// Delete the user field from all of the authentications (login and logout)\njsonPath = \"$.authentication..user\";\ndocumentClient.delete(TEST_AEROSPIKE_KEY, bins, jsonPath);\n\n// All the logins with \"id\" greater than 10\njsonPath = \"$.authentication.login[?(@.id \u003e 10)]\";\nobjectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, bins, jsonPath);\n```\n\n### JSONPath query operations\n\nDepending on how JSONPath query operations run they can be split into 2 types.\n\n#### 1-step JSONPath query operations\n\nOperations that use JSONPath containing only array and/or map elements.\n\nExamples:\n\n    $.store.book, $[0], $.store.book[0], $.store.book[0][1].title.\n\n#### 2-step JSONPath query operations\n\nOperations that use JSONPath containing wildcards, recursive descent, filters, functions, scripts.\n\nExamples:\n  \n    $.store.book[*].author, $.store..price, $.store.book[?(@.price \u003c 10)], $..book[(@.length-1)].\n\n## Batch operations\n\nStarting at version `2.0.0` there is support for batch operations.\n\nYou can now send CRUD operations (PUT, GET, APPEND, DELETE) in batches using JSONPath\nfor single and multiple bins. \nEach operation in a batch is performed on a single Aerospike key.\n\nLimitations:\n\n|                                                       | Unique key\u003cbr/\u003ewithin batch | Non-unique key\u003cbr/\u003ewithin batch                            | Multiple batch operations\u003cbr/\u003ehaving the same key and the same bin(s) |\n|-------------------------------------------------------|-----------------------------|------------------------------------------------------------|-----------------------------------------------------------------------|\n| [1-step operation](#1-step-jsonpath-query-operations) | Supported                   | Order of operations with non-unique keys is not guaranteed | Only 1-step GET operations, order not guaranteed                      |\n| [2-step operation](#2-step-jsonpath-query-operations) | Supported                   | Not supported                                              | Not supported                                                         |\n\nResults are returned as a List of BatchRecord objects, each of them contains the following:\n\n- Aerospike key.\n- Result code (0 in case of operation finished successfully or another predefined number\n  referring to a particular exception / error).\n- Record (contains requested values mapped to their respective bin names,\n  relevant in case of the GET operation).\n\nA use-case example can be sending a batch of operations at once to update bins storing events,\nor append values for single bins storing analytics, when many steps of the same kind need to be performed.\n\n### Using batch operations\n\nHere is a basic example of using batch operations:\n\n```java\n// Insert\nBatchOperation operation1 = new PutBatchOperation(\n    key1, \n    Collections.singletonList(documentBinName),\n    \"$.selected_filmography.2019\",\n    \"Ad Astra\"\n);\n\n// Update\nBatchOperation operation2 = new PutBatchOperation(\n    key2,\n    Collections.singletonList(documentBinName),\n    \"$.imdb_rank.rank\",\n    45\n);\n\n// Append\nBatchOperation operation3 = new AppendBatchOperation(\n    key3,\n    Collections.singletonList(documentBinName),\n    \"$.best_films_ranked[0].films\",\n    \"Men In Black\"\n);\n\n// Delete\nBatchOperation operation4 = new DeleteBatchOperation(\n    key4,\n    Collections.singletonList(documentBinName),\n    \"$.best_films_ranked[1]\"\n);\n\n// Get\nBatchOperation operation5 = new GetBatchOperation(\n    key5,\n    Collections.singletonList(documentBinName),\n    \"$.best_films_ranked[0].films[0]\"\n);\n\n// Update using JSONPath query\nBatchOperation operation6 = new PutBatchOperation(\n    key6,\n    Collections.singletonList(documentBinName2),\n    \"$.best_filmes_ranked[*].films[0]\",\n    \"Men In Black 2\"\n);\n\n// Assuming we have multiple similarly structured bins to read from\nString binName1 = \"events1Bin\";\nString binName2 = \"events2Bin\";\nString binName3 = \"events3Bin\";\nList\u003cString\u003e bins = new ArrayList\u003c\u003e();\nbins.add(binName1);\nbins.add(binName2);\nbins.add(binName3);\nBatchOperation operation7 = new GetBatchOperation(\n    key7,\n    bins,\n    \"$.imdb_rank.source\"\n);\n\n// Collecting operations and running\nList\u003cBatchOperation\u003e batchOpsList = new ArrayList\u003c\u003e();\nbatchOpsList.add(operation1, operation2, operation3, operation4,\n        operation5, operation6, operation7);\nList\u003cBatchRecord\u003e results = documentClient.batchPerform(batchOpsList, true);\n// Checking that all operations finished successfully\nassertEquals(0, results.stream().filter(res -\u003e res.resultCode != 0).count());\n```\n\n## References\n\n * See [AerospikeDocumentClient.java](https://github.com/aerospike/aerospike-document-lib/blob/main/src/main/java/com/aerospike/documentapi/AerospikeDocumentClient.java) for full details of the API.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faerospike%2Faerospike-document-lib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faerospike%2Faerospike-document-lib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faerospike%2Faerospike-document-lib/lists"}