GORM not serializing java.time types as expected - mongodb

I'm trying to get some java.time types (LocalDate, ZonedDateTime) to work with Grails 5, GORM 7, with a MongoDb 3.4 database. I'm using the MongoDb plugin v7.3.0, mongodb-driver-core/mongodb-driver-sync v4.5.0.
The appropriate codecs seem to be available from the package org.grails.datastore.bson.codecs, but they don't seem be be getting used. When a document is stored to Mongo, LocalDate and ZonedDateTime are serialized as strings, and when I try to retrieve them, I get this error:
Cannot convert value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'dob': no matching editors or conversion strategy found.
I've reviewed the GORM for MongoDb docs (https://gorm.grails.org/latest/mongodb/manual/), but there isn't much help there. I would assume the appropriate codecs would be automatically registered, but in case the weren't I followed that documentation to register the codecs in my application.yml file, but that didn't work either. Not sure next step on this, maybe I can't do this in Grails?
Edit: Added example repo: https://github.com/MichaelJRussell/mongo-test

Related

Default Mongodb settings for mongodb connection in spring-data-mongodb

I'm using Spring Boot and am trying to figure out how I can see a list of all the default properties for the MongoDB connection.
I looked at AbstractMongoClientConfiguration but it's abstract and I can't see where the defaults come from. Looking at the class hierarchy for this class I can't see any default implementation in the Spring libs I have.
Where are the defaults for this connection? I don't see any properties files either, but might be missing them.
Had the same issue not too long ago. The official spring-data-mongodb documentation doesn't really mention any details about connection properties and their default values.
However, you can find more detailed information about the connection parameters in the MongoDB Java Driver Documentation and even more detailed in the Connection string docs including some default values.
Spring Data MongoDB uses default values generated by calling the builder method for MongoClientSettings.

Maps/Collections in MongoDB Java driver

In the C# mongodb driver, there are 3 possible representations for Dictionaries:
Document, ArrayOfArrays, ArrayOfDocuments.
https://mongodb.github.io/mongo-csharp-driver/2.8/reference/bson/mapping/#dictionary-serialization-options
As far as I understand, the Java driver supports (only or by default) the "Document" representation.
Is there a Convention or other built-in way to configure the driver to use ArrayOfArrays?
I was not able to see anything related in the MongoDB Java Driver documentation.
According to the Java driver team, the answer is that while there's isn't a simple flag:
You can do it with a custom codec to handle Map conversions to a nested key, value array for all Maps.
Alternatively, you could create a custom annotation could be used to set the codec for a single Class field in the POJO. That way you would not have to worry about all Maps being treated the same by the codec registry.
If you want to store all maps in the same way, the first option is obviously easier. You can refer to the driver code to see how the built-in annotations are built.

google-cloud-datastore java client: Is there a way to infer schema and/or retrieve results as Json?

I am working on datastore datasource for apache-spark based on spark datasource V2 api. I was able to implement using hard-coded single entity but couldn't generalize it. Either I need to infer entity schema and translate entity record into Spark Row or read entity record as json and let the user translate into scala product (datastore java client is REST based so the payload is being pulled as json). I could see "entity.properties" as json key-values from within IntelliJ debugger which includes everything I need (column name, value, type etc.) but I can't use entity.properties due to access restrictions. Appreciate any ideas.
fixed by switching to low level API https://github.com/GoogleCloudPlatform/google-cloud-datastore
full source for spark-datastore-connector https://github.com/sgireddy/spark-datastore-connector

Scala, Morphia and Enumeration

I need to store Scala class in Morphia. With annotations it works well unless I try to store collection of _ <: Enumeration
Morphia complains that it does not have serializers for that type, and I am wondering, how to provide one. For now I changed type of collection to Seq[String], and fill it with invoking toString on every item in collection.
That works well, however I'm not sure if that is right way.
This problem is common to several available layers of abstraction on the top of MongoDB. It all come back to a base reason: there is no enum equivalent in json/bson. Salat for example has the same problem.
In fact, MongoDB Java driver does not support enums as you can read in the discussion going on here: https://jira.mongodb.org/browse/JAVA-268 where you can see the problem is still open. Most of the frameworks I have seen to use MongoDB with Java do not implement low-level functionalities such as this one. I think this choice makes a lot of sense because they leave you the choice on how to deal with data structures not handled by the low-level driver, instead of imposing you how to do it.
In general I feel that the absence of support comes not from technical limitation but rather from design choice. For enums, there are multiple way to map them with their pros and their cons, while for other data types is probably simpler. I don't know the MongoDB Java driver in detail, but I guess supporting multiple "modes" would have required some refactoring (maybe that's why they are talking about a new version of serialization?)
These are two strategies I am thinking about:
If you want to index on an enum and minimize space occupation, you will map the enum to an integer ( Not using the ordinal , please can set enum start value in java).
If your concern is queryability on the mongoshell, because your data will be accessed by data scientist, you would rather store the enum using its string value
To conclude, there is nothing wrong in adding an intermediate data structure between your native object and MongoDB. Salat support it through CustomTransformers, on Morphia maybe you would need to do the conversion explicitely. Go for it.

casbah mongodb more typesafe way to access object parameters

In casbah, there are two methods called .getAs and .getAsOrElse in MongoDBObject, which returns the relevant fields' values in the type which given as the type parameter.
val dbo:MongoDBObject = ...
dbo.getAs[String](param)
This must be using type casting, because we can get a Long as a String by giving it as the type parameter, which might caused to type cast exception in runtime. Is there any other typesafe way to retrieve the original type in the result?
This must be possible because the type information of the element should be there in the getAs's output.
Check out this excellent presentation on Salat by it's author. What you're looking for is Salat grater which can convert to and from DBObject.
Disclamer: I am biased as I'm the author of Subset
I built this small library "Subset" exactly for the reason to be able to work effectively with DBObject's fields (both scalar and sub-documents) in a type-safe manner. Look through Examples and see if it fits your needs.
The problem is that mongodb can store multiple types for a single field, so, I'm not sure what you mean by making this typesafe. There's no way to enforce it on the database side, so were you hoping that there is a way to enforce it on the casbah side? You could just do get("fieldName"), and get an Object, to be safest--but that's hardly an improvement, in my opinion.
I've been happy using Salat + Casbah, and when my database record doesn't match my Salat case class, I get a runtime exception. I just know that I have to run migration scripts when I change the types in my model, or create a new model for the new types (multiple models can be stored in the same collection). At least the Salat grater/DAO methods make it less of a hassle (you don't have to specify types every time you access a variable).