Skip to content

Commit 3cbdb02

Browse files
authored
feat(java-shell): Cursor.next() should return JS object instead of Java object (#1065)
* feat(JMongosh): support conversion from Java to JS for all object types * feat(JMongosh): cursor.next() should return JS object instead of Java object
1 parent d41dc0e commit 3cbdb02

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+207
-65
lines changed

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/MongoShellConverter.kt

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.mongodb.mongosh.result.*
66
import com.mongodb.mongosh.service.Either
77
import com.mongodb.mongosh.service.Left
88
import com.mongodb.mongosh.service.Right
9+
import org.bson.BsonTimestamp
910
import org.bson.Document
1011
import org.bson.json.JsonReader
1112
import org.bson.types.*
@@ -22,11 +23,22 @@ internal class MongoShellConverter(private val context: MongoShellContext, priva
2223

2324
fun toJs(o: Any?): Any? {
2425
return when (o) {
25-
is Iterable<*> -> toJs(o)
26-
is Array<*> -> toJs(o)
27-
is Map<*, *> -> toJs(o)
28-
Unit -> context.eval("undefined")
29-
else -> o
26+
is Iterable<*> -> toJs(o)
27+
is Array<*> -> toJs(o)
28+
is Map<*, *> -> toJs(o)
29+
is Unit -> context.eval("undefined")
30+
is MaxKey -> bsonTypes.maxKey.newInstance()
31+
is MinKey -> bsonTypes.minKey.newInstance()
32+
is Binary -> bsonTypes.binData.newInstance(toJs(o.data), o.type)
33+
is Symbol -> bsonTypes.bsonSymbol.newInstance(o.symbol)
34+
is Decimal128 -> bsonTypes.numberDecimal.newInstance(o.bigDecimalValue().toPlainString())
35+
is Long -> bsonTypes.numberLong.newInstance(o.toString())
36+
is Int -> bsonTypes.numberInt.newInstance(o.toString())
37+
is BsonTimestamp -> bsonTypes.timestamp.newInstance(o.inc, o.time)
38+
is CodeWithScope -> bsonTypes.code.newInstance(o.code, toJs(o.scope))
39+
is Code -> bsonTypes.code.newInstance(o.code)
40+
is DBRef -> bsonTypes.dbRef.newInstance(o.collectionName, toJs(o.id), o.databaseName)
41+
else -> o
3042
}
3143
}
3244

@@ -47,6 +59,14 @@ internal class MongoShellConverter(private val context: MongoShellContext, priva
4759
return array
4860
}
4961

62+
private fun toJs(array: ByteArray): Value {
63+
val jsArray = context.eval("[]")
64+
array.forEachIndexed { index, v ->
65+
jsArray.setArrayElement(index.toLong(), v)
66+
}
67+
return jsArray
68+
}
69+
5070
private fun toJs(list: Array<*>): Value {
5171
val array = context.eval("[]")
5272
list.forEachIndexed { index, v ->
@@ -96,25 +116,26 @@ internal class MongoShellConverter(private val context: MongoShellContext, priva
96116
// document with aggregation explain result also has type AggregationCursor, so we need to make sure that value contains cursor
97117
type == "AggregationCursor" && v.hasMember("_cursor") -> CursorResult(Cursor<Any?>(v, this))
98118
type == "InsertOneResult" -> InsertOneResult(v["acknowledged"]!!.asBoolean(), v["insertedId"]!!.asString())
99-
type == "DeleteResult" -> DeleteResult(v["acknowledged"]!!.asBoolean(), v["deletedCount"]!!.asLong())
119+
type == "DeleteResult" -> DeleteResult(v["acknowledged"]!!.asBoolean(), (toJava(v["deletedCount"]!!) as LongResult).value)
100120
type == "UpdateResult" -> {
101121
val res = if (v["acknowledged"]!!.asBoolean()) {
102122
UpdateResult.acknowledged(
103-
v["matchedCount"]!!.asLong(),
104-
v["modifiedCount"]!!.asLong(),
105-
null
123+
(toJava(v["matchedCount"]!!) as LongResult).value,
124+
(toJava(v["modifiedCount"]!!) as LongResult).value,
125+
null
106126
)
107127
} else UpdateResult.unacknowledged()
108128
MongoShellUpdateResult(res)
109129
}
110130
type == "BulkWriteResult" -> BulkWriteResult(
111-
v["acknowledged"]!!.asBoolean(),
112-
v["insertedCount"]!!.asLong(),
113-
v["matchedCount"]!!.asLong(),
114-
v["modifiedCount"]!!.asLong(),
115-
v["deletedCount"]!!.asLong(),
116-
v["upsertedCount"]!!.asLong(),
117-
(toJava(v["upsertedIds"]!!) as ArrayResult).value)
131+
v["acknowledged"]!!.asBoolean(),
132+
(toJava(v["insertedCount"]!!) as IntResult).value,
133+
(toJava(v["matchedCount"]!!) as IntResult).value,
134+
(toJava(v["modifiedCount"]!!) as IntResult).value,
135+
(toJava(v["deletedCount"]!!) as IntResult).value,
136+
(toJava(v["upsertedCount"]!!) as IntResult).value,
137+
(toJava(v["upsertedIds"]!!) as ArrayResult).value
138+
)
118139
type == "InsertManyResult" -> InsertManyResult(v["acknowledged"]!!.asBoolean(), toJava(v["insertedIds"]!!).value as List<String>)
119140
v.instanceOf(context, "RegExp") -> {
120141
val pattern = v["source"]!!.asString()
@@ -143,7 +164,7 @@ internal class MongoShellConverter(private val context: MongoShellContext, priva
143164
v.instanceOf(context, bsonTypes.dbRef) -> {
144165
val databaseName = v["db"]?.let { if (it.isNull) null else it }?.asString()
145166
val collectionName = v["collection"]!!.asString()
146-
val value = toJava(v["oid"]!!).value
167+
val value = toJava(v["oid"]!!).value!!
147168
DBRefResult(DBRef(databaseName, collectionName, value))
148169
}
149170
v.instanceOf(context, bsonTypes.numberLong) -> LongResult(JsonReader(v.invokeMember("toExtendedJSON").toString()).readInt64())

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/result/MongoShellResult.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ class DeleteResult(val acknowledged: Boolean, val deletedCount: Long) : MongoShe
111111
}
112112

113113
class BulkWriteResult(val acknowledged: Boolean,
114-
val insertedCount: Long,
115-
val matchedCount: Long,
116-
val modifiedCount: Long,
117-
val deletedCount: Long,
118-
val upsertedCount: Long,
114+
val insertedCount: Int,
115+
val matchedCount: Int,
116+
val modifiedCount: Int,
117+
val deletedCount: Int,
118+
val upsertedCount: Int,
119119
val upsertedIds: List<Any?>) : MongoShellResult<Map<String, Any>>() {
120120
override val value: Map<String, Any>
121121
get() = mapOf("acknowledged" to acknowledged,

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/service/Cursor.kt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,14 @@ internal class Cursor(private var helper: BaseMongoIterableHelper<*>, private va
191191

192192
@HostAccess.Export
193193
override fun next(): Any? {
194-
/* findOne returns single document as a result.
195-
* Mongosh core will try to defineProperty on it and fail if value is not wrapped in JS object */
196-
val shouldWrap = iterator == null && helper.limit() == 1
197194
val value = getOrCreateIterator().next()
198-
return if (shouldWrap) wrapper.wrap(value) // it's possibly a findOne call
199-
else value
195+
return converter.toJs(value)
200196
}
201197

202198
@HostAccess.Export
203199
override fun tryNext(): Any? {
204-
/* findOne returns single document as a result.
205-
* Mongosh core will try to defineProperty on it and fail if value is not wrapped in JS object */
206-
val shouldWrap = iterator == null && helper.limit() == 1
207200
val value = getOrCreateIterator().tryNext()
208-
return if (shouldWrap) wrapper.wrap(value) // it's possibly a findOne call
209-
else value
201+
return converter.toJs(value)
210202
}
211203

212204
@HostAccess.Export
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{ "acknowledged": true, "insertedId": "UNKNOWN" }
22
true
3-
[ { "_id": <ObjectID>, "a": 1, "objectId": <ObjectID>, "maxKey": {"$maxKey": 1}, "minKey": {"$minKey": 1}, "binData": {"$binary": {"base64": "MTIzNA==", "subType": "10"}}, "date": {"$date": {"$numberLong": "1355875200000"}}, "isoDate": {"$date": {"$numberLong": "1355875200000"}}, "numberInt": 24, "timestamp": Timestamp{value=429496729600, seconds=100, inc=0}, "undefined": null, "null": null, "uuid": <UUID> } ]
3+
[ { "_id": <ObjectID>, "a": 1, "objectId": <ObjectID>, "maxKey": {"$maxKey": 1}, "minKey": {"$minKey": 1}, "binData": {"$binary": {"base64": "MTIzNA==", "subType": "10"}}, "date": {"$date": {"$numberLong": "1355875200000"}}, "isoDate": {"$date": {"$numberLong": "1355875200000"}}, "numberInt": 24, "timestamp": {"$timestamp": {"t": 100, "i": 0}}, "undefined": null, "null": null, "uuid": <UUID> } ]
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
{ "_id": <ObjectID>, "name": "value1", "v": 1 }
1+
{ "_id": <ObjectID>, "name": "value1", "v": 1 }
2+
value1

packages/java-shell/src/test/resources/cursor/next.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ db.coll.insertOne({name: "value2", v: 2});
55
db.coll.insertOne({name: "value2", v: 3});
66
// command
77
db.coll.find().next();
8+
// command
9+
db.coll.find().next().name;
810
// clear
911
db.coll.drop();
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
SymbolResult: a
2-
SymbolResult: b
2+
SymbolResult: b
3+
SymbolResult: c
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
// before
2+
db.coll.insertOne({"_id": 1, v: new BSONSymbol('c')});
13
// command checkResultClass
24
BSONSymbol('a')
35
// command checkResultClass
4-
new BSONSymbol('b')
6+
new BSONSymbol('b')
7+
// command checkResultClass
8+
db.coll.find().toArray()[0].v;
9+
// clear
10+
db.coll.drop();
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
BinaryResult: {"$binary": {"base64": "MTIzNA==", "subType": "10"}}
22
BinaryResult: {"$binary": {"base64": "MTIzNA==", "subType": "10"}}
3-
BinaryResult: {"$binary": {"base64": "MTIzNA==", "subType": "04"}}
3+
BinaryResult: {"$binary": {"base64": "MTIzNA==", "subType": "04"}}
4+
BinaryResult: {"$binary": {"base64": "MTIzNA==", "subType": "10"}}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
// before
2+
db.coll.insertOne({"_id": 1, v: new BinData(16, 'MTIzNA==')})
13
// command checkResultClass
24
new BinData(16, 'MTIzNA==')
35
// command checkResultClass
46
BinData(16, 'MTIzNA==')
57
// command checkResultClass
6-
new BinData(4, 'MTIzNA==')
8+
new BinData(4, 'MTIzNA==')
9+
// command checkResultClass
10+
db.coll.find().toArray()[0].v
11+
// clear
12+
db.coll.drop()

0 commit comments

Comments
 (0)