Using java.util.Set domain property with Grails 3.1.x and Mongo 5.0.x plugin - mongodb

I'm trying to create an embedded collection in a Grails GORM Mongo domain class.
class User {
String name
Set<String> friends = []
}
I want to store a Set (a non-duplicated list) of other names of users.
When I try to save the User domain class:
new User(name: 'Bob').save(failOnError: true)
I get the error.
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for interface java.util.Set.
Changing the Set to List works fine but I don't want duplicates and don't want to have to manage that with a List.
Is there a way GORM will use the underlying Mongo $addToSet functionality.

It might be a GORM MongoDB issue. You can create an issue here with reproducing the issue.
But for now, you can do the workaround this problem using the List like this:
class User {
String name
List<String> friends = []
void removeDuplicate() {
this.friends?.unique()
}
def beforeInsert() {
removeDuplicate()
}
def beforeUpdate() {
removeDuplicate()
}
}

Related

Spring Data MongoDB - Is there a way to have an implicit timestamp?

Using my MongoDB and Spring Data MongoDB I am currently looking for a timestamp to be visible in the collection documents.
For now the document looks like this:
{
"_id":"f84fd693-e04b-4acb-9390-32ee755c1506",
"name":"Herbert",
"age":{"$numberInt":"21"},
"_class":"com.alemannigame.backend.domain.Character"
}
However I'd like to have a "timestamp": "1988-03-12T02:30:12+00:00" (example format) in it as well. Is there a way to do so without having to write logic in a Service to actually add a timestamp manually?
I thought about something like:
#Document(withTimestamp: true) // this
data class Character(
#Id
val id: String,
val name: String,
val age: Int
)
Could not find anything similar in the interwebs! Nifty solutions are welcome!
So I found something out about Lifecycle Events for Spring Data MongoDB (https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation).
This gave me several interceptors before a document is saved to MongoDB.
It's very straight forward to use. I chose the onBeforeConvert hook so I can manipulate my model before it is being saved.
As you can see I added the timestamp of the event to my source instance. I like Unix timestamps and since the event object already has that ready I reused it.
#Component
class MongoSaveInterceptor : AbstractMongoEventListener<Character>() {
override fun onBeforeConvert(event: BeforeConvertEvent<Character>) {
val source = event.source
source.timestamp = event.timestamp
}
}
When using MongoDB's implicit ObjectId (which I do not - I use an own UUID id) I think that event.timestamp is used here.

Common fields on graphql interface type in react apollo with a graphene backend

I have a python graphene-django backend with an interface and two types, let's say
interface InterfaceType {
id: ID!
name: String!
}
type Type1 implements InterfaceType {
aField: String
}
type Type2 implements InterfaceType {
anotherField: String
}
I'm able to query this from my react-apollo frontend using inline fragments:
query {
interfaceQuery {
...on Type1 {
id
name
}
...on Type1 {
id
name
}
}
}
But from what I understand it should also be possible to query the common fields simply as
query {
interfaceQuery
id
name
}
}
When I try this, however, I get the error Cannot query field "id" on type "InterfaceType". Did you mean to use an inline fragment on "Type1" or "Type2"?
I'm using an IntrospectionFragmentMatcher.
Am I misunderstanding and this kind of simple access of common fields is not possible, or is it just not implemented in either graphene or apollo?
If you're seeing that error, it's coming from the server, not Apollo. However, there's nothing specific to either Apollo or Graphene that should prevent you from querying the interface fields without a fragment.
The error is thrown because whatever schema you're using literally doesn't have that field on the provided type. Maybe you updated your schema without restarting the service, or were mistaken about what fields were actually part of the interface -- it's hard to know with only pseudocode provided.

Grails MongoDB Update object with Association

I can't seem to understand where I am going wrong currently when I attempt to update my User domain model that has a hasOne association to a Profile object.
My domain models are as follows:
class User {
static hasOne = [profile: Profile]
static fetchMode = [profile: 'eager']
ObjectId id
String username
}
class Profile {
static belongsTo = [user: User]
ObjectId id
String familyName
String givenName
}
I am able to persist a User with a profile originally but when attempting to update the User object I get validation errors.
Validation error occurred during call to save():
- Field error in object 'co.suitable.User' on field 'profile.familyName': rejected value [null]
- Field error in object 'co.suitable.User' on field 'profile.givenName': rejected value [null]
I am able to print out the user.profile ID and also the user.profile.familyName before saving the object. Like the following:
println(user.profile.familyName)
println(user.profile.id.toString())
user.save(flush: true, failOnError: true)
But I still get the validation errors before saving, i'd imagine that the println(user.profile.familyName) call is fetching the profile object if it already hasn't been loaded which I thought setting the fetchMode would have handled.
The object is able to successfully persist and save when I do:
user.profile = Profile.findById(user.profile.id)
println(user.profile.id.toString())
user.save(flush: true, failOnError: true)
I could wrap that in a service but I was hoping for a solution that would be handled by Grails if possible. Any advice or thoughts is much appreciated.
You should not apply the logic for the SQL DB to Mongo 1 to 1. Mongo and other document-oriented DBs are not originally intended to store the joins between collections. There are some workarounds, like db-refs, but they are to be used with caution.
For your case - with hasOne - I would suggest using mongo's subdocuments (mirrored as GORM's embedded objects) instead of referencing:
class User {
ObjectId id
String username
Profile profile
static embedded = [ 'profile' ]
}
class Profile {
String familyName
String givenName
}
thus you use the mongo in accordance to it's original puprose. Also querying is simpler and faster.

Is there a 'contains' functionality on a collection of ObjectId property of a domain object for withCriteria?

I have a Subscription domain object which has a List of ObjectId as one of its properties, say 'subscribed'. I'm on Grails 2.1.2 and MongoDB.
class Subscription {
ObjectId id
List<ObjectId> subscribers = [] as List<ObjectId>
static mapWith = "mongo"
}
I'm looking for something like this
def c = Subscription.withCriteria {
contains("subscribers", specificId)
}
Is there a way to do this?
Note: I'm aware of this very same question but provided answer to that question doesn't work for a property which is of type List of ObjectId.
See grails criteria docs. Try to use like this:
def c = Subscription.withCriteria {
subscribers{
eq("id",specificId)
}
}

How to map Grails domain object to a specific mongodb table name

I've setup a simple grails app looking at a mongodb.
My domain object looks like this:
class GoogleSearch {
String _id;
String id;
String query;
String site;
Object results;
Date date;
static mapping = {
table 'google_searches'
}
static constraints = {
}
}
However when I run the grails app up, it keeps reading/writing to a table named "googleSearch"
Does anyone know how I can override this default naming? Is it a gorm/mongodb thing?
Cheers
Basics of MongoDB. There is no concept of table. It is always collections. :)
Refer mapping as collection 'google_searches'.
For more details you can refer Grails MongoDB plugin.