Skip to content

Commit 5578bb6

Browse files
authored
feat: support field a compares to field b for cosmosdb (#104)
1 parent dbe5b99 commit 5578bb6

7 files changed

Lines changed: 120 additions & 20 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ java-cosmos is a client for Azure CosmosDB 's SQL API (also called documentdb fo
2323
<dependency>
2424
<groupId>com.github.thunderz99</groupId>
2525
<artifactId>java-cosmos</artifactId>
26-
<version>0.6.7</version>
26+
<version>0.6.8</version>
2727
</dependency>
2828
```
2929

@@ -254,6 +254,7 @@ The main difference between Partial update and Patch is that:
254254
"firstName STARTSWITH","H", // see cosmosdb STARTSWITH
255255
"desciption CONTAINS","Project manager",// see cosmosdb CONTAINS
256256
"fullName.last RegexMatch","[A-Z]{1}ank\\w+$", // see cosmosdb RegexMatch
257+
"mail !=",Condition.key("mail2"), // using field a compares to field b
257258
"certificates IS_DEFINED",true, // see cosmosdb IS_DEFINED
258259
"families.villa IS_DEFINED",false, // see cosmosdb IS_DEFINED
259260
"age IS_NUMBER",true, // see cosmosdb type check functions

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<groupId>com.github.thunderz99</groupId>
55
<artifactId>java-cosmos</artifactId>
66
<packaging>jar</packaging>
7-
<version>0.6.7</version>
7+
<version>0.6.8</version>
88
<name>${project.groupId}:${project.artifactId}$</name>
99
<description>A lightweight Azure CosmosDB client for Java</description>
1010
<url>https://github.com/thunderz99/java-cosmos</url>

src/main/java/io/github/thunderz99/cosmos/condition/Condition.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,4 +961,15 @@ public String getRawQuerySpecJson() {
961961
return JsonUtil.toJson(this.rawQuerySpec);
962962
}
963963

964+
/**
965+
* Return a FieldKey obj representing a keyName in json documents of db
966+
*
967+
* @param keyName
968+
* @return fieldKey obj
969+
*/
970+
public static FieldKey key(String keyName) {
971+
return new FieldKey(keyName);
972+
}
973+
974+
964975
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.github.thunderz99.cosmos.condition;
2+
3+
import io.github.thunderz99.cosmos.dto.RecordData;
4+
5+
/**
6+
* A class representing a key of a json field. e.g. "name" for "c.name" or "address.country" for "c.address.country"
7+
*
8+
* <pre>
9+
* e.g. find documents where mail field does not equal mail2 field
10+
* Condition.filter("mail !=", Condition.key("mail2"))
11+
* </pre>
12+
*/
13+
public class FieldKey extends RecordData {
14+
public String keyName;
15+
16+
public FieldKey(){
17+
}
18+
19+
public FieldKey(String keyName){
20+
this.keyName = keyName;
21+
}
22+
23+
}

src/main/java/io/github/thunderz99/cosmos/condition/SimpleExpression.java

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public CosmosSqlQuerySpec toQuerySpec(AtomicInteger paramIndex) {
7070
// fullName.last -> @param001_fullName__last
7171
// or
7272
// "829cc727-2d49-4d60-8f91-b30f50560af7.name" -> @param001_wg31gsa.name
73-
var paramName = getParamNameFromKey(this.key, paramIndex.get());
73+
var paramName = getParamNameFromKey(this.key, paramIndex.getAndIncrement());
7474

7575
var paramValue = this.value;
7676

@@ -88,7 +88,6 @@ public CosmosSqlQuerySpec toQuerySpec(AtomicInteger paramIndex) {
8888
// array equals or not
8989
if (Set.of("=", "!=").contains(this.operator)) {
9090
// use = or !=
91-
paramIndex.getAndIncrement();
9291
ret.setQueryText(String.format(" (%s %s %s)", Condition.getFormattedKey(this.key), this.operator, paramName));
9392
params.add(Condition.createSqlParameter(paramName, paramValue));
9493
} else {
@@ -99,32 +98,41 @@ public CosmosSqlQuerySpec toQuerySpec(AtomicInteger paramIndex) {
9998
ret.setQueryText(" (1=0)");
10099
} else {
101100
// use ARRAY_CONTAINS by default to minimize the sql length
102-
paramIndex.getAndIncrement();
103101
ret.setQueryText(buildArrayContains(this.key, paramName, coll, params));
104102
}
105103
}
106104

107105
} else {
108-
// single param value
106+
// single param value
109107

110-
if (StringUtils.isEmpty(this.operator)) {
111-
// set the default operator for scalar value
112-
this.operator = "=";
113-
}
108+
if (StringUtils.isEmpty(this.operator)) {
109+
// set the default operator for scalar value
110+
this.operator = "=";
111+
}
112+
113+
// paramName or fieldName
114+
var valuePart = "";
115+
116+
if (paramValue instanceof FieldKey) {
117+
// valuePart should be "mail2" for "c.mail != c.mail2"
118+
valuePart = Condition.getFormattedKey(((FieldKey) paramValue).keyName);
119+
} else {
120+
// valuePart should be "@param001_wg31gsa"
121+
valuePart = paramName;
122+
params.add(Condition.createSqlParameter(paramName, paramValue));
123+
}
114124

115-
paramIndex.getAndIncrement();
116-
// other types
117-
var formattedKey = Condition.getFormattedKey(this.key);
125+
// other types
126+
var formattedKey = Condition.getFormattedKey(this.key);
118127
if (this.type == OperatorType.BINARY_OPERATOR) { // operators, e.g. =, !=, <, >, LIKE
119128
//use c["key"] for cosmosdb reserved words
120-
ret.setQueryText(String.format(" (%s %s %s)", formattedKey, this.operator, paramName));
129+
ret.setQueryText(String.format(" (%s %s %s)", formattedKey, this.operator, valuePart));
121130
} else if (Condition.typeCheckFunctionPattern.asMatchPredicate().test(this.operator)) { // type check funcs: IS_DEFINED|IS_NUMBER|IS_PRIMITIVE, etc
122-
ret.setQueryText(String.format(" (%s(%s) = %s)", this.operator, formattedKey, paramName));
131+
ret.setQueryText(String.format(" (%s(%s) = %s)", this.operator, formattedKey, valuePart));
123132
} else { // other binary funcs. e.g. STARTSWITH, CONTAINS, ARRAY_CONTAINS
124-
ret.setQueryText(String.format(" (%s(%s, %s))", this.operator, formattedKey, paramName));
133+
ret.setQueryText(String.format(" (%s(%s, %s))", this.operator, formattedKey, valuePart));
125134
}
126135

127-
params.add(Condition.createSqlParameter(paramName, paramValue));
128136
}
129137

130138
ret.setParameters(params);

src/test/java/io/github/thunderz99/cosmos/CosmosDatabaseTest.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,64 @@ public void regex_should_work_with_filter() throws Exception {
838838
}
839839

840840
}
841-
841+
842+
@Test
843+
public void field_a_equals_field_b_should_work_with_filter() throws Exception {
844+
845+
// test condition that use field a and field b
846+
var id1 = "field_a_equals_field_b_should_work_with_filter1";
847+
var id2 = "field_a_equals_field_b_should_work_with_filter2";
848+
var partition = "FieldTest";
849+
try {
850+
851+
var data1 = Map.of("id", id1, "mail", "mail1", "uniqueKey", "aaa");
852+
var data2 = Map.of("id", id2, "mail", "mail2", "uniqueKey", "mail2");
853+
854+
db.upsert(coll, data1, partition);
855+
db.upsert(coll, data2, partition);
856+
857+
{
858+
// equal
859+
var cond = Condition.filter("mail", Condition.key("uniqueKey") //
860+
).sort("id", "ASC") //
861+
.limit(10) //
862+
.offset(0);
863+
864+
var results = db.find(coll, cond, partition).toMap();
865+
866+
assertThat(results.size()).isEqualTo(1);
867+
assertThat(results.get(0)).containsEntry("id", id2);
868+
}
869+
{
870+
// not equal
871+
var cond = Condition.filter("mail !=", Condition.key("uniqueKey") //
872+
).sort("id", "ASC") //
873+
.limit(10) //
874+
.offset(0);
875+
876+
var results = db.find(coll, cond, partition).toMap();
877+
878+
assertThat(results.size()).isEqualTo(1);
879+
assertThat(results.get(0)).containsEntry("id", id1);
880+
}
881+
{
882+
// greater than
883+
var cond = Condition.filter("mail >=", Condition.key("uniqueKey") //
884+
).sort("id", "ASC") //
885+
.limit(10) //
886+
.offset(0);
887+
888+
var results = db.find(coll, cond, partition).toMap();
889+
890+
assertThat(results.size()).isEqualTo(2);
891+
}
892+
} finally {
893+
db.delete(coll, id1, partition);
894+
db.delete(coll, id2, partition);
895+
}
896+
897+
}
898+
842899
@Test
843900
void aggregate_should_work() throws Exception {
844901
// test aggregate(simple)

src/test/java/io/github/thunderz99/cosmos/condition/ConditionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,8 @@ public void buildQuerySpec_should_work_empty_list() {
425425
{
426426
var q = Condition.filter("id", List.of(), "name", "Tom").toQuerySpec();
427427

428-
assertThat(q.getQueryText().trim()).isEqualTo("SELECT * FROM c WHERE (1=0) AND (c[\"name\"] = @param000_name) OFFSET 0 LIMIT 100");
429-
assertThat(q.getParameters()).hasSize(1);
428+
assertThat(q.getQueryText().trim()).isEqualTo("SELECT * FROM c WHERE (1=0) AND (c[\"name\"] = @param001_name) OFFSET 0 LIMIT 100");
429+
assertThat(q.getParameters()).hasSize(1);
430430
}
431431

432432
{

0 commit comments

Comments
 (0)