Skip to content

Commit

Permalink
FMWK-126 Release 2.0.0 preparation (#91)
Browse files Browse the repository at this point in the history
Co-authored-by: yrizhkov <[email protected]>
  • Loading branch information
agrgr and reugn authored Jan 26, 2023
1 parent c2fe9ec commit 8fa31ba
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 112 deletions.
104 changes: 69 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

This project provides an API for accessing and mutating Aerospike
[Collection Data Type](https://www.aerospike.com/docs/client/java/index.html) (CDT)
objects using a [JSONPath](https://goessner.net/articles/JsonPath/) syntax.
objects using [JSONPath](https://goessner.net/articles/JsonPath/) syntax.
This effectively provides a document API, with CDT objects used to represent
JSON documents in the Aerospike database.

Expand All @@ -15,8 +15,8 @@ The documentation for this project can be found on [javadoc.io](https://www.java

### Assumptions

* Familiarity with the Aerospike client for Java (see [Introduction - Java Client](https://www.aerospike.com/docs/client/java/index.html))
* Some knowledge of Aerospike CDTs (see reference above)
- Familiarity with the Aerospike client for Java (see [Introduction - Java Client](https://www.aerospike.com/docs/client/java/index.html))
- Some knowledge of the Aerospike CDTs (see reference above)

## Getting Started Blog Posts

Expand All @@ -36,7 +36,7 @@ Add the Maven dependency:
<dependency>
<groupId>com.aerospike</groupId>
<artifactId>aerospike-document-api</artifactId>
<version>1.2.0</version>
<version>2.0.0</version>
</dependency>
```

Expand Down Expand Up @@ -88,63 +88,63 @@ The Aerospike Document Client is instantiated as follows
* You can create a new AerospikeClient using other constructors - in this example we are using IP and Port only.

``` java
AerospikeClient client = new AerospikeClient(AEROSPIKE_SERVER_IP, AEROSPIKE_SERVER_PORT);
AerospikeDocumentClient documentClient = new AerospikeDocumentClient(client);
AerospikeClient client = new AerospikeClient(AEROSPIKE_SERVER_IP, AEROSPIKE_SERVER_PORT);
AerospikeDocumentClient documentClient = new AerospikeDocumentClient(client);
```

### Create

We add the example JSON document to our Aerospike database as follows

``` java
JsonNode jsonNode = JsonConverters.convertStringToJsonNode(jsonString);
// For details of Aerospike namespace/set/key see https://www.aerospike.com/docs/architecture/data-model.html
Key tommyLeeJonesDBKey = new Key(AEROSPIKE_NAMESPACE, AEROSPIKE_SET, "tommy-lee-jones.json");
String documentBinName = "documentBin";
documentClient.put(tommyLeeJonesDBKey, documentBinName, jsonNode);
JsonNode jsonNode = JsonConverters.convertStringToJsonNode(jsonString);
// For details of Aerospike namespace/set/key see https://www.aerospike.com/docs/architecture/data-model.html
Key tommyLeeJonesDBKey = new Key(AEROSPIKE_NAMESPACE, AEROSPIKE_SET, "tommy-lee-jones.json");
String documentBinName = "documentBin";
documentClient.put(tommyLeeJonesDBKey, documentBinName, jsonNode);
```

### Insert

We can add filmography for 2019 using the JSONPath ```$.selected_filmography.2019```

```java
List<String> _2019Films = new Vector<String>();
_2019Films.add("Ad Astra");
documentClient.put(tommyLeeJonesDBKey, documentBinName, "$.selected_filmography.2019",_2019Films);
List<String> _2019Films = new Vector<String>();
_2019Films.add("Ad Astra");
documentClient.put(tommyLeeJonesDBKey, documentBinName, "$.selected_filmography.2019",_2019Films);
```

### Update

Update Jones' IMDB ranking using the JSONPath ```$.imdb_rank.rank```

``` java
documentClient.put(tommyLeeJonesDBKey, documentBinName, "$.imdb_rank.rank",45);
documentClient.put(tommyLeeJonesDBKey, documentBinName, "$.imdb_rank.rank",45);
```

### Append

We can append to 'Rotten Tomatoes' list of best films using the reference ```$.best_films_ranked[0].films```

```java
documentClient.append(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[0].films","Rolling Thunder");
documentClient.append(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[0].films","The Three Burials");
documentClient.append(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[0].films","Rolling Thunder");
documentClient.append(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[0].films","The Three Burials");
```

### Delete

We can delete a node e.g. the Medium reviewer's rankings

```java
documentClient.delete(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[1]");
documentClient.delete(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[1]");
```

### Get

We can find out the name of Jones' best film according to 'Rotten Tomatoes' using the JSONPath ```$.best_films_ranked[0].films[0]```

```java
documentClient.get(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[0].films[0]");
documentClient.get(tommyLeeJonesDBKey, documentBinName, "$.best_films_ranked[0].films[0]");
```

## JSONPath Queries
Expand Down Expand Up @@ -379,20 +379,51 @@ jsonPath = "$.authentication.login[?(@.id > 10)]";
objectFromDB = documentClient.get(TEST_AEROSPIKE_KEY, bins, jsonPath);
```

### JSONPath query operations

Depending on how JSONPath query operations run they can be split into 2 types.

#### 1-step JSONPath query operations

Operations that use JSONPath containing only array and/or map elements.

Examples:

$.store.book, $[0], $.store.book[0], $.store.book[0][1].title.

#### 2-step JSONPath query operations

Operations that use JSONPath containing wildcards, recursive descent, filters, functions, scripts.

Examples:

$.store.book[*].author, $.store..price, $.store.book[?(@.price < 10)], $..book[(@.length-1)].

## Batch operations

Starting at version `2.0.0` there is support for batch operations.

You can now send operations (GET, PUT, APPEND, DELETE) in batches using json path or JSONPath query
for single and multiple bins.
You can now send CRUD operations (PUT, GET, APPEND, DELETE) in batches using JSONPath
for single and multiple bins.
Each operation in a batch is performed on a single Aerospike key.

Limitations:

Keys related limitations:
| | Unique key<br/>within batch | Non-unique key<br/>within batch | Multiple batch operations<br/>having the same key and the same bin(s) |
|-------------------------------------------------------|-----------------------------|------------------------------------------------------------|-----------------------------------------------------------------------|
| [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 |
| [2-step operation](#2-step-jsonpath-query-operations) | Supported | Not supported | Not supported |

- Operations order in a batch is preserved only for the operations with different keys.
- JSONPath queries operations are allowed in a batch only if they don't have repeating keys.
Results are returned as a List of BatchRecord objects, each of them contains the following:

A use-case example can be sending a batch of operations at once to update bins storing events,
or append values for single bins storing analytics, when many steps of the same kind need to be executed in sequence.
- Aerospike key.
- Result code (0 in case of operation finished successfully or another predefined number
referring to a particular exception / error).
- Record (contains requested values mapped to their respective bin names,
relevant in case of the GET operation).

A use-case example can be sending a batch of operations at once to update bins storing events,
or append values for single bins storing analytics, when many steps of the same kind need to be performed.

### Using batch operations

Expand Down Expand Up @@ -441,30 +472,33 @@ BatchOperation operation5 = new GetBatchOperation(
BatchOperation operation6 = new PutBatchOperation(
key6,
Collections.singletonList(documentBinName2),
"$.authentication..device",
"Mobile"
"$.best_filmes_ranked[*].films[0]",
"Men In Black 2"
);

// Get from multiple similarly structured bins
// Assuming we have multiple similarly structured bins to read from
String binName1 = "events1Bin";
String binName2 = "events2Bin";
String binName3 = "events3Bin";
List<String> bins = new ArrayList<>();
bins.add(binName1);
bins.add(binName2);
bins.add(binName3);
BatchOperation operation7 = new GetBatchOperation(
key7,
bins,
"$.authentication.logout.name"
"$.imdb_rank.source"
);

// Collecting operations
// Collecting operations and running
List<BatchOperation> batchOpsList = new ArrayList<>();
batchOpsList.add(operation1, operation2, operation3, operation4, operation5, operation6, operation7);
documentClient.batchPerform(batchOpsList, true);
batchOpsList.add(operation1, operation2, operation3, operation4,
operation5, operation6, operation7);
List<BatchRecord> results = documentClient.batchPerform(batchOpsList, true);
// Checking that all operations finished successfully
assertEquals(0, results.stream().filter(res -> res.resultCode != 0).count());
```


## References

* 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.

10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.aerospike</groupId>
<artifactId>aerospike-document-api</artifactId>
<version>1.2.0</version>
<version>2.0.0</version>

<name>Aerospike Document API</name>
<description>This project provides an API which allows Aerospike CDT (Collection Data Type) objects to be accessed
Expand All @@ -29,11 +29,11 @@
<maven.javadoc.plugin.version>3.3.0</maven.javadoc.plugin.version>
<maven.gpg.plugin.version>1.6</maven.gpg.plugin.version>
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
<jackson-databind.version>2.13.3</jackson-databind.version>
<aerospike-client.version>6.1.4</aerospike-client.version>
<jackson-databind.version>2.14.1</jackson-databind.version>
<aerospike-client.version>6.1.6</aerospike-client.version>
<json-path.version>2.7.0</json-path.version>
<lombok.version>1.18.22</lombok.version>
<junit-jupiter.version>5.9.0</junit-jupiter.version>
<lombok.version>1.18.24</lombok.version>
<junit-jupiter.version>5.9.2</junit-jupiter.version>
<mockito.version>4.9.0</mockito.version>
</properties>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public JsonPrefixException(String jsonString) {
*/
public static class JsonPathException extends DocumentApiException {
public JsonPathException(String jsonString) {
super(String.format("'%s' does not match JsonPath format", jsonString));
super(String.format("'%s' does not match JSONPath format", jsonString));
}
}

Expand All @@ -69,7 +69,7 @@ public JsonAppendException(String jsonString) {
*/
public static class JsonPathParseException extends DocumentApiException {
public JsonPathParseException(String jsonPathPart) {
super(String.format("Unable to parse '%s' as jsonPath token", jsonPathPart));
super(String.format("Unable to parse '%s' as JSONPath token", jsonPathPart));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,12 @@ public interface IAerospikeDocumentClient {

/**
* Perform batch operations.
* <p>Operations order is preserved only for the operations with different keys.</p>
* <p>The order and consistency of one-step (JSON path) operations with the same keys is not guaranteed.</p>
* <p>Two-step (JSONPath query) operations with the same keys are not allowed in a batch.</p>
*
* <p>Operations order is preserved only for those 1-step operations
* (with JSONPath that contains only array and/or map elements)
* that have unique Aerospike keys within a batch.</p>
* <p>Every 2-step operation (with JSONPath containing wildcards, recursive descent, filters, functions, scripts)
* should have unique Aerospike key within a batch.
*
* @param batchOperations a list of batch operations to apply.
* @param parallel whether batch processing stream operations should run in parallel.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import com.aerospike.documentapi.jsonpath.PathDetails;
import com.aerospike.documentapi.util.Lut;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Value;

import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -91,7 +89,7 @@ protected Map<String, Object> firstStepQueryResults() {
}

protected Object firstStepJsonPathQuery(Map.Entry<String, Object> entry) {
throw new UnsupportedOperationException("Not implemented");
throw new UnsupportedOperationException("Raw use of a method that should be called from extending classes");
}

protected Operation toPutOperation(String binName, Object objToPut, PathDetails pathDetails) {
Expand Down Expand Up @@ -125,11 +123,4 @@ record = new Record(bins, 0, 0);

return new BatchRecord(key, record, -2, false, true);
}

@Value
@RequiredArgsConstructor
protected static class Bin {
String name;
Object value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.aerospike.documentapi.token.ContextAwareToken;
import com.aerospike.documentapi.token.Token;
import com.aerospike.documentapi.token.TokenType;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -79,8 +78,22 @@ public void setJsonPathSecondStepQuery(String jsonPathSecondStepQuery) {

public void appendToJsonPathQuery(Token queryToken) {
String tokenString = queryToken.getQueryConcatString();
jsonPathSecondStepQuery += jsonPathSecondStepQuery.isEmpty()
? queryToken.getType() == TokenType.FUNCTION ? DOT + tokenString : tokenString
: DOT + tokenString;
switch (queryToken.getType()) {
case LIST:
case LIST_WILDCARD:
case SCAN:
case FILTER:
jsonPathSecondStepQuery += tokenString;
break;
case ROOT:
throw new UnsupportedOperationException(
"Unsupported operation: root token cannot be added to JSONPath query");
case MAP:
case FUNCTION:
case WILDCARD:
default:
jsonPathSecondStepQuery += DOT + tokenString;
break;
}
}
}
Loading

0 comments on commit 8fa31ba

Please sign in to comment.