How to create BSONDocument by passing it an array (rather than var args)

768 views
Skip to first unread message

sfte...@gmail.com

unread,
Aug 11, 2017, 5:24:58 PM8/11/17
to ReactiveMongo - http://reactivemongo.org
Lets say I am create a BSONDocument like this right now:

BSONDocument(
"field1" -> "value1",
"field2" -> "value2"
)

That works.

But lets say I want to do this:

val fields = List(
"field1" -> "value1",
"field2" -> "value2"
)

BSONDocument(fields)

This does not work?  It will only save a field here if the value is of BSONValue type and no automatic conversions happen.  So that means you must manually use a BSONValue in your list like this: 


val fields = List(
"field1" -> BSONDateTime(new Date().getTime),
"field2" -> "value2"
)

BSONDocument(fields)

Here, field1 will get written, but field2 will not.

I have also tried unwrapping the list directly to the variable argument constructor like so:

BSONDocument(fields: _*)  

This does not work, complains that expects BSONElementSet but got List[String,Product with Serializable].  I would think it should be using implicit conversion here but isn't.... 

So what am I missing?  Why can't I simply move my variable argument list of Tuple2 into a List, and pass that list to the BSONDocument apply method?

Cédric Chantepie

unread,
Aug 11, 2017, 5:57:02 PM8/11/17
to ReactiveMongo - http://reactivemongo.org
Even w/o considering the goal of creating a document, the List is mis-typed (Product with Serializable).

sfte...@gmail.com

unread,
Aug 11, 2017, 6:05:08 PM8/11/17
to ReactiveMongo - http://reactivemongo.org
Ignore that - It doesn't matter what type I use for the list, it doesn't work regardless.  For example:

val fields : List[(String,Any)] = List(
"field1" -> "value1",
"field2" -> "value2"
)

BSONDocument(fields)

This will fail with "Type mismatch, expected: Seq[BSONElementSet], actual: List[(String,Any)]

The problem (as far as I can tell) is that while you CAN do this:

BSONDocument(
"field1" -> "value1",
"field2" -> "value2"
)

You CANT do this:

val fields : List[(String,Any)] = List(
"field1" -> "value1",
"field2" -> "value2"
)

BSONDocument(fields)

Because it would appear that the implicit conversion to a BSONValue is not happening?  Doing the latter will write nothing to the MongoDB, unless each of those values is specifically a BSONValue like BSONString("value1").  THEN it will write.

Cédric Chantepie

unread,
Aug 12, 2017, 6:50:49 AM8/12/17
to ReactiveMongo - http://reactivemongo.org
Having Any or Product as type means there is a mistyping issue. The specific type for each element being "erased" and unified to a meaningless type, compiler has no way to find the appropriate code instances for each value.

Cédric Chantepie

unread,
Aug 12, 2017, 7:49:27 AM8/12/17
to ReactiveMongo - http://reactivemongo.org

sfte...@gmail.com

unread,
Aug 14, 2017, 3:06:35 PM8/14/17
to ReactiveMongo - http://reactivemongo.org
Do you have any suggestion as to how I can accomplish what I am after?

I want to be able to create a BSONDocument at runtime, with a variable number of parameters.  Ideally, this could be an list of Tuple2 objects, that I can pass to a BSONDocument() creator, like this:

val fields : List[(String,Any)] = List(
("field1","value1"),
("field2", 1234),
("field3", true)
)

BSONDocument(fields)


On Saturday, August 12, 2017 at 4:49:27 AM UTC-7, Cédric Chantepie wrote:
Similar to https://stackoverflow.com/questions/41446090/scala-no-json-serializer-found-for-type-any/41446446#41446446

Cédric Chantepie

unread,
Aug 14, 2017, 3:30:18 PM8/14/17
to ReactiveMongo - http://reactivemongo.org
As you should have read in the post for JSON, a list of JSON properties must be a list of (String, JsValue), and cannot be a list of (String, Any) (or a list of (String, Product)).
Similarly a list of BSON properties must be a list of (String, BSONValue).

sfte...@gmail.com

unread,
Aug 14, 2017, 3:43:30 PM8/14/17
to ReactiveMongo - http://reactivemongo.org
Clearly, I understand that it must be a Tuple[String,BSONValue]

But how can one do this:

BSONDocument(
"field1" -> "field2"
)

Here, as you can clearly see, "field2" is a String, not a BSONValue.  So, clearly, it is implicitly converted to a BSONValue.  

What I am after is the ability for this implicit conversion to be applied to a list.  Just as a BSONDocument can be created with any number of types like so:

BSONDocument(
"field1" -> "value1"   // String -> BSONString
"field2" -> 2  // Integer -> BSONInteger 
)

So too, I would like to be able to pass a list with these values which are then implicitly converted to BSONValues.  Is there just no way of doing this?

sfte...@gmail.com

unread,
Aug 14, 2017, 3:52:19 PM8/14/17
to ReactiveMongo - http://reactivemongo.org
Probably I am misunderstanding the implicit conversion.  When I think about it more, I do realize, that the compiler understands how to convert values in this case:

BSONDocument(
"field1":"value1"
)

Because the type of "value1" is known (String).  But a list of Any has no idea until runtime, what those values are, and so cannot implicitly convert them.

Cédric Chantepie

unread,
Aug 14, 2017, 4:31:42 PM8/14/17
to ReactiveMongo - http://reactivemongo.org
That's exactly why in such case of heterogenous values, the conversions must happen before being added added to the list, so that the types in this list are explicit unified in a meaningful way.
Reply all
Reply to author
Forward
0 new messages