Zorobay
Zorobay

Reputation: 649

Grails with MongoDB - how to define nested and reusable fields

I'm trying to rewrite my node.js + express backend in grails. I'm using a MongoDB database, where it is common to nest fields. In my current backend I have defined a schema (using mongoose), with a field such as:

normalized: {
    full_name: {type: String, trim: true, index: true}
},

I have also defined a separate, reusable schema called "locationSchema" that I can use for many of my fields, like so:

// Definition of reusable "sub-schema"
const locationSchema = new mongoose.Schema({
    country: {type: String, trim: true},
    region: {type: String, trim: true},
    city: {type: String, trim: true},
    notes: String,
    latitude: {type: Number},
    longitude: {type: Number}
}, {_id: false});

Which is then used, for example, to define the birth field:

birth: {
    date: {type: dateSchema, validate: dateValidator},
    location: locationSchema,
    notes: String
},

I did not manage to find much information on this, other than that it is possible to define nested fields as Maps, but how would I apply constraints to the sub-fields? And how would I use "sub-schemas" in Grails?


UPDATE WITH SOLUTION

I misunderstood the use of embedded. If you (like me) do not want to create tables/collections for embedded types, simply put the embedded class (like Location in my case) under src/main/... or define it in the same file as the domain class. This is stated in the documentation: http://docs.grails.org/3.1.1/ref/Domain%20Classes/embedded.html

My final, simplified solution is then:

grails-app/.../Person.groovy

class Person implements MongoEntity<Testy> {

    ObjectId id
    String s
    Location location

    static mapping = {
        collection "person"

    }

    static embedded = ['location']  // key part that was missing
}

src/main/.../Location.groovy

package hegardt.backend.grails.model.person

import grails.validation.Validateable


class Location implements Validateable {

    String country
    String region
    String city
    String notes
    Double latitude
    Double longitude

    static constraints = {
        country nullable: true
        region nullable: true
        city nullable: true
        notes nullable: true
        latitude nullable: true
        longitude nullable: true
    }
}

Now I can send requests with JSON data, and the mapping and saving works correctly, as well as validation on my embedded types!

Upvotes: 0

Views: 128

Answers (1)

injecteer
injecteer

Reputation: 20699

You should be using embedded object for your case.

class User {

  Birth birth

  static embedded = [ 'birth' ]

}

class Birth {
  Date date
  Location location
  String notes

  static constraints = {
    date validator:dateValidator
  }
}

Then if you do:

User u = new User()
u.birth = new Birth(...)
u.save()

the birth will be saved as a sub-document in User.

If you say:

new Birth(...).save()

then the GORM creates a new collection and fills the record in as usual.

Upvotes: 1

Related Questions