-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Quarkus Common Removal: Data API queryBuilder (#1022)
- Loading branch information
Showing
14 changed files
with
924 additions
and
146 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
src/main/java/io/stargate/sgv2/jsonapi/service/cql/ColumnUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Copyright DataStax, Inc. and/or The Stargate Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.stargate.sgv2.jsonapi.service.cql; | ||
|
||
import io.stargate.sgv2.api.common.cql.ReservedKeywords; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class ColumnUtils { | ||
|
||
private static final Pattern PATTERN_DOUBLE_QUOTE = Pattern.compile("\"", Pattern.LITERAL); | ||
private static final String ESCAPED_DOUBLE_QUOTE = Matcher.quoteReplacement("\"\""); | ||
/** | ||
* Updated regex pattern to support selecting collection entry lime map_column['entry_key'], | ||
* set_column['set_value'] | ||
*/ | ||
private static final Pattern UNQUOTED_IDENTIFIER = | ||
Pattern.compile("[a-z][a-z0-9_]*(\\['.*'\\])?"); | ||
|
||
/** | ||
* Given the raw (as stored internally) text of an identifier, return its CQL representation. That | ||
* is, unless the text is full lowercase and use only characters allowed in unquoted identifiers, | ||
* the result is double-quoted. | ||
*/ | ||
public static String maybeQuote(String text) { | ||
if (UNQUOTED_IDENTIFIER.matcher(text).matches() && !ReservedKeywords.isReserved(text)) { | ||
return text; | ||
} | ||
return '"' + PATTERN_DOUBLE_QUOTE.matcher(text).replaceAll(ESCAPED_DOUBLE_QUOTE) + '"'; | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
src/main/java/io/stargate/sgv2/jsonapi/service/cql/ExpressionUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Copyright The Stargate Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.stargate.sgv2.jsonapi.service.cql; | ||
|
||
import static io.stargate.sgv2.jsonapi.exception.ErrorCode.INVALID_LOGIC_OPERATOR; | ||
|
||
import com.bpodgursky.jbool_expressions.And; | ||
import com.bpodgursky.jbool_expressions.Expression; | ||
import com.bpodgursky.jbool_expressions.Or; | ||
import java.util.List; | ||
|
||
/** | ||
* Convenience expression builder | ||
* | ||
* <p>when construct jbool expression without specifying a comparator, it will use hashComparator by | ||
* default which will cause the order of expression indeterminate, and cause JSONAPI unit tests | ||
* failure By using this ExpressionUtils class, we pass a default comparator to keep expression | ||
* order as it is | ||
*/ | ||
public class ExpressionUtils<K> { | ||
|
||
public static <K> And<K> andOf(Expression<K>... expressions) { | ||
// expression as creation order | ||
return And.of(expressions, (e1, e2) -> 1); | ||
} | ||
|
||
public static <K> And<K> andOf(List<? extends Expression<K>> expressions) { | ||
// expression as creation order | ||
return And.of(expressions.toArray(new Expression[expressions.size()]), (e1, e2) -> 1); | ||
} | ||
|
||
public static <K> Or<K> orOf(List<? extends Expression<K>> expressions) { | ||
// expression as creation order | ||
return Or.of(expressions.toArray(new Expression[expressions.size()]), (e1, e2) -> 1); | ||
} | ||
|
||
public static <K> Or<K> orOf(Expression<K>... expressions) { | ||
// expression as creation order | ||
return Or.of(expressions, (e1, e2) -> 1); | ||
} | ||
|
||
public static <K> Expression<K> buildExpression( | ||
List<? extends Expression<K>> expressions, String logicOperator) { | ||
switch (logicOperator) { | ||
case "$and" -> { | ||
return andOf(expressions); | ||
} | ||
case "$or" -> { | ||
return orOf(expressions); | ||
} | ||
default -> throw INVALID_LOGIC_OPERATOR.toApiException(); | ||
} | ||
} | ||
|
||
public static <K> Expression<K>[] getAsArray(Expression<K>... expressions) { | ||
return expressions; | ||
} | ||
} |
137 changes: 137 additions & 0 deletions
137
src/main/java/io/stargate/sgv2/jsonapi/service/cql/builder/BuiltCondition.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package io.stargate.sgv2.jsonapi.service.cql.builder; | ||
|
||
import io.stargate.sgv2.api.common.cql.ColumnUtils; | ||
import io.stargate.sgv2.jsonapi.service.operation.model.impl.JsonTerm; | ||
import java.util.Objects; | ||
|
||
public final class BuiltCondition { | ||
|
||
public LHS lhs; | ||
|
||
public Predicate predicate; | ||
|
||
public JsonTerm jsonTerm; | ||
|
||
public BuiltCondition(LHS lhs, Predicate predicate, JsonTerm jsonTerm) { | ||
this.lhs = lhs; | ||
this.predicate = predicate; | ||
this.jsonTerm = jsonTerm; | ||
} | ||
|
||
public static BuiltCondition of(LHS lhs, Predicate predicate, JsonTerm jsonTerm) { | ||
return new BuiltCondition(lhs, predicate, jsonTerm); | ||
} | ||
|
||
public static BuiltCondition of(String columnName, Predicate predicate, JsonTerm jsonTerm) { | ||
return of(LHS.column(columnName), predicate, jsonTerm); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
StringBuilder builder = new StringBuilder(); | ||
// Append the LHS part of the condition | ||
if (lhs != null) { | ||
lhs.appendToBuilder(builder); | ||
} else { | ||
builder.append("null"); | ||
} | ||
// Append the predicate part of the condition | ||
if (predicate != null) { | ||
builder.append(" ").append(predicate); | ||
} else { | ||
builder.append(" null"); | ||
} | ||
// Append the JSON term part of the condition | ||
if (jsonTerm != null) { | ||
builder.append(" ").append(jsonTerm); | ||
} else { | ||
builder.append(" null"); | ||
} | ||
return builder.toString(); | ||
} | ||
|
||
/** | ||
* Represents the left hand side of a condition. | ||
* | ||
* <p>This is usually a column name, but technically can be: | ||
* | ||
* <ul> | ||
* <li>a column name ("c = ...") | ||
* <li>a specific element in a map column ("c[v] = ...") | ||
* <li>a tuple of column name ("(c, d, e) = ...") (not supported) | ||
* <li>the token of a tuple of column name ("TOKEN(c, d, e) = ...") (not supported) | ||
* </ul> | ||
*/ | ||
public abstract static class LHS { | ||
public static LHS column(String columnName) { | ||
return new ColumnName(columnName); | ||
} | ||
|
||
public static LHS mapAccess(String columnName, String key) { | ||
return new MapElement(columnName, key); | ||
} | ||
|
||
abstract void appendToBuilder(StringBuilder builder); | ||
|
||
static final class ColumnName extends LHS { | ||
private final String columnName; | ||
|
||
private ColumnName(String columnName) { | ||
this.columnName = columnName; | ||
} | ||
|
||
void appendToBuilder(StringBuilder builder) { | ||
builder.append(ColumnUtils.maybeQuote(columnName)); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if (other == this) { | ||
return true; | ||
} else if (other instanceof ColumnName) { | ||
ColumnName that = (ColumnName) other; | ||
return Objects.equals(this.columnName, that.columnName); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(columnName); | ||
} | ||
} | ||
|
||
static final class MapElement extends LHS { | ||
private final String columnName; | ||
private final String key; | ||
|
||
MapElement(String columnName, String key) { | ||
this.columnName = columnName; | ||
this.key = key; | ||
} | ||
|
||
void appendToBuilder(StringBuilder builder) { | ||
builder.append(ColumnUtils.maybeQuote(columnName)).append("[?]"); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if (other == this) { | ||
return true; | ||
} else if (other instanceof MapElement) { | ||
MapElement that = (MapElement) other; | ||
return Objects.equals(this.columnName, that.columnName) | ||
&& Objects.equals(this.key, that.key); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(columnName, key); | ||
} | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/java/io/stargate/sgv2/jsonapi/service/cql/builder/Predicate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package io.stargate.sgv2.jsonapi.service.cql.builder; | ||
|
||
public enum Predicate { | ||
EQ("="), | ||
NEQ("!="), | ||
LT("<"), | ||
GT(">"), | ||
LTE("<="), | ||
GTE(">="), | ||
IN("IN"), | ||
CONTAINS("CONTAINS"), | ||
NOT_CONTAINS("NOT CONTAINS"), | ||
CONTAINS_KEY("CONTAINS KEY"), | ||
; | ||
|
||
private final String cql; | ||
|
||
Predicate(String cql) { | ||
this.cql = cql; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return cql; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/io/stargate/sgv2/jsonapi/service/cql/builder/Query.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package io.stargate.sgv2.jsonapi.service.cql.builder; | ||
|
||
import com.datastax.oss.driver.api.core.cql.SimpleStatement; | ||
import java.util.List; | ||
|
||
/** | ||
* @param cql The query string. It can contain anonymous placeholders identified by a question mark | ||
* (?), or named placeholders prefixed by a column (:name). | ||
* @param values The values to fill the placeholders in the query string. | ||
*/ | ||
public record Query(String cql, List<Object> values) { | ||
|
||
public SimpleStatement queryToStatement() { | ||
SimpleStatement simpleStatement = SimpleStatement.newInstance(cql); | ||
return simpleStatement.setPositionalValues(values); | ||
} | ||
} |
Oops, something went wrong.