Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

* get method extended

* easy access query interface
  • Loading branch information...
commit 9c1cb5bad29c2f67f5871eb2b7db4aaca5a97cd5 1 parent aba39d6
Juri Kuehn authored
5 CHANGES.textile
View
@@ -2,6 +2,11 @@ h2. Change Log
Also see the "release notes of underlying morphia library":http://code.google.com/p/morphia/wiki/ReleaseNotes and the GORM-MongoDB "User Guide":http://jkuehn.github.com/gorm-mongodb/
+h3. v0.7.5 - 2011-10-23
+* MongoDB authentication configuration (thanks to Dave)
+* "Convenient access to morphias query methods":http://jkuehn.github.com/gorm-mongodb/ref/Domain Classes/query.html
+* Static get() method now also accepts com.mongodb.DBRef and com.google.code.morphia.Key instances
+
h3. v0.7.4 - 2011-10-13
* Grails 2.0 compatibility
* Added validation property to query parameters to optionally turn of validation (thanks to Alexander)
6 MongodbMorphiaGrailsPlugin.groovy
View
@@ -20,7 +20,7 @@ class MongodbMorphiaGrailsPlugin {
def scm = [ url: "https://github.com/jkuehn/gorm-mongodb" ]
// the plugin version
- def version = "0.7.4"
+ def version = "0.7.5"
// the version or versions of Grails the plugin is designed for
def grailsVersion = "1.3.4 > *"
// the other plugins this plugin depends on
@@ -39,7 +39,7 @@ class MongodbMorphiaGrailsPlugin {
def author = "Juri Kuehn"
def authorEmail = "juri.kuehn at gmail.com"
- def title = "Alternative MongoDB GORM based on the Morphia library (former gorm-mongodb)"
+ def title = "Alternative MongoDB GORM based on the Morphia library"
def description = '''GORM implementation for the MongoDB NoSQL database based on the Morphia library'''
// URL to the plugin's documentation
@@ -118,6 +118,8 @@ class MongodbMorphiaGrailsPlugin {
}
datastore.getByKey(clazz, delegate)
}
+
+ MongoPluginSupport.enhanceMorphiaQueryClass()
}
/**
2  README.textile
View
@@ -12,4 +12,4 @@ Special thanks to the contributors:
* Alex Duan
* Alexander Riss
* Hasan Ozgan
-
+* Dave Smith
2  TODO.textile
View
@@ -1,7 +1,7 @@
h1. TODOs / ideas for next releases
+* check code in MongoPluginSupport for changes borrowed from org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin
* implement "sparse index definitions":http://www.mongodb.org/display/DOCS/Indexes#Indexes-SparseIndexes for MongoDB 1.8
-* Check out Datastore getByKeys(keys) for Key.fetch() - if not working with fetched keys from db (has no kindClass), then raise issue
* write some documentation with examples for referencing and embedding entities
* document TransformationConfiguration annotation and put a page about inheritance in the docs
* document merge() instance method
10 application.properties
View
@@ -1,8 +1,8 @@
#Grails Metadata file
-#Thu Oct 13 18:24:25 CEST 2011
-app.grails.version=2.0.0.M2
+#Sat Oct 22 14:08:52 CEST 2011
+app.grails.version=2.0.0.RC1
app.name=mongodb-morphia
-app.version=0.7.4
-plugins.hibernate=2.0.0.M2
+app.version=0.7.5
+plugins.hibernate=2.0.0.RC1
plugins.release=1.0.0.RC3
-plugins.tomcat=2.0.0.M2
+plugins.tomcat=2.0.0.RC1
2  grails-app/conf/DataSource.groovy
View
@@ -4,7 +4,7 @@ mongodb {
if (currentHost == 'rem-juri') {
replicaSet = [ "localhost:27017"]
} else if (System.getProperty('user.name').toLowerCase() == 'juri') {
- replicaSet = [ "192.168.1.101:27017"]
+ replicaSet = [ "192.168.1.100:27017"]
} else {
replicaSet = [ "localhost:27017"]
}
4 src/docs/guide/1. Introduction.gdoc
View
@@ -9,9 +9,9 @@ Features implemented to far:
* Constraints and validation support
* Automatic timestamping of dateCreated and lastUpdated
* Gorm events beforeSave, afterSave, beforeDelete, afterDelete, beforeValidate (others, like afterLoad are supported by "Morphia":http://code.google.com/p/morphia/wiki/LifecycleMethods )
-* Works with MongoDB 1.6 and 1.8
+* Works with MongoDB 1.6, 1.8 and 2.0
* AST Transformations that inject all necessary fields and annotations for morphia
-* Lazy references supported
+* Lazy references supported (experimental)
* generate-all support
This plugin is not feature complete concerning the GORM api. Yet it has been proven to be very stable in our production environment. See the "quickstart":2.%20Quickstart.html page for a usage example. You can also download a "petclinic sample application port":https://github.com/jkuehn/gorm-mongodb which uses mongodb-morphia to see the use of embedded and referenced domain classes.
2  src/docs/ref/Domain Classes/get.gdoc
View
@@ -14,5 +14,5 @@ h2. Description
Parameters:
-* @id@ - The id of the object to retrieve
+* @id@ - The id of the object to retrieve. Alternatively, you can pass a "com.mongodb.DBRef":http://api.mongodb.org/java/current/com/mongodb/DBRef.html or a "com.google.code.morphia.Key":http://morphia.googlecode.com/svn/site/morphia/apidocs/com/google/code/morphia/Key.html object.
58 src/docs/ref/Domain Classes/query.gdoc
View
@@ -0,0 +1,58 @@
+h1. query
+
+h2. Purpose
+
+Finds all of the domain class instances using morphias "query":http://morphia.googlecode.com/svn/site/morphia/apidocs/com/google/code/morphia/query/QueryImpl.html
+
+h2. Examples
+
+{code:java}
+// get a query instance for Book class
+def query = Book.query()
+
+// get a query instance for Book class, precofigured with pagination params
+def query = Book.query(params)
+
+// pass a closure that will be executed in query context
+def query = Book.query([sort: "releaseDate", order: "desc"]) {
+ field('author').equal("Steven King")
+ field('title').startsWith("The")
+}
+
+// pass a closure only
+def query = Book.query() {
+ field('author').equal("Steven King")
+ field('title').startsWith("The")
+ offset(10)
+}.asList()
+
+// work with query object
+def query = Book.query()
+query.field('author').equal("Steven King")
+query.field('title').startsWith("The")
+query.offset(10)
+
+
+// Query interface provides many methods for returning the results
+query.asList() // return matched results as List
+query.get() // return first result
+for (Book book in query) { ... } // query implements the Iterable interface
+
+{code}
+
+h2. Description
+
+{code:java}
+Book.query( )
+Book.query( Map params )
+Book.query( Map params, Closure config )
+{code}
+
+Parameters:
+
+* @params@ - A Map containing parameters @max@, @offset@, @sort@, @order@ and @validation@ (to turn off morphia mapping validation)
+* @config@ - A closure that will be executed in context of Query
+
+Returns:
+
+Instance of a "QueryImpl":http://morphia.googlecode.com/svn/site/morphia/apidocs/com/google/code/morphia/query/QueryImpl.html
1,336 src/groovy/grails/plugins/mongodb/MongoPluginSupport.groovy
View
@@ -28,25 +28,31 @@ import com.mongodb.WriteConcern
import org.bson.types.ObjectId
import com.google.code.morphia.DatastoreImpl
import com.mongodb.DBObject
+import com.google.code.morphia.Key
+import com.mongodb.DBRef
+import com.google.code.morphia.AdvancedDatastore
+import org.apache.commons.lang.math.NumberUtils
/**
* Author: Juri Kuehn
* Date: 30.05.2010
*/
class MongoPluginSupport {
- private static final Log log = LogFactory.getLog(MongoDomainClass.class);
+ private static final Log log = LogFactory.getLog(MongoDomainClass.class);
- static final PROPERTY_INSTANCE_MAP = new SoftThreadLocalMap()
- public static final String MORPHIA_ATTRIBUTE = "morphiaDS"
+ static final PROPERTY_INSTANCE_MAP = new SoftThreadLocalMap()
+ public static final String MORPHIA_ATTRIBUTE = "morphiaDS"
- public static final String EVENT_BEFORE_SAVE = "beforeSave"
- public static final String EVENT_AFTER_SAVE = "afterSave"
- public static final String EVENT_BEFORE_DELETE = "beforeDelete"
- public static final String EVENT_AFTER_DELETE = "afterDelete"
- public static final String EVENT_BEFORE_VALIDATE = "beforeValidate"
+ public static final String EVENT_BEFORE_SAVE = "beforeSave"
+ public static final String EVENT_AFTER_SAVE = "afterSave"
+ public static final String EVENT_BEFORE_DELETE = "beforeDelete"
+ public static final String EVENT_AFTER_DELETE = "afterDelete"
+ public static final String EVENT_BEFORE_VALIDATE = "beforeValidate"
- // for dynamic finders
- static final COMPARATORS = Collections.unmodifiableList([
+ public static final int DEFAULT_MAX_RESULTS = 25
+
+ // for dynamic finders
+ static final COMPARATORS = Collections.unmodifiableList([
"IsNull",
"IsNotNull",
"LessThan",
@@ -60,720 +66,778 @@ class MongoPluginSupport {
"NotInList",
// "NotBetween", // OR operator not supported by mongodb yet
"Between" ])
- static final COMPARATORS_RE = COMPARATORS.join("|")
- static final DYNAMIC_FINDER_RE = /(\w+?)(${COMPARATORS_RE})?((And)(\w+?)(${COMPARATORS_RE})?)?/
+ static final COMPARATORS_RE = COMPARATORS.join("|")
+ static final DYNAMIC_FINDER_RE = /(\w+?)(${COMPARATORS_RE})?((And)(\w+?)(${COMPARATORS_RE})?)?/
// static final DYNAMIC_FINDER_RE = /(\w+?)(${COMPARATORS_RE})?((And|Or)(\w+?)(${COMPARATORS_RE})?)?/
- static enhanceDomainClass(MongoDomainClass domainClass, GrailsApplication application, ApplicationContext ctx) {
- addGrailsDomainPluginMethods(application, domainClass, ctx)
-
- addStaticMethods(application, domainClass, ctx)
- addInstanceMethods(application, domainClass, ctx)
- addDynamicFinderSupport(application, domainClass, ctx)
- addInitMethods(application, domainClass, ctx)
-
- ensureIndices(application, domainClass, ctx)
- }
-
- static void ensureIndices(application, domainClass, ctx) {
- def domain = domainClass.clazz
- final Datastore datastore = getMongoBean(application).datastore
-
- try {
- def f = domain.getDeclaredField("indexes")
- f.accessible = true
- def mappingClosure = f.get()
- def builder = new IndexInfoBuilder()
- mappingClosure.delegate = builder
- mappingClosure()
-
- builder.errors.each {
- log.error("Error in index definition for class ${domain.class.name}: $it")
- }
-
- for (i in builder.indexes) {
- // add index to db
- // @todo implement sparse indexes as soon as morphia provides the api - boolean:i.sparse
- datastore.ensureIndex(domain, i.name, i.fields as IndexFieldDef[], i.unique, i.dropDups)
- }
- } catch (NoSuchFieldException nsfe) {
- // no problem
- } catch (com.mongodb.MongoException mongoEx) {
- // usually communications problems, cannot ensure index
- throw mongoEx
- } catch (e) {
- throw new MappingException("Could not evaluate mapping for mongo domain " + domain.name + " - " + e.message)
+ static enhanceDomainClass(MongoDomainClass domainClass, GrailsApplication application, ApplicationContext ctx) {
+ addGrailsDomainPluginMethods(application, domainClass, ctx)
+
+ addStaticMethods(application, domainClass, ctx)
+ addInstanceMethods(application, domainClass, ctx)
+ addDynamicFinderSupport(application, domainClass, ctx)
+ addInitMethods(application, domainClass, ctx)
+
+ ensureIndices(application, domainClass, ctx)
}
- }
- private static addInstanceMethods(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
- def metaClass = dc.metaClass
- def domainClass = dc
- final MongoHolderBean mongoHolderBean = getMongoBean(application)
- final DatastoreImpl datastore = (DatastoreImpl)mongoHolderBean.datastore
+ static void ensureIndices(application, domainClass, ctx) {
+ def domain = domainClass.clazz
+ final Datastore datastore = getMongoBean(application).datastore
+
+ try {
+ def f = domain.getDeclaredField("indexes")
+ f.accessible = true
+ def mappingClosure = f.get()
+ def builder = new IndexInfoBuilder()
+ mappingClosure.delegate = builder
+ mappingClosure()
- metaClass.save = {->
- save(null)
+ builder.errors.each {
+ log.error("Error in index definition for class ${domain.class.name}: $it")
+ }
+
+ for (i in builder.indexes) {
+ // add index to db
+ // @todo implement sparse indexes as soon as morphia provides the api - boolean:i.sparse
+ datastore.ensureIndex(domain, i.name, i.fields as IndexFieldDef[], i.unique, i.dropDups)
+ }
+ } catch (NoSuchFieldException nsfe) {
+ // no problem
+ } catch (com.mongodb.MongoException mongoEx) {
+ // usually communications problems, cannot ensure index
+ throw mongoEx
+ } catch (e) {
+ throw new MappingException("Could not evaluate mapping for mongo domain " + domain.name + " - " + e.message)
+ }
}
- metaClass.save = {Map args = [:] ->
- // todo: add support for failOnError:true in grails 1.2 (GRAILS-4343)
+ private static addInstanceMethods(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
+ def metaClass = dc.metaClass
+ def domainClass = dc
+ final MongoHolderBean mongoHolderBean = getMongoBean(application)
+ final DatastoreImpl datastore = (DatastoreImpl)mongoHolderBean.datastore
- // only process if beforeSave didnt return false
- if (!triggerEvent(EVENT_BEFORE_SAVE, delegate) && validate()) {
- autoTimeStamp(application, delegate)
- if (datastore.save(delegate)) {
- triggerEvent(EVENT_AFTER_SAVE, delegate) // call only on successful save
- return delegate
+ metaClass.save = {->
+ save(null)
}
- }
- return null
- }
+ metaClass.save = {Map args = [:] ->
+ // todo: add support for failOnError:true in grails 1.2 (GRAILS-4343)
- /**
- * creates a key object that can be used for referencing
- */
- metaClass.createKey = {
- return datastore.getKey(delegate)
- }
+ // only process if beforeSave didnt return false
+ if (!triggerEvent(EVENT_BEFORE_SAVE, delegate) && validate()) {
+ autoTimeStamp(application, delegate)
+ if (datastore.save(delegate)) {
+ triggerEvent(EVENT_AFTER_SAVE, delegate) // call only on successful save
+ return delegate
+ }
+ }
- /**
- * @deprecated use createKey instead
- */
- metaClass.makeKey = {
- log.error "makeKey is deprecated, please use createKey instead"
- return datastore.getKey(delegate)
- }
+ return null
+ }
- /**
- * creates a DBRef object that can be used for referencing
- */
- metaClass.createDBRef = {
- return datastore.createRef(delegate)
- }
+ /**
+ * creates a key object that can be used for referencing
+ */
+ metaClass.createKey = {
+ return datastore.getKey(delegate)
+ }
- /**
- * merge instance values into database instance
- */
- metaClass.merge = {
- return datastore.merge(delegate)
- }
+ /**
+ * @deprecated use createKey instead
+ */
+ metaClass.makeKey = {
+ log.error "makeKey is deprecated, please use createKey instead"
+ return datastore.getKey(delegate)
+ }
- /**
- * creates a DBObject based on the values of this instance
- */
- metaClass.toDBObject = {
- return mongoHolderBean.morphia.toDBObject(delegate)
- }
+ /**
+ * creates a DBRef object that can be used for referencing
+ */
+ metaClass.createDBRef = {
+ return datastore.createRef(delegate)
+ }
- /**
- * call mongodb update function on this entity
- * http://code.google.com/p/morphia/wiki/Updating
- */
- metaClass.update = { Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
- if (!delegate.ident()) {
- throw new IllegalStateException("Cannot update instances without an id")
+ /**
+ * merge instance values into database instance
+ */
+ metaClass.merge = {
+ return datastore.merge(delegate)
}
- def query = datastore.createQuery(delegate.class)
- def updateOp = datastore.createUpdateOperations(delegate.class)
- query.filter(Mapper.ID_KEY, delegate.ident());
+ /**
+ * creates a DBObject based on the values of this instance
+ */
+ metaClass.toDBObject = {
+ return mongoHolderBean.morphia.toDBObject(delegate)
+ }
- data.delegate = updateOp
- data()
+ /**
+ * call mongodb update function on this entity
+ * http://code.google.com/p/morphia/wiki/Updating
+ */
+ metaClass.update = { Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
+ if (!delegate.ident()) {
+ throw new IllegalStateException("Cannot update instances without an id")
+ }
+ def query = datastore.createQuery(delegate.class)
+ def updateOp = datastore.createUpdateOperations(delegate.class)
- datastore.update(query, updateOp, createIfMissing, wc)
- }
+ query.filter(Mapper.ID_KEY, delegate.ident());
- metaClass.delete = { ->
- triggerEvent(EVENT_BEFORE_DELETE, delegate)
- datastore.delete(delegate)
- triggerEvent(EVENT_AFTER_DELETE, delegate)
- }
+ data.delegate = updateOp
+ data()
- metaClass.delete = { Map dontcare -> // in case flush:true is passed in
- delete()
- }
+ datastore.update(query, updateOp, createIfMissing, wc)
+ }
- // allow autowiring. @todo: make this method deprecated and move to factory http://code.google.com/p/morphia/issues/detail?id=65
- metaClass.autowire = {
- ctx.beanFactory.autowireBeanProperties(delegate, ctx.beanFactory.AUTOWIRE_BY_NAME, false)
- }
- }
-
- private static addStaticMethods(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
- def final metaClass = dc.metaClass
- def final domainClass = dc
- final MongoHolderBean mongoHolderBean = getMongoBean(application)
- final Datastore datastore = mongoHolderBean.datastore
-
- metaClass.static.get = { Serializable docId ->
- try {
- // fetch from db
- return datastore.get(domainClass.clazz, _checkedId(domainClass, docId))
- } catch (Exception e) {
- // fall through to return null
- log.error("Could not get instance from DB", e)
- }
- return null
- }
+ metaClass.delete = { ->
+ triggerEvent(EVENT_BEFORE_DELETE, delegate)
+ datastore.delete(delegate)
+ triggerEvent(EVENT_AFTER_DELETE, delegate)
+ }
- metaClass.static.getAll = { Collection docIds ->
- findAll('id in': docIds)
- }
+ metaClass.delete = { Map dontcare -> // in case flush:true is passed in
+ delete()
+ }
- // Foo.exists(1)
- metaClass.static.exists = {Serializable docId ->
- get(docId) != null
+ // allow autowiring. @todo: make this method deprecated and move to factory http://code.google.com/p/morphia/issues/detail?id=65
+ metaClass.autowire = {
+ ctx.beanFactory.autowireBeanProperties(delegate, ctx.beanFactory.AUTOWIRE_BY_NAME, false)
+ }
}
- metaClass.static.getCollection = {
- datastore.getCollection(domainClass.clazz)
- }
+ private static addStaticMethods(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
+ def final metaClass = dc.metaClass
+ def final domainClass = dc
+ final MongoHolderBean mongoHolderBean = getMongoBean(application)
+ final Datastore datastore = mongoHolderBean.datastore
- metaClass.static.deleteOne = { Serializable docId ->
- datastore.delete(domainClass.clazz, _checkedId(domainClass, docId))
- }
+ metaClass.static.get = { Object docId ->
+ // fetch from db
+ try {
+ // handle dbrefs and keys too
+ if (docId instanceof Key) {
+ return datastore.getByKey(domainClass.clazz, docId)
+ }
+ if (docId instanceof DBRef && datastore instanceof AdvancedDatastore) {
+ return datastore.get(domainClass.clazz, docId)
+ }
- // delete all documents with given ids
- metaClass.static.deleteAll = { List docIds ->
- datastore.delete(domainClass.clazz, docIds?.collect { _checkedId(domainClass, it) })
- }
+ // else: its a document id
+ return datastore.get(domainClass.clazz, _checkedId(domainClass, docId))
+ } catch (Exception e) {
+ // fall through to return null
+ log.error("Could not get instance from DB", e)
+ }
+ return null
+ }
- metaClass.static.deleteAll = { Map filter = [:] ->
- Query query = datastore.find(domainClass.clazz)
+ metaClass.static.getAll = { Collection docIds ->
+ findAll('id in': docIds)
+ }
- filter.each { k, v ->
- query.filter(k.toString(), v)
- }
+ // Foo.exists(1)
+ metaClass.static.exists = { Object docId ->
+ get(docId) != null
+ }
- datastore.delete(query)
- }
+ metaClass.static.getCollection = {
+ datastore.getCollection(domainClass.clazz)
+ }
- /**
- * @deprecated use count instead
- */
- metaClass.static.countAll = { Map filter = [:] ->
- log.error "countAll is deprecated, please use count instead"
- count(filter)
- }
+ metaClass.static.deleteOne = { Serializable docId ->
+ datastore.delete(domainClass.clazz, _checkedId(domainClass, docId))
+ }
- metaClass.static.count = { Map filter = [:] ->
- Query query = datastore.find(domainClass.clazz)
+ // delete all documents with given ids
+ metaClass.static.deleteAll = { List docIds ->
+ datastore.delete(domainClass.clazz, docIds?.collect { _checkedId(domainClass, it) })
+ }
- filter.each { k, v ->
- query.filter(k.toString(), v)
- }
+ metaClass.static.deleteAll = { Map filter = [:] ->
+ Query query = datastore.find(domainClass.clazz)
- datastore.getCount(query)
- }
+ filter.each { k, v ->
+ query.filter(k.toString(), v)
+ }
- /**
- * return only the first object, if any
- */
- metaClass.static.find = { Map filter = [:], Map queryParams = [:] ->
- queryParams['max'] = 1
+ datastore.delete(query)
+ }
- def res = findAll(filter, queryParams).toList()
- return res?res[0]:null
- }
+ /**
+ * @deprecated use count instead, will be removed soon
+ */
+ metaClass.static.countAll = { Map filter = [:] ->
+ throw new DeprecationException("countAll is deprecated, please use count instead")
+ }
- metaClass.static.findAll = { Map filter = [:], Map queryParams = [:] ->
- if (!queryParams.containsKey('max')) queryParams.max = 0 // list all by default
+ metaClass.static.count = { Map filter = [:] ->
+ Query query = datastore.find(domainClass.clazz)
- Query query = datastore.find(domainClass.clazz)
- configureQuery query, queryParams
+ filter.each { k, v ->
+ query.filter(k.toString(), v)
+ }
- filter.each { k, v ->
- query.filter(k.toString(), v)
- }
+ datastore.getCount(query)
+ }
- return query.fetch()
- }
+ /**
+ * return only the first object, if any
+ */
+ metaClass.static.find = { Map filter = [:], Map queryParams = [:] ->
+ queryParams['max'] = 1
- metaClass.static.list = { Map queryParams = [:] ->
- findAll([:], queryParams)
- }
+ def res = findAll(filter, queryParams).toList()
+ return res?res[0]:null
+ }
- /**
- * update closure used by update and updateFirst methods
- */
- def updateFunction = { boolean multi, filter, Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
- if (!(filter instanceof Map)) filter = [(Mapper.ID_KEY): _checkedId(domainClass, filter)]
+ metaClass.static.findAll = { Map filter = [:], Map queryParams = [:] ->
+ if (!queryParams.containsKey('max')) queryParams.max = 0 // list all by default
+
+ Query query = datastore.find(domainClass.clazz)
+ configureQuery query, queryParams
- def query = datastore.createQuery(domainClass.clazz)
- def updateOp = datastore.createUpdateOperations(domainClass.clazz)
+ filter.each { k, v ->
+ query.filter(k.toString(), v)
+ }
- filter.each { k, v ->
- query.filter(k.toString(), v)
+ return query.fetch()
}
- data.delegate = updateOp
- data()
+ metaClass.static.list = { Map queryParams = [:] ->
+ findAll([:], queryParams)
+ }
- if (multi) {
- datastore.update(query, updateOp, createIfMissing, wc)
- } else {
- datastore.updateFirst(query, updateOp, createIfMissing, wc)
+ /**
+ * update closure used by update and updateFirst methods
+ */
+ def updateFunction = { boolean multi, filter, Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
+ if (!(filter instanceof Map)) filter = [(Mapper.ID_KEY): _checkedId(domainClass, filter)]
+
+ def query = datastore.createQuery(domainClass.clazz)
+ def updateOp = datastore.createUpdateOperations(domainClass.clazz)
+
+ filter.each { k, v ->
+ query.filter(k.toString(), v)
+ }
+
+ data.delegate = updateOp
+ data()
+
+ if (multi) {
+ datastore.update(query, updateOp, createIfMissing, wc)
+ } else {
+ datastore.updateFirst(query, updateOp, createIfMissing, wc)
+ }
}
- }
- metaClass.static.update = { filter, Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
- updateFunction(true, filter, data, createIfMissing, wc)
- }
- metaClass.static.updateFirst = { filter, Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
- updateFunction(false, filter, data, createIfMissing, wc)
- }
+ metaClass.static.update = { filter, Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
+ updateFunction(true, filter, data, createIfMissing, wc)
+ }
+ metaClass.static.updateFirst = { filter, Closure data, boolean createIfMissing = false, WriteConcern wc = null ->
+ updateFunction(false, filter, data, createIfMissing, wc)
+ }
- /**
- * creates a new instance of this class and populates fields from DBObject
- */
- metaClass.static.fromDBObject = { DBObject dbObject ->
- return mongoHolderBean.morphia.fromDBObject(domainClass.clazz, dbObject)
- }
+ /**
+ * creates a new instance of this class and populates fields from DBObject
+ */
+ metaClass.static.fromDBObject = { DBObject dbObject ->
+ return mongoHolderBean.morphia.fromDBObject(domainClass.clazz, dbObject)
+ }
- }
-
- public static void configureQuery(Query query, Map queryParams) {
- // @todo be more graceful
- def sort = queryParams.get('sort')?.toString()
- def limit = (int)(queryParams.containsKey('max') ? queryParams.get('max') : 25).toInteger()
- def offset = (int)(queryParams.get('offset') ?: 0).toInteger()
- // handle the morphia query validation - default validation is set to true
- def validation = queryParams.containsKey('validation') ? (boolean) queryParams.get('validation') : true
-
- if(!validation){
- // disables query validation - this enables querying of embedded object properties like 'parent.embedded.property'
- query.disableValidation();
- }
- if (sort){
- // in case we have a sorting defined we also need to handle the sort order:
- // default order -> asc
- // asc will become an empty prefix in front or the sorting field
- // desc will become a '-' prefix
- def order = (queryParams.containsKey('order') && queryParams.get('order') == 'desc' )? '-' : ''
- query.order(order+sort)
- }
- query.limit(limit)
- query.offset(offset)
- }
-
- private static addDynamicFinderSupport(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
- def metaClass = dc.metaClass
- def domainClass = dc
-
- // This adds basic dynamic finder support.
- metaClass.static.methodMissing = { method, args ->
- def m = method =~ /^find(All|One)?By${DYNAMIC_FINDER_RE}$/
- if (m) {
- def fields = []
- def comparator = m[0][3]
- boolean returnOne = !(m[0][1] == "All")
- // How many arguments do we need to pass for the given
- // comparator?
- def numArgs = getArgCountForComparator(comparator)
-
- fields << [field:Introspector.decapitalize(m[0][2]),
- args:args[0..<numArgs], // @todo move args out of here, it'll stay for newMethod in memory unused
- argCount:numArgs,
- comparator:comparator]
-
- // Strip out that number of arguments from the ones
- // we've been passed.
- args = args[numArgs..<args.size()]
-
- // If we have a second clause, evaluate it now.
- def join = m[0][5]
-
- if (join) {
- comparator = m[0][7]
- numArgs = getArgCountForComparator(comparator)
- fields << [field: Introspector.decapitalize(m[0][6]),
- args:args[0..<numArgs],
- argCount:numArgs,
- comparator:comparator]
-
- // remove args for second parameter
- args = args[numArgs..<args.size()]
- }
-
- final int expectedMinArgsCount = (int)fields.inject(0) { acc, val -> acc+val.argCount }
- // cache new behavior
- def newMethod = { Object[] varArgs ->
- def localArgs = varArgs ? varArgs[0] : []
- if (localArgs.size() < expectedMinArgsCount) throw new MissingMethodException(method, delegate, varArgs)
-
- def filter = [:]
- def field1ArgCount = fields[0].argCount
- updateFilter(filter, fields[0].field, fields[0].comparator, localArgs[0..<field1ArgCount])
- if (fields.size()>1) {
- updateFilter(filter, fields[1].field, fields[1].comparator, localArgs[field1ArgCount..<(field1ArgCount+fields[1].argCount)])
- }
-
- // put options to the map
- Map queryParams
- if (localArgs.size() > expectedMinArgsCount && localArgs[expectedMinArgsCount] instanceof Map)
- queryParams = localArgs[expectedMinArgsCount]
- else
- queryParams = [:]
-
- // return only the first result
- if (returnOne) return find(filter, queryParams)
-
- // return the iterator for this collection
- return findAll(filter, queryParams)
- }
-
- // register new cached behavior on metaclass to speed up next invokation
- domainClass.metaClass.static."$method" = newMethod
-
- // Check whether we have any options, such as "sort".
- def queryParams = [:]
- if (args) {
- if(args[0] instanceof Map) {
- queryParams = args[0]
- }
- }
-
-
- def finalArgs = fields.collect { it.args }.flatten()
- finalArgs << queryParams
-
- // invoke new behavior
- return newMethod(finalArgs)
-
-
- } else {
- throw new MissingMethodException(method, delegate, args)
- }
- }
- }
+ /**
+ * do a morphia query
+ */
+ metaClass.static.query = { Map params, Closure config = null ->
+ Query query = datastore.createQuery(domainClass.clazz)
- private static addInitMethods(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
- def metaClass = dc.metaClass
- def domainClass = dc
+ if (params) query.setParams(params)
- metaClass.constructor = { Map map = [:] ->
- def instance = ctx.containsBean(domainClass.fullName) ? ctx.getBean(domainClass.fullName) : BeanUtils.instantiateClass(domainClass.clazz)
- DataBindingUtils.bindObjectToDomainInstance(domainClass, instance, map)
- DataBindingUtils.assignBidirectionalAssociations(instance, map, domainClass)
- return instance
- }
- metaClass.setProperties = {Object o ->
- DataBindingUtils.bindObjectToDomainInstance(domainClass, delegate, o)
- }
- metaClass.getProperties = {->
- new DataBindingLazyMetaPropertyMap(delegate)
- }
- }
-
- private static Object autoTimeStamp(GrailsApplication application, Object domain) {
-
- MongoDomainClass dc = (MongoDomainClass) application.getArtefact(MongoDomainClassArtefactHandler.TYPE, domain.getClass().getName())
- if (dc) {
- def metaClass = dc.metaClass
-
- MetaProperty property = metaClass.hasProperty(dc, GrailsDomainClassProperty.DATE_CREATED)
- def time = System.currentTimeMillis()
- if (property && domain[property.name] == null) {
- def now = property.getType().newInstance([time] as Object[])
- domain[property.name] = now
- }
-
- property = metaClass.hasProperty(dc, GrailsDomainClassProperty.LAST_UPDATED)
- if (property) {
- def now = property.getType().newInstance([time] as Object[])
- domain[property.name] = now
- }
- }
+ if (config) {
+ // execute closure in query context
+ config.delegate = query
+ config.resolveStrategy = Closure.DELEGATE_FIRST
+ config.call()
+ }
- return domain
- }
-
- /**
- * autoconvert to ObjectId if neccessary
- * @param id
- * @return
- */
- protected static Serializable _checkedId(domainClass, id) {
- if (domainClass.identifier.type == ObjectId.class && !(id instanceof ObjectId))
- return new ObjectId(id.toString())
-
- return id
- }
-
- private static MongoHolderBean getMongoBean(GrailsApplication application) {
- (MongoHolderBean)application.mainContext.getBean("mongo")
- }
-
- /**
- * call event on domain class
- * if event closure returns a boolean false this method
- * returns true, false otherwise
- *
- * @param event
- * @param entity
- * @return
- */
- private static boolean triggerEvent(String event, entity) {
- def result = false
- if (entity?.metaClass) {
- if(entity.metaClass.hasProperty(entity, event)) {
- def callable = entity."$event"
- if(callable instanceof Closure) {
- callable.resolveStrategy = Closure.DELEGATE_FIRST
- callable.delegate = entity
- result = callable.call()
- if (result instanceof Boolean) result = !result
- else {
- result = false
- }
- }
- }
- }
- return result
- }
-
- public static updateFilter(Map filter, String field, String comparator, values) {
- // default to equals
- switch(comparator) {
- case "IsNull": filter["$field exists"] = 0; break
- case "IsNotNull": filter["$field exists"] = 1; break
- case "Size": filter["$field size"] = values[0]; break
- case "All": filter["$field all"] = values[0]; break
- case "Between":
- filter["$field >="] = values[0];
- filter["$field <="] = values[1]; break
- case "NotBetween":
- filter["$field <"] = values[0];
- filter["$field >"] = values[1]; break
- case "InList": filter["$field in"] = values[0]; break
- case "NotInList": filter["$field nin"] = values[0]; break
- case "LessThan": filter["$field <"] = values[0]; break
- case "LessThanEquals": filter["$field <="] = values[0]; break
- case "GreaterThan": filter["$field >"] = values[0]; break
- case "GreaterThanEquals": filter["$field >="] = values[0]; break
- case "NotEqual": filter["$field !="] = values[0]; break
- default: filter["$field ="] = values[0]; break // equal
- }
- }
+ return query
+ }
+
+ metaClass.static.query = { Closure config = null ->
+ query(null, config)
+ }
- private static int getArgCountForComparator(String comparator) {
- if (comparator == "Between" || comparator == "NotBetween") {
- return 2
- }
- else if (["IsNull", "IsNotNull"].contains(comparator)) {
- return 0
- }
- else {
- return 1
- }
- }
-
- /**
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * COPY & PASTE FROM org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin START
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * added triggerEvent to validate method: triggerEvent(EVENT_BEFORE_VALIDATE, delegate)
- */
- static void addGrailsDomainPluginMethods(application, domainClass, ctx) {
- MetaClass metaClass = domainClass.metaClass
-
- metaClass.ident = {-> delegate[domainClass.identifier.name] }
- metaClass.constructor = { ->
- if (ctx.containsBean(domainClass.fullName)) {
- ctx.getBean(domainClass.fullName)
- }
- else {
- BeanUtils.instantiateClass(domainClass.clazz)
- }
- }
- metaClass.static.create = { ->
- if (ctx.containsBean(domainClass.fullName)) {
- ctx.getBean(domainClass.fullName)
- }
- else {
- BeanUtils.instantiateClass(domainClass.clazz)
- }
}
- addValidationMethods(application, domainClass, ctx)
- addRelationshipManagementMethods(domainClass)
- }
+ public static void configureQuery(Query query, Map queryParams) {
+ // @todo be more graceful
+ def sort = queryParams.get('sort')?.toString()
+ def limit = (int)(queryParams.containsKey('max') ? queryParams.get('max') : DEFAULT_MAX_RESULTS).toInteger()
+ def offset = (int)(queryParams.get('offset') ?: 0).toInteger()
+ // handle the morphia query validation - default validation is set to true
+ def validation = queryParams.containsKey('validation') ? (boolean) queryParams.get('validation') : true
- private static addValidationMethods(GrailsApplication application, GrailsDomainClass dc, ApplicationContext ctx) {
- def metaClass = dc.metaClass
- def domainClass = dc
+ if(!validation){
+ // disables query validation - this enables querying of embedded object properties like 'parent.embedded.property'
+ query.disableValidation();
+ }
+ if (sort){
+ // in case we have a sorting defined we also need to handle the sort order:
+ // default order -> asc
+ // asc will become an empty prefix in front or the sorting field
+ // desc will become a '-' prefix
+ def order = (queryParams.containsKey('order') && queryParams.get('order') == 'desc' )? '-' : ''
+ query.order(order+sort)
+ }
+ query.limit(limit)
+ query.offset(offset)
+ }
+
+ private static addDynamicFinderSupport(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
+ def metaClass = dc.metaClass
+ def domainClass = dc
+
+ // This adds basic dynamic finder support.
+ metaClass.static.methodMissing = { method, args ->
+ def m = method =~ /^find(All|One)?By${DYNAMIC_FINDER_RE}$/
+ if (m) {
+ def fields = []
+ def comparator = m[0][3]
+ boolean returnOne = !(m[0][1] == "All")
+ // How many arguments do we need to pass for the given
+ // comparator?
+ def numArgs = getArgCountForComparator(comparator)
+
+ fields << [field:Introspector.decapitalize(m[0][2]),
+ args:args[0..<numArgs], // @todo move args out of here, it'll stay for newMethod in memory unused
+ argCount:numArgs,
+ comparator:comparator]
+
+ // Strip out that number of arguments from the ones
+ // we've been passed.
+ args = args[numArgs..<args.size()]
+
+ // If we have a second clause, evaluate it now.
+ def join = m[0][5]
+
+ if (join) {
+ comparator = m[0][7]
+ numArgs = getArgCountForComparator(comparator)
+ fields << [field: Introspector.decapitalize(m[0][6]),
+ args:args[0..<numArgs],
+ argCount:numArgs,
+ comparator:comparator]
+
+ // remove args for second parameter
+ args = args[numArgs..<args.size()]
+ }
+
+ final int expectedMinArgsCount = (int)fields.inject(0) { acc, val -> acc+val.argCount }
+ // cache new behavior
+ def newMethod = { Object[] varArgs ->
+ def localArgs = varArgs ? varArgs[0] : []
+ if (localArgs.size() < expectedMinArgsCount) throw new MissingMethodException(method, delegate, varArgs)
+
+ def filter = [:]
+ def field1ArgCount = fields[0].argCount
+ updateFilter(filter, fields[0].field, fields[0].comparator, localArgs[0..<field1ArgCount])
+ if (fields.size()>1) {
+ updateFilter(filter, fields[1].field, fields[1].comparator, localArgs[field1ArgCount..<(field1ArgCount+fields[1].argCount)])
+ }
+
+ // put options to the map
+ Map queryParams
+ if (localArgs.size() > expectedMinArgsCount && localArgs[expectedMinArgsCount] instanceof Map)
+ queryParams = localArgs[expectedMinArgsCount]
+ else
+ queryParams = [:]
+
+ // return only the first result
+ if (returnOne) return find(filter, queryParams)
+
+ // return the iterator for this collection
+ return findAll(filter, queryParams)
+ }
+
+ // register new cached behavior on metaclass to speed up next invokation
+ domainClass.metaClass.static."$method" = newMethod
- registerConstraintsProperty(metaClass, domainClass)
+ // Check whether we have any options, such as "sort".
+ def queryParams = [:]
+ if (args) {
+ if(args[0] instanceof Map) {
+ queryParams = args[0]
+ }
+ }
+
+
+ def finalArgs = fields.collect { it.args }.flatten()
+ finalArgs << queryParams
- metaClass.hasErrors = {-> delegate.errors?.hasErrors() }
+ // invoke new behavior
+ return newMethod(finalArgs)
- def get
- def put
- try {
- def rch = application.classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder")
- get = {
- def attributes = rch.getRequestAttributes()
- if (attributes) {
- return attributes.request.getAttribute(it)
+
+ } else {
+ throw new MissingMethodException(method, delegate, args)
+ }
}
- return PROPERTY_INSTANCE_MAP.get().get(it)
- }
- put = { key, val ->
- def attributes = rch.getRequestAttributes()
- if (attributes) {
- attributes.request.setAttribute(key,val)
+ }
+
+ private static addInitMethods(GrailsApplication application, MongoDomainClass dc, ApplicationContext ctx) {
+ def metaClass = dc.metaClass
+ def domainClass = dc
+
+ metaClass.constructor = { Map map = [:] ->
+ def instance = ctx.containsBean(domainClass.fullName) ? ctx.getBean(domainClass.fullName) : BeanUtils.instantiateClass(domainClass.clazz)
+ DataBindingUtils.bindObjectToDomainInstance(domainClass, instance, map)
+ DataBindingUtils.assignBidirectionalAssociations(instance, map, domainClass)
+ return instance
}
- else {
- PROPERTY_INSTANCE_MAP.get().put(key,val)
+ metaClass.setProperties = {Object o ->
+ DataBindingUtils.bindObjectToDomainInstance(domainClass, delegate, o)
+ }
+ metaClass.getProperties = {->
+ new DataBindingLazyMetaPropertyMap(delegate)
}
- }
}
- catch (Throwable e) {
- get = { PROPERTY_INSTANCE_MAP.get().get(it) }
- put = { key, val -> PROPERTY_INSTANCE_MAP.get().put(key,val) }
+
+ private static Object autoTimeStamp(GrailsApplication application, Object domain) {
+
+ MongoDomainClass dc = (MongoDomainClass) application.getArtefact(MongoDomainClassArtefactHandler.TYPE, domain.getClass().getName())
+ if (dc) {
+ def metaClass = dc.metaClass
+
+ MetaProperty property = metaClass.hasProperty(dc, GrailsDomainClassProperty.DATE_CREATED)
+ def time = System.currentTimeMillis()
+ if (property && domain[property.name] == null) {
+ def now = property.getType().newInstance([time] as Object[])
+ domain[property.name] = now
+ }
+
+ property = metaClass.hasProperty(dc, GrailsDomainClassProperty.LAST_UPDATED)
+ if (property) {
+ def now = property.getType().newInstance([time] as Object[])
+ domain[property.name] = now
+ }
+ }
+
+ return domain
}
- metaClass.getErrors = { ->
- def errors
- def key = "org.codehaus.groovy.grails.ERRORS_${delegate.class.name}_${System.identityHashCode(delegate)}"
- errors = get(key)
- if (!errors) {
- errors = new BeanPropertyBindingResult( delegate, delegate.getClass().getName())
- put key, errors
- }
- errors
+ /**
+ * autoconvert to ObjectId if neccessary
+ * @param id
+ * @return
+ */
+ protected static Serializable _checkedId(domainClass, id) {
+ if (domainClass.identifier.type == ObjectId.class && !(id instanceof ObjectId))
+ return new ObjectId(id.toString())
+
+ return id
}
- metaClass.setErrors = { Errors errors ->
- def key = "org.codehaus.groovy.grails.ERRORS_${delegate.class.name}_${System.identityHashCode(delegate)}"
- put key, errors
+
+ private static MongoHolderBean getMongoBean(GrailsApplication application) {
+ (MongoHolderBean)application.mainContext.getBean("mongo")
}
- metaClass.clearErrors = { ->
- delegate.setErrors (new BeanPropertyBindingResult(delegate, delegate.getClass().getName()))
+
+ /**
+ * call event on domain class
+ * if event closure returns a boolean false this method
+ * returns true, false otherwise
+ *
+ * @param event
+ * @param entity
+ * @return
+ */
+ private static boolean triggerEvent(String event, entity) {
+ def result = false
+ if (entity?.metaClass) {
+ if(entity.metaClass.hasProperty(entity, event)) {
+ def callable = entity."$event"
+ if(callable instanceof Closure) {
+ callable.resolveStrategy = Closure.DELEGATE_FIRST
+ callable.delegate = entity
+ result = callable.call()
+ if (result instanceof Boolean) result = !result
+ else {
+ result = false
+ }
+ }
+ }
+ }
+ return result
+ }
+
+ public static updateFilter(Map filter, String field, String comparator, values) {
+ // default to equals
+ switch(comparator) {
+ case "IsNull": filter["$field exists"] = 0; break
+ case "IsNotNull": filter["$field exists"] = 1; break
+ case "Size": filter["$field size"] = values[0]; break
+ case "All": filter["$field all"] = values[0]; break
+ case "Between":
+ filter["$field >="] = values[0];
+ filter["$field <="] = values[1]; break
+ case "NotBetween":
+ filter["$field <"] = values[0];
+ filter["$field >"] = values[1]; break
+ case "InList": filter["$field in"] = values[0]; break
+ case "NotInList": filter["$field nin"] = values[0]; break
+ case "LessThan": filter["$field <"] = values[0]; break
+ case "LessThanEquals": filter["$field <="] = values[0]; break
+ case "GreaterThan": filter["$field >"] = values[0]; break
+ case "GreaterThanEquals": filter["$field >="] = values[0]; break
+ case "NotEqual": filter["$field !="] = values[0]; break
+ default: filter["$field ="] = values[0]; break // equal
+ }
}
- if (!domainClass.hasMetaMethod("validate")) {
- metaClass.validate = { ->
- triggerEvent(EVENT_BEFORE_VALIDATE, delegate)
- DomainClassPluginSupport.validateInstance(delegate, ctx)
- }
+
+ private static int getArgCountForComparator(String comparator) {
+ if (comparator == "Between" || comparator == "NotBetween") {
+ return 2
+ }
+ else if (["IsNull", "IsNotNull"].contains(comparator)) {
+ return 0
+ }
+ else {
+ return 1
+ }
}
- }
-
- /**
- * Registers the constraints property for the given MetaClass and domainClass instance
- */
- static void registerConstraintsProperty(MetaClass metaClass, GrailsDomainClass domainClass) {
- metaClass.'static'.getConstraints = { -> domainClass.constrainedProperties }
-
- metaClass.getConstraints = {-> domainClass.constrainedProperties }
- }
-
- private static addRelationshipManagementMethods(GrailsDomainClass dc) {
- def metaClass = dc.metaClass
- for (p in dc.persistantProperties) {
- def prop = p
- if (prop.basicCollectionType) { // @todo check on these - never true right now
- def collectionName = GrailsClassUtils.getClassNameRepresentation(prop.name)
- metaClass."addTo$collectionName" = { obj ->
- if (obj instanceof CharSequence && !(obj instanceof String)) {
- obj = obj.toString()
- }
- if (prop.referencedPropertyType.isInstance(obj)) {
- if (delegate[prop.name] == null) {
- delegate[prop.name] = GrailsClassUtils.createConcreteCollection(prop.type)
- }
- delegate[prop.name] << obj
- return delegate
- }
- else {
- throw new MissingMethodException("addTo${collectionName}", dc.clazz, [obj] as Object[])
- }
- }
- metaClass."removeFrom$collectionName" = { obj ->
- if (delegate[prop.name]) {
- if (obj instanceof CharSequence && !(obj instanceof String)) {
- obj = obj.toString()
+
+ /**
+ * add setParams method to morphias query object for pagination handling
+ */
+ public static void enhanceMorphiaQueryClass() {
+
+ def metaClass = Query.metaClass
+
+ /**
+ * construct a params array from current settings
+ */
+ metaClass.setParams = { Map params ->
+ /* @var delegate Query */
+ // sort
+ if (params.containsKey('sort')) {
+ String sortField = params.sort.toString()
+ String order = (params.containsKey('order') && params.order == 'desc' )? '-' : ''
+ delegate.order(order+sortField)
}
- delegate[prop.name].remove(obj)
- }
- return delegate
- }
- }
- else if (prop.oneToOne || prop.manyToOne) { // @todo check on these - never true right now
- def identifierPropertyName = "${prop.name}Id"
- if (!dc.hasMetaProperty(identifierPropertyName)) {
- def getterName = GrailsClassUtils.getGetterName(identifierPropertyName)
- metaClass."$getterName" = {-> GrailsDomainConfigurationUtil.getAssociationIdentifier(
- delegate, prop.name, prop.referencedDomainClass) }
- }
- }
- else if (prop.oneToMany || prop.manyToMany) { // @todo check on these - never true right now
- if (metaClass instanceof ExpandoMetaClass) {
- def propertyName = prop.name
- def collectionName = GrailsClassUtils.getClassNameRepresentation(propertyName)
- def otherDomainClass = prop.referencedDomainClass
-
- metaClass."addTo${collectionName}" = { Object arg ->
- Object obj
- if (delegate[prop.name] == null) {
- delegate[prop.name] = GrailsClassUtils.createConcreteCollection(prop.type)
+
+ // offset
+ if (params.containsKey('offset')) delegate.offset(NumberUtils.toInt(params.offset, 0))
+
+ // limit
+ if (params.containsKey('max')) delegate.limit(NumberUtils.toInt(params.max, -1))
+ }
+ }
+
+ /**
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * COPY & PASTE FROM org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin START
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * added triggerEvent to validate method: triggerEvent(EVENT_BEFORE_VALIDATE, delegate)
+ */
+ static void addGrailsDomainPluginMethods(application, domainClass, ctx) {
+ MetaClass metaClass = domainClass.metaClass
+
+ metaClass.ident = {-> delegate[domainClass.identifier.name] }
+ metaClass.constructor = { ->
+ if (ctx.containsBean(domainClass.fullName)) {
+ ctx.getBean(domainClass.fullName)
}
- if (arg instanceof Map) {
- obj = otherDomainClass.newInstance()
- obj.properties = arg
- delegate[prop.name].add(obj)
+ else {
+ BeanUtils.instantiateClass(domainClass.clazz)
}
- else if (otherDomainClass.clazz.isInstance(arg)) {
- obj = arg
- delegate[prop.name].add(obj)
+ }
+
+ metaClass.static.create = { ->
+ if (ctx.containsBean(domainClass.fullName)) {
+ ctx.getBean(domainClass.fullName)
}
else {
- throw new MissingMethodException("addTo${collectionName}", dc.clazz, [arg] as Object[])
+ BeanUtils.instantiateClass(domainClass.clazz)
}
- if (prop.bidirectional && prop.otherSide) {
- def otherSide = prop.otherSide
- if (otherSide.oneToMany || otherSide.manyToMany) {
- String name = prop.otherSide.name
- if (!obj[name]) {
- obj[name] = GrailsClassUtils.createConcreteCollection(prop.otherSide.type)
+ }
+
+ addValidationMethods(application, domainClass, ctx)
+ addRelationshipManagementMethods(domainClass)
+ }
+
+ private static addValidationMethods(GrailsApplication application, GrailsDomainClass dc, ApplicationContext ctx) {
+ def metaClass = dc.metaClass
+ def domainClass = dc
+
+ registerConstraintsProperty(metaClass, domainClass)
+
+ metaClass.hasErrors = {-> delegate.errors?.hasErrors() }
+
+ def get
+ def put
+ try {
+ def rch = application.classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder")
+ get = {
+ def attributes = rch.getRequestAttributes()
+ if (attributes) {
+ return attributes.request.getAttribute(it)
}
- obj[prop.otherSide.name].add(delegate)
- }
- else {
- obj[prop.otherSide.name] = delegate
- }
+ return PROPERTY_INSTANCE_MAP.get().get(it)
}
- delegate
- }
- metaClass."removeFrom${collectionName}" = {Object arg ->
- if (otherDomainClass.clazz.isInstance(arg)) {
- delegate[prop.name]?.remove(arg)
- if (prop.bidirectional) {
- if (prop.manyToMany) {
- String name = prop.otherSide.name
- arg[name]?.remove(delegate)
+ put = { key, val ->
+ def attributes = rch.getRequestAttributes()
+ if (attributes) {
+ attributes.request.setAttribute(key,val)
}
else {
- arg[prop.otherSide.name] = null
+ PROPERTY_INSTANCE_MAP.get().put(key,val)
}
- }
}
- else {
- throw new MissingMethodException("removeFrom${collectionName}", dc.clazz, [arg] as Object[])
+ }
+ catch (Throwable e) {
+ get = { PROPERTY_INSTANCE_MAP.get().get(it) }
+ put = { key, val -> PROPERTY_INSTANCE_MAP.get().put(key,val) }
+ }
+
+ metaClass.getErrors = { ->
+ def errors
+ def key = "org.codehaus.groovy.grails.ERRORS_${delegate.class.name}_${System.identityHashCode(delegate)}"
+ errors = get(key)
+ if (!errors) {
+ errors = new BeanPropertyBindingResult( delegate, delegate.getClass().getName())
+ put key, errors
+ }
+ errors
+ }
+ metaClass.setErrors = { Errors errors ->
+ def key = "org.codehaus.groovy.grails.ERRORS_${delegate.class.name}_${System.identityHashCode(delegate)}"
+ put key, errors
+ }
+ metaClass.clearErrors = { ->
+ delegate.setErrors (new BeanPropertyBindingResult(delegate, delegate.getClass().getName()))
+ }
+ if (!domainClass.hasMetaMethod("validate")) {
+ metaClass.validate = { ->
+ triggerEvent(EVENT_BEFORE_VALIDATE, delegate)
+ DomainClassPluginSupport.validateInstance(delegate, ctx)
+ }
+ }
+ }
+
+ /**
+ * Registers the constraints property for the given MetaClass and domainClass instance
+ */
+ static void registerConstraintsProperty(MetaClass metaClass, GrailsDomainClass domainClass) {
+ metaClass.'static'.getConstraints = { -> domainClass.constrainedProperties }
+
+ metaClass.getConstraints = {-> domainClass.constrainedProperties }
+ }
+
+ private static addRelationshipManagementMethods(GrailsDomainClass dc) {
+ def metaClass = dc.metaClass
+ for (p in dc.persistantProperties) {
+ def prop = p
+ if (prop.basicCollectionType) { // @todo check on these - never true right now
+ def collectionName = GrailsClassUtils.getClassNameRepresentation(prop.name)
+ metaClass."addTo$collectionName" = { obj ->
+ if (obj instanceof CharSequence && !(obj instanceof String)) {
+ obj = obj.toString()
+ }
+ if (prop.referencedPropertyType.isInstance(obj)) {
+ if (delegate[prop.name] == null) {
+ delegate[prop.name] = GrailsClassUtils.createConcreteCollection(prop.type)
+ }
+ delegate[prop.name] << obj
+ return delegate
+ }
+ else {
+ throw new MissingMethodException("addTo${collectionName}", dc.clazz, [obj] as Object[])
+ }
+ }
+ metaClass."removeFrom$collectionName" = { obj ->
+ if (delegate[prop.name]) {
+ if (obj instanceof CharSequence && !(obj instanceof String)) {
+ obj = obj.toString()
+ }
+ delegate[prop.name].remove(obj)
+ }
+ return delegate
+ }
+ }
+ else if (prop.oneToOne || prop.manyToOne) { // @todo check on these - never true right now
+ def identifierPropertyName = "${prop.name}Id"
+ if (!dc.hasMetaProperty(identifierPropertyName)) {
+ def getterName = GrailsClassUtils.getGetterName(identifierPropertyName)
+ metaClass."$getterName" = {-> GrailsDomainConfigurationUtil.getAssociationIdentifier(
+ delegate, prop.name, prop.referencedDomainClass) }
+ }
+ }
+ else if (prop.oneToMany || prop.manyToMany) { // @todo check on these - never true right now
+ if (metaClass instanceof ExpandoMetaClass) {
+ def propertyName = prop.name
+ def collectionName = GrailsClassUtils.getClassNameRepresentation(propertyName)
+ def otherDomainClass = prop.referencedDomainClass
+
+ metaClass."addTo${collectionName}" = { Object arg ->
+ Object obj
+ if (delegate[prop.name] == null) {
+ delegate[prop.name] = GrailsClassUtils.createConcreteCollection(prop.type)
+ }
+ if (arg instanceof Map) {
+ obj = otherDomainClass.newInstance()
+ obj.properties = arg
+ delegate[prop.name].add(obj)
+ }
+ else if (otherDomainClass.clazz.isInstance(arg)) {
+ obj = arg
+ delegate[prop.name].add(obj)
+ }
+ else {
+ throw new MissingMethodException("addTo${collectionName}", dc.clazz, [arg] as Object[])
+ }
+ if (prop.bidirectional && prop.otherSide) {
+ def otherSide = prop.otherSide
+ if (otherSide.oneToMany || otherSide.manyToMany) {
+ String name = prop.otherSide.name
+ if (!obj[name]) {
+ obj[name] = GrailsClassUtils.createConcreteCollection(prop.otherSide.type)
+ }
+ obj[prop.otherSide.name].add(delegate)
+ }
+ else {
+ obj[prop.otherSide.name] = delegate
+ }
+ }
+ delegate
+ }
+ metaClass."removeFrom${collectionName}" = {Object arg ->
+ if (otherDomainClass.clazz.isInstance(arg)) {
+ delegate[prop.name]?.remove(arg)
+ if (prop.bidirectional) {
+ if (prop.manyToMany) {
+ String name = prop.otherSide.name
+ arg[name]?.remove(delegate)
+ }
+ else {
+ arg[prop.otherSide.name] = null
+ }
+ }
+ }
+ else {
+ throw new MissingMethodException("removeFrom${collectionName}", dc.clazz, [arg] as Object[])
+ }
+ delegate
+ }
+ }
}
- delegate
- }
}
- }
}
- }
- /**
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * COPY & PASTE FROM org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin END
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- */
+ /**
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * COPY & PASTE FROM org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin END
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
}
20 test/integration/grails/plugins/mongodb/test/BasicPersistenceTests.groovy
View
@@ -4,6 +4,8 @@ import org.acme.Contact
import org.acme.Project
import org.acme.Task
import org.bson.types.ObjectId
+import com.mongodb.DBRef
+import com.google.code.morphia.Key
public class BasicPersistenceTests extends GroovyTestCase {
@@ -73,6 +75,24 @@ public class BasicPersistenceTests extends GroovyTestCase {
t2.delete()
}
+ void testGetVariants() {
+ def p1 = new Project(name: "InConcert2")
+ p1.startDate = new Date()
+ p1.save()
+
+ assertNotNull "should have saved new project", p1
+ assertNotNull "should have retrieved id of new project", p1.id
+
+ DBRef dbRef = p1.createDBRef()
+ Key key = p1.createKey()
+
+ assertEquals "fetch using get() by DBRef should work", Project.get(dbRef)?.id, p1.id
+ assertEquals "fetch using get() by Key should work", Project.get(key)?.id, p1.id
+ assertEquals "fetch using get() by Id should work", Project.get(p1.id)?.id, p1.id
+
+ p1.delete()
+ }
+
void testUpdateAndDelete() {
def id = ObjectId.get()
19 test/integration/grails/plugins/mongodb/test/StaticMethodsTests.groovy
View
@@ -40,6 +40,25 @@ class StaticMethodsTests extends GroovyTestCase {
assertEquals "find should find the searched task", Task.find(["name >": "S"], [sort: "-estimatedHours"])?.taskId, taskList[1].taskId
}
+ void testQueryInterface() {
+ assertEquals "findByProjectId should find all testobjects", Task.findAllByProjectId(projectId)*.taskId.sort(), taskList*.taskId.sort()
+ assertEquals "subsequent call to now cached findByProjectId should find all testobjects", Task.findAllByProjectId(projectId)*.taskId.sort(), taskList*.taskId.sort()
+
+ // test sorting
+ def found = Task.query([sort: "estimatedHours"]) {
+ field('projectId').equal(projectId)
+ field('name').startsWith("Sim")
+ }.asList()
+ println "Task List:"
+ taskList.each { println it.toString() }
+ println "Found entities: "
+ found.each { println it.toString() }
+
+ assertEquals "Task 1 should be at 0 place in sorted result", found[0].taskId, taskList[0].taskId
+ assertEquals "Task 4 should be at 1 place in sorted result", found[1].taskId, taskList[3].taskId
+ assertEquals "Task 2 should be at 2 place in sorted result", found[2].taskId, taskList[1].taskId
+ }
+
/**
* test deleteOne and deleteAll
*/
2  web-app/WEB-INF/tld/fmt.tld
View
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
Please sign in to comment.
Something went wrong with that request. Please try again.