Skip to content

Commit

Permalink
feat(msgpack-json-el): allow non-strict condtions
Browse files Browse the repository at this point in the history
Allows JsonPath conditions to be non-strict, evaluating to null when no values were found
  • Loading branch information
iamtakingiteasy committed Dec 17, 2018
1 parent 81c06d5 commit c8f4480
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 14 deletions.
29 changes: 28 additions & 1 deletion docs/src/reference/json-conditions.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,33 @@ $.orderCount >= 5 && $.orderCount < 15
</table>

A Null-Value can be used to check if a property is set (e.g., `$.owner == null`).
If the property doesn't exist, then the JSON Path evaluation fails.
If the any property in specified JSON Path doesn't exist, then it would be resolved as `null` and could be compared as such.

<table style="width:100%">
<tr>
<th>Payload</th>
<th>JSON Path</th>
<th>Value</th
</tr>

<tr>
<td>{"foo": 3}</td>
<td>$.foo</td>
<td>3</td>
</tr>

<tr>
<td>{"foo": 3}</td>
<td>$.bar</td>
<td>null</td>
</tr>

<tr>
<td>{"foo": 3}</td>
<td>$.bar.baz</td>
<td>null</td>
</tr>
</table>

### Comparison Operators

Expand Down Expand Up @@ -100,6 +126,7 @@ If the property doesn't exist, then the JSON Path evaluation fails.
The operators `<`, `<=`, `>` and `>=` can only be used for numbers.

If the values of an operator have different types, then the evaluation fails.
Comparing null or missing property with a number is considered as comparing different types.

### Logical Operators

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ public class JsonConditionInterpreter {

public boolean eval(final JsonCondition condition, final DirectBuffer json) {
cache.wrap(json);

return evalCondition(condition, json);
}

private boolean evalCondition(final JsonCondition condition, final DirectBuffer json) {
boolean isFulFilled = false;
final boolean isFulFilled;

if (condition instanceof Comparison) {
isFulFilled = evalComparison((Comparison) condition, json);
Expand Down Expand Up @@ -105,9 +104,17 @@ private MsgPackToken getPathResult(
final DirectBuffer resultBuffer = cachable ? cache.get(pathId) : null;

if (resultBuffer != null) {
if (resultBuffer.capacity() == 0) {
return MsgPackToken.NIL;
}
msgPackReader.wrap(resultBuffer, 0, resultBuffer.capacity());
} else {
readQueryResult(path.query(), json);
if (!readQueryResult(path.query(), json)) {
if (cachable) {
cache.put(pathId, 0, 0);
}
return MsgPackToken.NIL;
}

final int offset = visitor.currentResultPosition();
final int length = visitor.currentResultLength();
Expand All @@ -122,15 +129,14 @@ private MsgPackToken getPathResult(
return msgPackReader.readToken();
}

private void readQueryResult(JsonPathQuery query, DirectBuffer json) {
private boolean readQueryResult(JsonPathQuery query, DirectBuffer json) {
visitor.init(query.getFilters(), query.getFilterInstances());
traverser.wrap(json, 0, json.capacity());

traverser.traverse(visitor);

if (visitor.numResults() == 0) {
throw new JsonConditionException(
String.format("JSON path '%s' has no result.", bufferAsString(query.getExpression())));
return false;
} else if (visitor.numResults() > 1) {
// such a JSON path expression should not be valid
throw new JsonConditionException(
Expand All @@ -139,13 +145,12 @@ private void readQueryResult(JsonPathQuery query, DirectBuffer json) {
}

visitor.moveToResult(0);
return true;
}

private boolean equals(MsgPackToken x, MsgPackToken y) {
if (x.getType() == MsgPackType.NIL) {
return y.getType() == MsgPackType.NIL;
} else if (y.getType() == MsgPackType.NIL) {
return false;
if (x.getType() == MsgPackType.NIL || y.getType() == MsgPackType.NIL) {
return x.getType() == y.getType();
} else {
ensureSameType(x, y);

Expand Down
25 changes: 23 additions & 2 deletions json-el/src/test/java/io/zeebe/msgpack/el/JsonConditionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,37 @@ public void shouldFailIfTypeDoesntMatch() {
}

@Test
public void shouldFailIfJsonPathDoesntMatch() {
public void shouldFailIfMissingPropertyComparedRelatively() {
final CompiledJsonCondition condition = JsonConditionFactory.createCondition("$.foo > 3");
assertThat(condition.isValid()).isTrue();

thrown.expect(JsonConditionException.class);
thrown.expectMessage("JSON path '$.foo' has no result");
thrown.expectMessage("Cannot compare values of different types: NIL and INTEGER");

interpreter.eval(condition.getCondition(), asMsgPack("bar", 4));
}

@Test
public void shouldEqualToNullIfJsonPathDoesntMatch() {
final CompiledJsonCondition condition = JsonConditionFactory.createCondition("$.foo == null");
assertThat(condition.isValid()).isTrue();

final boolean result = interpreter.eval(condition.getCondition(), asMsgPack("bar", 4));

assertThat(result).isTrue();
}

@Test
public void shouldEqualToNullIfAnySegmentInJsonPathDoesntMatch() {
final CompiledJsonCondition condition =
JsonConditionFactory.createCondition("$.foo.baz == null");
assertThat(condition.isValid()).isTrue();

final boolean result = interpreter.eval(condition.getCondition(), asMsgPack("bar", 4));

assertThat(result).isTrue();
}

@Test
public void shouldFailIfTypeIsNil() {
final CompiledJsonCondition condition = JsonConditionFactory.createCondition("$.foo > 3");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
import org.agrona.concurrent.UnsafeBuffer;

public class MsgPackToken {
public static final MsgPackToken NIL = new MsgPackToken();

protected static final int MAX_MAP_ELEMENTS = 0x3fff_ffff;

protected MsgPackType type;
protected MsgPackType type = MsgPackType.NIL;
protected int totalLength;

// string
Expand Down

0 comments on commit c8f4480

Please sign in to comment.