XmlSlurper parsing a query result - parsing

I have a query that bring back a cell in my table the has all xml in it. I have it so I can spit out what is in the cell without any delimiters. Now i need to actually take each individual element and link them with my object. Is there any easy way to do this?
def sql
def dataSource
static transactional = true
def pullLogs(String username, String id) {
if(username != null && id != null) {
sql = new Sql(dataSource)
println "Data source is: " + dataSource.toString()
def schema = dataSource.properties.defaultSchema
sql.query('select USERID, AUDIT_DETAILS from DEV.AUDIT_LOG T WHERE XMLEXISTS(\'\$s/*/user[id=\"' + id + '\" or username=\"'+username+'\"]\' passing T.AUDIT_DETAILS as \"s\") ORDER BY AUDIT_EVENT', []) { ResultSet rs ->
while (rs.next()) {
def auditDetails = new XmlSlurper().parseText(rs.getString('AUDIT_EVENT_DETAILS'))
println auditDetails.toString
}
}
sql.close()
}
}
now this will give me that cell with those audit details in it. Bad thing is that is just puts all the information from the field in on giant string without the element tags. How would I go through and assign the values to a object. I have been trying to work with this example http://gallemore.blogspot.com/2008/04/groovy-xmlslurper.html with no luck since that works with a file.
I have to be missing something. I tried running another parseText(auditDetails) but haven't had any luck on that.
Any suggestions?
EDIT:
The xml int that field looks like
<user><username>scottsmith</username><timestamp>tues 5th 2009</timestamp></user>
^ simular to how it is except mine is ALOT longer. It comes out as "scottsmithtue 5th 2009" so on and so forth. I need to actually take those tags and link them to my object instead of just printing them in one conjoined string.

Just do
auditDetails.username
Or
auditDetails.timestamp
To access the properties you require

Related

Grails findBy multiple columns Ilike

I have a simple Tag class with only two fields, name and value,
class Tag {
String name
String value
}
and I'm trying to render an XML where I want to search for parts of both parameters via findBy...Ilike().
def getXml = {
render Tag.findAllByNameAndValueIlike("%${params.name}%", "%${params.value}%") as XML
}
But this doesn't give my any results. If I use only one parameter, it works as I expect:
def getXml = {
render Tag.findAllByNameIlike("%${params.name}%") as XML
}
My next question is probably going to be about filtering the results, and adding other "similar" tags to the returns list, so is there a way to solve the above with something like:
def getXml = {
list = Tag.findAllByNameIlike("%${params.name}%")
list.add(Some other stuff)
list.sortBy(Some thing, maby name length)
}
For your multiple-field ilike query you can use withCriteria:
def result = Tag.withCriteria {
ilike('name', "%${params.name}%")
ilike('value', "%${params.value}%")
}
This will return a list of Tag domains whose name matches the provided name and value matches the provided value.
The Criteria DSL will probably let you do most of the filtering you need, but you can also consider using some of the Groovy collection examples here.
You have to put the restrictions(InList, NotNull, etc) on each field of a dynamic finder. If you do not, it assumes equals. Here is what you were looking for:
Tag.findAllByNameIlikeAndValueIlike("%${params.name}%", "%${params.value}%")
Both answer are good. I tried both, but I have to say I like the withcCritia the best. It seems very flexibly.
def result = Tag.withCriteria {
if(params.name != null)
ilike('name', "%${params.name}%")
if(params.value != null)
ilike('value', "%${params.value}%")
}
result.add(new Tag('name': "something"))
render result as XML

Querying values from a Map Property in a Grails Domain Class

I found the solution Burt Beckwith offered in this question: add user define properties to a domain class and think this could be a viable option for us in some situations. In testing this, I have a domain with a Map property as described in the referenced question. Here is a simplified version (I have more non-map properties, but they are not relevant to this question):
class Space {
String spaceDescription
String spaceType
Map dynForm
String toString() {
return (!spaceType)?id:spaceType + " (" + spaceDescription + ")"
}
}
I have some instances of space saved with some arbitrary data in the dynForm Map like 'Test1':'abc' and 'Test2':'xyz'.
I am trying to query this data and have succesfully used HQL to filter doing the following:
String className = "Space"
Class clazz = grailsApplication.domainClasses.find { it.clazz.simpleName == className }.clazz
def res = clazz.executeQuery("select distinct space.id, space.spaceDescription from Space as space where space.dynForm['Test1'] = 'abc' " )
log.debug "" + res
I want to know if there is a way to select an individual item from the Map dynForm in a select statement. Somehting like this:
def res = clazz.executeQuery("select distinct space.id, elements(space.dynForm['Test1']) from Space as space where space.dynForm['Test1'] = 'abc' " )
I can select the entire map like this:
def res = clazz.executeQuery("select distinct elements(space.dynForm) from Space as space where space.dynForm['Test1'] = 'abc' " )
But I just want to get a specific instance based on the string idx.
how about to use Criteria, but i don't have any sql server so i haven't tested yet.
def results = Space.createCriteria().list {
dynForm{
like('Test1', 'abc')
}
}

Why sql.rows Groovy method is so slow

I tried to fetch some data with the sql.rows() Groovy method and it took a very long time to return the values.
So I tried the "standard" way and it's much much faster (150 times faster).
What am I missing ?
Look at the code below : the first method returns results in about 2500ms and the second in 15 ms !
class MyService {
javax.sql.DataSource dataSource
def SQL_QUERY = "select M_FIRSTNAME as firstname, M_LASTNAME as lastname, M_NATIONALITY as country from CT_PLAYER order by M_ID asc";
def getPlayers1(int offset, int maxRows)
{
def t = System.currentTimeMillis()
def sql = new Sql(dataSource)
def rows = sql.rows(SQL_QUERY, offset, maxRows)
println "time1 : ${System.currentTimeMillis()-t}"
return rows
}
def getPlayers2(int offset, int maxRows)
{
def t = System.currentTimeMillis();
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
statement.setMaxRows(offset + maxRows -1);
ResultSet resultSet = statement.executeQuery(SQL_QUERY);
def l_list =[];
if(resultSet.absolute(offset)) {
while (true) {
l_list << [
'firstname':resultSet.getString('firstname'),
'lastname' :resultSet.getString('lastname'),
'country' :resultSet.getString('country')
];
if(!resultSet.next()) break;
}
}
resultSet.close()
statement.close()
connection.close()
println "time2 : ${System.currentTimeMillis()-t}"
return l_list
}
When you call sql.rows, groovy eventually calls SqlGroovyMethods.toRowResult for each row returned by the resultSet.
This method interrogates the ResultSetMetaData for the resultSet each time to find the column names, and then fetches the data for each of these columns from the resultSet into a Map which it adds to the returned List.
In your second example, you directly get the columns required by name (as you know what they are), and avoid having to do this lookup every row.
I think I found the reason this method is so slow : statement.setMaxRows() is never called !
That means that a lot of useless data is sent by the database (when you want to see the first pages of a large datagrid)
I wonder how your tests would turn out if you try with setFetchSize instead of setMaxRows. A lot of this has to the underlying JDBC Driver's default behavior.

help with oauthService and linkedin

I am trying to iterate over a list of parameters, in a grails controller. when I have a list, longer than one element, like this:
[D4L2DYJlSw, 8OXQWKDDvX]
the following code works fine:
def recipientId = params.email
recipientId.each { test->
System.print(test + "\n")
}
The output being:
A4L2DYJlSw
8OXQWKDDvX
But, if the list only has one item, the output is not the only item, but each letter in the list. for example, if my params list is :
A4L2DYJlSwD
using the same code as above, the output becomes:
A
4
L
2
D
Y
J
l
S
w
can anyone tell me what's going on and what I am doing wrong?
thanks
jason
I run at the same problem a while ago! My solution for that it was
def gameId = params.gameId
def selectedGameList = gameId.class.isArray() ? Game.getAll(gameId as List) : Game.get(gameId);
because in my case I was getting 1 or more game Ids as parameters!
What you can do is the same:
def recipientId = params.email
if(recipientId.class.isArray()){
// smtg
}else{
// smtg
}
Because what is happening here is, as soon as you call '.each' groovy transform that object in a list! and 'String AS LIST' in groovy means char_array of that string!
My guess would be (from what I've seen with groovy elsewhere) is that it is trying to figure out what the type for recipientId should be since you haven't given it one (and it's thus dynamic).
In your first example, groovy decided what got passed to the .each{} closure was a List<String>. The second example, as there is only one String, groovy decides the type should be String and .each{} knows how to iterate over a String too - it just converts it to a char[].
You could simply make recipientId a List<String> I think in this case.
You can also try like this:
def recipientId = params.email instanceof List ? params.email : [params.email]
recipientId.each { test-> System.print(test + "\n") }
It will handle both the cases ..
Grails provides a built-in way to guarantee that a specific parameter is a list, even when only one was submitted. This is actually the preferred way to get a list of items when the number of items may be 0, 1, or more:
def recipientId = params.list("email")
recipientId.each { test->
System.print(test + "\n")
}
The params object will wrap a single item as a list, or return the list if there is more than one.

list.find(closure) and executing against that value

Really my question is "Can the code sample below be even smaller? Basically the code sample is designed to first look through a list of objects, find the most granular (in this case it is branch) and then query backwards depending on what object it finds.
1 - If it finds a branch, return the findAllBy against the branch
2 - If it finds a department, return the findAllBy against the department
3 - If it finds an organization, return the findAllBy against the organization
The goal is to find the most granular object (which is why order is important), but do I need to have two separate blocks (one to define the objects, the other to check if they exist)? Or can those two executions be made into one command...
def resp
def srt = [sort:"name", order:"asc"]
def branch = listObjects.find{it instanceof Branch}
def department = listObjects.find{it instanceof Department}
def organization = listObjects.find{it instanceof Organization}
resp = !resp && branch ? Employees.findAllByBranch(branch,srt) : resp
resp = !resp && department ? Employees.findAllByDepartment(department,srt) : resp
resp = !resp && organization ? Employees.findAllByOrganization(organization,srt) : resp
return resp
What I'm thinking is something along the lines of this:
def resp
resp = Employees.findAllByBranch(listObjects.find{it instanceof Branch})
resp = !resp ? Employees.findAllByDepartment(listObjects.find{it instanceof Department}) : resp
resp = !resp ? Employees.findAllByOrganization(listObjects.find{it instanceof Organization}) : resp
But I believe that will throw an exception since those objects might be null
You can shorten it up a bit more with findResult instead of a for in loop with a variable you need to def outside:
def listObjects // = some predetermined list that you've apparently created
def srt = [sort:"name", order:"asc"]
def result = [Branch, Department, Organization].findResult { clazz ->
listObjects?.find { it.class.isAssignableFrom(clazz) }?.with { foundObj ->
Employees."findAllBy${clazz.name}"(foundObj, srt)
}
}
findResult is similar to find, but it returns the result from the first non-null item rather than the item itself. It avoids the need for a separate collection variable outside of the loop.
Edit: what I had previously didn't quite match the behavior that I think you were looking for (I don't think the other answers do either, but I could be misunderstanding). You have to ensure that there's something found in the list before doing the findAllBy or else you could pull back null items which is not what you're looking for.
In real, production code, I'd actually do things a bit differently though. I'd leverage the JVM type system to only have to spin through the listObjects once and short circuit when it found the first Branch/Department/Organization like this:
def listObjects
def sort = [sort:"name", order:"asc"]
def result = listObjects?.findResult { findEmployeesFor(it, sort) }
... // then have these methods to actually exercise the type specific findEmployeesFor
def findEmployeesFor(Branch branch, sort) { Employees.findAllByBranch(branch, sort) }
def findEmployeesFor(Department department, sort { Employees.findAllByDepartment(department, sort)}
def findEmployeesFor(Organization organization, sort { Employees.findAllByOrganization(organization, sort)}
def findEmployeesFor(Object obj, sort) { return null } // if listObjects can hold non/branch/department/organization objects
I think that this code is actually clearer and it reduces the number of times we iterate over the list and the number of reflection calls we need to make.
Edit:
A for in loop is more efficient, since you want to break processing on first non-null result (i.e. in Groovy we cannot break out of a closure iteration with "return" or "break").
def resp
for(clazz in [Branch,Department,Organization]) {
resp = Employees."findAllBy${clazz.name}"(listObjects?.find{it instanceof $clazz})
if(resp) return
}
if(resp) // do something...
Original:
List results = [Branch,Department,Organization].collect{clazz->
Employees."findAllBy${clazz.name}"(listObjects?.find{it instanceof $clazz})
}
Enjoy Groovy ;--)
I think #virtualeyes nearly had it, but instead of a collect (which as he says you can't break out of), you want to use a find, as that stops running the first valid result it gets:
List results = [Branch,Department,Organization].find { clazz->
Employees."findAllBy${clazz.name}"(listObjects?.find{it instanceof clazz})
}

Resources