Grails query rows to arrays - sql

I'm new to Groovy and Grails. I think this problem probably has an easy answer, I just don't know it.
I have a database table:
id | category | caption | image | link
I have a query that lets me retrieve one row for each distinct item in 'category.'
I'm trying to return a map where each row is an array named by it's category.
e.g., If I select the rows:
[{category='foo', caption='stuff', ...} {category='bar', caption='things', ...}]
I want to be able to:
return [foo:foo, bar:bar]
where:
foo = [caption='stuff', ...]
bar = [caption='things', ...]
Thanks for any help.

You can transform your list using collect in Groovy, however, the result depends on the source data. I could not infer from your post that if you are returning one item per category or multiple.
def ls = Category.list()
def newList = ls.collect {
[(it.category):['caption':it.caption,'image':it.image,'link':it.link]]
}
will result in something like :
[
[bar:[caption:BarCaption-2, image:BarImage-2, link:BarLink-2]],
[bar:[caption:BarCaption-1, image:BarImage-1, link:BarLink-1]],
[bar:[caption:BarCaption-0, image:BarImage-0, link:BarLink-0]],
[foo:[caption:FooCaption-2, image:FooImage-2, link:FooLink-2]],
[foo:[caption:FooCaption-1, image:FooImage-1, link:FooLink-1]],
[foo:[caption:FooCaption-0, image:FooImage-0, link:FooLink-0]]
]
If you have multiple items per each category you probably want to return the list of each.
def bars = newList.findAll { it.containsKey 'bar' }
def foos = newList.findAll { it.containsKey 'foo' }
[foos:foos,bars:bars]

If I understand your question correctly (I think you mean to put ':' instead of '=' for your maps) then I think the following will work. If new to Groovy, you might find the Groovy web console at http://groovyconsole.appspot.com/ useful. You can try snippets of code out easily, like the following:
def listOfMaps = [[category:'foo', caption:'stuff', something:'else'], [category:'bar', caption:'things', another:'thing']]
def mapOfMaps = [:]
listOfMaps.each { mapOfMaps += [(it.remove('category')) : it] }
assert mapOfMaps == [foo:[caption:'stuff', something:'else'], bar:[caption:'things', another:'thing']]

Related

Groovy: comparing items from 2 different lists having different format

I've a list
def a = [kitkat lollypop orea android-11]
another list
def b = ["kitkat", "lollypop", "honeycomb", "nougat", "android-11"]
I need to do some task on list a items if they exist in list b if they don't exist in list b then do something else.
One thing to note is list a items are separated by space and no quotes whereas list be is by comma and has items within quotes; due to this things I've tried below did not work.
I've tried the following:
1)
a.split(" ").each{
def itema = it
b.split(",").each{
if(b.contains(itema){
//do something
}
}
//do something else
)
a.split(" ").each{
if(b.split(",").contains(it){
//do something
}
//do something else
}
for(itema in a){
for(itemb in b){
if(itema == itemb){
//do something
}
}
//do something else
}
a.intersect(b)
but this threw exception due to list a having spaces between elements
I think what I want is create a new list from list a by changing it to have comma separated items with quotes and then could use one of the above solution
or is there a better way to do in groovy what I wanted?
You're almost there in examples 1 and 2, but there are a few issues.
I'm going to assume this:
def a = [kitkat lollypop orea android-11]
is actually a String, based on how you're attempting to split it. In my solution I'm going to assume it's this:
def a = "[kitkat lollypop orea android-11]"
Using the String in 'a', take everything between the two brackets. From there, split the String by the space character in order to get a list of items. Then, using this new items list, we're going to go one by one and iterate through list b to see if we have any matches:
def items = a.takeBetween("[", "]")
items.split(/\s/).each {
if (it in b) {
println "I'm in list b - $it"
} else {
println "I'm not in list b - $it"
}
}
This will result in:
I'm in list b - kitkat
I'm in list b - lollypop
I'm not in list b - orea
I'm in list b - android-11

Is there a way to match dynamic object keys?

I'm looking for a simple technique to match objects where the key may not be known in advance (e.g. we may fetch the schema as part of the test). As a contrived example:
Scenario:
* def result = { foo: 'bar' }
* def key = 'foo'
Then match result == { '#(key)': 'bar' }
...which doesn't currently work.
Once you realize there is a JavaScript engine behind the scenes, you will get even more ideas :)
* def result = { foo: 'bar' }
* def key = 'foo'
* def expected = {}
* expected[key] = 'bar'
Then match result == expected
Also do a search for other answers [karate] dynamic you will find many interesting examples such as this one: https://stackoverflow.com/a/57226061/143475

How do I loop thought each DB field to see if range is correct

I have this response in soapUI:
<pointsCriteria>
<calculatorLabel>Have you registered for inContact, signed up for marketing news from FNB/RMB Private Bank, updated your contact details and chosen to receive your statements</calculatorLabel>
<description>Be registered for inContact, allow us to communicate with you (i.e. update your marketing consent to 'Yes'), receive your statements via email and keep your contact information up to date</description>
<grades>
<points>0</points>
<value>No</value>
</grades>
<grades>
<points>1000</points>
<value>Yes</value>
</grades>
<label>Marketing consent given and Online Contact details updated in last 12 months</label>
<name>c21_mrktng_cnsnt_cntct_cmb_point</name>
</pointsCriteria>
There are many many many pointsCriteria and I use the below xquery to give me the DB value and Range of what that field is meant to be:
<return>
{
for $x in //pointsCriteria
return <DBRange>
<db>{data($x/name/text())}</db>
<points>{data($x//points/text())}</points>
</DBRange>
}
</return>
And i get the below response
<return><DBRange><db>c21_mrktng_cnsnt_cntct_cmb_point</db><points>0 1000</points></DBRange>
That last bit sits in a property transfer. I need SQL to bring back all rows where that DB field is not in that points range (field can only be 0 or 1000 in this case), my problem is I dont know how to loop through each DBRange/DBrange in this manner? please help
I'm not sure that I really understand your question, however I think that you want to make queries in your DB using specific table with a column name defined in your <db> field of your xml, and using as values the values defined in <points> field of the same xml.
So you can try using a groovy TestStep, first parse your Xml and get back your column name, and your points. To iterate over points if the values are separated with a blank space you can make a split(" ") to get a list and then use each() to iterate over the points on this list. Then using groovy.sql.Sql you can perform the queries in your DB.
Only one more thing, you need to put the JDBC drivers for your vendor DB in $SOAPUI_HOME/bin/ext and then restart SOAPUI in order that it can load the necessary driver classes.
So the follow code approach can achieve your goal:
import groovy.sql.Sql
import groovy.util.XmlSlurper
// soapui groovy testStep requires that first register your
// db vendor drivers, as example I use oracle drivers...
com.eviware.soapui.support.GroovyUtils.registerJdbcDriver( "oracle.jdbc.driver.OracleDriver")
// connection properties db (example for oracle data base)
def db = [
url : 'jdbc:oracle:thin:#db_host:d_bport/db_name',
username : 'yourUser',
password : '********',
driver : 'oracle.jdbc.driver.OracleDriver'
]
// create the db instance
def sql = Sql.newInstance("${db.url}", "${db.username}", "${db.password}","${db.driver}")
def result = '''<return>
<DBRange>
<db>c21_mrktng_cnsnt_cntct_cmb_point</db>
<points>0 1000</points>
</DBRange>
</return>'''
def resXml = new XmlSlurper().parseText(result)
// get the field
def field = resXml.DBRange.db.text()
// get the points
def points = resXml.DBRange.points.text()
// points are separated by blank space,
// so split to get an array with the points
def pointList = points.split(" ")
// for each point make your query
pointList.each {
def sqlResult = sql.rows "select * from your_table where ${field} = ?",[it]
log.info sqlResult
}
sql.close();
Hope this helps,
Thanks again for your help #albciff, I had to add this into a multidimensional array (I renamed field to column and result is a large return from the Xquery above)
def resXml = new XmlSlurper().parseText(result)
//get the columns and points ranges
def Column = resXml.DBRange.db*.text()
def Points = resXml.DBRange.points*.text()
//sorting it all out into a multidimensional array (index per index)
count = 0
bigList = Column.collect
{
[it, Points[count++]]
}
//iterating through the array
bigList.each
{//creating two smaller lists and making it readable for sql part later
def column = it[0]
def points = it[1]
//further splitting the points to test each
pointList = points.split(" ")
pointList.each
{//test each points range per column
def sqlResult = sql.rows "select * from my_table where ${column} <> ",[it]
log.info sqlResult
}
}
sql.close();
return;

Passing a list to SQL each row call Groovy

I am currently rendering a list of sql rows from a database using:
Sql sql = new Sql(dataSource)
def list = []
def index = 0
params.mrnaIds.each { mrnaName ->
sql.eachRow ("select value from patient_mrna where mrna_id=$mrnaId") { row ->
list[index] = row.value
index++
}
}
render list
However I would like to avoid assigning the values to a list before rendering them.
The variable params.mrnaIds is coming from a multi select input, so it could either be a single string or a string array containing ids. Is there a way to iterate through these ids inside the eachRow method?
I would like to be able to execute something like:
render sql.eachRow ("select value from patient_mrna where mrna_id=?", params.mrnaIds) { row ->
list[index] = row.value
index++
}
But I'm not completely sure that there is a way to call eachRow with this functionality. If there is not, is there some other way to render the results without storing them in a list?
I think you can render each row:
sql.eachRow( someQuery, someParams ){ row ->
render row as JSON
}
There is rows() to return a list instead ok working with it (like eachRow() is used for). It also shares all the different arguments. E.g.:
render sql.rows("select value from patient_mrna where mrna_id=?", params).collect{ it.value }

SQL to Magento model understanding

Understanding Magento Models by reference of SQL:
select * from user_devices where user_id = 1
select * from user_devices where device_id = 3
How could I perform the same using my magento models? getModel("module/userdevice")
Also, how can I find the number of rows for each query
Following questions have been answered in this thread.
How to perform a where clause ?
How to retrieve the size of the result set ?
How to retrieve the first item in the result set ?
How to paginate the result set ? (limit)
How to name the model ?
You are referring to Collections
Some references for you:
http://www.magentocommerce.com/knowledge-base/entry/magento-for-dev-part-5-magento-models-and-orm-basics
http://alanstorm.com/magento_collections
http://www.magentocommerce.com/wiki/1_-_installation_and_configuration/using_collections_in_magento
lib/varien/data/collection/db.php and lib/varien/data/collection.php
So, assuming your module is set up correctly, you would use a collection to retrieve multiple objects of your model type.
Syntax for this is:
$yourCollection = Mage::getModel('module/userdevice')->getCollection()
Magento has provided some great features for developers to use with collections. So your example above is very simple to achieve:
$yourCollection = Mage::getModel('module/userdevice')->getCollection()
->addFieldToFilter('user_id', 1)
->addFieldToFilter('device_id', 3);
You can get the number of objects returned:
$yourCollection->count() or simply count($yourCollection)
EDIT
To answer the question posed in the comment: "what If I do not require a collection but rather just a particular object"
This depends if you still require both conditions in the original question to be satisfied or if you know the id of the object you wish to load.
If you know the id of the object then simply:
Mage::getModel('module/userdevice')->load($objectId);
but if you wish to still load based on the two attributes:
user_id = 1
device_id = 3
then you would still use a collection but simply return the first object (assuming that only one object could only ever satisfy both conditions).
For reuse, wrap this logic in a method and place in your model:
public function loadByUserDevice($userId, $deviceId)
{
$collection = $this->getResourceCollection()
->addFieldToFilter('user_id', $userId)
->addFieldToFilter('device_id', $deviceId)
->setCurPage(1)
->setPageSize(1)
;
foreach ($collection as $obj) {
return $obj;
}
return false;
}
You would call this as follows:
$userId = 1;
$deviceId = 3;
Mage::getModel('module/userdevice')->loadByUserDevice($userId, $deviceId);
NOTE:
You could shorten the loadByUserDevice to the following, though you would not get the benefit of the false return value should no object be found:
public function loadByUserDevice($userId, $deviceId)
{
$collection = $this->getResourceCollection()
->addFieldToFilter('user_id', $userId)
->addFieldToFilter('device_id', $deviceId)
;
return $collection->getFirstItem();
}