Grails Mongodb plugin does not save - mongodb

I have a requirement in my grails app to connect to an existing mongodb server. I am using the plugin ':mongodb:3.0.3' to do this. I have a domain class that is basically empty
class Thing {
static mapWith = "mongo"
String id
String name
static constraints = { }
static mapping = {
collection "myThing"
id column: '_id', generator: 'assigned'
version false
}
}
And I am trying to save a new domain object in an integration test -- a precursor to a controller action:
def "create the thing"() {
def thing = new Thing(name: "name", id:"foobar")
when:
def foo = thing.save(flush: true, failOnError:true)
then:
Thing.findByName("name")
}
The test fails because Thing.findByName returns null, however, when I dig into the low level API, I can save an object. The following test passes:
def "create the thing low level"() {
when:
Thing.collection.insert([name:"name"])
then:
Thing.findByName("name")
}
I have looked at the other stackoverflow questions and they seem to deal with:
Not using flush, which I am
Not having the configuration setup propertly, which I do because I have been fetching records from it, and the low level API works.
constraint errors which I don't have since failOnError is true
What am I doing wrong? How can I get GORM saves to work because I would like to use the grails tools available.
NOTE: This is not just a test issue, trying to save something in a controller using GORM also does not work. Calling this action:
def save() {
def thing = new Thing()
thing.name = "foobar"
respond thing.save(failOnError: true, flush: true)
}
Returns { "class": "com.Thing", "id": null, "name": "foobar" } and a look at the database tells me it is not saved.

You must add this line at the top of your integration test and it should start saving. By default GORM will not persists even in IntegrationTests
static Boolean transactional = false
You test would look something like below,
class TestIntegrationSpec extends IntegrationSpec {
static Boolean transactional = false
def "my test"() {
...
}
...
}

Related

gorm - Grails MongoDB not updating a list when doing addTo/removeFrom

I am currently using Grails 2.5.4, with the MongoDB plugin (org.grails.plugins:mongodb:6.0.0.RC2) and whenever I try to update a List of any domain class, it doesn't work.
When this code executes, the remove function succeeds and so does the save method. But the record on the database keeps the same.
def updateMessage(){
String id = '-1001066675850-7184293742'
Message message = Message.findById(id)
def entity = message.entities.get(0)
message.removeFromEntities(entity)
message.save(validate: true, failOnError: true, flush: true)
render message.entities
}
Message domain class is mapped like this:
class Message {
User fromUser
String text
Chat chat
Date sentDate
List<MessageEntity> entities
String sticker
Point location
String id
boolean pinned = false
static belongsTo = [entities: MessageEntity]
static constraints = {
chat nullable: false
fromUser nullable: false
sentDate nullable: false
}
static hasMany = [entities: MessageEntity]
static mapping = {
id generator: 'assigned'
location geoIndex: '2dsphere'
}
}
I've tried creating a new list from a scratch, adding it to the Message and it still won't update.
Is there anything i'm missing on the documentation?
Apparently updating lists was broken on either GORM for MongoDB 6.0.0.RC2 or for some particular settings on my (And a friend's) environment.
Solved it by downgrading to version 3.0.1.

Integration test with fixtures in grails

In my Integration test, I have #Shared fields that are loaded from fixtures(Fixtures plugin for grails). Those fields are aliases for the fixture beans. Look at the code I have now!
#TestFor(OrganizationUserService)
class OrganizationUserServiceSpec extends Specification {
def fixtureLoader
#Shared
def fixture
#Shared
def activeOrganizationOne
#Shared
def activeChild1
def setup() {
fixture = fixtureLoader.load {
build {
activeOrganizationOne(Organization) {
name = "Active Organization"
active = true
parent = null
}
activeChild1(Organization) {
name = "Active Child One"
active = true
parent = activeOrganizationOne
}
}
activeOrganizationOne = fixture.activeOrganizationOne
activeChild1 = fixture.activeChild1
}
void "deactivateOrganization deactivates an organization and all its children"() {
given:
service.organizationService = new OrganizationService()
service.userService = new UserService()
when:
service.deactivateOrganization(activeOrganizationOne)
//the above method deactivates activeOrganizationOne and activeChild1
//and activeChild1 will have a disableReason = PARENT_DEACTIVATION
then:
!activeOrganizationOne.active
!activeChild1.active
DisableReason.PARENT_DEACTIVATION.toString() == activeChild1.disableReason.toString()
}
}
Surprising it is for me that it works. activeChild1 is found by the service method via GORM request. How is it then the #Shared object in the test class modified?
To explain more, deactivateOrganization gets the childs of the organization using Organization.findAllByParent(org). In which org is the parameter passed to deactivateOrganization. if deactivateOrganization only modifies the objects that are returned from the GORM call, then how does #shared fields in the test catch the change? I didn't refresh anything.
I am on Grails 2.3.11 and running with mongoDB.
I am trying to create a standard on how to right Integration tests. So, I want to know the right way to work with fixtures and Integration tests.
Thanks In Advance;

Grails with MongoDB, Object id, and scaffold

I have data writing to a mongoDB database with issues using integration tests and the Grails scaffolding. When trying to select a domain instance from the 'list' type page, I get the error "[domain name] not found with id null".
I am sure it is because of the Grails url [controller]/[action]/[id]. This id is a string and needs to be converted to an ObjectId for Grails queries.
Is there a way to do this so that it affects a specified domain or even better yet, all of the domains at once?
I guess as I'm writing my app, I can convert it to an ObjectId from within the action method, but I'd like to have the scaffolding work or provide a global solution.
I believe this is happening because the show() method (that the Grails scaffolding functionality generates as an action) accepts an id parameter of type Long ie.
def show(Long id) {
def suiteInstance = Suite.get(id)
if (!suiteInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'suite.label', default: 'MyDomainClass'), id])
redirect(action: "list")
return
}
[suiteInstance: suiteInstance]
}
which binds the id parameter to the argument. Because the ObjectId can't be converted to a Long, it ends up being null, hence the call to MyDomainClass.get(id) fails with the error message.
You can get around this by overriding the show() action in your scaffolded controller so that it expects an ObjectId or String, but I would say the proper fix for this is to update the Grails scaffolding plugin so it is a little more liberal in the types of IDs it accepts.
I had this problem as well. You can keep the domain object id as an ObjectId and update the controller as follows:
domain Object:
import org.bson.types.ObjectId;
class DomainObject {
ObjectId id
// Add other member variables...
}
Controller:
def show(String id) {
def domainObjectInstance = domainObject.get(new ObjectId(id))
if (!domainObjectInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'domainObject.label', default: 'DomainObject'), id])
redirect(action: "list")
return
}
[domainObjectInstance: domainObjectInstance]
}
You would also need to update your other controller methods that use id as well such as edit, update etc.
Additionally, if you want the grails default controller generation to work like this for all your domain objects you can update the template as coderLMN suggests.
The get(params.id) call in show() method will NOT convert params.id String to an ObjectId object, so the domain instance will be null, then the following code takes you to list action with an error message:
if (!exampleInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'example.label', default: 'Example'), params.id])
redirect(action: "list")
return
}
Possible solutions:
you can run "grails install-template" command, so that the scaffolding templates in src/templates/scaffolding/ directory can be modified. Then you have new scaffold ready to generate customized controllers, views, tests for all your Domain classes.
A simpler solution is to define the id property as String instead of ObjectId. A String id will be equal to objectId.toString(), in this case your scaffold will work.
In domain classes keep you id type as ObjectId and keep scaffold = true for all respective controllers.
In Domain class :
ObjectId id
In respective controller :
static scaffold = true
Clear all existing collections from Mongo
I guess that's sufficient to have Grails-Mongo app up & running, considering you have correctly configured mongo-plugin

Search implementation for a RESTful interface on Grails

I'm building a RESTful interface on a Grails 2.1.1 application. How should I implement search operations? I don't want to repeat huge amounts of code, which my current thinking would require.
The server structure is quite normal Grails-MVC: domain classes represent data, controllers offer the interface and services have the business logic. I use command objects for data binding in controllers but not on service methods. The client is a web UI. My goal is to have search URLs like this:
/cars/?q=generic+query+from+all+fields
/cars/?color=red&year=2011
(I'm aware of the debate on the RESTfulness of this kind of URLs with query strings: RESTful URL design for search. While I think this is the best model for my purpose, I'm open to alternatives if they make the API and the implementation better.)
As you can see from the code examples below my problem is with the second kind of URL, the field-specific search. In order to implement this kind of search operation for several domain classes with lots of fields my method signatures would explode.
There probably is a "Groovy way" to do this but I'm still a bit of a n00b in finer Groovy tricks :)
Domain:
class Car {
String color
int year
}
Controller:
class CarsController {
def carService
def list(ListCommand cmd) {
def result
if (cmd.q) {
result = carService.search(cmd.q, cmd.max, cmd.offset, cmd.order, cmd.sort)
}
else {
result = carService.search(cmd.color, cmd.year, cmd.max, cmd.offset, cmd.order, cmd.sort)
}
render result as JSON
}
class ListCommand {
Integer max
Integer offset
String order
String sort
String q
String color // I don't want this field in command
int year // I don't want this field in command
static constraints = {
// all nullable
}
}
// show(), save(), update(), delete() and their commands clipped
}
Service:
class CarService {
List<Car> search(q, max=10, offset=0, order="asc", sort="id") {
// ...
}
List<Car> search(color, year, max=10, offset=0, order="asc", sort="id") {
// ...
}
}
UrlMappings:
class UrlMappings {
static mappings = {
name restEntityList: "/$controller"(parseRequest: true) {
action = [GET: "list", POST: "save"]
}
name restEntity: "/$controller/$id"(parseRequest: true) {
action = [GET: "show", PUT: "update", POST: "update", DELETE: "delete"]
}
}
}
You can get all this parameters from params, like:
result = carService.search(params.color, params.year as Integer, cmd.max, cmd.offset, cmd.order, cmd.sort)
All values of params map are strings, so you should convert it to appropriate data structures in controller (and it's better to check that params.year is actual number)
Update
If you don't want to writer field names, you can pass it as a Map:
resutl = carService.search(params)
where
List<Car> search(Map params)

Persisting dynamic groovy properties with GORM MongoDB

I am currently trying to persist the following class with the GORM MongoDB plugin for grails:
class Result {
String url
def Result(){
}
static constraints = {
}
static mapWith="mongo"
static mapping = {
collection "results"
database "crawl"
}
}
The code I'm running to persist this class is the following:
class ResultIntegrationTests {
#Before
void setUp() {
}
#After
void tearDown() {
}
#Test
void testSomething() {
Result r = new Result();
r.setUrl("http://heise.de")
r.getMetaClass().setProperty("title", "This is how it ends!")
println(r.getTitle())
r.save(flush:true)
}
}
This is the result in MongoDB:
{ "_id" : NumberLong(1), "url" : "http://heise.de", "version" : 0 }#
Now the url is properly persisted with MongoDB but the dynamic property somehow is not seen by the mapper - although the println(r.getTitle()) works perfectly fine.
I am new to groovy so I thought that someone with a little more experience could help me out with this problem. Is there a way to make this dynamically added property visible to the mapping facility? If yes how can I do that?
Thanks a lot for any advice...
Rather than adding random properties to the metaClass and hoping that Grails will both scan the metaClass looking for your random properties and then persist them, why not just add a Map to your domain class, (or a new Key/Value domain class which Result can hasMany) so you can add random extra properties to it as you want.
try this doc
#Test
void testSomething() {
Result r = new Result();
r.url = "http://heise.de"
r.['title'] = "This is how it ends!" //edit: forgot the subscript
println r.['title']
r.save(flush:true)
}
BTW, Instead of using gorm or hibernate you can always use directly java api / gmongo.