From 6eaca9aa75e3b92cc525cb5e81e2781fda3c3516 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 11 Jan 2021 16:19:27 +0000 Subject: [PATCH 1/4] Scala: Ensure implicit conversion keeps type fidelity When converting from a BsonDocument to org.bson.Document the scala driver previously used json. The implementation should not rely on the json output format, but rather use the Codecs to do the conversion. JAVA-3916 --- .../scala/org/mongodb/scala/package.scala | 22 ++++++++-- .../org/mongodb/scala/ScalaPackageSpec.scala | 42 +++++++++++++++---- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/driver-scala/src/main/scala/org/mongodb/scala/package.scala b/driver-scala/src/main/scala/org/mongodb/scala/package.scala index 95a1e415fbd..8d8cd446f21 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/package.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/package.scala @@ -16,11 +16,18 @@ package org.mongodb +import org.bson.codecs.{ BsonDocumentCodec, DecoderContext, DocumentCodec, EncoderContext } +import org.bson.{ BsonBinaryReader, BsonBinaryWriter, ByteBufNIO } +import org.bson.io.{ BasicOutputBuffer, ByteBufferBsonInput } +import org.bson.json.{ JsonMode, JsonWriterSettings } + import _root_.scala.language.implicitConversions import _root_.scala.reflect.ClassTag import org.mongodb.scala.bson.BsonDocument import org.mongodb.scala.internal.WriteConcernImplicits +import java.nio.ByteBuffer + /** * The MongoDB Scala Driver package * @@ -392,9 +399,18 @@ package object scala extends ClientSessionImplicits with ObservableImplicits wit implicit def bsonDocumentToDocument(doc: BsonDocument): Document = new Document(doc) - implicit def bsonDocumentToUntypedDocument(doc: BsonDocument): org.bson.Document = - org.bson.Document.parse(doc.toJson()) + implicit def documentToUntypedDocument(doc: Document): org.bson.Document = + bsonDocumentToUntypedDocument(doc.underlying) + + implicit def bsonDocumentToUntypedDocument(doc: BsonDocument): org.bson.Document = { + val writer: BsonBinaryWriter = new BsonBinaryWriter(new BasicOutputBuffer()) + new BsonDocumentCodec().encode(writer, doc, EncoderContext.builder().build()) - implicit def documentToUntypedDocument(doc: Document): org.bson.Document = org.bson.Document.parse(doc.toJson()) + val buffer: BasicOutputBuffer = writer.getBsonOutput.asInstanceOf[BasicOutputBuffer]; + val reader: BsonBinaryReader = + new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(buffer.toByteArray)))) + + new DocumentCodec().decode(reader, DecoderContext.builder().build()) + } } diff --git a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala index d89c58d3e26..13ad7117b2b 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala @@ -17,15 +17,14 @@ package org.mongodb.scala import java.util.concurrent.TimeUnit - import _root_.scala.concurrent.duration.Duration - import com.mongodb.{ MongoCredential => JMongoCredential } - +import org.bson.BsonDocumentWrapper +import org.bson.codecs.DocumentCodec import org.mongodb.scala -import org.mongodb.scala.bson.BsonString +import org.mongodb.scala.MongoClient.DEFAULT_CODEC_REGISTRY +import org.mongodb.scala.bson._ import org.mongodb.scala.model._ -import org.scalatest.{ FlatSpec, Matchers } class ScalaPackageSpec extends BaseSpec { @@ -66,9 +65,9 @@ class ScalaPackageSpec extends BaseSpec { it should "be able to create Documents" in { val doc = Document("a" -> BsonString("1")) - val doc2 = org.mongodb.scala.bson.collection.Document("a" -> BsonString("1")) + val doc2 = collection.Document("a" -> BsonString("1")) - doc shouldBe a[org.mongodb.scala.bson.collection.immutable.Document] + doc shouldBe a[collection.immutable.Document] doc should equal(doc2) } @@ -141,4 +140,33 @@ class ScalaPackageSpec extends BaseSpec { val javaCredential5 = JMongoCredential.createGSSAPICredential("userName") scalaCredential5 should equal(javaCredential5) } + + it should "implicitly convert to org.Bson.document with type fidelity" in { + + val bsonDocument = Document( + "null" -> BsonNull(), + "int32" -> BsonInt32(32), + "int64" -> BsonInt64(64L), + "decimal128" -> BsonDecimal128(128), + "boolean" -> BsonBoolean(true), + "date" -> BsonDateTime(123456789), + "double" -> BsonDouble(1.1), + "string" -> BsonString("String"), + "minKey" -> BsonMinKey(), + "maxKey" -> BsonMaxKey(), + "javaScript" -> BsonJavaScript("function () {}"), + "objectId" -> BsonObjectId(), + "codeWithScope" -> BsonJavaScriptWithScope("function () {}", Document()), + "regex" -> BsonRegularExpression("/(.*)/"), + "symbol" -> BsonSymbol(Symbol("sym")), + "timestamp" -> BsonTimestamp(), + "undefined" -> BsonUndefined(), + "binary" -> BsonBinary(Array[Byte](128.toByte)), + "array" -> BsonArray(List("a", "b", "c")), + "document" -> Document("a" -> 1, "b" -> List(1, 2, 3)) + ) + + val document: org.bson.Document = bsonDocument + BsonDocumentWrapper.asBsonDocument(document, DEFAULT_CODEC_REGISTRY) should equal(bsonDocument.underlying) + } } From 224e1425d815ef57aecaee74df947a7cf8f9db6e Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 11 Jan 2021 16:55:30 +0000 Subject: [PATCH 2/4] Code review updates --- .../scala/org/mongodb/scala/package.scala | 22 +++++-------------- .../org/mongodb/scala/ScalaPackageSpec.scala | 2 +- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/driver-scala/src/main/scala/org/mongodb/scala/package.scala b/driver-scala/src/main/scala/org/mongodb/scala/package.scala index 8d8cd446f21..970f287786e 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/package.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/package.scala @@ -16,17 +16,13 @@ package org.mongodb -import org.bson.codecs.{ BsonDocumentCodec, DecoderContext, DocumentCodec, EncoderContext } -import org.bson.{ BsonBinaryReader, BsonBinaryWriter, ByteBufNIO } -import org.bson.io.{ BasicOutputBuffer, ByteBufferBsonInput } -import org.bson.json.{ JsonMode, JsonWriterSettings } - -import _root_.scala.language.implicitConversions -import _root_.scala.reflect.ClassTag +import org.bson.BsonDocumentReader +import org.bson.codecs.{ DecoderContext, DocumentCodec } import org.mongodb.scala.bson.BsonDocument import org.mongodb.scala.internal.WriteConcernImplicits -import java.nio.ByteBuffer +import _root_.scala.language.implicitConversions +import _root_.scala.reflect.ClassTag /** * The MongoDB Scala Driver package @@ -402,15 +398,9 @@ package object scala extends ClientSessionImplicits with ObservableImplicits wit implicit def documentToUntypedDocument(doc: Document): org.bson.Document = bsonDocumentToUntypedDocument(doc.underlying) + private lazy val DOCUMENT_CODEC = new DocumentCodec() implicit def bsonDocumentToUntypedDocument(doc: BsonDocument): org.bson.Document = { - val writer: BsonBinaryWriter = new BsonBinaryWriter(new BasicOutputBuffer()) - new BsonDocumentCodec().encode(writer, doc, EncoderContext.builder().build()) - - val buffer: BasicOutputBuffer = writer.getBsonOutput.asInstanceOf[BasicOutputBuffer]; - val reader: BsonBinaryReader = - new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(buffer.toByteArray)))) - - new DocumentCodec().decode(reader, DecoderContext.builder().build()) + DOCUMENT_CODEC.decode(new BsonDocumentReader(doc), DecoderContext.builder().build()) } } diff --git a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala index 13ad7117b2b..5ee4b16b27d 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala @@ -141,7 +141,7 @@ class ScalaPackageSpec extends BaseSpec { scalaCredential5 should equal(javaCredential5) } - it should "implicitly convert to org.Bson.document with type fidelity" in { + it should "implicitly convert to org.bson.document with type fidelity" in { val bsonDocument = Document( "null" -> BsonNull(), From 5d701f66491207803ce0f37e8b0c9a0743895f96 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 12 Jan 2021 10:19:55 +0000 Subject: [PATCH 3/4] Use Long.maxValue for the long value --- .../src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala index 5ee4b16b27d..3db5592d765 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala @@ -146,7 +146,7 @@ class ScalaPackageSpec extends BaseSpec { val bsonDocument = Document( "null" -> BsonNull(), "int32" -> BsonInt32(32), - "int64" -> BsonInt64(64L), + "int64" -> BsonInt64(Long.MaxValue), "decimal128" -> BsonDecimal128(128), "boolean" -> BsonBoolean(true), "date" -> BsonDateTime(123456789), From 84cb99f90734234192e19aceb84cb119d7624654 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 13 Jan 2021 14:53:33 +0000 Subject: [PATCH 4/4] CR update --- .../src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala index 3db5592d765..3a91b8c3034 100644 --- a/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala +++ b/driver-scala/src/test/scala/org/mongodb/scala/ScalaPackageSpec.scala @@ -147,7 +147,7 @@ class ScalaPackageSpec extends BaseSpec { "null" -> BsonNull(), "int32" -> BsonInt32(32), "int64" -> BsonInt64(Long.MaxValue), - "decimal128" -> BsonDecimal128(128), + "decimal128" -> BsonDecimal128(128.1), "boolean" -> BsonBoolean(true), "date" -> BsonDateTime(123456789), "double" -> BsonDouble(1.1),