Grails MongoDB Update object with Association - mongodb

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.

Related

GORM - get raw DB value for domain class properties

I'm using GORM for MongoDB in my Grails 3 web-app to manage read/writes from DB.
I have the following 2 domain classes:
class Company {
String id
}
class Team {
String id
Company company
}
For teams, their company is saved on DB as String, and with GORM I can simply use team.company to get an instance of Company domain class.
However, I need to override the getter for company, and I need the raw value for company id (as stored on DB), without GORM getting in the way and performing its magic.
Is there a way to get the raw String value?
Any help is welcome! Thanks in advance
Update (May 27)
Investigating #TaiwaneseDavidCheng suggestion, I updated my code to
class Company {
String id
}
class Team {
String id
Company company
String companyId
static mapping = {
company attr: "company" // optional
companyId attr: "company", insertable: false, updateable: false
}
}
Please note that I'm using GORM for MongoDB, which (citing the manual) tries to be as compatible as possible with GORM for Hibernate, but requires a slightly different implementation.
However I found out (by trial&error) that GORM for MongoDB doesn't support a similar solution, as it seems only one property at a time can be mapped to a MongoDB document property.
In particular the last property in alphabetical order wins, e.g. companyId in my example.
I figured out a way to make the whole thing work, I'm posting my own answer below.
given a non-insertable non-updateable column "companyId" in domain class
class Company {
String id
}
class Team {
String id
Company company
Long companyId
static mapping = {
company column:"companyId"
companyId column:"companyId",insertable: false,updateable: false
}
}
(Follows the edit to my question above)
I defined a custom mapping, and made use of Grails transients by also defining custom getter and setter for team's company.
class Company {
String id
}
class Team {
String id
Company company
String companyId
static mapping = {
companyId attr: "company" // match against MongoDB property
}
static transients = [ 'company' ] // non-persistent property
Company getCompany() {
return Company.get(companyId)
}
void setCompany(Company company) {
companyId = company.id
}
}

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

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()
}
}

spring-data-mongo, how to return _id back for saved objects from mongo?

I am newbie to the Spring Data mongo. I have documents which has same FirstName say John, but MiddleName and LastName are different.
Also from UI, some students populating data (feeding data via forms) which has also FirstName say John and again MiddleName and LastName would be different.
Now, when I am saving User Object (which has FirstName, MiddleName, LastName, Age, Sex etc..etc..) into mongo using MongoTemplate. I need to return back "_id" (which mongo create by default if we don't provide it explicitly) of those each saved User object.
Could you please provide any example / guidance? Please help.
If you are saving with mongo template your object Id will be set after insertion (as Oliver Gierke has writen) of the object so you can do it like this.
//User object annotated with #Document
User user = new User(String name);
user.setWhatever(something);
mongoTemplate.save(user);
//now the user object should be populated with generated id;
return user.getId();
but you can use normal CrudRepository and use it with
<mongo:repositories base-package="your.package" />
Spring Data MongoDB will automatically populate the identifier property of your domain object with the generated identifier value.
#Document
class User {
ObjectId id; // by convention, use #Id if you want to use a different name
String firstname, lastname;
…
}
If an object of this class is persisted with the id property set to null, the object will have the property set after it has been persisted via MongoTempalte.
All of this is also described in the reference documentation.

Retrieve a value from a user entry in mongoDB

Completly new to mongoDB I'm trying to get the score field from a user in the "users" database that I have.
public static DBCursor getScore(String username) {
DBObject get = new QueryBuilder().put("name").is(username).get();
return getUsers().find(get);
}
Once I get the user object, how can I get the attribute that I want?
I suggest to use Jongo. It makes your life much more easier. Here is the website: http://jongo.org/

Lazyloading collection in play-salat

Is it possible to load a collection lazy with Sala?
e.g. I have an object like
Example 1 (in this case, the whole user list is loaded when retrieving the object)
case class Test(
#Key("_id") _id: ObjectId = new ObjectId,
name: String,
users: List[User]) {
}
or Example 2 (the object is loaded without the list, but no idea how to get the users list)
case class Test(
#Key("_id") _id: ObjectId = new ObjectId,
name: String) {
#Persist val users: List[User] = List()
}
How can I load the object in the first example without the users list?
or: How can I load the users list in the second example?
Thanks in advance!
Salat author here.
Salat doesn't have anything like ORM lazy loading. The #Persist annotation is meant to persist fields outside of the constructor, but suppresses deserialization because only fields in the constructor will be deserialized.
But you can easily decide when making the query whether you want the list of users or not.
case class Test(#Key("_id") id = new ObjectId, name: String, users: List[User] = Nil)
You can persist the users as embedded documents inside the test document, and then use the second argument of the query, the ref, to exclude (0) or include (1) fields in the object.
TestDAO.find(/* query */, MongoDBObject("users" -> 0))
The other strategy is to break out user documents into a child collection - see https://github.com/novus/salat/wiki/ChildCollection for more information. In this example, Test is the "parent" and User is the "child".
The strategy there is that in the parent DAO, when saving, you override the save methods to save users using the child DAO, and then save the parent object with users set to Nil.
Then, by default, a Test instance is retrieved with users set to Nil.
If you want to retrieve Test with users, you will need to add a find method to your DAO that manually:
find the test document
use the _id field of the test document to query for user documents by parent id - this will yield List[User]
deserialize the test document to an instance of Test using grater[Test] and copy it with the list of users