{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Aerospike Document API for JSON Documents\n", "\n", "This notebook illustrates the Aerospike Document API, which is used to store a JSON document in Aerospike database and perform operations on a stored JSON document, with code examples.\n", "\n", "This notebook requires the Aerospike Database running locally with Java kernel and Aerospike Java Client. To create a Docker container that satisfies the requirements and holds a copy of Aerospike notebooks, visit the [Aerospike Notebooks Repo](https://github.com/aerospike-examples/interactive-notebooks)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction\n", "The Document API is introduced in blog posts [this](https://medium.com/aerospike-developer-blog/aerospike-document-api-fd8870b4106c) and [this](https://medium.com/aerospike-developer-blog/aerospike-document-api-jsonpath-queries-bd6260b2d076).\n", " \n", "This goal of this notebook is to demonstrate the examples from these blog posts and also to allow easy experimentation with Document API and JSONPath capabilities. \n", "\n", "The main topics in this notebook include:\n", "- Document API methods and code examples\n", "- JSONPath overview and code examples\n", "- Additional JSONPath capabilities and code examples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prerequisites\n", "This tutorial assumes familiarity with the following topics:\n", "- [Aerospike Notebooks - Readme and Tips](../readme_tips.ipynb)\n", "- [Hello World](hello_world.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ensure database is running\n", "This notebook requires that Aerospike Database is running." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "import io.github.spencerpark.ijava.IJava;\n", "import io.github.spencerpark.jupyter.kernel.magic.common.Shell;\n", "IJava.getKernelInstance().getMagics().registerMagics(Shell.class);\n", "%sh asd" ] }, { "cell_type": "markdown", "metadata": { "hide_input": false }, "source": [ "### Download and Install Additional Components\n", "Aerospike Java client 5.1.3 and the document api library." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "%%loadFromPOM\n", "\n", " \n", " com.aerospike\n", " aerospike-client\n", " 5.1.3\n", " \n", " \n", " com.aerospike\n", " aerospike-document-api\n", " 1.0.0\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define Convenience Functions\n", "Define convenience functions `readJSONFromAFile` and `truncateTestData`." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "import java.io.IOException;\n", "import java.nio.charset.StandardCharsets;\n", "import java.nio.file.Files;\n", "import java.nio.file.Paths;\n", "import java.util.stream.Stream;\n", "\n", "// convenience function to read JSON doc from a file\n", "String readJSONFromAFile(String filePath) throws IOException {\n", " StringBuilder contentBuilder = new StringBuilder();\n", " Stream stream = Files.lines(Paths.get(filePath), StandardCharsets.UTF_8);\n", " stream.forEach(s -> contentBuilder.append(s).append(\"\\n\"));\n", " return contentBuilder.toString();\n", "}\n", "\n", "import com.aerospike.client.AerospikeException;\n", "\n", "// convenience function to truncate test data\n", "void truncateTestData() {\n", " try {\n", " aerospikeClient.truncate(null, NAMESPACE, SET, null);\n", " }\n", " catch (AerospikeException e) {\n", " // ignore\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Document API\n", "\n", "The Aerospike Document API provides CRUD operations at arbitrary points within a JSON document as described in [this](https://medium.com/aerospike-developer-blog/aerospike-document-api-fd8870b4106c) blog post.\n", "\n", "## Interface \n", "The document API is as represented in the interface [IAerospikeDocumentClient](https://github.com/aerospike/aerospike-document-lib/blob/main/src/main/java/com/aerospike/documentapi/IAerospikeDocumentClient.java), which is shown below for convenience. The API is fairly simple and has `get`, `put`, `append`, and `delete` methods, and yet is powerful as it allows a JSONPath argument to specify parts of the document simply and expressively to apply these methods.\n", "\n", "
\n",
    "public interface IAerospikeDocumentClient {\n",
    "\n",
    "    /**\n",
    "     * Retrieve the object in the document with key documentKey that is referenced by the JSON path.\n",
    "     *\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path to get the reference from.\n",
    "     * returns Object referenced by jsonPath.\n",
    "     */\n",
    "    Object get(Key documentKey, String documentBinName, String jsonPath)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Retrieve the object in the document with key documentKey that is referenced by the JSON path.\n",
    "     *\n",
    "     * readPolicy  An Aerospike read policy to use for the get() operation.\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path to get the reference from.\n",
    "     * returns Object referenced by jsonPath.\n",
    "     */\n",
    "    Object get(Policy readPolicy, Key documentKey, String documentBinName, String jsonPath)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Put a document.\n",
    "     *\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonObject  A JSON object to put.\n",
    "     */\n",
    "    void put(Key documentKey, String documentBinName, JsonNode jsonObject);\n",
    "\n",
    "    /**\n",
    "     * Put a document.\n",
    "     *\n",
    "     * writePolicy An Aerospike write policy to use for the put() operation.\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonObject  A JSON object to put.\n",
    "     */\n",
    "    void put(WritePolicy writePolicy, Key documentKey, String documentBinName, JsonNode jsonObject);\n",
    "\n",
    "    /**\n",
    "     * Put a map representation of a JSON object at a particular path in a JSON document.\n",
    "     *\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path to put the given JSON object in.\n",
    "     * jsonObject  A JSON object to put in the given JSON path.\n",
    "     */\n",
    "    void put(Key documentKey, String documentBinName, String jsonPath, Object jsonObject)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Put a map representation of a JSON object at a particular path in a JSON document.\n",
    "     *\n",
    "     * writePolicy An Aerospike write policy to use for the put() and operate() operations.\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path to put the given JSON object in.\n",
    "     * jsonObject  A JSON object to put in the given JSON path.\n",
    "     */\n",
    "    void put(WritePolicy writePolicy, Key documentKey, String documentBinName, String jsonPath, Object jsonObject)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Append an object to a list in a document specified by a JSON path.\n",
    "     *\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path that includes a list to append the given JSON object to.\n",
    "     * jsonObject  A JSON object to append to the list at the given JSON path.\n",
    "     */\n",
    "    void append(Key documentKey, String documentBinName, String jsonPath, Object jsonObject)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Append an object to a list in a document specified by a JSON path.\n",
    "     *\n",
    "     * writePolicy An Aerospike write policy to use for the operate() operation.\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path that includes a list to append the given JSON object to.\n",
    "     * jsonObject  A JSON object to append to the list at the given JSON path.\n",
    "     */\n",
    "    void append(WritePolicy writePolicy, Key documentKey, String documentBinName, String jsonPath, Object jsonObject)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Delete an object in a document specified by a JSON path.\n",
    "     *\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path for the object deletion.\n",
    "     */\n",
    "    void delete(Key documentKey, String documentBinName, String jsonPath)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "\n",
    "    /**\n",
    "     * Delete an object in a document specified by a JSON path.\n",
    "     *\n",
    "     * writePolicy An Aerospike write policy to use for the operate() operation.\n",
    "     * documentKey An Aerospike Key.\n",
    "     * documentBinName The bin name that will store the json.\n",
    "     * jsonPath    A JSON path for the object deletion.\n",
    "     */\n",
    "    void delete(WritePolicy writePolicy, Key documentKey, String documentBinName, String jsonPath)\n",
    "            throws JsonPathParser.JsonParseException, DocumentApiException, JsonProcessingException;\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code Examples\n", "The code examples from the blog post [Aerospike Document API](https://medium.com/aerospike-developer-blog/aerospike-document-api-fd8870b4106c) are shown below.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example JSON Document\n", "The example JSON document used is stored in file `doc_api_example_tommyleejones.json`, and has this json:\n", "\n", "
\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",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Store JSON Document" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Iniialize Document Client\n", "Create a document client using an Aerospike client." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initialized Aerospike client and connected to the cluster.\n", "Initialized document client from the Aerospike client.\n" ] } ], "source": [ "import com.aerospike.client.AerospikeClient;\n", "import com.aerospike.documentapi.*;\n", "\n", "final String NAMESPACE = \"test\";\n", "final String SET = \"document-api\";\n", "\n", "AerospikeClient aerospikeClient = new AerospikeClient(\"localhost\", 3000);\n", "System.out.println(\"Initialized Aerospike client and connected to the cluster.\");\n", "AerospikeDocumentClient documentClient = new AerospikeDocumentClient(aerospikeClient);\n", "System.out.println(\"Initialized document client from the Aerospike client.\");;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Read JSON Document\n", "Using the convenience function `readJSONFromAFile` defined above, read a json file." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Read JSON doc from file.\n", "JSON doc: {\"forenames\":[\"Tommy\",\"Lee\"],\"surname\":\"Jones\",\"date_of_birth\":{\"day\":15,\"month\":9,\"year\":1946},\"selected_filmography\":{\"2012\":[\"Lincoln\",\"Men In Black 3\"],\"2007\":[\"No Country For Old Men\"],\"2002\":[\"Men in Black 2\"],\"1997\":[\"Men in Black\",\"Volcano\"],\"1994\":[\"Natural Born Killers\",\"Cobb\"],\"1991\":[\"JFK\"],\"1980\":[\"Coal Miner's Daughter\",\"Barn Burning\"]},\"imdb_rank\":{\"source\":\"https://www.imdb.com/list/ls050274118/\",\"rank\":51},\"best_films_ranked\":[{\"source\":\"http://www.rottentomatoes.com\",\"films\":[\"The Fugitive\",\"No Country For Old Men\",\"Men In Black\",\"Coal Miner's Daughter\",\"Lincoln\"]},{\"source\":\"https://medium.com/the-greatest-films-according-to-me/10-greatest-films-of-tommy-lee-jones-97426103e3d6\",\"films\":[\"The Three Burials of Melquiades Estrada\",\"The Homesman\",\"No Country for Old Men\",\"In the Valley of Elah\",\"Coal Miner's Daughter\"]}]}\n" ] } ], "source": [ "import com.fasterxml.jackson.databind.JsonNode;\n", "import com.aerospike.documentapi.JsonConverters;\n", "\n", "// Read the json document into a string.\n", "String jsonString = readJSONFromAFile(\"doc_api_example_tommyleejones.json\");\n", "System.out.println(\"Read JSON doc from file.\");\n", "\n", "// Convert JSON string to a JsonNode\n", "JsonNode jsonNode = JsonConverters.convertStringToJsonNode(jsonString);\n", "System.out.format(\"JSON doc: %s\\n\", jsonNode);;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Add JSON Document to Database" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stored JSON doc in database.\n" ] } ], "source": [ "import com.aerospike.client.Key;\n", "import com.jayway.jsonpath.JsonPath;\n", "\n", "// Construct a key\n", "Key tommyLeeJonesDBKey = new Key(NAMESPACE, SET, \"tommy-lee-jones.json\");\n", "\n", "// Add the document to database\n", "String documentBinName = \"documentBin\";\n", "documentClient.put(tommyLeeJonesDBKey, documentBinName, jsonNode);\n", "System.out.println(\"Stored JSON doc in database.\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Read" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "The Fugitive" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// indexed array elements\n", "documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films[0]\" );" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Coal Miner's Daughter, Barn Burning]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// complex component denoted by a path\n", "documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$.selected_filmography.1980\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Update" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Ad Astra]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// add year 2019 and films\n", "List _2019Films = new Vector();\n", "_2019Films.add(\"Ad Astra\");\n", "documentClient.put(tommyLeeJonesDBKey, documentBinName, \"$.selected_filmography.2019\", _2019Films);\n", "// read the update\n", "documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$.selected_filmography.2019\" );" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "45" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// update the imdb rank\n", "documentClient.put(tommyLeeJonesDBKey, documentBinName, \"$.imdb_rank.rank\", 45);\n", "// read the update\n", "documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$.imdb_rank.rank\" );" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[The Fugitive, No Country For Old Men, Men In Black, Coal Miner's Daughter, Lincoln, Rolling Thunder, The Three Burials]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// append films to best_films_ranked\n", "documentClient.append(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films\", \"Rolling Thunder\");\n", "documentClient.append(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films\", \"The Three Burials\");\n", "// read the updates\n", "documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$.best_films_ranked[0].films\" );" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Delete" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Before delete: \n", "{imdb_rank={rank=45, source=https://www.imdb.com/list/ls050274118/}, forenames=[Tommy, Lee], surname=Jones, date_of_birth={month=9, day=15, year=1946}, selected_filmography={1997=[Men in Black, Volcano], 2019=[Ad Astra], 2007=[No Country For Old Men], 1994=[Natural Born Killers, Cobb], 2002=[Men in Black 2], 1991=[JFK], 1980=[Coal Miner's Daughter, Barn Burning], 2012=[Lincoln, Men In Black 3]}, best_films_ranked=[{films=[The Fugitive, No Country For Old Men, Men In Black, Coal Miner's Daughter, Lincoln, Rolling Thunder, The Three Burials], source=http://www.rottentomatoes.com}, {films=[The Three Burials of Melquiades Estrada, The Homesman, No Country for Old Men, In the Valley of Elah, Coal Miner's Daughter], source=https://medium.com/the-greatest-films-according-to-me/10-greatest-films-of-tommy-lee-jones-97426103e3d6}]}\n", "After delete: \n", "{}\n" ] } ], "source": [ "// print state before deletion\n", "Object docObject = documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$\");\n", "System.out.format(\"Before delete: \\n%s\\n\", docObject);\n", "// delete all descendants of the document root\n", "documentClient.delete(tommyLeeJonesDBKey, documentBinName, \"$..*\");\n", "// print state after deletion\n", "docObject = documentClient.get(tommyLeeJonesDBKey, documentBinName, \"$\");\n", "System.out.format(\"After delete: \\n%s\\n\", docObject);;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# JSONPath Queries\n", "Please refer to this introduction to JSONPath: [JSONPath, XPATH for JSON](https://goessner.net/articles/JsonPath/). \n", "\n", "## Syntax\n", "The following table summarizes its syntax.\n", "\n", "JSONPath | Description\n", ":------- |:-----------\n", "\\$ | the root object/element\n", "@ | the current object/element\n", ". or [] | child operator\n", ".. | recursive descent\n", "* | wildcard. All objects/elements regardless their names\n", "[] | subscript operator in array\n", "[,] | alternate names or array indices as a set\n", "[start:end:step] | array slice operator \n", "[?()] | boolean filter expression\n", "() | script expression" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use Examples\n", "The following table summarizes its key uses.\n", "\n", "JSONPath | Result\n", ":-------|:-------\n", "\\$.store.book[*].author | the authors of all books in the store\n", "\\$..author | all authors\n", "\\$.store.* | all things in store, which are some books and a red bicycle\n", "\\$.store..price | the price of everything in the store\n", "\\$..book[2] | the third book\n", "\\$..book[(@.length-1)]\n", "\\$..book[-1:] | the last book in order\n", "\\$..book[0,1]\n", "\\$..book[:2] | the first two books\n", "\\$..book[?(@.isbn)] | filter all books with isbn number\n", "\\$..book[?(@.price<10)] | filter all books cheaper than 10\n", "\\$..* | all members of JSON structure\n", "\\$..book.length() | the number of books" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code Examples\n", "Below are the code examples from the blog post [Aerospike Document API: JSONPath Queries](https://medium.com/aerospike-developer-blog/aerospike-document-api-jsonpath-queries-bd6260b2d076)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example JSON Document\n", "The example JSON document it uses is stored in file `doc_api_example_store.json`, and has this json:\n", "\n", "
\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",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Store JSON Document\n", "#### Read JSON Document from File" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Read JSON doc from file.\n" ] } ], "source": [ "String jsonString = readJSONFromAFile(\"doc_api_example_store.json\");\n", "System.out.println(\"Read JSON doc from file.\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Add JSON Document to Database" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\"store\":{\"book\":[{\"category\":\"reference\",\"author\":\"Nigel Rees\",\"title\":\"Sayings of the Century\",\"price\":8.95,\"ref\":[1,2]},{\"category\":\"fiction\",\"author\":\"Evelyn Waugh\",\"title\":\"Sword of Honour\",\"price\":12.99,\"ref\":[2,4,16]},{\"category\":\"fiction\",\"author\":\"Herman Melville\",\"title\":\"Moby Dick\",\"isbn\":\"0-553-21311-3\",\"price\":8.99,\"ref\":[1,3,5]},{\"category\":\"fiction\",\"author\":\"J. R. R. Tolkien\",\"title\":\"The Lord of the Rings\",\"isbn\":\"0-395-19395-8\",\"price\":22.99,\"ref\":[1,2,7]}],\"bicycle\":{\"color\":\"red\",\"price\":19.95}},\"expensive\":10}\n", "Stored JSON doc to database.\n" ] } ], "source": [ "// Store the JSON doc in the database and read it back.\n", "import com.aerospike.client.Key;\n", "import com.aerospike.documentapi.*;\n", "import com.fasterxml.jackson.databind.JsonNode;\n", "import com.jayway.jsonpath.JsonPath;\n", "\n", "// Create a document client via an existing aerospikeClient\n", "AerospikeDocumentClient documentClient = new AerospikeDocumentClient(aerospikeClient);\n", "\n", "// Convert JSON string to a JsonNode\n", "JsonNode jsonNode = JsonConverters.convertStringToJsonNode(jsonString);\n", "System.out.println(jsonNode);\n", "\n", "// Construct an appropriate key\n", "Key documentKey = new Key(NAMESPACE, SET, \"jsonExampleKey\");\n", "\n", "String documentBinName = \"documentBin\";\n", "// Add to database\n", "documentClient.put(documentKey, documentBinName, jsonNode);\n", "System.out.println(\"Stored JSON doc to database.\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query All Subnodes" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{bicycle={color=red, price=19.95}, book=[{ref=[1, 2], category=reference, title=Sayings of the Century, author=Nigel Rees, price=8.95}, {ref=[2, 4, 16], category=fiction, title=Sword of Honour, author=Evelyn Waugh, price=12.99}, {ref=[1, 3, 5], category=fiction, title=Moby Dick, author=Herman Melville, price=8.99, isbn=0-553-21311-3}, {ref=[1, 2, 7], category=fiction, title=The Lord of the Rings, author=J. R. R. Tolkien, price=22.99, isbn=0-395-19395-8}]}\n" ] } ], "source": [ "// Get all products, both books and bicycles\n", "String jsonPath = \"$.store.*\";\n", "Object objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.println(objectFromDB);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query Specific Field" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\"Nigel Rees\",\"Evelyn Waugh\",\"Herman Melville\",\"J. R. R. Tolkien\"]\n" ] } ], "source": [ "// Get the authors of all books\n", "String jsonPath = \"$.store.book[*].author\";\n", "Object objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.println(objectFromDB);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Modify Field" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\"J.K. Rowling\",\"J.K. Rowling\",\"J.K. Rowling\",\"J.K. Rowling\"]\n" ] } ], "source": [ "// 3. Modify the authors of all books to “J.K. Rowling”\n", "// Get the authors of all books\n", "String jsonPath = \"$.store.book[*].author\";\n", "String jsonObject = \"J.K. Rowling\";\n", "// Modify the authors of all books to \"J.K. Rowling\"\n", "documentClient.put(documentKey, documentBinName, jsonPath, jsonObject);\n", "Object objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.println(objectFromDB);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query with Exists Predicate" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{\"ref\":[1,3,5],\"category\":\"fiction\",\"title\":\"Moby Dick\",\"author\":\"J.K. Rowling\",\"price\":8.99,\"isbn\":\"0-553-21311-3\"},{\"ref\":[1,2,7],\"category\":\"fiction\",\"title\":\"The Lord of the Rings\",\"author\":\"J.K. Rowling\",\"price\":22.99,\"isbn\":\"0-395-19395-8\"}]\n" ] } ], "source": [ "// 4. Get all the books that have an ISBN number\n", "jsonPath = \"$..book[?(@.isbn)]\";\n", "objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.println(objectFromDB);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query with Boolean Predicate" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{\"ref\":[1,2],\"category\":\"reference\",\"title\":\"Sayings of the Century\",\"author\":\"J.K. Rowling\",\"price\":8.95},{\"ref\":[1,3,5],\"category\":\"fiction\",\"title\":\"Moby Dick\",\"author\":\"J.K. Rowling\",\"price\":8.99,\"isbn\":\"0-553-21311-3\"}]\n" ] } ], "source": [ "// 5. Get all the books in store cheaper than 10\n", "jsonPath = \"$.store.book[?(@.price < 10)]\";\n", "objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.println(objectFromDB);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Query with RegEx" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{\"ref\":[1,2],\"category\":\"reference\",\"title\":\"Sayings of the Century\",\"author\":\"J.K. Rowling\",\"price\":8.95},{\"ref\":[2,4,16],\"category\":\"fiction\",\"title\":\"Sword of Honour\",\"author\":\"J.K. Rowling\",\"price\":12.99},{\"ref\":[1,3,5],\"category\":\"fiction\",\"title\":\"Moby Dick\",\"author\":\"J.K. Rowling\",\"price\":8.99,\"isbn\":\"0-553-21311-3\"},{\"ref\":[1,2,7],\"category\":\"fiction\",\"title\":\"The Lord of the Rings\",\"author\":\"J.K. Rowling\",\"price\":22.99,\"isbn\":\"0-395-19395-8\"}]\n" ] } ], "source": [ "// 6. Get all the books matching regex (ignore case)\n", "jsonPath = \"$..book[?(@.author =~ /.*ROWLING/i)]\";\n", "objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.println(objectFromDB);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Delete Field" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Before delete: [19.95,8.95,12.99,8.99,22.99]\n", "After delete: []\n" ] } ], "source": [ "// 7. Delete the price field of every object exists in store\n", "// Get the price of everything\n", "String jsonPath = \"$.store..price\";\n", "Object objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.format(\"Before delete: %s\\n\", objectFromDB);\n", "// Delete the price field of every object exists in the store\n", "documentClient.delete(documentKey, documentBinName, jsonPath);\n", "Object objectFromDB = documentClient.get(documentKey, documentBinName, jsonPath);\n", "System.out.format(\"After delete: %s\\n\", objectFromDB);;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# More JSONPath Capabilities and Examples\n", "Restore the original `store` document for the following examples." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{store={bicycle={color=red, price=19.95}, book=[{ref=[1, 2], category=reference, title=Sayings of the Century, author=Nigel Rees, price=8.95}, {ref=[2, 4, 16], category=fiction, title=Sword of Honour, author=Evelyn Waugh, price=12.99}, {ref=[1, 3, 5], category=fiction, title=Moby Dick, author=Herman Melville, price=8.99, isbn=0-553-21311-3}, {ref=[1, 2, 7], category=fiction, title=The Lord of the Rings, author=J. R. R. Tolkien, price=22.99, isbn=0-395-19395-8}]}, expensive=10}" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "documentClient.put(documentKey, documentBinName, jsonNode);\n", "documentClient.get(documentKey, documentBinName, \"$\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions\n", "JSONPath has functions `min`, `sum`, `size`, `length`, etc. See the [JSONPath repo](https://github.com/json-path/JsonPath_) for a full list." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// length(): find number of references to Moby Dick\n", "jsonPath = \"$..store.book[?(@.title == 'Moby Dick')].ref.length()\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "13.48" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// avg(): find average price of books\n", "jsonPath = \"$..book..price.avg()\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Filter Operators\n", "Operators like `in` and `anyof` are supported. See the [JSONPath repo](https://github.com/json-path/JsonPath) for a full list." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{\"ref\":[1,2],\"category\":\"reference\",\"title\":\"Sayings of the Century\",\"author\":\"Nigel Rees\",\"price\":8.95}]" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// in: books with category in ['reference', 'biography']\n", "jsonPath = \"$..book[?(@.category in ['reference', 'biography'])]\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{\"ref\":[1,2],\"category\":\"reference\",\"title\":\"Sayings of the Century\",\"author\":\"Nigel Rees\",\"price\":8.95},{\"ref\":[2,4,16],\"category\":\"fiction\",\"title\":\"Sword of Honour\",\"author\":\"Evelyn Waugh\",\"price\":12.99},{\"ref\":[1,2,7],\"category\":\"fiction\",\"title\":\"The Lord of the Rings\",\"author\":\"J. R. R. Tolkien\",\"price\":22.99,\"isbn\":\"0-395-19395-8\"}]" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// in: books that have 2 in their reference list\n", "jsonPath = \"$..book[?(2 in @.ref)]\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{\"ref\":[1,2],\"category\":\"reference\",\"title\":\"Sayings of the Century\",\"author\":\"Nigel Rees\",\"price\":8.95},{\"ref\":[1,3,5],\"category\":\"fiction\",\"title\":\"Moby Dick\",\"author\":\"Herman Melville\",\"price\":8.99,\"isbn\":\"0-553-21311-3\"},{\"ref\":[1,2,7],\"category\":\"fiction\",\"title\":\"The Lord of the Rings\",\"author\":\"J. R. R. Tolkien\",\"price\":22.99,\"isbn\":\"0-395-19395-8\"}]" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// anyof: books whose ref list has any of 1, 3, 7, or 100.\n", "jsonPath = \"$..book[?(@.ref anyof [1, 3, 7, 100])]\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Predicates" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{\"ref\":[2,4,16],\"category\":\"fiction\",\"title\":\"Sword of Honour\",\"author\":\"Evelyn Waugh\",\"price\":12.99}]" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// books with price between 10 and 20\n", "jsonPath = \"$..book[?(@.price > 10 && @.price < 20)]\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[{\"ref\":[1,2],\"category\":\"reference\",\"title\":\"Sayings of the Century\",\"author\":\"Nigel Rees\",\"price\":8.95},{\"ref\":[1,3,5],\"category\":\"fiction\",\"title\":\"Moby Dick\",\"author\":\"Herman Melville\",\"price\":8.99,\"isbn\":\"0-553-21311-3\"}]" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// comparing with another element\n", "jsonPath = \"$..book[?(@.price < $.expensive)]\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{\"ref\":[1,3,5],\"category\":\"fiction\",\"title\":\"Moby Dick\",\"author\":\"Herman Melville\",\"price\":8.99,\"isbn\":\"0-553-21311-3\"}]" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// composable conditions: books in fiction category with isbn and priced less than 10\n", "jsonPath = \"$..book[?(@.category == 'fiction' && @.isbn && @.price < 10 )]\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Try Your Own Path" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{store={bicycle={color=red, price=19.95}, book=[{ref=[1, 2], category=reference, title=Sayings of the Century, author=Nigel Rees, price=8.95}, {ref=[2, 4, 16], category=fiction, title=Sword of Honour, author=Evelyn Waugh, price=12.99}, {ref=[1, 3, 5], category=fiction, title=Moby Dick, author=Herman Melville, price=8.99, isbn=0-553-21311-3}, {ref=[1, 2, 7], category=fiction, title=The Lord of the Rings, author=J. R. R. Tolkien, price=22.99, isbn=0-395-19395-8}]}, expensive=10}" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// substitute your json path\n", "jsonPath = \"$\";\n", "documentClient.get(documentKey, documentBinName, jsonPath );" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cleaning Up\n", "Remove tutorial data and close connection." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Removed tutorial data and closed server connection.\n" ] } ], "source": [ "truncateTestData();\n", "aerospikeClient.close();\n", "System.out.println(\"Removed tutorial data and closed server connection.\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Further Exploration and Resources\n", "Here are some links for further exploration.\n", "\n", "## Resources\n", "- Blog posts\n", " - [Aerospike Document API](https://medium.com/aerospike-developer-blog/aerospike-document-api-fd8870b4106c)\n", " - [Aerospike Document API: JSONPath Queries](https://medium.com/aerospike-developer-blog/aerospike-document-api-jsonpath-queries-bd6260b2d076)\n", "- Related notebooks\n", " - [Working with Lists](java-working_with_lists.ipynb)\n", " - [Working with Maps](java-working_with_maps.ipynb)\n", " - [Implementing SQL Operations: SELECT](sql_select.ipynb), \n", " - [Implementing SQL Operations: CREATE, UPDATE, DELETE](sql_updates.ipynb)\n", "- Aerospike Developer Hub\n", " - [Java Developers Resources](https://developer.aerospike.com/java-developers)\n", "- Github repos\n", " - [Aerospike Document API Library](https://github.com/aerospike/aerospike-document-lib)\n", " - [Jayway JSONPath](https://github.com/json-path/JsonPath)\n", " - [Java code examples](https://github.com/aerospike/aerospike-client-java/tree/master/examples/src/com/aerospike/examples)\n", " - [Java Client](https://www.aerospike.com/docs/client/java/index.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exploring Other Notebooks\n", "\n", "Visit [Aerospike notebooks repo](https://github.com/aerospike/aerospike-dev-notebooks.docker) to run additional Aerospike notebooks. To run a different notebook, download the notebook from the repo to your local machine, and then click on File->Open in the notebook menu, and select Upload." ] } ], "metadata": { "kernelspec": { "display_name": "Java", "language": "java", "name": "java" }, "language_info": { "codemirror_mode": "java", "file_extension": ".jshell", "mimetype": "text/x-java-source", "name": "Java", "pygments_lexer": "java", "version": "11.0.8+10-LTS" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }