This is an automated email from the ASF dual-hosted git repository.

vjasani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/master by this push:
     new 328855df3b PHOENIX-7662 BSON Condition Function contains() (#2218)
328855df3b is described below

commit 328855df3b4f5e7fb8c06f1fed17f01c16523cf5
Author: Viraj Jasani <vjas...@apache.org>
AuthorDate: Fri Jul 11 13:59:45 2025 -0700

    PHOENIX-7662 BSON Condition Function contains() (#2218)
---
 .../src/main/antlr3/PhoenixBsonExpression.g        |   3 +
 .../util/bson/CommonComparisonExpressionUtils.java |  18 +++
 .../util/bson/SQLComparisonExpressionUtils.java    |  72 ++++++++-
 .../util/bson/UpdateExpressionUtils.java           |  26 +---
 .../parse/DocumentFieldContainsParseNode.java      |  62 ++++++++
 .../org/apache/phoenix/parse/ParseNodeFactory.java |   5 +
 .../java/org/apache/phoenix/end2end/Bson1IT.java   |  48 +++++-
 .../util/bson/ComparisonExpressionUtilsTest.java   | 165 +++++++++++++++++++++
 8 files changed, 376 insertions(+), 23 deletions(-)

diff --git a/phoenix-core-client/src/main/antlr3/PhoenixBsonExpression.g 
b/phoenix-core-client/src/main/antlr3/PhoenixBsonExpression.g
index 86313300ef..9edccc51d2 100644
--- a/phoenix-core-client/src/main/antlr3/PhoenixBsonExpression.g
+++ b/phoenix-core-client/src/main/antlr3/PhoenixBsonExpression.g
@@ -33,6 +33,7 @@ tokens
     FIELD = 'field_exists';
     FIELD_NOT = 'field_not_exists';
     BEGINS_WITH = 'begins_with';
+    CONTAINS = 'contains';
 }
 
 @parser::header {
@@ -235,6 +236,8 @@ boolean_expression returns [ParseNode ret]
         |   (ATTR_NOT | FIELD_NOT) ( LPAREN t=literal RPAREN {$ret = 
factory.documentFieldExists(t, false); } )
         |   BEGINS_WITH ( LPAREN l=value_expression COMMA r=value_expression 
RPAREN
                 {$ret = factory.documentFieldBeginsWith(l, r); } )
+        |   CONTAINS ( LPAREN l=value_expression COMMA r=value_expression 
RPAREN
+                {$ret = factory.documentFieldContains(l, r); } )
     ;
 
 value_expression returns [ParseNode ret]
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/CommonComparisonExpressionUtils.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/CommonComparisonExpressionUtils.java
index af739b720d..51944a7468 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/CommonComparisonExpressionUtils.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/CommonComparisonExpressionUtils.java
@@ -39,6 +39,24 @@ public class CommonComparisonExpressionUtils {
   private static final Logger LOGGER =
       LoggerFactory.getLogger(CommonComparisonExpressionUtils.class);
 
+  /**
+   * Returns true if the given BsonValue represents Set data structure.
+   *
+   * @param bsonValue The value.
+   * @return True if the given BsonValue represents Set data structure.
+   */
+  static boolean isBsonSet(final BsonValue bsonValue) {
+    if (!bsonValue.isDocument()) {
+      return false;
+    }
+    BsonDocument bsonDocument = (BsonDocument) bsonValue;
+    if (bsonDocument.size() == 1 && bsonDocument.containsKey("$set")) {
+      BsonValue value = bsonDocument.get("$set");
+      return value != null && value.isArray();
+    }
+    return false;
+  }
+
   /**
    * Comparison operators supported for the Document value comparisons.
    */
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/SQLComparisonExpressionUtils.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/SQLComparisonExpressionUtils.java
index 92c22fc6ff..8f9126168a 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/SQLComparisonExpressionUtils.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/SQLComparisonExpressionUtils.java
@@ -7,7 +7,7 @@
  * "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
+ *     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,
@@ -22,6 +22,7 @@ import org.apache.phoenix.parse.AndParseNode;
 import org.apache.phoenix.parse.BetweenParseNode;
 import org.apache.phoenix.parse.DocumentFieldExistsParseNode;
 import org.apache.phoenix.parse.DocumentFieldBeginsWithParseNode;
+import org.apache.phoenix.parse.DocumentFieldContainsParseNode;
 import org.apache.phoenix.parse.BsonExpressionParser;
 import org.apache.phoenix.parse.EqualParseNode;
 import org.apache.phoenix.parse.GreaterThanOrEqualParseNode;
@@ -34,6 +35,7 @@ import org.apache.phoenix.parse.NotEqualParseNode;
 import org.apache.phoenix.parse.NotParseNode;
 import org.apache.phoenix.parse.OrParseNode;
 import org.apache.phoenix.parse.ParseNode;
+import org.bson.BsonArray;
 import org.bson.BsonDocument;
 import org.bson.BsonString;
 import org.bson.BsonValue;
@@ -177,6 +179,17 @@ public final class SQLComparisonExpressionUtils {
       fieldName = replaceExpressionFieldNames(fieldName, keyAliasDocument, 
sortedKeyNames);
       final String prefixValue = (String) value.getValue();
       return beginsWith(fieldName, prefixValue, rawBsonDocument, 
comparisonValuesDocument);
+    } else if (parseNode instanceof DocumentFieldContainsParseNode) {
+      final DocumentFieldContainsParseNode documentFieldContainsParseNode =
+              (DocumentFieldContainsParseNode) parseNode;
+      final LiteralParseNode fieldKey =
+              (LiteralParseNode) documentFieldContainsParseNode.getFieldKey();
+      final LiteralParseNode value =
+              (LiteralParseNode) documentFieldContainsParseNode.getValue();
+      String fieldName = (String) fieldKey.getValue();
+      fieldName = replaceExpressionFieldNames(fieldName, keyAliasDocument, 
sortedKeyNames);
+      final String containsValue = (String) value.getValue();
+      return contains(fieldName, containsValue, rawBsonDocument, 
comparisonValuesDocument);
     } else if (parseNode instanceof EqualParseNode) {
       final EqualParseNode equalParseNode = (EqualParseNode) parseNode;
       final LiteralParseNode lhs = (LiteralParseNode) equalParseNode.getLHS();
@@ -576,4 +589,61 @@ public final class SQLComparisonExpressionUtils {
     }
   }
 
+  /**
+   * Returns true if the value of the field contains the specified value. The 
field can be:
+   * - A String that contains a particular substring
+   * - A Set that contains a particular element within the set
+   * - A List that contains a particular element within the list
+   * For other data types, returns false.
+   *
+   * @param fieldKey The field key for which value is checked for contains.
+   * @param containsValue The value to check against the field value.
+   * @param rawBsonDocument Bson Document representing the cell value on which 
the comparison is
+   * to be performed.
+   * @param comparisonValuesDocument Bson Document with values placeholder.
+   * @return True if the value of the field contains containsValue, false 
otherwise.
+   */
+  private static boolean contains(final String fieldKey, final String 
containsValue,
+                                  final RawBsonDocument rawBsonDocument,
+                                  final BsonDocument comparisonValuesDocument) 
{
+    BsonValue topLevelValue = rawBsonDocument.get(fieldKey);
+    BsonValue fieldValue = topLevelValue != null ?
+            topLevelValue :
+            CommonComparisonExpressionUtils.getFieldFromDocument(fieldKey, 
rawBsonDocument);
+    if (fieldValue == null) {
+      return false;
+    }
+    BsonValue containsBsonValue = comparisonValuesDocument.get(containsValue);
+    if (containsBsonValue == null) {
+      return false;
+    }
+
+    if (fieldValue.isString()) {
+      if (!containsBsonValue.isString()) {
+        return false;
+      }
+      String fieldStr = ((BsonString) fieldValue).getValue();
+      String containsStr = ((BsonString) containsBsonValue).getValue();
+      return fieldStr.contains(containsStr);
+    } else if (fieldValue.isArray()) {
+      List<BsonValue> fieldValues = ((BsonArray) fieldValue).getValues();
+      return fieldValues.stream().anyMatch(element -> areEqual(element, 
containsBsonValue));
+    } else if (CommonComparisonExpressionUtils.isBsonSet(fieldValue)) {
+      List<BsonValue> fieldValues =
+              ((BsonArray) ((BsonDocument) 
fieldValue).get("$set")).getValues();
+      return fieldValues.stream().anyMatch(element -> areEqual(element, 
containsBsonValue));
+    }
+    return false;
+  }
+
+  private static boolean areEqual(BsonValue value1, BsonValue value2) {
+    if (value1 == null && value2 == null) {
+      return true;
+    }
+    if (value1 == null || value2 == null) {
+      return false;
+    }
+    return value1.equals(value2);
+  }
+
 }
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/UpdateExpressionUtils.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/UpdateExpressionUtils.java
index 031a93b04c..02420076a1 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/UpdateExpressionUtils.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/expression/util/bson/UpdateExpressionUtils.java
@@ -147,7 +147,7 @@ public class UpdateExpressionUtils {
       String fieldKey = deleteEntry.getKey();
       BsonValue newVal = deleteEntry.getValue();
       BsonValue topLevelValue = bsonDocument.get(fieldKey);
-      if (!isBsonSet(newVal)) {
+      if (!CommonComparisonExpressionUtils.isBsonSet(newVal)) {
         throw new RuntimeException("Type of new value to be removed should be 
sets only");
       }
       // If the top level field exists, perform the operation here and return.
@@ -225,7 +225,8 @@ public class UpdateExpressionUtils {
       String fieldKey = addEntry.getKey();
       BsonValue newVal = addEntry.getValue();
       BsonValue topLevelValue = bsonDocument.get(fieldKey);
-      if (!newVal.isNumber() && !newVal.isDecimal128() && !isBsonSet(newVal)) {
+      if (!newVal.isNumber() && !newVal.isDecimal128()
+              && !CommonComparisonExpressionUtils.isBsonSet(newVal)) {
         throw new RuntimeException(
             "Type of new value to be updated should be either number or sets 
only");
       }
@@ -777,24 +778,6 @@ public class UpdateExpressionUtils {
     }
   }
 
-  /**
-   * Returns true if the given BsonValue represents Set data structure.
-   *
-   * @param bsonValue The value.
-   * @return True if the given BsonValue represents Set data structure.
-   */
-  private static boolean isBsonSet(final BsonValue bsonValue) {
-    if (!bsonValue.isDocument()) {
-      return false;
-    }
-    BsonDocument bsonDocument = (BsonDocument) bsonValue;
-    if (bsonDocument.size() == 1 && bsonDocument.containsKey("$set")) {
-      BsonValue value = bsonDocument.get("$set");
-      return value != null && value.isArray();
-    }
-    return false;
-  }
-
   /**
    * Returns true if both values represent Set data structure and the contents 
of the Set are
    * of same type.
@@ -806,7 +789,8 @@ public class UpdateExpressionUtils {
    */
   private static boolean areBsonSetOfSameType(final BsonValue bsonValue1,
       final BsonValue bsonValue2) {
-    if (!isBsonSet(bsonValue1) || !isBsonSet(bsonValue2)) {
+    if (!CommonComparisonExpressionUtils.isBsonSet(bsonValue1)
+            || !CommonComparisonExpressionUtils.isBsonSet(bsonValue2)) {
       return false;
     }
     BsonArray bsonArray1 = (BsonArray) ((BsonDocument) bsonValue1).get("$set");
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/DocumentFieldContainsParseNode.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/DocumentFieldContainsParseNode.java
new file mode 100644
index 0000000000..e8e736f734
--- /dev/null
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/DocumentFieldContainsParseNode.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.phoenix.parse;
+
+import org.apache.phoenix.compile.ColumnResolver;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Parse Node to help determine whether the document field contains a given 
value.
+ */
+public class DocumentFieldContainsParseNode extends CompoundParseNode {
+
+    DocumentFieldContainsParseNode(ParseNode fieldKey, ParseNode value) {
+        super(Arrays.asList(fieldKey, value));
+    }
+
+    @Override
+    public <T> T accept(ParseNodeVisitor<T> visitor) throws SQLException {
+        List<T> l = java.util.Collections.emptyList();
+        if (visitor.visitEnter(this)) {
+            l = acceptChildren(visitor);
+        }
+        return visitor.visitLeave(this, l);
+    }
+
+    @Override
+    public void toSQL(ColumnResolver resolver, StringBuilder buf) {
+        List<ParseNode> children = getChildren();
+        buf.append("contains(");
+        children.get(0).toSQL(resolver, buf);
+        buf.append(", ");
+        children.get(1).toSQL(resolver, buf);
+        buf.append(")");
+    }
+
+    public ParseNode getFieldKey() {
+        return getChildren().get(0);
+    }
+
+    public ParseNode getValue() {
+        return getChildren().get(1);
+    }
+}
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index f70f4ad16f..d48352d125 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -303,6 +303,11 @@ public class ParseNodeFactory {
         return new DocumentFieldBeginsWithParseNode(fieldKey, value);
     }
 
+    public DocumentFieldContainsParseNode documentFieldContains(ParseNode 
fieldKey,
+                                                                ParseNode 
value) {
+        return new DocumentFieldContainsParseNode(fieldKey, value);
+    }
+
     public ColumnDef columnDef(ColumnName columnDefName, String sqlTypeName,
                                boolean isArray, Integer arrSize, Boolean 
isNull,
                                Integer maxLength, Integer scale, boolean isPK,
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson1IT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson1IT.java
index 5ed87f4837..f93f85b64e 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson1IT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson1IT.java
@@ -207,6 +207,49 @@ public class Bson1IT extends ParallelStatsDisabledIT {
       assertEquals(bsonDocument2, document2);
 
       assertFalse(rs.next());
+
+      conditionExpression =
+              "begins_with(Title, :TitlePrefix) AND contains(#attr_5, 
:Attr5Value) "
+                      + "AND contains(#0, :NestedList1String)";
+
+      conditionDoc = new BsonDocument();
+      conditionDoc.put("$EXPR", new BsonString(conditionExpression));
+      conditionDoc.put("$VAL", compareValuesDocument);
+      BsonDocument keyDoc = new BsonDocument();
+      keyDoc.put("#attr_5", new BsonString("attr_5"));
+      keyDoc.put("#0", new BsonString("NestedList1"));
+      conditionDoc.put("$KEYS", keyDoc);
+
+      query = "SELECT * FROM " + tableName + " WHERE 
BSON_CONDITION_EXPRESSION(COL, '"
+              + conditionDoc.toJson() + "')";
+      rs = conn.createStatement().executeQuery(query);
+
+      assertTrue(rs.next());
+      assertEquals("pk0002", rs.getString(1));
+      assertEquals(4596.354, rs.getDouble(2), 0.0);
+      document2 = (BsonDocument) rs.getObject(3);
+      assertEquals(bsonDocument2, document2);
+
+      assertFalse(rs.next());
+
+      conditionExpression =
+              "contains(attr_5, :NonExistentValue) OR begins_with(Title, 
:TitlePrefix)";
+
+      conditionDoc = new BsonDocument();
+      conditionDoc.put("$EXPR", new BsonString(conditionExpression));
+      conditionDoc.put("$VAL", compareValuesDocument);
+
+      query = "SELECT * FROM " + tableName + " WHERE 
BSON_CONDITION_EXPRESSION(COL, '"
+              + conditionDoc.toJson() + "')";
+      rs = conn.createStatement().executeQuery(query);
+
+      assertTrue(rs.next());
+      assertEquals("pk0002", rs.getString(1));
+      assertEquals(4596.354, rs.getDouble(2), 0.0);
+      document2 = (BsonDocument) rs.getObject(3);
+      assertEquals(bsonDocument2, document2);
+
+      assertFalse(rs.next());
     }
   }
 
@@ -222,7 +265,10 @@ public class Bson1IT extends ParallelStatsDisabledIT {
             "  \":Ids1\" : \"12\",\n" +
             "  \":NMap1_NList1\" : \"NListVal01\",\n" +
             "  \":InPublication\" : false,\n" +
-            "  \":NestedList1_xyz0123\" : \"xyz0123\"\n" +
+            "  \":NestedList1_xyz0123\" : \"xyz0123\",\n" +
+            "  \":Attr5Value\" : \"str001\",\n" +
+            "  \":NestedList1String\" : \"1234abcd\",\n" +
+            "  \":NonExistentValue\" : \"does_not_exist\"\n" +
             "}";
     return RawBsonDocument.parse(json);
   }
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/util/bson/ComparisonExpressionUtilsTest.java
 
b/phoenix-core/src/test/java/org/apache/phoenix/util/bson/ComparisonExpressionUtilsTest.java
index a6eafb6be7..1178cbcb0a 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/util/bson/ComparisonExpressionUtilsTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/util/bson/ComparisonExpressionUtilsTest.java
@@ -1665,6 +1665,171 @@ public class ComparisonExpressionUtilsTest {
             rawBsonDocument, compareValues));
   }
 
+  @Test
+  public void testContainsFunction() {
+    RawBsonDocument rawBsonDocument = getContainsTestDocument();
+    RawBsonDocument compareValues = getContainsCompareValDocument();
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :TitleSubstring)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Description, :DescriptionWord)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(NestedMap1.Title, :TitleSubstring)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :NonExistentSubstring)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Description, :WrongWord)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Tags, :TagScience)", rawBsonDocument, compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Numbers, :NumberFive)", rawBsonDocument, compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(NestedList1, :NestedListString)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Tags, :NonExistentTag)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Numbers, :NumberTen)", rawBsonDocument, compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Categories, :CategoryFiction)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(StatusSet, :StatusActive)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Categories, :NonExistentCategory)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(StatusSet, :StatusInactive)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(BinaryDataSet, :BinaryHello)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(BinaryDataSet, :BinaryNotFound)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :TitleSubstring) AND contains(Tags, 
:TagScience)", rawBsonDocument,
+            compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :NonExistentSubstring) OR contains(Tags, 
:TagScience)",
+            rawBsonDocument, compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :NonExistentSubstring) AND contains(Tags, 
:TagScience)",
+            rawBsonDocument, compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "NOT contains(Title, :NonExistentSubstring)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "NOT contains(Title, :TitleSubstring)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(NonExistentField, :TitleSubstring)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :NonExistentValue)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Title, :NumberFive)", rawBsonDocument, compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Numbers, :TitleSubstring)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(Id, :TitleSubstring)", rawBsonDocument, compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(InPublication, :TitleSubstring)", rawBsonDocument, 
compareValues));
+
+    assertTrue(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(NestedMap1.SubTags, :TagMath)", rawBsonDocument, 
compareValues));
+
+    assertFalse(SQLComparisonExpressionUtils.evaluateConditionExpression(
+            "contains(NestedMap1.SubTags, :NonExistentTag)", rawBsonDocument, 
compareValues));
+  }
+
+  private static RawBsonDocument getContainsTestDocument() {
+    String json = "{\n" +
+            "  \"Title\" : \"Advanced Data Science and Machine Learning\",\n" +
+            "  \"Description\" : \"This book covers comprehensive topics in 
Quantum Computing\",\n" +
+            "  \"Tags\" : [ \"science\", \"technology\", \"programming\", 
\"AI\" ],\n" +
+            "  \"Numbers\" : [ 1, 2, 3, 5, 8, 13 ],\n" +
+            "  \"NestedList1\" : [ -485.34, \"1234abcd\", \"xyz0123\", 
\"test_string\" ],\n" +
+            "  \"Categories\" : { \"$set\" : [ \"fiction\", \"educational\", 
\"technical\" ] },\n" +
+            "  \"StatusSet\" : { \"$set\" : [ \"active\", \"published\", 
\"available\" ] },\n" +
+            "  \"BinaryDataSet\" : { \"$set\" : [ {\n" +
+            "    \"$binary\" : {\n" +
+            "      \"base64\" : \"SGVsbG8=\",\n" +
+            "      \"subType\" : \"00\"\n" +
+            "    }\n" +
+            "  }, {\n" +
+            "    \"$binary\" : {\n" +
+            "      \"base64\" : \"V29ybGQ=\",\n" +
+            "      \"subType\" : \"00\"\n" +
+            "    }\n" +
+            "  }, {\n" +
+            "    \"$binary\" : {\n" +
+            "      \"base64\" : \"VGVzdA==\",\n" +
+            "      \"subType\" : \"00\"\n" +
+            "    }\n" +
+            "  } ] },\n" +
+            "  \"NestedMap1\" : {\n" +
+            "    \"Title\" : \"Nested Advanced Data Science Guide\",\n" +
+            "    \"SubTags\" : [ \"mathematics\", \"statistics\", 
\"algorithms\" ],\n" +
+            "    \"InnerSet\" : { \"$set\" : [ \"regression\", 
\"classification\" ] }\n" +
+            "  },\n" +
+            "  \"Id\" : 101.01,\n" +
+            "  \"InPublication\" : true\n" +
+            "}";
+    return RawBsonDocument.parse(json);
+  }
+
+  private static RawBsonDocument getContainsCompareValDocument() {
+    String json = "{\n" +
+            "  \":TitleSubstring\" : \"Data Science\",\n" +
+            "  \":DescriptionWord\" : \"Quantum Comput\",\n" +
+            "  \":NonExistentSubstring\" : \"Quantum Physics\",\n" +
+            "  \":WrongWord\" : \"geology\",\n" +
+            "  \":TagScience\" : \"science\",\n" +
+            "  \":TagMath\" : \"mathematics\",\n" +
+            "  \":NonExistentTag\" : \"biology\",\n" +
+            "  \":NumberFive\" : 5,\n" +
+            "  \":NumberTen\" : 10,\n" +
+            "  \":NestedListString\" : \"test_string\",\n" +
+            "  \":CategoryFiction\" : \"fiction\",\n" +
+            "  \":NonExistentCategory\" : \"romance\",\n" +
+            "  \":StatusActive\" : \"active\",\n" +
+            "  \":StatusInactive\" : \"inactive\",\n" +
+            "  \":NonExistentValue\" : \"does_not_exist\",\n" +
+            "  \":BinaryHello\" : {\n" +
+            "    \"$binary\" : {\n" +
+            "      \"base64\" : \"SGVsbG8=\",\n" +
+            "      \"subType\" : \"00\"\n" +
+            "    }\n" +
+            "  },\n" +
+            "  \":BinaryNotFound\" : {\n" +
+            "    \"$binary\" : {\n" +
+            "      \"base64\" : \"Tm90Rm91bmQ=\",\n" +
+            "      \"subType\" : \"00\"\n" +
+            "    }\n" +
+            "  }\n" +
+            "}";
+    return RawBsonDocument.parse(json);
+  }
+
   private static RawBsonDocument getCompareValDocument() {
     String json = "{\n" +
             "  \"$Id20\" : 101.011,\n" +

Reply via email to