I am trying to use recursion in groovy to traverse a tree relationship. The below code runs one cycle, upto childNodes && recurClosure(childNodes ) , but doesn't call the closure recurClosure again. At that instant childNodes had two objects (array list) same type as root.
In the code, recurClosure is defined and calls with a list of objects (root). It then iterates through each element and fines the child nodes (uses grails dsl for this).If the childNodes is not null, it recursively calls the parent method.
Should I break it up, or what is wrong?
def parentval
def root = Domain.list()
def recurClosure
recurClosure = {inroot ->
inroot.each {
returnList << it
parentval = it
childNodes = Domain.withCriteria {
eq('parent', parentval )
}
}
childNodes && recurClosure(childNodes )
}(root)
return returnList
}
thanks in advance.
Update: The following exception is noted
ERROR [2010-06-24 08:20:04,742] [groovy.grails.web.errors.GrailsExceptionResolver] Cannot invoke method call() on null object
java.lang.NullPointerException: Cannot invoke method call() on null object
at com.bsr.test.DomainService$_closure2_closure7.doCall(com.bsr.test.DomainService:68)
at com.bsr.test.DomainService$_closure2.doCall(com.bsr.test.DomainService:58)
at com.bsr.test.DomainController$_closure3.doCall(DomainController.groovy:45)
at com.bsr.test.DomainController$_closure3.doCall(DomainController.groovy)
at org.apache.shiro.web.servlet.ShiroFilter.executeChain(ShiroFilter.java:687)
at org.apache.shiro.web.servlet.ShiroFilter.doFilterInternal(ShiroFilter.java:616)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81)
at java.lang.Thread.run(Thread.java:619)
Update 2: Now trying Daniel's suggestion.
{ inroot ->
inroot.each {
returnList << it
parentval = it
childNodes = Domain.withCriteria {
eq('parent', parentval )
}
if(childNodes)
call(childNodes)
}
/*if(childNodes)
call(childNodes)*/
}(root)
In the above implementation, root is an arraylist, The inner closure takes each element out of it and recursively calls the anonymous closure. When I moved the 'call' inside the each closure, it doesn't call the outer anonymous closure, but the inroot.each {} itself. So, I get an error
ERROR [2010-06-24 08:47:46,438] [groovy.grails.web.errors.GrailsExceptionResolver] instance not of expected entity type: java.util.ArrayList is not a: com.bsr.test.Domain
I see a blog post about how to name the closure through 'this' > I'll update my finding.. thanks
Update 3: The way to call the outer closure is owner.call(childNodes)
The problem is, that by
recurClosure = {
[...]
}(root)
you don't assign the closure to recurClosure, but rather the return value of its invocation! Thus, of course, you can't call the closure via recurClosure()...
Two possible solutions:
First define the closure, and then call it separately, as air_blob suggested:
def recurClosure = {
[...]
}
recurClosure(root)
Use the implicit call() function for recursion. This way you can even work with an anonymous closure. IMHO a very nice way to implement recursion in Groovy:
{ inroot ->
inroot.each {
returnList << it
parentval = it
childNodes = Domain.withCriteria {
eq('parent', parentval )
}
}
if(childNodes)
call(childNodes)
}(root)
Two more comments on your code:
You may want to declare returnList: def returnList = []
While childNodes && recurClosure(childNodes ) may do what you want, it's much more readable to sacrifice one more char and spell out the if.. ;-)
Don't you want to recursively call your closure inside the each?
Another (higher-level) remark on your code: If the parents and their children are of the same type (Domain), won't Domain.list() actually return all children, too? Is there really a need for traversing the tree manually?
Do you get any exceptions?
Have you tried invoking it separately like this:
def recurClosure
recurClosure = {inroot ->
[... stuff ...]
}
recurClosure(root)
What exactly do you want to do in this line:
childNodes && recurClosure(childNodes )
Related
I noticed some behaviour I don't quite understand. It's possible to chain where queries as long as the preceding where query wasn't returned from a function.
def w = Subscription.where { topicId == 1 }
w.where { user.id == 1 }.findAll()
//works as expected
def f() {
Subscription.where { topicId == 1 }
}
f().where { user.id == 1 }.findAll()
//doesn't filter by user.id
def f() {
Subscription.where { topicId == 1 }
}
f().build { eq 'user.id', 1L }
//works
I don't mind using DetachedCriteria#build(). I'd just like to understand :-D
--- Edit
Grails 2.4.2
Grails contains a global AST transformation...
that transforms where methods into detached criteria queries.
It essentially looks for uses of where() and changes the code so that it uses detached criterias. Most likely, calling where() as you are, on the return of a method, particularly a method which returns Object, is not being acknowledged up by the AST, so the query basically ends up being unchanged and doing nothing.
It's nothing that Groovy is doing, but rather something Grails-specific. I do not claim a full, nor anything near full, understanding of what's happening, but you can get the gist of it by looking at the DetachedCriteriaTransformer source code.
I have a function (the caller) that returns certain values. Before they are returned, they are added to by the result of calling another function (the callee), is there a neater way to add the callee function's resultant values to the caller function's resultant values before the are returned?
def funcA() { // Caller Function
def a,b,c
a = blah
b = blah blah
...
def (d,e) = funcB()
a += d // Is there a neater way to encapsulate
b += e // these additions somehow into the previous line? maybe kind of like
// (a,b) += funcB() ?
return [a,b,c]
}
def funcB() { // Callee Function
def d,e
...
return [d,e]
}
to bad we don't have a zip, then something like [a,b].zip()*.sum() would work.
transpose() only gives you the pairs (not the remainder). so this only works, if your lists are the same length:
assert [43,668]==[[1,2,3],[42,666]].transpose()*.sum()
One could attempt to fill shorter list with zeros in this case, so the sum is not influenced (at that point this depends on the type there).
So this function is a workaround:
def zipadd(a,b) {
def l = [a,b]
// iterate over the size of the longer of the two lists
(0..(l*.size().max()-1)).collect{
// get the pairs, filter null, sum both
l*.getAt(it).findAll{it!=null}.sum()
}
}
assert [43,668,3]==zipadd([1,2,3],[42,666])
assert [43,668,3]==zipadd([42,666],[1,2,3])
assert [24,93,0,1]==zipadd([1,0,0,1], [23,93])
If you use pass l directly (a list of lists) instead of a and b this should also work with more than two elements.
I'm using data binding with parent/child relationships in Grails 2.3.7 and am having trouble with deletes. The form has many optional children, and to keep the database tidy I'd like to purge blank (null) values. I've found some nice articles which suggest using removeAll to filter my entries but I can't get remove or removeAll to work!
For example... (Parent has 10 children, 5 are blank)
def update(Parent parent) {
parent.children.getClass() // returns org.hibernate.collection.PersistentSet
parent.children.size() // returns 10
parent.children.findAll{ it.value == null }.size() // returns 5
parent.children.removeAll{ it.value == null } // returns TRUE
parent.children.size() // Still returns 10!!!
}
I've read PersistentSet is finicky about equals() and hashCode() being implemented manually, which I've done in every domain class. What baffles me is how removeAll can return true, indicating the Collection has changed, yet it hasn't. I've been stuck on this for a couple days now so any tips would be appreciated. Thanks.
Update:
I've been experimenting with the Child hashcode and that seems to be the culprit. If I make a bare-bones hashcode based on the id (bad practice) then removeAll works, but if I include the value it stops working again. For example...
// Sample 1: Works with removeAll
int hashCode() {
int hash1 = id.hashCode()
return hash1
}
// Sample 2: Doesn't work with removeAll
int hashCode() {
int hash1 = id.hashCode()
int hash2 = value == null ? 0 : value.hashCode()
return hash1 + hash2
}
// Sample Domain classes (thanks Burt)
class Parent {
static hasMany = [children: Child]
}
class Child {
String name
String value
static constraints = {
value nullable: true
}
}
This behavior is explained by the data binding step updating data, making it dirty. (ie: child.value.isDirty() == true) Here's how I understand it.
First Grails data binding fetches the Parent and children, and the hashcode of each Child is calculated. Next, data updates are applied which makes child.value dirty (if it changed) but the Set's hashcodes remain unchanged. When removeAll finds a match it builds a hashCode with the dirty data, but that hashcode is NOT found in the Set so it can't remove it. Essentially removeAll will only work if ALL of my hashCode variables are clean.
So if the data must be clean to remove it, one solution is to save it twice. Like this...
// Parent Controller
def update(Parent parent) {
parent.children.removeAll{ it.value == null } // Removes CLEAN children with no value
parent.save(flush:true)
parent.refresh() // parent.children is now clean
parent.children.removeAll{ it.value == null } // Removes (formerly dirty) children
parent.save(flush:true) // Success!
}
This works though it's not ideal. First I must allow null values in the database, though they only exist briefly and I don't want them. And second it's kinda inefficient to do two saves. Surely there must be a better way?
hashCode and equals weirdness aren't an issue here - there are no contains calls or something similar that would use the hashCode value and potentially miss the actual data. If you look at the implementation of removeAll you can see that it uses an Iterator to call your closure on every instance and remove any where the closure result is truthy, and return true if at least one was removed. Using this Parent class
class Parent {
static hasMany = [children: Child]
}
and this Child
class Child {
String name
String value
static constraints = {
value nullable: true
}
}
and this code to create test instances:
def parent = new Parent()
5.times {
parent.addToChildren(name: 'c' + it)
}
5.times {
parent.addToChildren(name: 'c2' + it, value: 'asd')
}
parent.save()
it prints 5 for the final size(). So there's probably something else affecting this. You shouldn't have to, but you can create your own removeAll that does the same thing, and if you throw in some println calls you might figure out what's up:
boolean removeAll(collection, Closure remove) {
boolean atLeastOne = false
Iterator iter = collection.iterator()
while (iter.hasNext()) {
def c = iter.next()
if (remove(c)) {
iter.remove()
atLeastOne = true
}
}
atLeastOne
}
Invoke this as
println removeAll(parent.children) { it.value == null }
In a Grails application, I am trying to prevent the creation of cycles in a directed graph. The user is able to assign a parent to a node, but no node should be its own parent's ancestor. I have written a simple setup function that calls checkLineageForTarget, which is the recursive function that does the heavy lifting:
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
// true means that this is a safe addition
// false means that this addition creates a cycle
boolean retVal = stillToProcess.each {
Collection<Node> itsParents = getParentNodes(it)
if (it.id == target){
println("found a loop on " + target);
return false; // loop detected!
}
if (itsParents.empty){ return true; } // end of the line
return checkLineageForTarget(target, itsParents)
}
// at this point, retVal is always true, even when the "found a loop [...]" condition is met
return retVal;
}
This "works," in that it prints the "found a loop [...]" message, but outside of the closure, retVal is true, the calling function attempts to add the new parent/child relationship, and my stack runneth over.
What is my misunderstanding?
The each method returns the same collection it was invoked on, so retVal is probably not the boolean "true", but is evaluated as "truthly" (as it is a collection, it would mean it's not empty).
If you want to check a condition for every element in a collection, you might use every.
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
stillToProcess.every { node ->
node.id != target && checkLineageForTarget(target, getParentNodes(node))
}
}
Note that I didn't need check the .empty condition on the parent nodes collection because that will be filtered by the recursive call to checkLineageForTarget (i.e. calling .every on an empty collection always returns true). Also, because of the short-circuiting of the && operator, the iteration stops as soon as node.id == target :)
.each appears to return the Object being looped over when it is done. You are assign this to a boolean and it is being coerced to true. You probably want to use .every for your task. It returns true only if each iteration returns true and it will stop looping when it hits the first false. You can find more information in the groovy docs.
When you return inside a Closure, it's like returning inside a method call within the method - it's local to that scope and has no impact on the real method the closure is being called in. In this case you can use one of the other approaches suggested (e.g. every) or use a regular for loop since it works the same as the Groovy each (i.e. it's null-safe and supports but doesn't require types) but you can break out of the loop or return and since you're in a real for loop it will return from the method:
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
for (Node node in stillToProcess) {
Collection<Node> itsParents = getParentNodes(node)
...
}
...
}
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