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" +