Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes with cassandra not operator #721

Merged
merged 18 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
*/
public enum ValueComparisonOperator implements FilterOperator {
EQ("$eq"),
NE("$ne"),
IN("$in"),
NIN("$nin"),
GT("$gt"),
GTE("$gte"),
LT("$lt"),
LTE("$lte");
/*NE("$ne");*/

private String operator;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ private void validate(
switch (valueComparisonOperator) {
case IN -> {
if (filterOperation.operand().value() instanceof List<?> list) {
if (list.size() > operationsConfig.defaultPageSize()) {
if (list.size() > operationsConfig.maxInOperatorValueSize()) {
Yuqi-Du marked this conversation as resolved.
Show resolved Hide resolved
throw new JsonApiException(
ErrorCode.INVALID_FILTER_EXPRESSION,
"$in operator must have at most "
Expand All @@ -173,17 +173,27 @@ private void validate(
ErrorCode.INVALID_FILTER_EXPRESSION, "$in operator must have `ARRAY`");
}
}
case NIN -> {
if (filterOperation.operand().value() instanceof List<?> list) {
if (list.size() > operationsConfig.maxInOperatorValueSize()) {
throw new JsonApiException(
ErrorCode.INVALID_FILTER_EXPRESSION,
"$nin operator must have at most "
+ operationsConfig.maxInOperatorValueSize()
+ " values");
}
} else {
throw new JsonApiException(
ErrorCode.INVALID_FILTER_EXPRESSION, "$nin operator must have `ARRAY`");
}
}
}
}

if (filterOperation.operator() instanceof ElementComparisonOperator elementComparisonOperator) {
switch (elementComparisonOperator) {
case EXISTS:
if (filterOperation.operand().value() instanceof Boolean b) {
if (!b)
throw new JsonApiException(
ErrorCode.INVALID_FILTER_EXPRESSION, "$exists operator supports only true");
} else {
if (!(filterOperation.operand().value() instanceof Boolean)) {
throw new JsonApiException(
ErrorCode.INVALID_FILTER_EXPRESSION, "$exists operator must have `BOOLEAN`");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ public GeneralResource(MeteredCommandProcessor meteredCommandProcessor) {
})))
@POST
public Uni<RestResponse<CommandResult>> postCommand(@NotNull @Valid GeneralCommand command) {

// call processor
return meteredCommandProcessor
.processCommand(CommandContext.empty(), command)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ public interface OperationsConfig {
@WithDefault("20")
int maxDocumentInsertCount();

/**
* @return Maximum size of _id values array that can be sent in $in operator <code>100</code>
* command.
*/
/** @return Maximum size of values array that can be sent in $in/$nin operator */
@Max(100)
@Positive
@WithDefault("100")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ interface Fields {
*/
String DATA_CONTAINS = "array_contains";

/** Text map support _id $ne and _id $nin on both atomic value and array element */
String QUERY_TEXT_MAP_COLUMN_NAME = "query_text_values";

/** Physical table column name that stores the vector field. */
String VECTOR_SEARCH_INDEX_COLUMN_NAME = "query_vector_value";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public Uni<Supplier<CommandResult>> execute(QueryExecutor queryExecutor) {
}

private SimpleStatement buildSelectQuery() {
List<Expression<BuiltCondition>> expressions =
final List<Expression<BuiltCondition>> expressions =
ExpressionBuilder.buildExpressions(logicalExpression, null);
List<Object> collect = new ArrayList<>();
if (expressions != null && !expressions.isEmpty() && expressions.get(0) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,13 @@ default Uni<FindResponse> findDocument(
.items(queries.stream())
.onItem()
.transformToUniAndMerge(
query -> {
simpleStatement -> {
if (vectorSearch) {
return queryExecutor.executeVectorSearch(
query, Optional.ofNullable(pageState), pageSize);
simpleStatement, Optional.ofNullable(pageState), pageSize);
} else {
return queryExecutor.executeRead(query, Optional.ofNullable(pageState), pageSize);
return queryExecutor.executeRead(
simpleStatement, Optional.ofNullable(pageState), pageSize);
}
})
.onItem()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.stargate.sgv2.jsonapi.service.operation.model.impl;

import static io.stargate.sgv2.jsonapi.config.constants.DocumentConstants.Fields.DATA_CONTAINS;
import static io.stargate.sgv2.jsonapi.config.constants.DocumentConstants.Fields.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
Expand All @@ -10,7 +10,6 @@
import io.stargate.bridge.proto.QueryOuterClass;
import io.stargate.sgv2.api.common.cql.builder.BuiltCondition;
import io.stargate.sgv2.api.common.cql.builder.Predicate;
import io.stargate.sgv2.jsonapi.config.constants.DocumentConstants;
import io.stargate.sgv2.jsonapi.exception.ErrorCode;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.service.cqldriver.serializer.CQLBindValues;
Expand All @@ -20,10 +19,7 @@
import io.stargate.sgv2.jsonapi.util.JsonUtil;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -71,10 +67,20 @@ public enum Operator {
*/
MAP_EQUALS,
/**
* This represents eq operation for array element or automic value operation against
* This represents ne to be run against map type index columns like array_size, sub_doc_equals
* and array_equals.
*/
Yuqi-Du marked this conversation as resolved.
Show resolved Hide resolved
MAP_NOT_EQUALS,
/**
* This represents eq operation for array element or atomic value operation against
* array_contains
*/
EQ,
/**
* This represents NE operation for array element or atomic value operation against
* array_contains
*/
NE,
/**
* This represents greater than to be run against map type index columns for number and date
* type
Expand Down Expand Up @@ -134,11 +140,21 @@ public BuiltCondition get() {
DATA_CONTAINS,
Predicate.CONTAINS,
new JsonTerm(getHashValue(new DocValueHasher(), key, value)));
case NE:
return BuiltCondition.of(
DATA_CONTAINS,
Predicate.NOT_CONTAINS,
new JsonTerm(getHashValue(new DocValueHasher(), key, value)));
case MAP_EQUALS:
return BuiltCondition.of(
BuiltCondition.LHS.mapAccess(columnName, Values.NULL),
Predicate.EQ,
new JsonTerm(key, value));
case MAP_NOT_EQUALS:
return BuiltCondition.of(
BuiltCondition.LHS.mapAccess(columnName, Values.NULL),
Predicate.NEQ,
new JsonTerm(key, value));
case GT:
return BuiltCondition.of(
BuiltCondition.LHS.mapAccess(columnName, Values.NULL),
Expand Down Expand Up @@ -251,7 +267,8 @@ boolean canAddField() {
public static class IDFilter extends DBFilterBase {
public enum Operator {
EQ,
IN;
NE,
IN
}

protected final IDFilter.Operator operator;
Expand All @@ -262,7 +279,7 @@ public IDFilter(IDFilter.Operator operator, DocumentId value) {
}

public IDFilter(IDFilter.Operator operator, List<DocumentId> values) {
super(DocumentConstants.Fields.DOC_ID);
super(DOC_ID);
this.operator = operator;
this.values = values;
}
Expand Down Expand Up @@ -294,7 +311,25 @@ public List<BuiltCondition> getAll() {
BuiltCondition.LHS.column("key"),
Predicate.EQ,
new JsonTerm(CQLBindValues.getDocumentIdValue(values.get(0)))));

case NE:
final DocumentId documentId = (DocumentId) values.get(0);
if (documentId.value() instanceof BigDecimal numberId) {
return List.of(
BuiltCondition.of(
BuiltCondition.LHS.mapAccess("query_dbl_values", Values.NULL),
Predicate.NEQ,
new JsonTerm(DOC_ID, numberId)));
} else if (documentId.value() instanceof String strId) {
return List.of(
BuiltCondition.of(
BuiltCondition.LHS.mapAccess("query_text_values", Values.NULL),
Predicate.NEQ,
new JsonTerm(DOC_ID, strId)));
} else {
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_DATA_TYPE,
String.format("Unsupported $ne operand value : %s", documentId.value()));
}
case IN:
if (values.isEmpty()) return List.of();
return values.stream()
Expand All @@ -305,7 +340,6 @@ public List<BuiltCondition> getAll() {
Predicate.EQ,
new JsonTerm(CQLBindValues.getDocumentIdValue(v))))
.collect(Collectors.toList());

default:
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_OPERATION,
Expand All @@ -328,9 +362,7 @@ boolean canAddField() {
}
}

/**
* based on values of fields other than document id: for filtering on non-id field use InFilter.
*/
/** non_id($in, $nin), _id($nin) */
public static class InFilter extends DBFilterBase {
private final List<Object> arrayValue;
protected final InFilter.Operator operator;
Expand All @@ -344,9 +376,10 @@ JsonNode asJson(JsonNodeFactory nodeFactory) {
boolean canAddField() {
return false;
}
// IN operator for non-id field filtering

public enum Operator {
IN;
IN,
NIN,
}

public InFilter(InFilter.Operator operator, String path, List<Object> arrayValue) {
Expand Down Expand Up @@ -386,7 +419,46 @@ public List<BuiltCondition> getAll() {
Predicate.CONTAINS,
new JsonTerm(getHashValue(new DocValueHasher(), getPath(), v))))
.collect(Collectors.toList());

case NIN:
if (values.isEmpty()) return List.of();
if (!this.getPath().equals(DOC_ID)) {
return values.stream()
.map(
v ->
BuiltCondition.of(
DATA_CONTAINS,
Predicate.NOT_CONTAINS,
new JsonTerm(getHashValue(new DocValueHasher(), getPath(), v))))
.collect(Collectors.toList());
} else {
// can not use stream here, since lambda parameter casting is not allowed
List<BuiltCondition> conditions = new ArrayList<>();
for (Object value : values) {
if (value instanceof DocumentId) {
Object docIdValue = ((DocumentId) value).value();
if (docIdValue instanceof BigDecimal numberId) {
BuiltCondition condition =
BuiltCondition.of(
BuiltCondition.LHS.mapAccess("query_dbl_values", Values.NULL),
Predicate.NEQ,
new JsonTerm(DOC_ID, numberId));
conditions.add(condition);
} else if (docIdValue instanceof String strId) {
BuiltCondition condition =
BuiltCondition.of(
BuiltCondition.LHS.mapAccess("query_text_values", Values.NULL),
Predicate.NEQ,
new JsonTerm(DOC_ID, strId));
conditions.add(condition);
} else {
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_DATA_TYPE,
String.format("Unsupported $nin operand value: %s", docIdValue));
}
}
}
return conditions;
}
default:
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_OPERATION,
Expand All @@ -398,7 +470,8 @@ public List<BuiltCondition> getAll() {
/** DB filter / condition for testing a set value */
public abstract static class SetFilterBase<T> extends DBFilterBase {
public enum Operator {
CONTAINS;
CONTAINS,
NOT_CONTAINS;
}

protected final String columnName;
Expand Down Expand Up @@ -433,6 +506,8 @@ public BuiltCondition get() {
switch (operator) {
case CONTAINS:
return BuiltCondition.of(columnName, Predicate.CONTAINS, new JsonTerm(value));
case NOT_CONTAINS:
return BuiltCondition.of(columnName, Predicate.NOT_CONTAINS, new JsonTerm(value));
default:
throw new JsonApiException(
ErrorCode.UNSUPPORTED_FILTER_OPERATION,
Expand All @@ -447,8 +522,8 @@ public BuiltCondition get() {
* <p>NOTE: cannot do != null until we get NOT CONTAINS in the DB for set
*/
public static class IsNullFilter extends SetFilterBase<String> {
public IsNullFilter(String path) {
super("query_null_values", path, path, Operator.CONTAINS);
public IsNullFilter(String path, SetFilterBase.Operator operator) {
super("query_null_values", path, path, operator);
}

@Override
Expand All @@ -463,13 +538,13 @@ boolean canAddField() {
}

/**
* Filter for document where a field exists
* Filter for document where a field exists or not
*
* <p>NOTE: cannot do != null until we get NOT CONTAINS in the DB for set
* <p>NOTE: cannot do != null until we get NOT CONTAINS in the DB for set ?????TODO
*/
public static class ExistsFilter extends SetFilterBase<String> {
public ExistsFilter(String path, boolean existFlag) {
super("exist_keys", path, path, Operator.CONTAINS);
super("exist_keys", path, path, existFlag ? Operator.CONTAINS : Operator.NOT_CONTAINS);
}

@Override
Expand Down Expand Up @@ -519,13 +594,16 @@ boolean canAddField() {
return false;
}
}

/** Filter for document where array matches (data in same order) as the array in request */
public static class ArrayEqualsFilter extends MapFilterBase<String> {
private final List<Object> arrayValue;

public ArrayEqualsFilter(DocValueHasher hasher, String path, List<Object> arrayData) {
super("query_text_values", path, Operator.MAP_EQUALS, getHash(hasher, arrayData));
public ArrayEqualsFilter(
DocValueHasher hasher,
String path,
List<Object> arrayData,
MapFilterBase.Operator operator) {
super("query_text_values", path, operator, getHash(hasher, arrayData));
this.arrayValue = arrayData;
}

Expand All @@ -547,8 +625,12 @@ boolean canAddField() {
public static class SubDocEqualsFilter extends MapFilterBase<String> {
private final Map<String, Object> subDocValue;

public SubDocEqualsFilter(DocValueHasher hasher, String path, Map<String, Object> subDocData) {
super("query_text_values", path, Operator.MAP_EQUALS, getHash(hasher, subDocData));
public SubDocEqualsFilter(
DocValueHasher hasher,
String path,
Map<String, Object> subDocData,
MapFilterBase.Operator operator) {
super("query_text_values", path, operator, getHash(hasher, subDocData));
this.subDocValue = subDocData;
}

Expand Down
Loading