Lets say we have 3 tables:
products (title, product_type_id)
product_types (title)
product_type_options (product_type_id, size)
I want to load product, its type & ONLY specific product_type_options (product_type_options.size = 'XL')
Is it possible to do without N+1 query in rails?
Something like:
Product.includes( product_type: [product_type_options] )
.where("product_type_options.size = 'XL'")
I can not it get it to work with where parameter. Any idea?
This should work
Product.includes(product_type: :product_type_options)
.where(product_types: { product_type_options: { size: 'XL' } })
#Iceman was close... the reason it's not seeing 'product_types' is because it needs to be represented as an explicit hash inside the where clause, then Ruby will do the proper nesting...
Product.includes(product_type: :product_type_options)
.where({ product_types: { product_type_options: { size: 'XL' } } })
Strange, but adding the extra {} in the where clause makes the nested includes work.
Please try this, this is a small syntax change. I have not tested it. Let me know if it works :)
Product.includes( product_type: [product_type_options] )
.where("product_type_options.size" => 'XL')
Related
Since I'm unable to use SQLite in a node-webkit app, I'm looking at LokiJS but I cannot seem to find an easy way of making joins or some kind of relations between 2 datasets.
One dataset is artists and the other is media.
Since it could possibly hold 5000 artists and a million medias, I think it would be better to avoid repetition by making the artist a subdocument of a media. Also if I modify the artist of 1000 media entries, it would require a lot of writing instead of just updating one single entry in the artist dataset.
The only join I found create the returned json as followed:
Object {
left: {
// artists
},
right: {
// media
}
}
What I would prefer is something more traditionnal like:
{
artist_id:
artist_name:
media_id:
media_title:
}
I looked at NeDB which offer joins more easily but wasn't updated for over 2 years now. and I cannot get SQLite3 to compile for nodewebkit 0.28.0 and got no answers from SO. Anyway, LokiJS looks way more fun to play with anyway since it's in memory and on disk.
So how can I join artists and media?
Thanks!
joinResult.map(data => {
let a = {}
a.artist_id = data.left.artist_id
a.artist_name = data.left.artist_name
a.media_id = data.right.media_id
a.media_title = data.right.media_title
return a
})
Use lodash to do final data manipulation.
I don't think left, right object should be a problem here. The code I am using is
const q = db.card.eqJoin(db.deck, "deckId", "$loki", (l, r) => {
const {front, back, note, srsLevel, nextReview, tags} = l;
return {
front, back, note, srsLevel, nextReview, tags,
deck: r.name
};
}).find(query);
const count = q.copy().count();
const data = q.offset(offset).limit(limit).data();
I have a Domain class
class Hashtag {
String tag
}
Why
Hashtag.where { lower(tag) == "#london" }.list()
works ok, but
Hashtag.where { lower(tag) in [ "#london", "#paris" ] }.list()
results in
org.springframework.dao.InvalidDataAccessResourceUsageException: Unsupported function [lower] defined in query for property [hashtag] with type [class java.lang.String]
How to write such query properly?
Thanks!
I can't answer why using lower() with in() did not work. I read the source but I don't know ASTs well enough to understand it.
But, to solve your problem you can use a derived property to perform the lower().
class Hashtag {
String tag
String loweredTag
static mapping {
loweredTag formula: 'lower(tag)'
}
}
Then you can use the derived property in the where query:
Hashtag.where { loweredTag in [ "#london", "#paris" ] }.list()
Answering my own question, I've found alternative way to do case-insensitive comparison - using Criteria. An advantage of this method is that values in cities also could be mixed cases.
def cities = ["#LONdon", "#PAris"]
Hashtag.createCriteria().list {
or {
cities.each { eq("tag", it, [ignoreCase: true])
}
}
I'm not sure if it will work as per your need.
But documentation says that you may use subqueries in where block like below:
Hashtag.where { lower(tag).of{} in [ "#london", "#paris" ] }.list()
Please try it and let me know if it doesn't work.
Hope it helps!
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() }
}
New to groovy, grails.
I have the following query where I want to match a few paramater exactly to as passed(eq), but for one, which I want to using a 'like'
if (params.ret_code) {
ret_cod = params.ret_code+"%"
}
def srchresults = DmnObj.where {
if (params.doc_num) { doc_num == params.doc_num.trim() } //works as expected
//How do I do this????
if (params.retn_code) { retn_code like ret_cod }
}
Tried this, but in-vain.
how do I set retn_code with a like?
Thank you!
This is how you can do this
// case sensitive like
def result = Domain.where {
fieldName ==~ "value"
}
// case insensitive like
def result = Domain.where {
fieldName =~ "value"
}
Remember to prefix, suffix or both the value with %. For more about where queries https://grails.github.io/grails-doc/latest/guide/GORM.html#whereQueries
You can do like this:
var feel []DB.Feelings
db.Where("column_name LIKE ?", "%"+yourText+"%").Limit(your_limit).Offset(your_offset).Find(&feel)
column_name is your column name
Limit and Offset are not required
You can do like this:
def result = DomainClass.where{
like('fieldName', '%'+myVariable+'%')
}
I suggest you have a look at the Grails documentation (section Domain Class Usage): you will find several interesting ways to filter your domain class objects as for example HQL queries or findAllBy* dynamic methods.
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