content: /*
   * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
   *
   * This program is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, version 2.0, as published by the
   * Free Software Foundation.
   *
   * This program is also distributed with certain software (including but not
   * limited to OpenSSL) that is licensed under separate terms, as designated in a
   * particular file or component or in included license documentation. The
   * authors of MySQL hereby grant you an additional permission to link the
   * program and your derivative works with the separately licensed software that
   * they have included with MySQL.
   *
   * Without limiting anything contained in the foregoing, this file, which is
   * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
   * version 1.0, a copy of which can be found at
   * http://oss.oracle.com/licenses/universal-foss-exception.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
   * for more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
   */
  
  package testsuite.x.devapi;
  
  import static com.mysql.cj.xdevapi.Expression.expr;
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertFalse;
  import static org.junit.jupiter.api.Assertions.assertNotNull;
  import static org.junit.jupiter.api.Assertions.assertNull;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  import static org.junit.jupiter.api.Assumptions.assumeTrue;
  
  import java.io.StringReader;
  import java.math.BigDecimal;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.concurrent.Callable;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.ExecutionException;
  
  import org.junit.jupiter.api.Disabled;
  import org.junit.jupiter.api.Test;
  
  import com.mysql.cj.ServerVersion;
  import com.mysql.cj.xdevapi.AddResult;
  import com.mysql.cj.xdevapi.Collection;
  import com.mysql.cj.xdevapi.DbDoc;
  import com.mysql.cj.xdevapi.DbDocImpl;
  import com.mysql.cj.xdevapi.DocResult;
  import com.mysql.cj.xdevapi.JsonArray;
  import com.mysql.cj.xdevapi.JsonLiteral;
  import com.mysql.cj.xdevapi.JsonNumber;
  import com.mysql.cj.xdevapi.JsonParser;
  import com.mysql.cj.xdevapi.JsonString;
  import com.mysql.cj.xdevapi.ModifyStatement;
  import com.mysql.cj.xdevapi.ModifyStatementImpl;
  import com.mysql.cj.xdevapi.Result;
  import com.mysql.cj.xdevapi.Session;
  import com.mysql.cj.xdevapi.SessionFactory;
  import com.mysql.cj.xdevapi.XDevAPIError;
  
  /**
   * @todo
   */
  public class CollectionModifyTest extends BaseCollectionTestCase {
      @Test
      public void testSet() {
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\"}").execute(); // Requires manual _id.
          } else {
              this.collection.add("{}").execute();
          }
  
          this.collection.modify("true").set("a", "Value for a").execute();
          this.collection.modify("1 == 1").set("b", "Value for b").execute();
          this.collection.modify("false").set("c", "Value for c").execute();
          this.collection.modify("0 == 1").set("d", "Value for d").execute();
  
          DocResult res = this.collection.find("a = 'Value for a'").execute();
          DbDoc jd = res.next();
          assertEquals("Value for a", ((JsonString) jd.get("a")).getString());
          assertEquals("Value for b", ((JsonString) jd.get("b")).getString());
          assertNull(jd.get("c"));
          assertNull(jd.get("d"));
      }
  
      @Test
      public void testUnset() {
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\", \"x\":\"100\", \"y\":\"200\", \"z\":1}").execute(); // Requires manual _id.
              this.collection.add("{\"_id\": \"2\", \"a\":\"100\", \"b\":\"200\", \"c\":1}").execute();
          } else {
              this.collection.add("{\"x\":\"100\", \"y\":\"200\", \"z\":1}").execute();
              this.collection.add("{\"a\":\"100\", \"b\":\"200\", \"c\":1}").execute();
          }
  
          this.collection.modify("true").unset("$.x").unset("$.y").execute();
          this.collection.modify("true").unset("$.a", "$.b").execute();
  
          DocResult res = this.collection.find().execute();
          DbDoc jd = res.next();
          assertNull(jd.get("x"));
          assertNull(jd.get("y"));
          assertNull(jd.get("a"));
          assertNull(jd.get("b"));
      }
  
      @Test
      public void testReplace() {
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\", \"x\":100}").execute(); // Requires manual _id.
          } else {
              this.collection.add("{\"x\":100}").execute();
          }
          this.collection.modify("true").change("$.x", "99").execute();
  
          DocResult res = this.collection.find().execute();
          DbDoc jd = res.next();
          assertEquals("99", ((JsonString) jd.get("x")).getString());
      }
  
      @Test
      public void testArrayAppend() {
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\", \"x\":[8,16,32]}").execute(); // Requires manual _id.
          } else {
              this.collection.add("{\"x\":[8,16,32]}").execute();
          }
          this.collection.modify("true").arrayAppend("$.x", "64").execute();
  
          DocResult res = this.collection.find().execute();
          DbDoc jd = res.next();
          JsonArray xArray = (JsonArray) jd.get("x");
          assertEquals(new Integer(8), ((JsonNumber) xArray.get(0)).getInteger());
          assertEquals(new Integer(16), ((JsonNumber) xArray.get(1)).getInteger());
          assertEquals(new Integer(32), ((JsonNumber) xArray.get(2)).getInteger());
          // TODO: better arrayAppend() overloads?
          assertEquals("64", ((JsonString) xArray.get(3)).getString());
          assertEquals(4, xArray.size());
      }
  
      @Test
      public void testArrayInsert() {
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\", \"x\":[1,2]}").execute(); // Requires manual _id.
          } else {
              this.collection.add("{\"x\":[1,2]}").execute();
          }
          this.collection.modify("true").arrayInsert("$.x[1]", 43).execute();
          // same as append
          this.collection.modify("true").arrayInsert("$.x[3]", 44).execute();
  
          DocResult res = this.collection.find().execute();
          DbDoc jd = res.next();
          JsonArray xArray = (JsonArray) jd.get("x");
          assertEquals(new Integer(1), ((JsonNumber) xArray.get(0)).getInteger());
          assertEquals(new Integer(43), ((JsonNumber) xArray.get(1)).getInteger());
          assertEquals(new Integer(2), ((JsonNumber) xArray.get(2)).getInteger());
          assertEquals(new Integer(44), ((JsonNumber) xArray.get(3)).getInteger());
          assertEquals(4, xArray.size());
      }
  
      @Test
      public void testJsonModify() {
          DbDoc nestedDoc = new DbDocImpl().add("z", new JsonNumber().setValue("100"));
          DbDoc doc = new DbDocImpl().add("x", new JsonNumber().setValue("3")).add("y", nestedDoc);
  
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\", \"x\":1, \"y\":1}").execute(); // Requires manual _id.
              this.collection.add("{\"_id\": \"2\", \"x\":2, \"y\":2}").execute();
              this.collection.add(doc.add("_id", new JsonString().setValue("3"))).execute(); // Inject an _id.
              this.collection.add("{\"_id\": \"4\", \"x\":4, \"m\":1}").execute();
          } else {
              this.collection.add("{\"x\":1, \"y\":1}").execute();
              this.collection.add("{\"x\":2, \"y\":2}").execute();
              this.collection.add(doc).execute();
              this.collection.add("{\"x\":4, \"m\":1}").execute();
          }
  
          assertThrows(XDevAPIError.class, "Parameter 'criteria' must not be null or empty.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.modify(null).set("y", nestedDoc).execute();
                  return null;
              }
          });
  
          assertThrows(XDevAPIError.class, "Parameter 'criteria' must not be null or empty.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.modify(" ").set("y", nestedDoc).execute();
                  return null;
              }
          });
  
          this.collection.modify("y = 1").set("y", nestedDoc).execute();
          this.collection.modify("y = :n").set("y", nestedDoc).bind("n", 2).execute();
  
          this.collection.modify("x = 1").set("m", 1).execute();
          this.collection.modify("true").change("$.m", nestedDoc).execute();
  
          assertEquals(1, this.collection.find("x = :x").bind("x", 1).execute().count());
          assertEquals(0, this.collection.find("y = :y").bind("y", 2).execute().count());
          assertEquals(3, this.collection.find("y = {\"z\": 100}").execute().count());
          assertEquals(2, this.collection.find("m = {\"z\": 100}").execute().count());
  
          // TODO check later whether it's possible; for now placeholders are of Scalar type only
          //assertEquals(1, this.collection.find("y = :y").bind("y", nestedDoc).execute().count());
  
          // literal won't match JSON docs
          assertEquals(0, this.collection.find("y = :y").bind("y", "{\"z\": 100}").execute().count());
  
          DocResult res = this.collection.find().execute();
          while (res.hasNext()) {
              DbDoc jd = res.next();
              if (jd.get("y") != null) {
                  assertEquals(nestedDoc.toString(), ((DbDoc) jd.get("y")).toString());
              }
              if (jd.get("m") != null) {
                  assertEquals(nestedDoc.toString(), ((DbDoc) jd.get("m")).toString());
              }
          }
      }
  
      @Test
      public void testArrayModify() {
          JsonArray xArray = new JsonArray().addValue(new JsonString().setValue("a")).addValue(new JsonNumber().setValue("1"));
          DbDoc doc = new DbDocImpl().add("x", new JsonNumber().setValue("3")).add("y", xArray);
  
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              this.collection.add("{\"_id\": \"1\", \"x\":1, \"y\":[\"b\", 2]}").execute(); // Requires manual _id.
              this.collection.add("{\"_id\": \"2\", \"x\":2, \"y\":22}").execute();
              this.collection.add(doc.add("_id", new JsonString().setValue("3"))).execute(); // Inject an _id.
          } else {
              this.collection.add("{\"x\":1, \"y\":[\"b\", 2]}").execute();
              this.collection.add("{\"x\":2, \"y\":22}").execute();
              this.collection.add(doc).execute();
          }
  
          this.collection.modify("true").arrayInsert("$.y[1]", 44).execute();
          this.collection.modify("x = 2").change("$.y", xArray).execute();
          this.collection.modify("x = 3").set("y", xArray).execute();
  
          DocResult res = this.collection.find().execute();
          while (res.hasNext()) {
              DbDoc jd = res.next();
              if (((JsonNumber) jd.get("x")).getInteger() == 1) {
                  assertEquals((new JsonArray().addValue(new JsonString().setValue("b")).addValue(new JsonNumber().setValue("44"))
                          .addValue(new JsonNumber().setValue("2"))).toString(), (jd.get("y")).toString());
              } else {
                  assertEquals(xArray.toString(), jd.get("y").toString());
              }
          }
      }
  
      /**
       * Tests fix for BUG#24471057, UPDATE FAILS WHEN THE NEW VALUE IS OF TYPE DBDOC WHICH HAS ARRAY IN IT.
       * 
       * @throws Exception
       *             if the test fails.
       */
      @Test
      public void testBug24471057() throws Exception {
          String docStr = "{\"B\" : 2, \"ID\" : 1, \"KEY\" : [1]}";
          if (!mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              docStr = docStr.replace("{", "{\"_id\": \"1\", "); // Inject an _id.
          }
          DbDoc doc1 = JsonParser.parseDoc(new StringReader(docStr));
  
          AddResult res = this.collection.add(doc1).execute();
          this.collection.modify("ID=1").set("$.B", doc1).execute();
  
          // expected doc
          DbDoc doc2 = JsonParser.parseDoc(new StringReader(docStr));
          doc2.put("B", doc1);
          if (mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.5"))) {
              doc2.put("_id", new JsonString().setValue(res.getGeneratedIds().get(0)));
          }
          DocResult docs = this.collection.find().execute();
          DbDoc doc = docs.next();
          assertEquals(doc2.toString(), doc.toString());
  
          // DbDoc as an array member
          DbDoc doc3 = JsonParser.parseDoc(new StringReader(docStr));
          ((JsonArray) doc1.get("KEY")).add(doc3);
          this.collection.modify("ID=1").set("$.B", doc1).execute();
  
          // expected doc
          doc2.put("B", doc1);
          docs = this.collection.find().execute();
          doc = docs.next();
          assertEquals(doc2.toString(), doc.toString());
      }
  
      @Test
      public void testMergePatch() throws Exception {
          assumeTrue(mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.3")), "MySQL 8.0.3+ is required to run this test.");
  
          // 1. Update the name and zip code of match
          this.collection.add("{\"_id\": \"1\", \"name\": \"Alice\", \"address\": {\"zip\": \"12345\", \"street\": \"32 Main str\"}}").execute();
          this.collection.add("{\"_id\": \"2\", \"name\": \"Bob\", \"address\": {\"zip\": \"325226\", \"city\": \"San Francisco\", \"street\": \"42 2nd str\"}}")
                  .execute();
  
          this.collection.modify("_id = :id").patch(JsonParser.parseDoc("{\"name\": \"Joe\", \"address\": {\"zip\":\"91234\"}}")).bind("id", "1").execute();
  
          DocResult docs = this.collection.find().orderBy("$._id").execute();
          assertTrue(docs.hasNext());
          assertEquals(JsonParser.parseDoc("{\"_id\": \"1\", \"name\": \"Joe\", \"address\": {\"zip\": \"91234\", \"street\": \"32 Main str\"}}").toString(),
                  docs.next().toString());
          assertTrue(docs.hasNext());
          assertEquals(JsonParser
                  .parseDoc("{\"_id\": \"2\", \"name\": \"Bob\", \"address\": {\"zip\": \"325226\", \"city\": \"San Francisco\", \"street\": \"42 2nd str\"}}")
                  .toString(), docs.next().toString());
          assertFalse(docs.hasNext());
  
          // Delete the address field of match
          this.collection.modify("_id = :id").patch("{\"address\": null}").bind("id", "1").execute();
  
          docs = this.collection.find().orderBy("$._id").execute();
          assertTrue(docs.hasNext());
          assertEquals(JsonParser.parseDoc("{\"_id\": \"1\", \"name\": \"Joe\"}").toString(), docs.next().toString());
          assertTrue(docs.hasNext());
          assertEquals(JsonParser
                  .parseDoc("{\"_id\": \"2\", \"name\": \"Bob\", \"address\": {\"zip\": \"325226\", \"city\": \"San Francisco\", \"street\": \"42 2nd str\"}}")
                  .toString(), docs.next().toString());
          assertFalse(docs.hasNext());
  
          String id = "a6f4b93e1a264a108393524f29546a8c";
          this.collection.add("{\"_id\" : \"" + id + "\"," //
                  + "\"title\" : \"AFRICAN EGG\"," //
                  + "\"description\" : \"A Fast-Paced Documentary of a Pastry Chef And a Dentist who must Pursue a Forensic Psychologist in The Gulf of Mexico\"," //
                  + "\"releaseyear\" : 2006," //
                  + "\"language\" : \"English\"," //
                  + "\"duration\" : 130," //
                  + "\"rating\" : \"G\"," //
                  + "\"genre\" : \"Science fiction\"," //
                  + "\"actors\" : [" //
                  + "    {\"name\" : \"MILLA PECK\"," //
                  + "     \"country\" : \"Mexico\"," //
                  + "     \"birthdate\": \"12 Jan 1984\"}," //
                  + "    {\"name\" : \"VAL BOLGER\"," //
                  + "     \"country\" : \"Botswana\"," //
                  + "     \"birthdate\": \"26 Jul 1975\" }," //
                  + "    {\"name\" : \"SCARLETT BENING\"," //
                  + "     \"country\" : \"Syria\"," //
                  + "     \"birthdate\": \"16 Mar 1978\" }" //
                  + "    ]," //
                  + "\"additionalinfo\" : {" //
                  + "    \"director\" : {" //
                  + "        \"name\": \"Sharice Legaspi\"," //
                  + "        \"age\":57," //
                  + "        \"awards\": [" //
                  + "            {\"award\": \"Best Movie\"," //
                  + "             \"movie\": \"THE EGG\"," //
                  + "             \"year\": 2002}," //
                  + "            {\"award\": \"Best Special Effects\"," //
                  + "             \"movie\": \"AFRICAN EGG\"," //
                  + "             \"year\": 2006}" //
                  + "            ]" //
                  + "        }," //
                  + "    \"writers\" : [\"Rusty Couturier\", \"Angelic Orduno\", \"Carin Postell\"]," //
                  + "    \"productioncompanies\" : [\"Qvodrill\", \"Indigoholdings\"]" //
                  + "    }" //
                  + "}").execute();
  
          // Adding a new field to multiple documents
          this.collection.modify("language = :lang").patch("{\"translations\": [\"Spanish\"]}").bind("lang", "English").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          DbDoc doc = docs.next();
          assertNotNull(doc.get("translations"));
          JsonArray arr = (JsonArray) doc.get("translations");
          assertEquals(1, arr.size());
          assertEquals("Spanish", ((JsonString) arr.get(0)).getString());
  
          this.collection.modify("additionalinfo.director.name = :director").patch("{\"additionalinfo\": {\"musicby\": \"Sakila D\" }}")
                  .bind("director", "Sharice Legaspi").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("additionalinfo"));
          DbDoc doc2 = (DbDoc) doc.get("additionalinfo");
          assertNotNull(doc2.get("musicby"));
          assertEquals("Sakila D", ((JsonString) doc2.get("musicby")).getString());
  
          this.collection.modify("additionalinfo.director.name = :director").patch("{\"additionalinfo\": {\"director\": {\"country\": \"France\"}}}")
                  .bind("director", "Sharice Legaspi").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("additionalinfo"));
          assertNotNull(((DbDoc) doc.get("additionalinfo")).get("director"));
          doc2 = (DbDoc) ((DbDoc) doc.get("additionalinfo")).get("director");
          assertNotNull(doc2.get("country"));
          assertEquals("France", ((JsonString) doc2.get("country")).getString());
  
          // Replacing/Updating a field's value in multiple documents
          this.collection.modify("language = :lang").patch("{\"translations\": [\"Spanish\", \"Italian\"]}").bind("lang", "English").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("translations"));
          arr = (JsonArray) doc.get("translations");
          assertEquals(2, arr.size());
          assertEquals("Spanish", ((JsonString) arr.get(0)).getString());
          assertEquals("Italian", ((JsonString) arr.get(1)).getString());
  
          this.collection.modify("additionalinfo.director.name = :director").patch("{\"additionalinfo\": {\"musicby\": \"The Sakila\" }}")
                  .bind("director", "Sharice Legaspi").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("additionalinfo"));
          doc2 = (DbDoc) doc.get("additionalinfo");
          assertNotNull(doc2.get("musicby"));
          assertEquals("The Sakila", ((JsonString) doc2.get("musicby")).getString());
  
          this.collection.modify("additionalinfo.director.name = :director").patch("{\"additionalinfo\": {\"director\": {\"country\": \"Canada\"}}}")
                  .bind("director", "Sharice Legaspi").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("additionalinfo"));
          assertNotNull(((DbDoc) doc.get("additionalinfo")).get("director"));
          doc2 = (DbDoc) ((DbDoc) doc.get("additionalinfo")).get("director");
          assertNotNull(doc2.get("country"));
          assertEquals("Canada", ((JsonString) doc2.get("country")).getString());
  
          // Removing a field from multiple documents:
          this.collection.modify("language = :lang").patch("{\"translations\": null}").bind("lang", "English").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNull(doc.get("translations"));
  
          this.collection.modify("additionalinfo.director.name = :director").patch("{\"additionalinfo\": {\"musicby\": null }}")
                  .bind("director", "Sharice Legaspi").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("additionalinfo"));
          doc2 = (DbDoc) doc.get("additionalinfo");
          assertNull(doc2.get("musicby"));
  
          this.collection.modify("additionalinfo.director.name = :director").patch("{\"additionalinfo\": {\"director\": {\"country\": null}}}")
                  .bind("director", "Sharice Legaspi").execute();
          docs = this.collection.find("_id = :id").bind("id", id).limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertNotNull(doc.get("additionalinfo"));
          assertNotNull(((DbDoc) doc.get("additionalinfo")).get("director"));
          doc2 = (DbDoc) ((DbDoc) doc.get("additionalinfo")).get("director");
          assertNull(doc2.get("country"));
  
          // Using expressions
  
          this.collection.modify("_id = :id").patch("{\"zip\": address.zip-300000, \"street\": CONCAT($.name, '''s street: ', $.address.street)}").bind("id", "2")
                  .execute();
  
          this.collection.modify("_id = :id").patch("{\"city\": UPPER($.address.city)}").bind("id", "2").execute();
  
          docs = this.collection.find("_id = :id").bind("id", "2").limit(1).execute();
          assertTrue(docs.hasNext());
          doc = docs.next();
          assertEquals(25226, ((JsonNumber) doc.get("zip")).getBigDecimal().intValue());
          assertEquals("Bob's street: 42 2nd str", ((JsonString) doc.get("street")).getString());
          assertEquals("SAN FRANCISCO", ((JsonString) doc.get("city")).getString());
          doc2 = (DbDoc) doc.get("address");
          assertNotNull(doc2);
          assertEquals("325226", ((JsonString) doc2.get("zip")).getString());
          assertEquals("42 2nd str", ((JsonString) doc2.get("street")).getString());
          assertEquals("San Francisco", ((JsonString) doc2.get("city")).getString());
      }
  
      /**
       * Tests fix for BUG#27185332, WL#11210:ERROR IS THROWN WHEN NESTED EMPTY DOCUMENTS ARE INSERTED TO COLLECTION.
       * 
       * @throws Exception
       *             if the test fails.
       */
      @Test
      public void testBug27185332() throws Exception {
          assumeTrue(mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.3")), "MySQL 8.0.3+ is required to run this test.");
  
          DbDoc doc = JsonParser.parseDoc("{\"_id\": \"qqq\", \"nullfield\": {}, \"theme\": {         }}");
          assertEquals("qqq", ((JsonString) doc.get("_id")).getString());
          assertEquals(new DbDocImpl(), doc.get("nullfield"));
          assertEquals(new DbDocImpl(), doc.get("theme"));
  
          String id = "qqq";
          this.collection.add("{\"_id\" : \"" + id + "\"," //
                  + "\"title\" : \"THE TITLE\"," //
                  + "\"description\" : \"Author's story\"," //
                  + "\"releaseyear\" : 2012," //
                  + "\"language\" : \"English\"," //
                  + "\"theme\" : \"fiction\"," //
                  + "\"roles\" : ["//
                  + "    {\"name\" : \"Role 1\"," //
                  + "     \"country\" : [\"Thailand\", \"Singapore\"]," //
                  + "     \"birthdate\": \"12 Jan 1984\"}," //
                  + "    {\"name\" : \"Role 2\"," //
                  + "     \"country\" : \"Bali\"," //
                  + "     \"birthdate\": \"26 Jul 1975\" }," //
                  + "    {\"name\" : \"Role 3\"," //
                  + "     \"country\" : \"Doha\"," //
                  + "     \"birthdate\": \"16 Mar 1978\" }" //
                  + "    ]," //
                  + "\"additionalinfo\" : {" //
                  + "    \"author\" : {" //
                  + "        \"name\": \"John Gray\"," //
                  + "        \"age\":57," //
                  + "        \"awards\": [" //
                  + "            {\"award\": \"Best Writer\"," //
                  + "             \"book\": \"MAFM WAFV\"," //
                  + "             \"year\": 2002}," //
                  + "            {\"award\": \"Best Imagination\"," //
                  + "             \"book\": \"THE TITLE\"," //
                  + "             \"year\": 2006}" //
                  + "            ]" //
                  + "        }," //
                  + "    \"otherwriters\" : [\"Rusty Couturier\", \"Angelic Orduno\", \"Carin Postell\"]," //
                  + "    \"production\" : [\"Qvodrill\", \"Indigoholdings\"]" //
                  + "    }" //
                  + "}").execute();
          this.collection.modify("true").patch("{\"nullfield\": { \"nested\": null}}").execute();
          DocResult docs = this.collection.find("_id = :id").bind("id", id).execute();
          assertTrue(docs.hasNext());
          doc = docs.next(); //   <---- Error at this line
          assertNotNull(doc.get("nullfield"));
          assertEquals(new DbDocImpl(), doc.get("nullfield"));
      }
  
      @Test
      public void testReplaceOne() {
          assumeTrue(mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.3")), "MySQL 8.0.3+ is required to run this test.");
  
          Result res = this.collection.replaceOne("someId", "{\"_id\":\"someId\",\"a\":3}");
          assertEquals(0, res.getAffectedItemsCount());
  
          this.collection.add("{\"_id\":\"existingId\",\"a\":1}").execute();
  
          res = this.collection.replaceOne("existingId", new DbDocImpl().add("a", new JsonNumber().setValue("2")));
          assertEquals(1, res.getAffectedItemsCount());
  
          DbDoc doc = this.collection.getOne("existingId");
          assertNotNull(doc);
          assertEquals(2, ((JsonNumber) doc.get("a")).getInteger());
  
          // Original behavior changed by Bug#32770013.
          assertThrows(XDevAPIError.class, "Replacement document has an _id that is different than the matched document\\.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.replaceOne("nonExistingId", "{\"_id\":\"existingId\",\"a\":3}");
                  return null;
              }
          });
  
          // Original behavior changed by Bug#32770013.
          assertThrows(XDevAPIError.class, "Replacement document has an _id that is different than the matched document\\.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.replaceOne("", "{\"_id\":\"existingId\",\"a\":3}");
                  return null;
              }
          });
  
          /*
           * FR5.2 The id of the document must remain immutable:
           * 
           * Use a collection with some documents
           * Fetch a document
           * Unset _id and modify any other field of the document
           * Call replaceOne() giving original ID and modified document: expect affected = 1
           * Fetch the document again, ensure other document modifications took place
           * Ensure the number of documents in the collection is unaltered
           */
          this.collection.remove("true").execute();
          assertEquals(0, this.collection.count());
          this.collection.add("{\"_id\":\"id1\",\"a\":1}").execute();
  
          doc = this.collection.getOne("id1");
          assertNotNull(doc);
          doc.remove("_id");
          ((JsonNumber) doc.get("a")).setValue("3");
          res = this.collection.replaceOne("id1", doc);
          assertEquals(1, res.getAffectedItemsCount());
  
          doc = this.collection.getOne("id1");
          assertNotNull(doc);
          assertEquals("id1", ((JsonString) doc.get("_id")).getString());
          assertEquals(new Integer(3), ((JsonNumber) doc.get("a")).getInteger());
          assertEquals(1, this.collection.count());
  
          // null document
          assertThrows(XDevAPIError.class, "Parameter 'doc' must not be null.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.replaceOne("id1", (DbDoc) null);
                  return null;
              }
          });
          assertThrows(XDevAPIError.class, "Parameter 'jsonString' must not be null.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.replaceOne("id2", (String) null);
                  return null;
              }
          });
  
          // null id parameter
          assertThrows(XDevAPIError.class, "Parameter 'id' must not be null.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.replaceOne(null, new DbDocImpl().add("a", new JsonNumber().setValue("2")));
                  return null;
              }
          });
          assertThrows(XDevAPIError.class, "Parameter 'id' must not be null.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.replaceOne(null, "{\"_id\": \"id100\", \"a\": 100}");
                  return null;
              }
          });
  
          assertNull(this.collection.getOne(null));
      }
  
      /**
       * Tests fix for BUG#27226293, JSONNUMBER.GETINTEGER() & NUMBERFORMATEXCEPTION.
       */
      @Test
      public void testBug27226293() {
          this.collection.add("{ \"_id\" : \"doc1\" , \"name\" : \"bob\" , \"age\": 45 }").execute();
  
          DocResult result = this.collection.find("name = 'bob'").execute();
          DbDoc doc = result.fetchOne();
          assertEquals(new Integer(45), ((JsonNumber) doc.get("age")).getInteger());
  
          // After fixing server Bug#88230 (MySQL 8.0.4) the next operation returns
          // decimal age value 46.0 instead of integer 46
          this.collection.modify("name='bob'").set("age", expr("$.age + 1")).execute();
          doc = this.collection.find("name='bob'").execute().fetchOne();
          int age = ((JsonNumber) doc.get("age")).getInteger();
          assertEquals(46, age);
      }
  
      @Test
      public void testPreparedStatements() {
          assumeTrue(mysqlVersionMeetsMinimum(ServerVersion.parseVersion("8.0.14")), "MySQL 8.0.14+ is required to run this test.");
  
          try {
              // Prepare test data.
              testPreparedStatementsResetData();
  
              SessionFactory sf = new SessionFactory();
  
              /*
               * Test common usage.
               */
              Session testSession = sf.getSession(this.testProperties);
  
              int sessionThreadId = getThreadId(testSession);
              assertPreparedStatementsCount(sessionThreadId, 0, 1);
              assertPreparedStatementsStatusCounts(testSession, 0, 0, 0);
  
              Collection testCol1 = testSession.getDefaultSchema().getCollection(this.collectionName + "_1");
              Collection testCol2 = testSession.getDefaultSchema().getCollection(this.collectionName + "_2");
              Collection testCol3 = testSession.getDefaultSchema().getCollection(this.collectionName + "_3");
              Collection testCol4 = testSession.getDefaultSchema().getCollection(this.collectionName + "_4");
  
              // Initialize several ModifyStatement objects.
              ModifyStatement testModify1 = testCol1.modify("true").set("ord", expr("$.ord * 10")); // Modify all.
              ModifyStatement testModify2 = testCol2.modify("$.ord >= :n").set("ord", expr("$.ord * 10")); // Criteria with one placeholder.
              ModifyStatement testModify3 = testCol3.modify("$.ord >= :n AND $.ord <= :n + 1").set("ord", expr("$.ord * 10")); // Criteria with same placeholder repeated.
              ModifyStatement testModify4 = testCol4.modify("$.ord >= :n AND $.ord <= :m").set("ord", expr("$.ord * 10")); // Criteria with multiple placeholders.
  
              assertPreparedStatementsCountsAndId(testSession, 0, testModify1, 0, -1);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify2, 0, -1);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify3, 0, -1);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify4, 0, -1);
  
              // A. Set binds: 1st execute -> non-prepared.
              assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 10, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify1, 0, -1);
              assertTestPreparedStatementsResult(testModify2.bind("n", 2).execute(), 3, testCol2.getName(), 1, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify2, 0, -1);
              assertTestPreparedStatementsResult(testModify3.bind("n", 2).execute(), 2, testCol3.getName(), 1, 20, 30, 4);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify3, 0, -1);
              assertTestPreparedStatementsResult(testModify4.bind("n", 2).bind("m", 3).execute(), 2, testCol4.getName(), 1, 20, 30, 4);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify4, 0, -1);
  
              assertPreparedStatementsStatusCounts(testSession, 0, 0, 0);
              testPreparedStatementsResetData();
  
              // B. Set sort resets execution count: 1st execute -> non-prepared.
              assertTestPreparedStatementsResult(testModify1.sort("$._id").execute(), 4, testCol1.getName(), 10, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify1, 0, -1);
              assertTestPreparedStatementsResult(testModify2.sort("$._id").execute(), 3, testCol2.getName(), 1, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify2, 0, -1);
              assertTestPreparedStatementsResult(testModify3.sort("$._id").execute(), 2, testCol3.getName(), 1, 20, 30, 4);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify3, 0, -1);
              assertTestPreparedStatementsResult(testModify4.sort("$._id").execute(), 2, testCol4.getName(), 1, 20, 30, 4);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify4, 0, -1);
  
              assertPreparedStatementsStatusCounts(testSession, 0, 0, 0);
              testPreparedStatementsResetData();
  
              // C. Set binds reuse statement: 2nd execute -> prepare + execute.
              assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 10, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 1, testModify1, 1, 1);
              assertTestPreparedStatementsResult(testModify2.bind("n", 3).execute(), 2, testCol2.getName(), 1, 2, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 2, testModify2, 2, 1);
              assertTestPreparedStatementsResult(testModify3.bind("n", 3).execute(), 2, testCol3.getName(), 1, 2, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 3, testModify3, 3, 1);
              assertTestPreparedStatementsResult(testModify4.bind("m", 4).execute(), 3, testCol4.getName(), 1, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify4, 4, 1);
  
              assertPreparedStatementsStatusCounts(testSession, 4, 4, 0);
              testPreparedStatementsResetData();
  
              // D. Set binds reuse statement: 3rd execute -> execute.
              assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 10, 20, 30, 40);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify1, 1, 2);
              assertTestPreparedStatementsResult(testModify2.bind("n", 4).execute(), 1, testCol2.getName(), 1, 2, 3, 40);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify2, 2, 2);
              assertTestPreparedStatementsResult(testModify3.bind("n", 1).execute(), 2, testCol3.getName(), 10, 20, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify3, 3, 2);
              assertTestPreparedStatementsResult(testModify4.bind("m", 2).execute(), 1, testCol4.getName(), 1, 20, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify4, 4, 2);
  
              assertPreparedStatementsStatusCounts(testSession, 4, 8, 0);
              testPreparedStatementsResetData();
  
              // E. Set new values deallocates and resets execution count: 1st execute -> deallocate + non-prepared.
              assertTestPreparedStatementsResult(testModify1.set("ord", expr("$.ord * 100")).execute(), 4, testCol1.getName(), 100, 200, 300, 400);
              assertPreparedStatementsCountsAndId(testSession, 3, testModify1, 0, -1);
              assertTestPreparedStatementsResult(testModify2.set("ord", expr("$.ord * 100")).execute(), 1, testCol2.getName(), 1, 2, 3, 400);
              assertPreparedStatementsCountsAndId(testSession, 2, testModify2, 0, -1);
              assertTestPreparedStatementsResult(testModify3.set("ord", expr("$.ord * 100")).execute(), 2, testCol3.getName(), 100, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 1, testModify3, 0, -1);
              assertTestPreparedStatementsResult(testModify4.set("ord", expr("$.ord * 100")).execute(), 1, testCol4.getName(), 1, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify4, 0, -1);
  
              assertPreparedStatementsStatusCounts(testSession, 4, 8, 4);
              testPreparedStatementsResetData();
  
              // F. No Changes: 2nd execute -> prepare + execute.
              assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 100, 200, 300, 400);
              assertPreparedStatementsCountsAndId(testSession, 1, testModify1, 1, 1);
              assertTestPreparedStatementsResult(testModify2.execute(), 1, testCol2.getName(), 1, 2, 3, 400);
              assertPreparedStatementsCountsAndId(testSession, 2, testModify2, 2, 1);
              assertTestPreparedStatementsResult(testModify3.execute(), 2, testCol3.getName(), 100, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 3, testModify3, 3, 1);
              assertTestPreparedStatementsResult(testModify4.execute(), 1, testCol4.getName(), 1, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify4, 4, 1);
  
              assertPreparedStatementsStatusCounts(testSession, 8, 12, 4);
              testPreparedStatementsResetData();
  
              // G. Set limit for the first time deallocates and re-prepares: 1st execute -> re-prepare + execute.
              assertTestPreparedStatementsResult(testModify1.limit(1).execute(), 1, testCol1.getName(), 100, 2, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify1, 1, 1);
              assertTestPreparedStatementsResult(testModify2.limit(1).execute(), 1, testCol2.getName(), 1, 2, 3, 400);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify2, 2, 1);
              assertTestPreparedStatementsResult(testModify3.limit(1).execute(), 1, testCol3.getName(), 100, 2, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify3, 3, 1);
              assertTestPreparedStatementsResult(testModify4.limit(1).execute(), 1, testCol4.getName(), 1, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify4, 4, 1);
  
              assertPreparedStatementsStatusCounts(testSession, 12, 16, 8);
              testPreparedStatementsResetData();
  
              // H. Set limit reuse prepared statement: 2nd execute -> execute.
              assertTestPreparedStatementsResult(testModify1.limit(2).execute(), 2, testCol1.getName(), 100, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify1, 1, 2);
              assertTestPreparedStatementsResult(testModify2.limit(2).execute(), 1, testCol2.getName(), 1, 2, 3, 400);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify2, 2, 2);
              assertTestPreparedStatementsResult(testModify3.limit(2).execute(), 2, testCol3.getName(), 100, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify3, 3, 2);
              assertTestPreparedStatementsResult(testModify4.limit(2).execute(), 1, testCol4.getName(), 1, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify4, 4, 2);
  
              assertPreparedStatementsStatusCounts(testSession, 12, 20, 8);
              testPreparedStatementsResetData();
  
              // I. Set sort deallocates and resets execution count, set limit has no effect: 1st execute -> deallocate + non-prepared.
              assertTestPreparedStatementsResult(testModify1.sort("$._id").limit(1).execute(), 1, testCol1.getName(), 100, 2, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 3, testModify1, 0, -1);
              assertTestPreparedStatementsResult(testModify2.sort("$._id").limit(1).execute(), 1, testCol2.getName(), 1, 2, 3, 400);
              assertPreparedStatementsCountsAndId(testSession, 2, testModify2, 0, -1);
              assertTestPreparedStatementsResult(testModify3.sort("$._id").limit(1).execute(), 1, testCol3.getName(), 100, 2, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 1, testModify3, 0, -1);
              assertTestPreparedStatementsResult(testModify4.sort("$._id").limit(1).execute(), 1, testCol4.getName(), 1, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 0, testModify4, 0, -1);
  
              assertPreparedStatementsStatusCounts(testSession, 12, 20, 12);
              testPreparedStatementsResetData();
  
              // J. Set limit reuse statement: 2nd execute -> prepare + execute.
              assertTestPreparedStatementsResult(testModify1.limit(2).execute(), 2, testCol1.getName(), 100, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 1, testModify1, 1, 1);
              assertTestPreparedStatementsResult(testModify2.limit(2).execute(), 1, testCol2.getName(), 1, 2, 3, 400);
              assertPreparedStatementsCountsAndId(testSession, 2, testModify2, 2, 1);
              assertTestPreparedStatementsResult(testModify3.limit(2).execute(), 2, testCol3.getName(), 100, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 3, testModify3, 3, 1);
              assertTestPreparedStatementsResult(testModify4.limit(2).execute(), 1, testCol4.getName(), 1, 200, 3, 4);
              assertPreparedStatementsCountsAndId(testSession, 4, testModify4, 4, 1);
  
              assertPreparedStatementsStatusCounts(testSession, 16, 24, 12);
              testPreparedStatementsResetData();
  
              testSession.close();
              assertPreparedStatementsCount(sessionThreadId, 0, 10); // Prepared statements won't live past the closing of the session.
  
              /*
               * Test falling back onto non-prepared statements.
               */
              testSession = sf.getSession(this.testProperties);
              int origMaxPrepStmtCount = this.session.sql("SELECT @@max_prepared_stmt_count").execute().fetchOne().getInt(0);
  
              try {
                  // Allow preparing only one more statement.
                  this.session.sql("SET GLOBAL max_prepared_stmt_count = ?").bind(getPreparedStatementsCount() + 1).execute();
  
                  sessionThreadId = getThreadId(testSession);
                  assertPreparedStatementsCount(sessionThreadId, 0, 1);
                  assertPreparedStatementsStatusCounts(testSession, 0, 0, 0);
  
                  testCol1 = testSession.getDefaultSchema().getCollection(this.collectionName + "_1");
                  testCol2 = testSession.getDefaultSchema().getCollection(this.collectionName + "_2");
  
                  testModify1 = testCol1.modify("true").set("ord", expr("$.ord * 10"));
                  testModify2 = testCol2.modify("true").set("ord", expr("$.ord * 10"));
  
                  // 1st execute -> don't prepare.
                  assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 10, 20, 30, 40);
                  assertPreparedStatementsCountsAndId(testSession, 0, testModify1, 0, -1);
                  assertTestPreparedStatementsResult(testModify2.execute(), 4, testCol2.getName(), 10, 20, 30, 40);
                  assertPreparedStatementsCountsAndId(testSession, 0, testModify2, 0, -1);
  
                  assertPreparedStatementsStatusCounts(testSession, 0, 0, 0);
                  testPreparedStatementsResetData();
  
                  // 2nd execute -> prepare + execute.
                  assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 10, 20, 30, 40);
                  assertPreparedStatementsCountsAndId(testSession, 1, testModify1, 1, 1);
                  assertTestPreparedStatementsResult(testModify2.execute(), 4, testCol2.getName(), 10, 20, 30, 40); // Fails preparing, execute as non-prepared.
                  assertPreparedStatementsCountsAndId(testSession, 1, testModify2, 0, -1);
  
                  assertPreparedStatementsStatusCounts(testSession, 2, 1, 0); // Failed prepare also counts.
                  testPreparedStatementsResetData();
  
                  // 3rd execute -> execute.
                  assertTestPreparedStatementsResult(testModify1.execute(), 4, testCol1.getName(), 10, 20, 30, 40);
                  assertPreparedStatementsCountsAndId(testSession, 1, testModify1, 1, 2);
                  assertTestPreparedStatementsResult(testModify2.execute(), 4, testCol2.getName(), 10, 20, 30, 40); // Execute as non-prepared.
                  assertPreparedStatementsCountsAndId(testSession, 1, testModify2, 0, -1);
  
                  assertPreparedStatementsStatusCounts(testSession, 2, 2, 0);
                  testPreparedStatementsResetData();
  
                  testSession.close();
                  assertPreparedStatementsCount(sessionThreadId, 0, 10); // Prepared statements won't live past the closing of the session.
              } finally {
                  this.session.sql("SET GLOBAL max_prepared_stmt_count = ?").bind(origMaxPrepStmtCount).execute();
              }
          } finally {
              for (int i = 0; i < 4; i++) {
                  dropCollection(this.collectionName + "_" + (i + 1));
              }
          }
      }
  
      private void testPreparedStatementsResetData() {
          for (int i = 0; i < 4; i++) {
              Collection col = this.session.getDefaultSchema().createCollection(this.collectionName + "_" + (i + 1), true);
              col.remove("true").execute();
              col.add("{\"_id\":\"1\", \"ord\": 1}", "{\"_id\":\"2\", \"ord\": 2}", "{\"_id\":\"3\", \"ord\": 3}", "{\"_id\":\"4\", \"ord\": 4}").execute();
          }
      }
  
      @SuppressWarnings("hiding")
      private void assertTestPreparedStatementsResult(Result res, int expectedAffectedItemsCount, String collectionName, int... expectedValues) {
          assertEquals(expectedAffectedItemsCount, res.getAffectedItemsCount());
          DocResult docRes = this.schema.getCollection(collectionName).find().execute();
          assertEquals(expectedValues.length, docRes.count());
          for (int v : expectedValues) {
              assertEquals(v, ((JsonNumber) docRes.next().get("ord")).getInteger().intValue());
          }
      }
  
      @Test
      @SuppressWarnings("deprecation")
      public void testDeprecateWhere() throws Exception {
          this.collection.add("{\"_id\":\"1\", \"ord\": 1}", "{\"_id\":\"2\", \"ord\": 2}", "{\"_id\":\"3\", \"ord\": 3}", "{\"_id\":\"4\", \"ord\": 4}",
                  "{\"_id\":\"5\", \"ord\": 5}", "{\"_id\":\"6\", \"ord\": 6}", "{\"_id\":\"7\", \"ord\": 7}", "{\"_id\":\"8\", \"ord\": 8}").execute();
  
          ModifyStatement testModify = this.collection.modify("$.ord <= 2");
  
          assertTrue(testModify.getClass().getMethod("where", String.class).isAnnotationPresent(Deprecated.class));
  
          assertEquals(2, testModify.set("$.one", "1").execute().getAffectedItemsCount());
          assertEquals(4, ((ModifyStatementImpl) testModify).where("$.ord > 4").set("$.two", "2").execute().getAffectedItemsCount());
      }
  
      @Test
      public void testCollectionModifyBasic() throws Exception {
          int i = 0, maxrec = 30, recCnt = 0;
          DbDoc doc = null;
          Result res = null;
          String s1 = buildString((10), 'X');
          /* add(DbDoc[] docs) */
          DbDoc[] jsonlist = new DbDocImpl[maxrec];
  
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(10 * (i + 1))));
              if (i % 3 == 2) {
                  newDoc2.add("F3", JsonLiteral.TRUE);
              } else if (i % 3 == 1) {
                  newDoc2.add("F3", JsonLiteral.NULL);
              } else {
                  newDoc2.add("F3", JsonLiteral.FALSE);
              }
              newDoc2.add("tmp1", new JsonString().setValue("tempdata-" + i));
              newDoc2.add("tmp2", new JsonString().setValue("tempForChange-" + i));
              jsonlist[i] = newDoc2;
              newDoc2 = null;
          }
          this.collection.add(jsonlist).execute();
  
          assertEquals((maxrec), this.collection.count());
  
          /* fetch all */
          DocResult docs = this.collection.find("CAST($.F2 as SIGNED)> 0").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3 as f3").execute();
          i = 0;
          while (docs.hasNext()) {
              doc = docs.next();
              assertEquals(String.valueOf(i + 1000), (((JsonString) doc.get("_id")).getString()));
              assertEquals((long) (10 * (i + 1)), (long) (((JsonNumber) doc.get("f2")).getInteger()));
              i++;
          }
          assertEquals((maxrec), i);
  
          /* Modify using empty Condition */
          assertThrows(XDevAPIError.class, "Parameter 'criteria' must not be null or empty.",
                  () -> CollectionModifyTest.this.collection.modify("").set("$.F1", "Data_New").execute());
  
          /* Modify using null Condition */
          assertThrows(XDevAPIError.class, "Parameter 'criteria' must not be null or empty.",
                  () -> CollectionModifyTest.this.collection.modify(null).set("$.F1", "Data_New").execute());
  
          /* Modify with true Condition using Set */
          res = this.collection.modify("true").set("$.F1", "Data_True").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
          docs = this.collection.find("$.F1 Like '%True'").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3 as f3").execute();
          recCnt = count_data(docs);
          assertEquals(maxrec, recCnt);
  
          res = this.collection.modify("1 == 1").set("$.F1", "Data_New").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
          docs = this.collection.find("$.F1 Like '%New'").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3 as f3").execute();
          recCnt = count_data(docs);
          assertEquals(maxrec, recCnt);
  
          /* Modify with a false Condition */
          res = this.collection.modify("false").set("$.F1", "False_Data").execute();
          assertEquals(0, res.getAffectedItemsCount());
          // Modify with a Condition which results to false
          res = this.collection.modify("0 == 1").set("$.F1", "False_Data").execute();
          assertEquals(0, res.getAffectedItemsCount());
  
          /* Un Set */
          // Test UnSet with condition
          docs = this.collection.find("$.tmp1 Like 'tempdata%'").fields("$._id as _id, $.tmp1 as tp").execute();
          recCnt = count_data(docs);
          // Total Rec with $.tmp1 Like 'tempdata%'
          assertEquals(maxrec, recCnt);
          res = this.collection.modify("CAST($._id as SIGNED) % 2").unset("$.tmp1").execute();
          assertEquals(maxrec / 2, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp1 Like 'tempdata%'").fields("$._id as _id, $.tmp1 as tp").execute();
          recCnt = count_data(docs);
          // Total records after Unset(with condition)
          assertEquals(maxrec / 2, recCnt);
          // Test true condition with unset
          res = this.collection.modify("1 == 1").unset("$.tmp1").execute();
          assertEquals(maxrec / 2, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp1 Like 'tempdata%'").fields("$._id as _id, $.tmp1 as tp").execute();
          recCnt = count_data(docs);
          // Total records after Unset(without condition)
          assertEquals(0, recCnt);
  
          /* Test for Change().unset tmp2 for half of the total records.Call change without condition. */
          res = this.collection.modify("CAST($._id as SIGNED) % 2").unset("$.tmp2").execute();
          assertEquals(maxrec / 2, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp2 Like 'tempForChange%'").fields("$._id as _id, $.tmp2 as tp").execute();
          recCnt = count_data(docs);
          // Total records after Unset(with condition)
          assertEquals((maxrec / 2), recCnt);
  
          // Test for Change()
          res = this.collection.modify("true").change("$.tmp2", "Changedata").execute();
          assertEquals(maxrec / 2, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp2 Like 'Changedata'").fields("$._id as _id, $.tmp2 as tp").execute();
          recCnt = count_data(docs);
          // Total records Changed after modify().change(without condition)
          assertEquals((maxrec / 2), recCnt);
  
          // Test for set () after unset
          res = this.collection.modify("true").set("$.tmp2", "Changedata1").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp2 Like 'Changedata1'").fields("$._id as _id, $.tmp2 as tp").execute();
          recCnt = count_data(docs);
          // Total Records Set when Half of the records were unset
          assertEquals(maxrec, recCnt);
  
          // Test for set () after unset All
          res = this.collection.modify("true").unset("$.tmp2").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp2 IS Not NULL").fields("$._id as _id, $.tmp2 as tp").execute();
          recCnt = count_data(docs);
          // Total Records After unsetting all
          assertEquals(0, recCnt);
  
          res = this.collection.modify("1 == 1").set("$.tmp2", "Changedata3").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp2 Like 'Changedata3'").fields("$._id as _id, $.tmp2 as tp").execute();
          recCnt = count_data(docs);
          // Total Records Set when All the records were unset
          assertEquals(maxrec, recCnt);
  
          // Modify with Condition using Set
          res = this.collection.modify("$._id = '1001' and  $.F1 Like '%New' ").set("$.F1", s1).execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.F1 = '" + s1 + "'").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3 as f3").execute();
          recCnt = count_data(docs);
          assertEquals(1, recCnt);
      }
  
      @Test
      public void testCollectionModifySortLimit() throws Exception {
          int i = 0, maxrec = 30, recCnt = 0;
          DbDoc doc = null;
          Result res = null;
          /* add(DbDoc[] docs) */
          DbDoc[] jsonlist = new DbDocImpl[maxrec];
  
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1001)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(10 * (i + 1))));
              newDoc2.add("F3", new JsonNumber().setValue(String.valueOf(1 + i)));
              newDoc2.add("tmp1", new JsonString().setValue("tempdata-" + i));
              newDoc2.add("tmp2", new JsonNumber().setValue(String.valueOf(-1)));
              jsonlist[i] = newDoc2;
              newDoc2 = null;
          }
          this.collection.add(jsonlist).execute();
  
          assertEquals((maxrec), this.collection.count());
  
          /* fetch all */
          DocResult docs = this.collection.find("CAST($.F3 as SIGNED)>= 0").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3 as f3").execute();
          i = 0;
          while (docs.hasNext()) {
              doc = docs.next();
              assertEquals(String.valueOf(i + 1001), (((JsonString) doc.get("_id")).getString()));
              assertEquals((long) (10 * (i + 1)), (long) (((JsonNumber) doc.get("f2")).getInteger()));
              i++;
          }
          assertEquals((maxrec), i);
  
          /* With Sort and limit */
          res = this.collection.modify("$.F3 < 10 and  $.F1 Like 'Field-1%' and CAST($.F3 as SIGNED) > 2").set("$.tmp1", "UpdData").sort("$.F1 asc").limit(5)
                  .execute();
          assertEquals(5, res.getAffectedItemsCount());
          docs = this.collection.find("$.tmp1 = 'UpdData'").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3 as f3").execute();
          i = 2;
          while (docs.hasNext()) {
              doc = docs.next();
              assertEquals(String.valueOf(i + 1001), (((JsonString) doc.get("_id")).getString()));
              assertEquals((long) (i + 1), (long) (((JsonNumber) doc.get("f3")).getInteger()));
              i++;
          }
  
          // Unset with Condition using Set With Sort and limit
          res = this.collection.modify("$.F3 < 10 and  $.F1 Like 'Field-1%' and CAST($.F3 as SIGNED) > 2").unset("$.tmp1").sort("$.F1 asc").limit(5).execute();
          assertEquals(5, res.getAffectedItemsCount());
          // Unset keys which is already unset
          res = this.collection.modify("$.F3 < 11 and  $.F1 Like 'Field-1%' and CAST($.F3 as SIGNED) > 2").unset("$.tmp1").sort("$.F1 asc").limit(6).execute();
          assertEquals(1, res.getAffectedItemsCount());
          // set keys which is already unset
          res = this.collection.modify("$.F3 < 11 and  $.F1 Like 'Field-1%' and CAST($.F3 as SIGNED) > 2").set("$.tmp1", "UpdData").sort("$.F1 asc").limit(6)
                  .execute();
          assertEquals(6, res.getAffectedItemsCount());
  
          // set keys which is already unset
          res = this.collection.modify("$.F3 < 11 and  $.F1 Like 'Field-1%' and CAST($.F3 as SIGNED) > 2").set("$.F2", -2147483648).sort("$.F1 asc").limit(6)
                  .execute();
          assertEquals(6, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.F2 as SIGNED) = -2147483648").fields("$._id as _id, $.F2 as f2").execute();
          recCnt = count_data(docs);
          assertEquals(6, recCnt);
          res = this.collection.modify("CAST($.F3 as SIGNED) < 11 and  $.F1 Like 'Field-1%' and CAST($.F3 as SIGNED) > 2").set("$.tmp2", 2147483647)
                  .sort("$.F1 asc").limit(6).execute();
          assertEquals(6, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.F2 as SIGNED) = (CAST($.tmp2 as SIGNED)*-1)-1").fields("$._id as _id, $.F2 as f2").execute();
          recCnt = count_data(docs);
          assertEquals(6, recCnt);
          docs = this.collection.find("(CAST($.F2 as SIGNED) * -1) - 1 = CAST($.tmp2 as SIGNED)").fields("$._id as _id, $.F2 as f2").execute();
          recCnt = count_data(docs);
          assertEquals(6, recCnt);
  
          res = this.collection.modify("CAST($.F3 as SIGNED) < 11 and  $.F1 Like 'Field-1%'").set("$.tmp2", 9999).set("$.F2", 9999).sort("$.F1 asc").execute();
          assertEquals(10, res.getAffectedItemsCount());
  
          /* set,unset,change together */
          //  res = coll.modify("CAST($.F3 as SIGNED) < 11").set("$.tmp2",$.tmp2+1).change("$.F1","concat($.F1,'Rajesh')").unset("$.tmp1").sort("$.F1 asc").execute();
          res = this.collection.modify("CAST($.F3 as SIGNED) < 11").set("$.tmp2", 9898).change("$.F1", "'Rajesh'").unset("$.tmp1").sort("$.F1 asc").execute();
          assertEquals(10, res.getAffectedItemsCount());
  
          docs = this.collection.find("CAST($.tmp2 as SIGNED) = 9898").fields("$._id as _id, $.F2 as f2").execute();
          recCnt = count_data(docs);
          assertEquals(10, recCnt);
  
          docs = this.collection.find("$.F1 like '%Rajesh'''").fields("$._id as _id, $.F1 as f1").execute();
          recCnt = count_data(docs);
          assertEquals(10, recCnt);
  
          res = this.collection.modify("CAST($.F3 as SIGNED) < 11").unset("$.tmp1").set("$.tmp2", 9897).change("$.F1", "Rajesh").sort("$.F1 asc").execute();
          assertEquals(10, res.getAffectedItemsCount());
  
          res = this.collection.modify("true").unset("$.tmp1").set("$.tmp2", 9897).change("$.F1", "Rajesh").sort("$.F1 asc").limit(5).execute();
          assertEquals(5, res.getAffectedItemsCount());
  
          res = this.collection.modify("false").unset("$.tmp1").set("$.tmp2", 9897).change("$.F1", "Rajesh").sort("$.F1 asc").limit(5).execute();
          assertEquals(0, res.getAffectedItemsCount());
  
      }
  
      @Test
      @Disabled("$.F4 = 9223372036854775807 condition without quotes bind() not supported with modify.")
      public void testCollectionModifyBind() throws Exception {
          int i = 0, maxrec = 10, recCnt = 0;
          Result res = null;
          /* add(DbDoc[] docs) */
          DbDoc[] jsonlist = new DbDocImpl[maxrec];
          long l1 = Long.MAX_VALUE, l2 = Long.MIN_VALUE, l3 = 2147483647;
          System.out.println("l = ===" + l1);
  
          double d1 = 100.4567;
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(d1 + i)));
              newDoc2.add("F3", new JsonNumber().setValue(String.valueOf(l1 - i)));
              newDoc2.add("F4", new JsonNumber().setValue(String.valueOf(l2 + i)));
              newDoc2.add("F5", new JsonNumber().setValue(String.valueOf(l3 + i)));
              newDoc2.add("F6", new JsonString().setValue((2000 + i) + "-02-" + (i * 2 + 10)));
              jsonlist[i] = newDoc2;
              newDoc2 = null;
          }
          this.collection.add(jsonlist).execute();
          assertEquals((maxrec), this.collection.count());
  
          /* find */
          DocResult docs = this.collection.find("CAST($.F3 as SIGNED)=2147483649").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3+0 as f3").execute();
          recCnt = count_data(docs);
          assertEquals(maxrec, recCnt);
  
          //  /*
          res = this.collection.modify("$.F4 = ?").set("$.F4", 1).bind(new Object[] { l2 }).sort("$.F1 asc").execute();
          assertEquals(1, res.getAffectedItemsCount());
          //  */
      }
  
      /*
       * Using Big int and Double
       */
      @Test
      public void testCollectionModifyDataTypes() throws Exception {
          int i = 0, maxrec = 10, recCnt = 0;
          DbDoc doc = null;
          Result res = null;
          /* add(DbDoc[] docs) */
          DbDoc[] jsonlist = new DbDocImpl[maxrec];
          long l1 = Long.MAX_VALUE, l2 = Long.MIN_VALUE, l3 = 2147483647;
          System.out.println("l = ===" + l1);
  
          double d1 = 100.4567;
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(d1 + i)));
              newDoc2.add("F3", new JsonNumber().setValue(String.valueOf(l1 - i)));
              newDoc2.add("F4", new JsonNumber().setValue(String.valueOf(l2 + i)));
              newDoc2.add("F5", new JsonNumber().setValue(String.valueOf(l3 + i)));
              newDoc2.add("F6", new JsonString().setValue((2000 + i) + "-02-" + (i * 2 + 10)));
              jsonlist[i] = newDoc2;
              newDoc2 = null;
          }
          this.collection.add(jsonlist).execute();
          assertEquals((maxrec), this.collection.count());
  
          /* find without Condition */
          DocResult docs = this.collection.find("CAST($.F5 as SIGNED)=2147483649").fields("$._id as _id, $.F1 as f1, $.F2 as f2, $.F3+0 as f3").execute();
          recCnt = count_data(docs);
          assertEquals(1, recCnt);
  
          /* condition on Double */
          res = this.collection.modify("CAST($.F2 as DECIMAL(10,4)) =" + d1).set("$.F1", "UpdData1").sort("CAST($.F2 as DECIMAL(10,4))").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'UpdData1'").fields("$._id as _id, $.F2 as f2").execute();
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(d1)), (((JsonNumber) doc.get("f2")).getBigDecimal()));
          assertEquals(String.valueOf(1000), (((JsonString) doc.get("_id")).getString()));
  
          /* condition on Big Int */
          res = this.collection.modify("CAST($.F3 as SIGNED) =" + l1).set("$.F1", "UpdData2").sort("CAST($.F3 as SIGNED)").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'UpdData2'").fields("$._id as _id, $.F3 as f3").execute();
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(l1)), (((JsonNumber) doc.get("f3")).getBigDecimal()));
          assertEquals(String.valueOf(1000), (((JsonString) doc.get("_id")).getString()));
  
          /* condition on Big Int */
          res = this.collection.modify("CAST($.F5 as SIGNED) >= " + l3 + " and  CAST($.F5 as SIGNED) < " + l1 + " and CAST($.F5 as SIGNED) > " + l2)
                  .set("$.F1", "AllUpd").sort("CAST($.F5 as SIGNED)").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'AllUpd'").fields("$._id as _id, $.F5 as f5").orderBy(" CAST($.F5 as SIGNED)").execute();
  
          i = 0;
          while (docs.hasNext()) {
              doc = docs.next();
              assertEquals(String.valueOf(i + 1000), (((JsonString) doc.get("_id")).getString()));
              assertEquals(new BigDecimal(String.valueOf(i + l3)), (((JsonNumber) doc.get("f5")).getBigDecimal()));
              i++;
          }
          assertEquals(maxrec, i);
  
          /* condition on Double */
          res = this.collection.modify("CAST($.F5 as SIGNED) =" + l3 + "+" + 3).set("$.F1", "UpdData3").sort("CAST($.F5 as SIGNED)").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'UpdData3'").fields("$._id as _id, $.F5 as f5").execute();
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(3 + l3)), (((JsonNumber) doc.get("f5")).getBigDecimal()));
          assertEquals(String.valueOf(1000 + 3), (((JsonString) doc.get("_id")).getString()));
  
          /* condition on Double */
          res = this.collection.modify("CAST($.F5 as SIGNED) - 3 =" + l3).set("$.F1", "UpdData4").sort("CAST($.F5 as SIGNED)").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'UpdData4'").fields("$._id as _id, $.F5 as f5").execute();
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(3 + l3)), (((JsonNumber) doc.get("f5")).getBigDecimal()));
          assertEquals(String.valueOf(1000 + 3), (((JsonString) doc.get("_id")).getString()));
  
          /* condition on date */
          res = this.collection.modify("$.F6 + interval 6 day = '2007-03-02' ").set("$.F1", "UpdData5").sort("$.F6").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'UpdData5'").fields("$._id as _id, $.F6 as f6").execute();
          doc = docs.next();
          assertEquals("2007-02-24", (((JsonString) doc.get("f6")).getString()));
          assertEquals(String.valueOf(1000 + 7), (((JsonString) doc.get("_id")).getString()));
          assertFalse(docs.hasNext());
      }
  
      /*
       * Update Using Expressions
       */
      @Test
      public void testCollectionModifyExpr() throws Exception {
          int i = 0, maxrec = 10;
          DbDoc doc = null;
          Result res = null;
          DocResult docs = null;
          DbDoc[] jsonlist = new DbDocImpl[maxrec];
          long l1 = Long.MAX_VALUE, l2 = Long.MIN_VALUE, l3 = 2147483647;
          double d1 = 100.4567;
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(d1 + i)));
              newDoc2.add("F3", new JsonNumber().setValue(String.valueOf(l1 - i)));
              newDoc2.add("F4", new JsonNumber().setValue(String.valueOf(l2 + i)));
              newDoc2.add("F5", new JsonNumber().setValue(String.valueOf(l3 + i)));
              newDoc2.add("F6", new JsonString().setValue((2000 + i) + "-02-" + (i * 2 + 10)));
              jsonlist[i] = newDoc2;
              newDoc2 = null;
          }
          this.collection.add(jsonlist).execute();
          assertEquals((maxrec), this.collection.count());
  
          /* condition on Double */
          res = this.collection.modify("CAST($.F2 as DECIMAL(10,4)) =" + d1).set("$.F1", expr("concat('data',$.F1,'UpdData1')"))
                  .sort("CAST($.F2 as DECIMAL(10,4))").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 like 'data%UpdData1'").fields("$._id as _id, $.F2 as f2").execute();
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(d1)), (((JsonNumber) doc.get("f2")).getBigDecimal()));
          assertEquals(String.valueOf(1000), (((JsonString) doc.get("_id")).getString()));
  
          res = this.collection.modify("CAST($.F2 as DECIMAL(10,4)) =" + d1).set("$.F6", expr("$.F6 + interval 6 day")).sort("CAST($.F2 as DECIMAL(10,4))")
                  .execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F6 + interval 6 day = '2000-02-22'").fields("$._id as _id, $.F6 as f6").execute();
          doc = docs.next();
          assertEquals("2000-02-16", (((JsonString) doc.get("f6")).getString()));
          assertEquals(String.valueOf(1000), (((JsonString) doc.get("_id")).getString()));
  
          res = this.collection.modify("$.F6= '2004-02-18'").set("$.F6", expr("$.F6 + interval 11 day")).set("$.F1", "NewData")
                  .sort("CAST($.F2 as DECIMAL(10,4))").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("$.F1 = 'NewData'").fields("$._id as _id, $.F6 as f6").execute();
          doc = docs.next();
          assertEquals("2004-02-29", (((JsonString) doc.get("f6")).getString()));
          assertEquals(String.valueOf(1004), (((JsonString) doc.get("_id")).getString()));
  
          /* condition on Big Int */
          res = this.collection.modify("CAST($.F3 as SIGNED) =" + l1).set("$.F3", expr("CAST($.F3 as SIGNED)  -1")).sort("CAST($.F3 as SIGNED)").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          res = this.collection.modify("CAST($.F3 as SIGNED) + 1  =" + l1).set("$.F3", expr("CAST($.F3 as SIGNED) + 1")).sort("CAST($.F3 as SIGNED)").execute();
          assertEquals(2, res.getAffectedItemsCount());
  
          docs = this.collection.find("CAST($.F3 as SIGNED)=" + l1).fields("$._id as _id, $.F3 as f3").orderBy("$._id asc").execute();
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(l1)), (((JsonNumber) doc.get("f3")).getBigDecimal()));
          assertEquals(String.valueOf(1000), (((JsonString) doc.get("_id")).getString()));
  
          doc = docs.next();
          assertEquals(new BigDecimal(String.valueOf(l1)), (((JsonNumber) doc.get("f3")).getBigDecimal()));
          assertEquals(String.valueOf(1001), (((JsonString) doc.get("_id")).getString()));
          assertFalse(docs.hasNext());
  
          /* condition on Big Int.Compex Expression */
          res = this.collection.modify("CAST($.F4 as SIGNED) < 0").set("$.F1", "Abcd")
                  .set("$.F4", expr("((CAST($.F4 as SIGNED) + CAST($.F3 as SIGNED)) * 1)/1.1 + 1 ")).execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
  
          res = this.collection.modify("true").set("$.F1", expr("concat('data',$.F1,'UpdData1')")).sort("CAST($.F2 as DECIMAL(10,4))").execute();
          assertEquals(10, res.getAffectedItemsCount());
  
          res = this.collection.modify("false").set("$.F1", "Abcd").set("$.F4", expr("((CAST($.F4 as SIGNED) + CAST($.F3 as SIGNED)) * 1)/1.1 + 1 ")).execute();
          assertEquals(0, res.getAffectedItemsCount());
      }
  
      @Test
      public void testCollectionModifyArray() throws Exception {
          int i = 0, j = 0, maxrec = 8, arraySize = 30;
          int lStr = 1024 * 800;
          JsonArray yArray = null;
          DbDoc doc = null;
          DocResult docs = null;
          Result res = null;
          String s1 = buildString((lStr), 'X');
          long l3 = 2147483647;
          double d1 = 1000.1234;
  
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonNumber().setValue(String.valueOf(i + 1)));
  
              JsonArray jarray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  jarray.addValue(new JsonNumber().setValue(String.valueOf((l3 + j + i))));
              }
              newDoc2.add("ARR1", jarray);
  
              JsonArray karray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  karray.addValue(new JsonNumber().setValue(String.valueOf((d1 + j + i))));
              }
              newDoc2.add("ARR2", karray);
              JsonArray larray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  larray.addValue(new JsonString().setValue("St_" + i + "_" + j));
              }
              newDoc2.add("ARR3", larray);
              this.collection.add(newDoc2).execute();
              newDoc2 = null;
              jarray = null;
          }
          assertEquals((maxrec), this.collection.count());
  
          //Update Array data using expr
          res = this.collection.modify("$.F1 = 1").change("$.ARR1[1]", expr("$.ARR1[1] / $.ARR1[1]")).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
  
          docs = this.collection.find("CAST($.ARR1[1] as SIGNED) = 1").orderBy("$._id").execute();
          doc = docs.next();
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          /* Unset Array element */
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").unset("$.ARR1[1]").sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.F1 = 1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize - 1, yArray.size());
          assertEquals(new BigDecimal("2147483647"), (((JsonNumber) yArray.get(0)).getBigDecimal()));
          assertEquals(new BigDecimal("2147483649"), (((JsonNumber) yArray.get(1)).getBigDecimal()));
          assertFalse(docs.hasNext());
  
          /* set Array element */
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").set("$.ARR1[1]", 90).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.F1 = 1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize - 1, yArray.size());
          assertEquals(new BigDecimal("2147483647"), (((JsonNumber) yArray.get(0)).getBigDecimal()));
          assertEquals(new BigDecimal("90"), (((JsonNumber) yArray.get(1)).getBigDecimal()));
          assertFalse(docs.hasNext());
  
          /* set Array element */
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").set("$.ARR1[2]", 91).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.F1 = 1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize - 1, yArray.size());
          assertEquals(new BigDecimal("2147483647"), (((JsonNumber) yArray.get(0)).getBigDecimal()));
          assertEquals(new BigDecimal("90"), (((JsonNumber) yArray.get(1)).getBigDecimal()));
          assertEquals(new BigDecimal("91"), (((JsonNumber) yArray.get(2)).getBigDecimal()));
          assertFalse(docs.hasNext());
  
          /* set Array element (String) */
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").set("$.ARR3[1]", s1).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("length($.ARR3[1]) = " + lStr).orderBy("$._id").execute();
          doc = docs.next();
          assertEquals((long) 1, (long) (((JsonNumber) doc.get("F1")).getInteger()));
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize, yArray.size());
          assertEquals("St_0_0", (((JsonString) yArray.get(0)).getString()));
          assertEquals("St_0_2", (((JsonString) yArray.get(2)).getString()));
          assertFalse(docs.hasNext());
  
          /* set Array element (String) */
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").set("$.ARR3[1]", expr("concat($.ARR3[1], $.ARR3[1])")).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("length($.ARR3[1]) = " + (lStr * 2)).orderBy("$._id").execute();
          doc = docs.next();
          assertEquals((long) 1, (long) (((JsonNumber) doc.get("F1")).getInteger()));
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize, yArray.size());
          assertEquals("St_0_0", (((JsonString) yArray.get(0)).getString()));
          assertEquals("St_0_2", (((JsonString) yArray.get(2)).getString()));
          assertFalse(docs.hasNext());
  
          /* Change Array elements of all rows (String) */
          res = this.collection.modify("CAST($.F1 as SIGNED) >= 1").set("$.ARR3[1]", expr("concat($.ARR3[1], '" + s1 + "')")).sort("$._id").execute();
          assertEquals(maxrec, res.getAffectedItemsCount());
          docs = this.collection.find("length($.ARR3[1]) > " + (lStr)).orderBy("$._id").fields(expr("{'cnt':count($._id)}")).execute();
          doc = docs.next();
          assertEquals((long) maxrec, (long) ((JsonNumber) doc.get("cnt")).getInteger());
          assertFalse(docs.hasNext());
  
          /* Unset Array element(String) */
          res = this.collection.modify("$.F1 = 1").unset("$.ARR3[1]").sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.F1 as SIGNED) = 1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize - 1, yArray.size());
          assertEquals("St_0_0", (((JsonString) yArray.get(0)).getString()));
          assertEquals("St_0_2", (((JsonString) yArray.get(1)).getString()));
          assertFalse(docs.hasNext());
  
          /* Unset Array element(String) */
          res = this.collection.modify("CAST($.F1 as SIGNED) > 1").unset("$.ARR3[1]").sort("$._id").execute();
          assertEquals((maxrec - 1), res.getAffectedItemsCount());
          docs = this.collection.find("length($.ARR3[1]) < " + (lStr)).orderBy("$._id").fields(expr("{'cnt':count($._id)}")).execute();
          doc = docs.next();
          assertEquals((long) maxrec, (long) ((JsonNumber) doc.get("cnt")).getInteger());
          assertFalse(docs.hasNext());
      }
  
      /* ArrayAppend() for int double and string */
      @Test
      public void testCollectionModifyArrayAppend() throws Exception {
          int i = 0, j = 0, maxrec = 8, arraySize = 30;
          int lStr = 10;
          JsonArray yArray = null;
          DbDoc doc = null;
          DocResult docs = null;
          Result res = null;
          String s1 = buildString((lStr), '.');
          long l3 = 2147483647;
          double d1 = 1000.1234;
  
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonNumber().setValue(String.valueOf(i + 1)));
  
              JsonArray jarray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  jarray.addValue(new JsonNumber().setValue(String.valueOf((l3 + j + i))));
              }
              newDoc2.add("ARR1", jarray);
  
              JsonArray karray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  karray.addValue(new JsonNumber().setValue(String.valueOf((d1 + j + i))));
              }
              newDoc2.add("ARR2", karray);
              JsonArray larray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  larray.addValue(new JsonString().setValue("St_" + i + "_" + j));
              }
              newDoc2.add("ARR3", larray);
              this.collection.add(newDoc2).execute();
              newDoc2 = null;
              jarray = null;
          }
          assertEquals((maxrec), this.collection.count());
  
          // Append 1 number in the array (ARR1) where $.F1 = 1
          res = this.collection.modify("$.F1 = 1").arrayAppend("$.ARR1", -1).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR1[" + (arraySize) + "] as SIGNED) = -1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize + 1, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Append 3 numbers in the array (ARR1) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayAppend("$.ARR1", -2).arrayAppend("$.ARR1", -3).arrayAppend("$.ARR1", -4).sort("$._id")
                  .execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR1[" + (arraySize) + "] as SIGNED) = -1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize + 4, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Append 1 number in the array (ARR2) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayAppend("$.ARR2", -4321.4321).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR2[" + (arraySize) + "] as DECIMAL(10,4)) = -4321.4321").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR2");
          assertEquals(arraySize + 1, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Append 3 number in the array (ARR2) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayAppend("$.ARR2", 4321.1234).arrayAppend("$.ARR2", 4321.9847)
                  .arrayAppend("$.ARR2", -4321.9888).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR2[" + (arraySize) + "] as  DECIMAL(10,4)) =  -4321.4321").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR2");
          assertEquals(arraySize + 4, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Append 1 String in the array (ARR3) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayAppend("$.ARR3", s1).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.ARR3[" + (arraySize) + "] = '" + s1 + "'").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize + 1, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Append 5 Strings in the array (ARR3) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayAppend("$.ARR3", s1 + "1").arrayAppend("$.ARR3", s1 + "2").arrayAppend("$.ARR3", s1 + "3")
                  .arrayAppend("$.ARR3", s1 + "4").arrayAppend("$.ARR3", s1 + "5").sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.ARR3[" + (arraySize) + "] = '" + s1 + "'").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize + 6, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
      }
  
      /* ArrayInsert() for int , double and string */
      @Test
      public void testCollectionModifyArrayInsert() throws Exception {
          int i = 0, j = 0, maxrec = 8, arraySize = 30;
          int lStr = 10;
          JsonArray yArray = null;
          DbDoc doc = null;
          DocResult docs = null;
          Result res = null;
          String s1 = buildString((lStr), '.');
          long l3 = 2147483647;
          double d1 = 1000.1234;
  
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonNumber().setValue(String.valueOf(i + 1)));
  
              JsonArray jarray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  jarray.addValue(new JsonNumber().setValue(String.valueOf((l3 + j + i))));
              }
              newDoc2.add("ARR1", jarray);
  
              JsonArray karray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  karray.addValue(new JsonNumber().setValue(String.valueOf((d1 + j + i))));
              }
              newDoc2.add("ARR2", karray);
              JsonArray larray = new JsonArray();
              for (j = 0; j < (arraySize); j++) {
                  larray.addValue(new JsonString().setValue("St_" + i + "_" + j));
              }
              newDoc2.add("ARR3", larray);
              this.collection.add(newDoc2).execute();
              newDoc2 = null;
              jarray = null;
          }
          assertEquals((maxrec), this.collection.count());
  
          // Insert to a aposistion > arraySize Shld Work same as Append
          // Insert 1 number in the array (ARR1) after position arraySize where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayInsert("$.ARR1[" + (arraySize * 2) + "]", -1).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR1[" + (arraySize) + "] as SIGNED) = -1").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize + 1, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Insert 3 numbers in the array (ARR1) where $.F1 = 2
          res = this.collection.modify("CAST($.F1 as SIGNED) = 2").arrayInsert("$.ARR1[0]", -2).arrayInsert("$.ARR1[1]", -3).arrayInsert("$.ARR1[2]", -4)
                  .sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR1[0] as SIGNED) = -2").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals((long) (arraySize + 3), (long) yArray.size());
          assertEquals((long) (2), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertEquals((long) (-2), (long) (((JsonNumber) yArray.get(0)).getInteger()));
          assertEquals((long) (-3), (long) (((JsonNumber) yArray.get(1)).getInteger()));
          assertEquals((long) (-4), (long) (((JsonNumber) yArray.get(2)).getInteger()));
          assertFalse(docs.hasNext());
  
          // Insert 3 numbers in the array (ARR1) where $.F1 = 3
          res = this.collection.modify("CAST($.F1 as SIGNED) = 3").arrayInsert("$.ARR1[2]", -4).arrayInsert("$.ARR1[1]", -3).arrayInsert("$.ARR1[0]", -2)
                  .sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR1[2] as SIGNED) = -3").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR1");
          assertEquals(arraySize + 3, yArray.size());
          assertEquals((3), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertEquals((long) (-2), (long) (((JsonNumber) yArray.get(0)).getInteger()));
          assertEquals((long) (-3), (long) (((JsonNumber) yArray.get(2)).getInteger()));
          assertEquals((long) (-4), (long) (((JsonNumber) yArray.get(4)).getInteger()));
          assertFalse(docs.hasNext());
  
          // Insert 1 number in the array (ARR2) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayInsert("$.ARR2[1]", -4321.4321).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR2[1] as DECIMAL(10,4)) = -4321.4321").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR2");
          assertEquals((long) (arraySize + 1), (long) yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Insert 3 number in the array (ARR2) where $.F1 = 2
          res = this.collection.modify("CAST($.F1 as SIGNED) = 2").arrayInsert("$.ARR2[2]", 4321.1234).arrayInsert("$.ARR2[0]", 4321.9847)
                  .arrayInsert("$.ARR2[1]", -4321.9888).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("CAST($.ARR2[0] as  DECIMAL(10,4)) =  4321.9847").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR2");
          assertEquals(arraySize + 3, yArray.size());
          assertEquals((long) (2), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertEquals(new BigDecimal(String.valueOf("4321.1234")), (((JsonNumber) yArray.get(4)).getBigDecimal()));
          assertEquals(new BigDecimal(String.valueOf("4321.9847")), (((JsonNumber) yArray.get(0)).getBigDecimal()));
          assertEquals(new BigDecimal(String.valueOf("-4321.9888")), (((JsonNumber) yArray.get(1)).getBigDecimal()));
          assertFalse(docs.hasNext());
  
          // Insert 1 String in the array (ARR3) where $.F1 = 1
          res = this.collection.modify("CAST($.F1 as SIGNED) = 1").arrayInsert("$.ARR3[1]", s1).sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.ARR3[1]  = '" + s1 + "'").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize + 1, yArray.size());
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Insert 3 Strings in the array (ARR3) where $.F1 = 2
          res = this.collection.modify("CAST($.F1 as SIGNED) = 2").arrayInsert("$.ARR3[1]", s1).arrayInsert("$.ARR3[2]", s1).arrayInsert("$.ARR3[0]", "")
                  .sort("$._id").execute();
          assertEquals(1, res.getAffectedItemsCount());
          docs = this.collection.find("$.ARR3[0]  = ''").orderBy("$._id").execute();
          doc = docs.next();
          yArray = (JsonArray) doc.get("ARR3");
          assertEquals(arraySize + 3, yArray.size());
          assertEquals((long) (2), (long) (((JsonNumber) doc.get("F1")).getInteger()));
          assertFalse(docs.hasNext());
  
          // Insert 3 Strings in the array (ARR3) using an empty condition
          assertThrows(XDevAPIError.class, "Parameter 'criteria' must not be null or empty.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.modify(" ").arrayInsert("$.ARR3[1]", s1).arrayInsert("$.ARR3[2]", s1).arrayInsert("$.ARR3[0]", "")
                          .sort("$._id").execute();
                  return null;
              }
          });
  
          // Insert 3 Strings in the array (ARR3) using null condition
          assertThrows(XDevAPIError.class, "Parameter 'criteria' must not be null or empty.", new Callable<Void>() {
              public Void call() throws Exception {
                  CollectionModifyTest.this.collection.modify(null).arrayInsert("$.ARR3[1]", s1).arrayInsert("$.ARR3[2]", s1).arrayInsert("$.ARR3[0]", "")
                          .sort("$._id").execute();
                  return null;
              }
          });
      }
  
      @Test
      public void testCollectionModifyAsync() throws Exception {
          int i = 0, j = 0, maxrec = 10;
          DbDoc doc = null;
          AddResult res = null;
          Result res2 = null;
          CompletableFuture<AddResult> asyncRes = null;
          CompletableFuture<Result> asyncRes2 = null;
          CompletableFuture<DocResult> asyncDocs = null;
          DocResult docs = null;
          double d = 100.123;
  
          /* add().executeAsync() maxrec num of records */
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(d + i)));
              newDoc2.add("F3", new JsonNumber().setValue(String.valueOf(100 + i)));
              newDoc2.add("T", new JsonNumber().setValue(String.valueOf(i)));
              asyncRes = this.collection.add(newDoc2).executeAsync();
              res = asyncRes.get();
              assertEquals(1, res.getAffectedItemsCount());
              newDoc2 = null;
          }
  
          assertEquals((maxrec), this.collection.count());
  
          asyncDocs = this.collection.find("F3 >= ? and F3 < ?").bind(100, 100006).fields(expr("{'cnt':count($.F1)}")).executeAsync();
          docs = asyncDocs.get();
          doc = docs.next();
          assertEquals((long) maxrec, (long) (((JsonNumber) doc.get("cnt")).getInteger()));
  
          /* Simple Update with executeAsync */
          asyncRes2 = this.collection.modify("$.F3 > 100").unset("$.T").sort("$.F3 desc").executeAsync();
          res2 = asyncRes2.get();
          assertEquals((maxrec - 1), res2.getAffectedItemsCount());
  
          asyncRes2 = this.collection.modify("$.F3 >= 100").change("$.T", expr("10000+1")).sort("$.F3 desc").executeAsync();
          res2 = asyncRes2.get();
          assertEquals((1), res2.getAffectedItemsCount());
  
          asyncDocs = this.collection.find("$.T >= ? ").bind(10000).fields(expr("{'cnt':count($.T)}")).executeAsync();
          docs = asyncDocs.get();
          doc = docs.next();
          assertEquals((long) (1), (long) (((JsonNumber) doc.get("cnt")).getInteger()));
  
          asyncRes2 = this.collection.modify("$.F3 >= 100").unset("$.T").sort("$.F3 desc").executeAsync();
          res2 = asyncRes.get();
          assertEquals(1, res2.getAffectedItemsCount());
  
          asyncRes2 = this.collection.modify("$.F3 >= 100").set("$.T", expr("10000+3")).sort("$.F3 desc").executeAsync();
          res2 = asyncRes2.get();
          assertEquals(maxrec, res2.getAffectedItemsCount());
  
          asyncDocs = this.collection.find("$.T >= ? ").bind(10000).fields(expr("{'cnt':count($.T)}")).executeAsync();
          docs = asyncDocs.get();
          doc = docs.next();
          assertEquals((long) maxrec, (long) (((JsonNumber) doc.get("cnt")).getInteger()));
  
          CompletableFuture<?> futures[] = new CompletableFuture<?>[501];
          //List <Object>futures =  new ArrayList<Object>();
          j = 0;
          for (i = 0; i < 500; ++i, j++) {
              if (j >= maxrec) {
                  j = 0;
              }
              futures[i] = this.collection.modify("$.F3 = " + (100 + j)).change("$.T", i).executeAsync();
          }
          for (i = 0; i < 500; ++i, j++) {
              // res = ((CompletableFuture<Result>) futures.get(i)).get();
              res2 = (Result) futures[i].get();
              assertEquals(1, res2.getAffectedItemsCount());
          }
  
          futures[i] = this.collection.modify("true").change("$.T", -1).executeAsync();
  
          // wait for them all to finish
          CompletableFuture.allOf(futures).get();
  
          asyncDocs = this.collection.find("$.T = ? ").bind(-1).fields(expr("{'cnt':count($.T)}")).executeAsync();
          docs = asyncDocs.get();
          doc = docs.next();
          assertEquals((long) maxrec, (long) (((JsonNumber) doc.get("cnt")).getInteger()));
      }
  
      @SuppressWarnings("unchecked")
      @Test
      public void testCollectionModifyAsyncMany() throws Exception {
          int i = 0, maxrec = 10;
          int NUMBER_OF_QUERIES = 1000;
          DbDoc doc = null;
          AddResult res = null;
          Result res2 = null;
          CompletableFuture<AddResult> asyncRes = null;
          CompletableFuture<DocResult> asyncDocs = null;
          DocResult docs = null;
          double d = 100.123;
  
          /* add().executeAsync() maxrec num of records */
          for (i = 0; i < maxrec; i++) {
              DbDoc newDoc2 = new DbDocImpl();
              newDoc2.add("_id", new JsonString().setValue(String.valueOf(i + 1000)));
              newDoc2.add("F1", new JsonString().setValue("Field-1-Data-" + i));
              newDoc2.add("F2", new JsonNumber().setValue(String.valueOf(d + i)));
              newDoc2.add("F3", new JsonNumber().setValue(String.valueOf(1000 + i)));
              newDoc2.add("T", new JsonNumber().setValue(String.valueOf(i)));
              asyncRes = this.collection.add(newDoc2).executeAsync();
              res = asyncRes.get();
              assertEquals(1, res.getAffectedItemsCount());
              newDoc2 = null;
          }
  
          assertEquals((maxrec), this.collection.count());
  
          List<Object> futures = new ArrayList<>();
          for (i = 0; i < NUMBER_OF_QUERIES; ++i) {
              if (i % 3 == 0) {
                  futures.add(this.collection.modify("$.F3 % 2 = 0 ").change("$.T", expr("1000000+" + i)).sort("$.F3 desc").executeAsync());
              } else if (i % 3 == 1) {
                  futures.add(this.collection.modify("$.F3 = " + (1000 + i)).change("$.T", expr("NON_EXISTING_FUNCTION()")).sort("$.F3 desc").executeAsync());//Error
              } else {
                  futures.add(this.collection.modify("$.F3 % 2 = 1 ").change("$.T", expr("$.F3+" + i)).sort("$.F3 desc").executeAsync());
              }
          }
  
          for (i = 0; i < NUMBER_OF_QUERIES; ++i) {
              if (i % 3 == 0) {
                  res2 = ((CompletableFuture<AddResult>) futures.get(i)).get();
                  assertEquals((maxrec) / 2, res2.getAffectedItemsCount());
              } else if (i % 3 == 1) {
                  int i1 = i;
                  assertThrows(ExecutionException.class, ".*FUNCTION " + this.schema.getName() + ".NON_EXISTING_FUNCTION does not exist.*",
                          () -> ((CompletableFuture<Result>) futures.get(i1)).get());
              } else {
                  res2 = ((CompletableFuture<Result>) futures.get(i)).get();
                  assertEquals((maxrec) / 2, res2.getAffectedItemsCount());
              }
          }
  
          asyncDocs = this.collection.find("$.T > :X ").bind("X", 1000000).fields(expr("{'cnt':count($.T)}")).executeAsync();
          docs = asyncDocs.get();
          doc = docs.next();
          assertEquals((long) (maxrec) / 2, (long) (((JsonNumber) doc.get("cnt")).getInteger()));
  
          asyncDocs = this.collection.find("$.T > :X and $.T < :Y").bind("X", 1000).bind("Y", 1000000).fields(expr("{'cnt':count($.T)}")).executeAsync();
          docs = asyncDocs.get();
          doc = docs.next();
          assertEquals((long) (maxrec) / 2, (long) (((JsonNumber) doc.get("cnt")).getInteger()));
      }
  }
  