Skip to content

Commit

Permalink
Changes for all and size operator (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
maheshrajamani authored Feb 9, 2023
1 parent 49d86c6 commit 75ad05e
Show file tree
Hide file tree
Showing 9 changed files with 545 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.stargate.sgv2.jsonapi.api.model.command.clause.filter;

/** List of element level operator that can be used in Filter clause */
public enum ArrayComparisonOperator implements FilterOperator {
ALL("$all"),
SIZE("$size");

private String operator;

ArrayComparisonOperator(String operator) {
this.operator = operator;
}

@Override
public String getOperator() {
return operator;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,12 @@ private static JsonLiteral<?> getLiteral(Object value) {
if (value instanceof String) {
return new JsonLiteral<>((String) value, JsonType.STRING);
}
if (value instanceof List) {
return new JsonLiteral<>((List<Object>) value, JsonType.ARRAY);
}
throw new JsonApiException(
ErrorCode.FILTER_UNRESOLVABLE, String.format("Unsupported filter value type %s", value));
ErrorCode.FILTER_UNRESOLVABLE,
String.format("Unsupported filter value type %s", value.getClass()));
}

public List<FilterOperation> match(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class FilterOperatorUtils {
for (FilterOperator filterOperator : ElementComparisonOperator.values()) {
addComparisonOperator(filterOperator);
}
for (FilterOperator filterOperator : ArrayComparisonOperator.values()) {
addComparisonOperator(filterOperator);
}
}

private static void addComparisonOperator(FilterOperator filterOperator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.ArrayNode;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ComparisonExpression;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.FilterClause;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.FilterOperator;
Expand Down Expand Up @@ -82,6 +83,10 @@ private static Object jsonNodeValue(String path, JsonNode node) {
if (path.equals(DocumentConstants.Fields.DOC_ID)) {
return DocumentId.fromJson(node);
}
return jsonNodeValue(node);
}

private static Object jsonNodeValue(JsonNode node) {
switch (node.getNodeType()) {
case BOOLEAN:
return node.booleanValue();
Expand All @@ -91,6 +96,15 @@ private static Object jsonNodeValue(String path, JsonNode node) {
return node.textValue();
case NULL:
return null;
case ARRAY:
{
ArrayNode arrayNode = (ArrayNode) node;
List<Object> arrayVals = new ArrayList<>(arrayNode.size());
for (JsonNode element : arrayNode) {
arrayVals.add(jsonNodeValue(element));
}
return arrayVals;
}
default:
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_DATA_TYPE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.stargate.sgv2.jsonapi.service.bridge.executor.QueryExecutor;
import io.stargate.sgv2.jsonapi.service.bridge.serializer.CustomValueSerializers;
import io.stargate.sgv2.jsonapi.service.operation.model.ReadOperation;
import io.stargate.sgv2.jsonapi.service.shredding.model.DocValueHasher;
import io.stargate.sgv2.jsonapi.service.shredding.model.DocumentId;
import java.math.BigDecimal;
import java.util.ArrayList;
Expand Down Expand Up @@ -249,17 +250,52 @@ public ExistsFilter(String path, boolean existFlag) {
}
}

/** Filter for document where all values exists for an array */
public static class AllFilter extends SetFilterBase<String> {
public AllFilter(DocValueHasher hasher, String path, Object arrayValue) {
super("array_contains", getHashValue(hasher, path, arrayValue), Operator.CONTAINS);
}
}

/** Filter for document where array has specified number of elements */
public static class SizeFilter extends MapFilterBase<Integer> {
public SizeFilter(String path, Integer size) {
super("array_size", path, Operator.EQ, size);
}
}

private static QueryOuterClass.Value getValue(Object value) {
if (value instanceof String) {
return Values.of((String) value);
} else if (value instanceof BigDecimal) {
return Values.of((BigDecimal) value);
} else if (value instanceof Byte) {
return Values.of((Byte) value);
} else if (value instanceof Integer) {
return Values.of((Integer) value);
}
return Values.of((String) null);
}

private static String getHashValue(DocValueHasher hasher, String path, Object arrayValue) {
return path + " " + getHash(hasher, arrayValue);
}

private static String getHash(DocValueHasher hasher, Object arrayValue) {
if (arrayValue == null) {
return hasher.nullValue().hash().hash();
} else if (arrayValue instanceof String) {
return hasher.stringValue((String) arrayValue).hash().hash();
} else if (arrayValue instanceof BigDecimal) {
return hasher.numberValue((BigDecimal) arrayValue).hash().hash();
} else if (arrayValue instanceof Boolean) {
return hasher.booleanValue((Boolean) arrayValue).hash().hash();
}
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_DATA_TYPE,
String.format("Unsupported filter data type %s", arrayValue.getClass()));
}

private static QueryOuterClass.Value getDocumentIdValue(DocumentId value) {
return Values.of(CustomValueSerializers.getDocumentIdValue(value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import io.stargate.sgv2.jsonapi.api.model.command.Command;
import io.stargate.sgv2.jsonapi.api.model.command.CommandContext;
import io.stargate.sgv2.jsonapi.api.model.command.Filterable;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ArrayComparisonOperator;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ElementComparisonOperator;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.JsonType;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ValueComparisonOperator;
import io.stargate.sgv2.jsonapi.exception.ErrorCode;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.service.operation.model.ReadOperation;
import io.stargate.sgv2.jsonapi.service.operation.model.impl.FindOperation;
import io.stargate.sgv2.jsonapi.service.shredding.model.DocValueHasher;
import io.stargate.sgv2.jsonapi.service.shredding.model.DocumentId;
import java.math.BigDecimal;
import java.util.ArrayList;
Expand All @@ -37,6 +39,8 @@ public abstract class FilterableResolver<T extends Command & Filterable> {
private static final Object DYNAMIC_BOOL_GROUP = new Object();
private static final Object DYNAMIC_NULL_GROUP = new Object();
private static final Object EXISTS_GROUP = new Object();
private static final Object ALL_GROUP = new Object();
private static final Object SIZE_GROUP = new Object();

private final boolean findOne;
private final boolean readDocument;
Expand Down Expand Up @@ -65,7 +69,7 @@ public FilterableResolver(ObjectMapper objectMapper, boolean findOne, boolean re
.addMatchRule(this::findDynamic, FilterMatcher.MatchStrategy.GREEDY)
.matcher()
.capture(ID_GROUP)
.compareValues("_id", EnumSet.of(ValueComparisonOperator.EQ), JsonType.STRING)
.compareValues("_id", EnumSet.of(ValueComparisonOperator.EQ), JsonType.DOCUMENT_ID)
.capture(DYNAMIC_NUMBER_GROUP)
.compareValues("*", EnumSet.of(ValueComparisonOperator.EQ), JsonType.NUMBER)
.capture(DYNAMIC_TEXT_GROUP)
Expand All @@ -75,8 +79,11 @@ public FilterableResolver(ObjectMapper objectMapper, boolean findOne, boolean re
.capture(DYNAMIC_NULL_GROUP)
.compareValues("*", EnumSet.of(ValueComparisonOperator.EQ), JsonType.NULL)
.capture(EXISTS_GROUP)
.compareValues("*", EnumSet.of(ElementComparisonOperator.EXISTS), JsonType.BOOLEAN);
;
.compareValues("*", EnumSet.of(ElementComparisonOperator.EXISTS), JsonType.BOOLEAN)
.capture(ALL_GROUP)
.compareValues("*", EnumSet.of(ArrayComparisonOperator.ALL), JsonType.ARRAY)
.capture(SIZE_GROUP)
.compareValues("*", EnumSet.of(ArrayComparisonOperator.SIZE), JsonType.NUMBER);
}

protected ReadOperation resolve(CommandContext commandContext, T command) {
Expand Down Expand Up @@ -192,6 +199,28 @@ private ReadOperation findDynamic(CommandContext commandContext, CaptureGroups<T
});
}

final CaptureGroup<List<Object>> allGroups =
(CaptureGroup<List<Object>>) captures.getGroupIfPresent(ALL_GROUP);
if (allGroups != null) {
allGroups.consumeAllCaptures(
expression -> {
final DocValueHasher docValueHasher = new DocValueHasher();
for (Object arrayValue : expression.value()) {
filters.add(
new FindOperation.AllFilter(docValueHasher, expression.path(), arrayValue));
}
});
}

final CaptureGroup<BigDecimal> sizeGroups =
(CaptureGroup<BigDecimal>) captures.getGroupIfPresent(SIZE_GROUP);
if (sizeGroups != null) {
sizeGroups.consumeAllCaptures(
expression ->
filters.add(
new FindOperation.SizeFilter(expression.path(), expression.value().intValue())));
}

FilteringOptions filteringOptions = getFilteringOption(captures.command());
return new FindOperation(
commandContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ArrayComparisonOperator;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ComparisonExpression;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.ElementComparisonOperator;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.FilterClause;
Expand Down Expand Up @@ -146,10 +147,44 @@ public void mustHandleExists() throws Exception {
assertThat(filterClause.comparisonExpressions()).hasSize(1).contains(expectedResult);
}

@Test
public void mustHandleAll() throws Exception {
String json =
"""
{"allPath" : {"$all": ["a", "b"]}}
""";
final ComparisonExpression expectedResult =
new ComparisonExpression(
"allPath",
List.of(
new ValueComparisonOperation(
ArrayComparisonOperator.ALL,
new JsonLiteral(List.of("a", "b"), JsonType.ARRAY))));
FilterClause filterClause = objectMapper.readValue(json, FilterClause.class);
assertThat(filterClause.comparisonExpressions()).hasSize(1).contains(expectedResult);
}

@Test
public void mustHandleSize() throws Exception {
String json =
"""
{"sizePath" : {"$size": 2}}
""";
final ComparisonExpression expectedResult =
new ComparisonExpression(
"sizePath",
List.of(
new ValueComparisonOperation(
ArrayComparisonOperator.SIZE,
new JsonLiteral(new BigDecimal(2), JsonType.NUMBER))));
FilterClause filterClause = objectMapper.readValue(json, FilterClause.class);
assertThat(filterClause.comparisonExpressions()).hasSize(1).contains(expectedResult);
}

@Test
public void unsupportedFilterTypes() {
String json = """
{"boolType": ["a"]}
{"boolType": {"a" : "b"}}
""";

Throwable throwable = catchThrowable(() -> objectMapper.readValue(json, FilterClause.class));
Expand Down
Loading

0 comments on commit 75ad05e

Please sign in to comment.