How can I pass variable holding a value instead of passing a value directly to groovy search criteria?
for (def payee in payees)
{
def results = resp.cases.find("eq 'hrid','7547') // hard code values work
def results = resp.cases.find("eq 'hrid',??????) // how can I pass payee
}
I'm new to this. Please help. thanks
Basing on examples, check it:
def results = resp.cases.find { case -> case.hrid == payee }
GORM criteria queries are quite different than your example. Let's say you have a Payee domain class with a property named hrid:
grails-app/domain/com/something/Payee.groovy
class Payee {
String hrid
}
To get a list of all the Payee instances with an hrid of 7547, you'd run a criteria query like this:
String hridValue = '7547'
List payees = Payee.withCriteria {
eq 'hrid', hridValue
}
I explain Grails GORM queries in depth in a series of articles starting right here.
Related
I am having issues trying to query an Eloquent relationship.
I have 2 tables
tbl_deals
id
deal_id
merchant_id
tbl_merchants
id
merchant_id
merchant_url_text
I defined a deal model as
class deal extends Model
{
public function merchant() {
return $this->hasOne('App\merchant', 'merchant_id', 'merchant_id');
}
}
Now, I want to query all deals based where merchant_url_text = a variable in my controller
Here's what I am trying
$deals = deal::with('merchant')->get(); //Retrieving all the deals with merchants
If I return $deals its giving me all deals with merchant relationship.
How do I constraint the deals by saying where merchant_url_text = $variable
I am trying
return $deals->where('merchant_url_text', $merchant_url_text)->get();
but it is giving me an error saying :
"Missing argument 1 for Illuminate\Support\Collection::get(), called in ..."
I tried to lookup the documentation for querying relationships in Laravel Docs. It talks about this example
$user = App\User::find(1);
$user->posts()->where('active', 1)->get();
In this case, its trying to get the first user and finding corresponding posts related to the user.
In my case I want to filter from all deals, the deals that have merchant_url_text = a specific variable in my controller.
Any help on how I can achieve this?
Thanks
Try the following code :
$specific_merchant_url_text = "i don't know";
$deals_with_specific_merchant_url_text = [];
$deals = deal::with('merchant')->get();
foreach($deals as $deal)
if( $deal->merchant->merchant_url_text==$specific_merchant_url_text)
array_push($deals_with_specific_merchant_url_text, $deal);
So you get array of deals with specific merchant url text in deals_with_specific_merchant_url_text.
Another approach using DB object :
$deals = DB::table('deals')
->join('merchants', 'deals.merchant_id', '=', 'merchants.id')
->select('deals.*')
->where('merchants.merchant_url_text', $merchant_url_text)
->get();
Yours with raw :
$deals = deal::selectRaw('tbl_deals.*')
->Join('merchants','deals.merchant_id','=','merchants.merchant_id')
->where('merchants.merchant_url_text', '=', $merchant_url_text) ->get();
Hope this helps.
With takes a callback. This will bring back all deals but only eager load merchants that match.
$deals = Deal::with(['merchant' => function ($query) use ($url_text){
return $query->where('merchant_url_text', $url_text);
}])->get();
Flip-side: if you only want deals with a matching merchant, use where has.
$deals = Deal::whereHas('merchant', function ($query) use ($url_text){
return $query->where('merchant_url_text', $url_text);
})->get();
If you want both, chain them:
$deals = Deal::whereHas('merchant', function ($query) use ($url_text){
return $query->where('merchant_url_text', $url_text);
})->with(['merchant' => function ($query) use ($url_text){
return $query->where('merchant_url_text', $url_text);
}])->get();
I capitalized deal because it's standard. It wasn't capitalized in your example.
You can use query builder join query .
DB::table('tbl_merchants')
->join('tbl_deals', 'tbl_merchants.merchant_id', '=','tbl_deals.merchant_id')
->where('tbl_merchants.merchant_url_text',$merchant_url_text)
->get();
I have a domain class
class Url {
UUID id
String url
static hasMany = [
indications:UrlIndication
]
...
}
And
class UrlIndication {
UUID id
String name
static belongsTo = Url
...
}
I want to choose urls so that it has all the necessary UrlIndication elements in a given list indicationsId.
For that I use an association and criteria like this one:
indications {
and {
indicationsId.each{
indication->
eq ('id',UUID.fromString(indication as String))
}
}
}
However, all I got is an empty result. Can you suggest any modifications/ other methods so that I can do this? Thanks in advance
Your query returned an empty list because it's the equivalent of the expression (pseudo-code): if 1 = 1 and 1 = 2 and 1 = 3
Such an expression would always be false. in or inList would not work for the reason #innovatism described.
In theory, Criteria's eqAll() or HQL's = ALL would work. But, I don't know for sure because I could not get either one to work.
What will work is to use inList to return a subset of Urls: those which contain at least one of the UrlIndication IDs. Then use Groovy's containsAll() to finish the job.
def ids = indicationsId.collect { UUID.fromString(it as String) }
Url.createCriteria()
.buildCriteria {
indications {
inList 'id', ids
}
}
.setResultTransformer(org.hibernate.Criteria.DISTINCT_ROOT_ENTITY)
.list()
.findAll {
it.indications.id.containsAll(ids)
}
Since the query has the potential to return duplicate Url instances, the ResultTransformer is set to return a unique list.
Finally, findAll() is used along with containsAll() to filter the list further.
Using eqAll (maybe)
Something like the following might work. Something funky is going on with Grails' HibernateCriteriaBuilder that causes the eqAll method to look up properties in the root entity; completely ignoring the sub criteria. So the following uses Hibernate directly. It didn't work for me, but it's as close as I could get. And it gave me a head-ache!
Url.createCriteria().buildCriteria {}
.createCriteria('indications', 'i')
.add(org.hibernate.criterion.Property.forName('i.id').eqAll(org.hibernate.criterion.DetachedCriteria.forClass(UrlIndication)
.add(org.hibernate.criterion.Restrictions.in('id', ids))
.setProjection(org.hibernate.criterion.Property.forName('id'))
))
.setResultTransformer(org.hibernate.Criteria.DISTINCT_ROOT_ENTITY)
.list()
The problem I had is I could not get Restrictions.in to work. Restrictions.eq works fine.
the in clause should do:
indications {
'in' 'id', indicationsId.collect{ UUID.fromString indication.toString() }
}
What is their difference and why and where we need to use them,i think it seems like they have no difference at all to me ?
withCriteria { ... } is essentially shorthand for createCriteria().list { ... }. If you need to use any of the other criteria methods (get, count, ...) or pass pagination parameters to list then you have to use the long-hand form.
SomeDomain.createCriteria().list(max:10, offset:50) {
// ...
}
It's worth adding what I just came across in the grails documentation for createCriteria().
Because that query includes pagination parameters (max and offset), this will return a PagedResultList which has a getTotalCount() method to return the total number of matching records for pagination. Two queries are still run, but they are run for you and the results and total count are combined in the PagedResultList.
Source
This means you can use getTotalCount() without having to initiate the call (it's made for you). This is very helpful. The example documentation shows:
def c = Account.createCriteria()
def results = c.list (max: 10, offset: 10) {
like("holderFirstName", "Fred%")
and {
between("balance", 500, 1000)
eq("branch", "London")
}
order("holderLastName", "desc")
}
println "Rendering ${results.size()} Accounts of ${results.totalCount}"
This capability is not available when using withCriteria().
Example of createCriteria():
def criteria = OfferCredit.createCriteria {
offer {
eq('status', LeverageUtils.ACTIVE_STATUS)
ge('expirationDate', new Date())
}
user {
eq('userId', userId)
}
eq('status', LeverageUtils.ACTIVE_STATUS)
order('creationDate', 'asc')
}
criteria.list()
Example of withCriteria():
List<Supermarket> results = Supermarket.withCriteria {
like("sp_street", params.street)
productSupermarket {
product {
idEq(params.product)
}
// or just eq('product', someProduct)
}
maxResults(10)
}
withCriteria executes and returns the list. It provides a closure using which you can customize the criteria before it gets executed.
createCriteria just creates a criteria object which you can modify and then explicitly call the list method to execute.
If criteria is simple or if it is defined in a single place it is better to use withCriteria.
If you need to pass the criteria around (create it in one function and pass it to others) createCriteria would be better. I think withCriteria support is limited.
withCriteria ->
Purpose -> Allows inline execution of Criteria queries.
If no matching records are found, an empty List is returned.
If a projection is specified:
returns a single value if it only contains one field
a List in case there are multiple fields in the projection
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
When using projection on the properties, the result is returned as the list with the elements in the same sequence as that defined in the projections block. At the same time the property names are missing from the list and that is really disadvantageous to the developer as the result would be passed along and the caller needs to know what value belongs to which property. Is there a way to return a map from the Criteria query with property name as the key to the value?
so, the following code:
def c = Trade.createCriteria()
def remicTrades = c.list {
projections {
property('title', 'title')
property('author.name', 'author')
}
def now = new Date()
between('publishedDate', now-365, now)
}
This returns:
[['book1', 'author1']['book2', 'author2']]
Instead I would like it to return:
[[book:'book1', author:'author1'][book:'book2', author:'author2']]
I know I can arrange this way after getting the result but I earnestly feel that the property alias should have been used by the criteria to return a list of map that mimics the result of the SQL query and not a bland list.
Duplicate: Grails queries with criteria: how to get back a map with column?
And the corresponding answer (and solution): https://stackoverflow.com/a/16409512/1263227
Use resultTransformer.
import org.hibernate.criterion.CriteriaSpecification
Trade.withCriteria {
resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
projections {
property('title', 'title')
property('author.name', 'author')
}
def now = new Date()
between('publishedDate', now-365, now)
}
Agree with your question reasoning, this really should be part of the core GORM solution. That said, here's my workaround;
def props = ['name','phone']
def query = Person.where {}.projections {
props.each{
property(it)
}
}
def people = query.list().collect{ row->
def cols = [:]
row.eachWithIndex{colVal, ind->
cols[props[ind]] = colVal
}
cols
}
println people // shows [['name':'John','phone':'5551212'],['name':'Magdalena','phone':'5552423']]