I know there has to be a GORMy way of doing this.
class A {
def foo
C c
}
class B {
def bar
C c
}
class C {
def x
def y
def z
}
I want to get all of C where A.foo == 'foo' and where B.bar == 'bar'.
I know I can do it from one or the other parent classes, eg:
def my_cs = A.withCriteria { eq('foo', 'foo') }*.c
...and I could separately grab all my B's where bar == 'bar' and loop through my_cs... but that seems inefficient and I feel like there must be a reasonable way to do it through the criteria syntax.
Is that possible, or is there another acceptable way of doing this?
Thanks.
-------- solution ----------
A.withCriteria {
createAlias('c.B', 'cb', CriteriaSpecification.LEFT_JOIN)
eq('foo', 'foo')
isNull('cb.c')
projections {
property 'c'
}
}
I found that I did not need to test bar for B.
If your C class doesn't have a reference back to As of Bs then I think getting the As and Bs separately and then combining them might be your only real option. However, I don't think it's really that bad.
If you want to make sure you have a collection of unique Cs then that's easy, too (no looping required). See http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#unique%28%29
e.g.
def my_cs = []
my_cs.addAll(A.withCriteria { eq('foo', 'foo') }*.c)
my_cs.addAll(B.withCriteria { eq('bar', 'bar') }*.c)
my_cs.unique(true) // Removes duplicates from the list
Related
For example, the groovy File class has a nice iterator that will filter out just directories and not files:
void eachDir(Closure closure)
When I use eachDir, I have to use the verbose method of creating the collection first and appending to it:
def collection = []
dir1.eachDir { dir ->
collection << dir
}
Any way to get it back to the nice compact collect syntax?
I don't know of any "idiomatic" way of doing this, nice riddle! =D
You can try passing the eachDir, or any similar function, to a function that will collect its iterations:
def collectIterations(fn) {
def col = []
fn {
col << it
}
col
}
And now you can use it as:
def dir = new File('/path/to/some/dir')
def subDirs = collectIterations(dir.&eachDir)
def file = new File('/path/to/some/file')
def lines = collectIterations(file.&eachLine)
(that last example is equivalent to file.readLines())
And only for bonus points, you may define this function as a method in the Closure class:
Closure.metaClass.collectIterations = {->
def col = []
delegate.call {
col << it
}
col
}
def dir = new File('/path/to/some/dir')
def subDirs = dir.&eachDir.collectIterations()
def file = new File('/path/to/some/file')
def lines = file.&eachLine.collectIterations()
Update: On the other hand, you might also do:
def col = []
someDir.eachDir col.&add
Which I think is quite less convoluted, but it's not leveraging the collect method as you requested :)
Not for the specific example that you're talking about. File.eachDir is sort of a weird implementation IMO. It would have been nice if they implemented iterator() on File so that you could use the normal iterator methods on them rather than the custom built ones that just execute a closure.
The easiest way to get a clean one liner that does what you're looking for is to use listFiles instead combined with findAll:
dir1.listFiles().findAll { it.directory }
If you look at the implementation of eachDir, you'll see that it's doing this (and a whole lot more that you don't care about for this instance) under the covers.
For many similar situations, inject is the method that you'd be looking for to have a starting value that you change as you iterate through a collection:
def sum = [1, 2, 3, 4, 5].inject(0) { total, elem -> total + elem }
assert 15 == sum
I am trying to find the proper syntax for a query that I know has got to be very common but couldn't find a code example for.
class ObjA {
...
static hasMany = [b:ObjB]
}
if a is an instance of ObjA, I want to perform a query like:
a.b.findAllBsSuchThat(b.someproperty = somevalue)
In order to avoid (N+1) queries for lazy associations per a, you can use a criteria as:
ObjA.withCriteria {
b {
eq 'someProperty', someValue
}
}
or where queries:
ObjA.where { b.someProperty == somevalue }.list()
If you use something like a.b.findAllBsSuchThat(b.someproperty = somevalue) then you would be getting all b's for a and then filtering on the result. This will affect the performance and will unnecessary.
I have two very similar methods in Grails, something like "calculate statistics by os" and "calculate statistics by browser" - effectively both prepare some things, then run a similar query on the DB, then do things with the results. The only part where the methods differ is the query they run in the middle of my method -
def summary = c.list {
eq('browser', Browser.get(1)) // OR eq('os', OS.get(1))
between('date', dates.start, dates.end)
}
It occurred to me that the ideal way to refactor it would be to pass in the first line of the closure as a method parameter. Like
doStats (Closure query) {
...
def summary = c.list {
query
between('date', dates.start, dates.end)
}
}
I tried this but "query" gets ignored. I tried query() instead but then the query clause is executed where defined, so this doesn't work either. I suppose I could just pass the whole closure as a parameter but that seems wrong - the query might also get more complicated in future.
Anyone have any better ideas?
I found leftShift operator useful for composing closure from two separate ones. What you can do is:
Closure a = { /*...*/ }
Closure b = { /*...*/ }
Closure c = a << b
Take a look at this example:
def criteria = {
projection Projections.distinct(Projections.property('id'))
and {
eq 'owner.id', userDetails.id
if (filter.groupId) {
eq 'group.id', filter.groupId
}
}
}
List<Long> ids = Contact.createCriteria().list(criteria << {
maxResults filter.max
firstResult filter.offset
})
Integer totalCount = Contact.createCriteria().count(criteria)
What you can see here is that I'm creating a criteria for listing ant counting GORM objects. Criterias for both cases are almost the same, but for listing purposes I also need to include limit and offset from command object.
You're using the criteria DSL which might be different than plain groovy closures.
To do what you're asking, you can use the method described here -
http://mrhaki.blogspot.com/2010/06/grails-goodness-refactoring-criteria.html
and put your query in to private method.
The more elegant solution for this is to use named queries in grails -
http://grails.org/doc/latest/ref/Domain%20Classes/namedQueries.html
Look at the
recentPublicationsWithBookInTitle {
// calls to other named queries…
recentPublications()
publicationsWithBookInTitle()
}
example -
Not sure about with the Grails Criteria builder, but with other builders, you can do something like:
doStats (Closure query) {
def summary = c.list {
query( it )
between('date', dates.start, dates.end)
}
}
And call this via:
def f = { criteria ->
criteria.eq( 'browser', Browser.get( 1 ) )
}
doStats( f )
If not, you're probably best looking at named queries like tomas says
In Rails, one could use:
returning Person.create do |p|
p.first_name = "Collin"
p.last_name = "VanDyck"
end
Avoiding having to do this:
person = Person.create
person.first_name = "Collin"
person.last_name = "VanDyck"
person
I think the former way is cleaner and less repetitive. I find myself creating this method in my Scala projects:
def returning[T](value: T)(fn: (T) => Unit) : T = {
fn(value)
value
}
I know that it is of somewhat limited utility due to the tendency of objects to be immutable, but for example working with Lift, using this method on Mapper classes works quite well.
Is there a Scala analog for "returning" that I'm not aware of? Or, is there a similar way to do this in Scala that's more idiomatic?
Your method looks fine, though I normally do this by adding a method for side-effects, which can include changing internal state (or also stuff like println):
class SideEffector[A](a: A) {
def effect(f: (A => Any)*) = { f.foreach(_(a)); a }
}
implicit def can_have_side_effects[A](a: A) = new SideEffector(a)
scala> Array(1,2,3).effect(_(2) = 5 , _(0) = -1)
res0: Array[Int] = Array(-1, 2, 5)
Edit: just in case it's not clear how this would be useful in the original example:
Person.create.effect(
_.first_name = "Collin",
_.last_name = "VanDyck"
)
Edit: changed the name of the method to "effect". I don't know why I didn't go that way before--side effect, not side effect for the naming.
Can't really improve much on what you've already written. As you quite correctly pointed out, idiomatic Scala tends to favour immutable objects, so this kind of thing is of limited use.
Plus, as a one-liner it's really not that painful to implement yourself if you need it!
def returning[T](value: T)(fn: T => Unit) : T = { fn(value); value }
I would do:
scala> case class Person(var first_name: String = "", var last_name: String = "")
defined class Person
scala> Person(first_name="Collin", last_name="VanDyck")
res1: Person = Person(Collin,VanDyck)
I don't understand why Vasil deleted his own answer, but I like it a lot (it was precisely what I was going to suggest):
val person = Person.create
locally { import person._
first_name = "Collin"
last_name = "VanDyck"
}
person
One of the features people have been asking for is the ability to auto-import something. If it were possible, then you could do this:
def returning[T](import value: T)(fn: => Unit) : T = { fn; value }
returning(Person.create) {
first_name = "Collin"
last_name = "VanDyck"
}
That is not possible at the moment, nor is it in Scala's roadmap. But some people do ask for something like that now and again.
It is possible to avoid repeating the variable name like so:
val person = Person.create
locally { import person._
first_name = "Collin"
last_name = "VanDyck"
}
Note that this only works for vals. Also, locally is a Predef method that helps to create blocks just to limit variable scope, without running afoul of Scala's semicolon inference. This keeps the import from getting in your way once you have finished initializing the person.
Another suggestion would be using the forward pipe operator from Scalaz.
val person = Person.create |> { p =>
p.firstName = "Collin"
p.lastName = "VanDyck"
p // or p.saveMe
}
The difference is that you would have to return the value yourself, if you want to assign it. If you do not need the return value (as in your initial example), things are easier:
Person.create |> { p =>
p.firstName = "Collin"
p.lastName = "VanDyck"
p.save
}
And there you go.
I was reluctant to really use it in my own code (even though I kind of favour this way of doing it – but it is only documented in scalaz and maybe hard to figure out for other people looking at the code), so I hope these examples do work.
You could of course define your own ‘forward and returning pipe’ using |>.
class ReturningPipe[A](value: A) {
import Scalaz._
def |>>[B](f: A => B):A = value.|>(a => { f(a); value})
}
implicit def returningPipe[A](value: A) = new ReturningPipe(value)
Is this possible to convert in createCriteria()?
SELECT * FROM node WHERE (node.type = 'act' AND nid NOT IN (SELECT nid FROM snbr_act_community)) LIMIT 10
I know there's a 'in' operator and here's what I have so far:
def c = VolunteerOpportunity.createCriteria()
def matchingActs = c.list {
node {
eq('type', 'act')
}
maxResults(10)
}
Just want to see if this is possible. Otherwise, I guess this is possible in HQL right?
thanks Sammyrulez for the code. got an idea from that. tested it but it didn't work. i fixed it and here's the final working code:
def ids = [14400 as long, 14401 as long]
def c = VolunteerOpportunity.createCriteria()
def matchingActs = c.list {
node {
eq('type', 'act')
not { 'in'(ids) }
}
maxResults(10)
}
now i know how to use 'not' operator. thanks a lot!
not tried it myself but looking at the Grails doc and hibernate api you create nodes on this builder map with the static methods found in the Restrictions class of the Hibernate Criteria API 1. So something like
def c = VolunteerOpportunity.createCriteria()
def matchingActs = c.list {
node {
not(in('propertyName', ['val1','val2']))
}
maxResults(10)
}
Since you chain the in method (that returns a Criterion) with the not method (that takes a Criterion as argument and returns a negated version)
this is the solution :
def resultat=EnteteImputationBudgetaire.createCriteria().get{
between("dateDebutPeriode", dateDebut, dateFin)
and{ eq 'natureImputationBudgetaire','FONCTIONNEMENT' }
maxResults(1)
}
def resultat2=ParametragePlanBudgetaire.createCriteria().list() {
like('composantBudgetaire','6%')
if(resultat?.details) {
not {
'in'('composantBudgetaire',resultat?.details?.imputationBudgetaire)
}
}
}
According to Grails documentation about creating criteria here, you can use something like this:
not {'in'("age",[18..65])}
In this example, you have a property named "age" and you want to get rows that are NOT between 18 and 65. Of course, the [18..65] part can be substituted with any list of values or range you need.
Just remembering: in this case you don't have to use parenthesis and you can use inList, for example:
not { inList 'age',[18..65] }