Given the following domain structure:
Book {
static hasMany = [tags: Tag]
}
Tag {
String name
}
I'm trying to find a way that given a Book I can find any other books containing any of this book's tags.
I've tried:
Book.findAllByTagsInList(myBook.tags)
But as expected the 'List in List' query isn't producing the required results.
You can use Criteria as
def books = Book.createCriteria().listDistinct{
tags{
'in'('id', myBook.tags*.id)
}
}
or use HQL as
def books = Book.executeQuery("select distinct b from Book as b \
inner join b.tags as tag \
where tag.id in (:tags)",
[tags: myBook.tags*.id])
Related
I have these tables
object BooksAuthors : Table(name = "books_authors") {
val book = reference("book_id", Books, onDelete = ReferenceOption.CASCADE)
val author = reference("author_id", Authors, onDelete = ReferenceOption.CASCADE)
}
object Books : IntIdTable() {
val title = varchar("title", 250)
val isbn = varchar("isbn", 13)
}
object Authors : IntIdTable() {
val email = varchar("email", 100).uniqueIndex()
}
i would like to write a query that returns all books that dont have a specific author so i wrote this
suspend fun getBooksWithoutAuthorId(authorId: Int): List<BookDTO> = DbFactory.dbQuery {
val query = BooksAuthors.innerJoin(Books).select { BooksAuthors.author neq authorId }
Book.wrapRows(query).map { it.toDTO() }
}
But the query returns books that have the author. What am i doing wrong?
As Sebastian Redl mentioned, there could be books with multiple authors and your query doesn't cover that case.
Correct Exposed query should be:
val query = Books.select {
Books.id notInSubQuery
BooksAuthors.slice(BooksAuthors.book).select { BooksAuthors.author eq authorId }
}
It looks like you have an n:m mapping where a book can have multiple authors.
Your query, as written, finds any book that an author other than your selected one has authored.
This means if authors Alice and Bob wrote a book together, and you want to find books "not by Bob", you would still find the book because Alice took part.
Your desired query cannot be expressed as a simple join; you need nested queries instead.
Something like equivalent to this SQL:
SELECT * from books b WHERE ? NOT IN (
SELECT ab.author_id FROM authors_books ab WHERE ab.book_id = b.id);
Though I'm afraid I don't know how to express this in Exposed.
https://www.db-fiddle.com/f/7BsVUW95g6L4rXDBCoaXK3/0
I have the following 1 - M (one way) relationship:
Customer (1) -> (M) Address
I am trying to filter the addresses for a specific customer that contain certain text e.g.
def results = Customer.withCriteria {
eq "id", 995L
addresses {
ilike 'description', '%text%'
}
}
The problem is that this returns the Customer and when I in turn access the "addresses" it gives me the full list of addresses rather than the filtered list of addresses.
It's not possible for me to use Address.withCriteria as I can't access the association table from the criteria query.
I'm hoping to avoid reverting to a raw SQL query as this would mean not being able to use a lot functionality that's in place to build up criteria queries in a flexible and reusable manner.
Would love to hear any thoughts ...
I believe the reason for the different behavior in 2.1 is documented here
Specifically this point:
The previous default of LEFT JOIN for criteria queries across associations is now INNER JOIN.
IIRC, Hibernate doesn't eagerly load associations when you use an inner join.
Looks like you can use createAlias to specify an outer join example here:
My experience with this particular issue is from experience with NHibernate, so I can't really shed more light on getting it working correctly than that. I'll happily delete this answer if it turns out to be incorrect.
Try this:
def results = Customer.createCriteria().listDistinct() {
eq('id', 995L)
addresses {
ilike('description', '%Z%')
}
}
This gives you the Customer object that has the correct id and any matching addresses, and only those addresses than match.
You could also use this query (slightly modified) to get all customers that have a matching address:
def results = Customer.createCriteria().listDistinct() {
addresses {
ilike('description', '%Z%')
}
}
results.each {c->
println "Customer " + c.name
c.addresses.each {address->
println "Address " + address.description
}
}
EDIT
Here are the domain classes and the way I added the addresses:
class Customer {
String name
static hasMany = [addresses: PostalAddress]
static constraints = {
}
}
class PostalAddress {
String description
static belongsTo = [customer: Customer]
static constraints = {
}
}
//added via Bootstrap for testing
def init = { servletContext ->
def custA = new Customer(name: 'A').save(failOnError: true)
def custB = new Customer(name: 'B').save(failOnError: true)
def custC = new Customer(name: 'C').save(failOnError: true)
def add1 = new PostalAddress(description: 'Z1', customer: custA).save(failOnError: true)
def add2 = new PostalAddress(description: 'Z2', customer: custA).save(failOnError: true)
def add3 = new PostalAddress(description: 'Z3', customer: custA).save(failOnError: true)
def add4 = new PostalAddress(description: 'W4', customer: custA).save(failOnError: true)
def add5 = new PostalAddress(description: 'W5', customer: custA).save(failOnError: true)
def add6 = new PostalAddress(description: 'W6', customer: custA).save(failOnError: true)
}
When I run this I get the following output:
Customer A
Address Z3
Address Z1
Address Z2
I know there are several questions regarding this topic. However; I cannot find one that is directly related to my problem.
I have 3tables in a DB and the PK's from those 3 tables form a composite PK in a XRef table.
I need to be able to select Distinct items based on 2 of the keys just for display on a report.
public IEnumerable<AssemblyPrograms> GetProgramAssemblies()
{
var assembliesList = (from c in eModel.Assemblies.ToList()
join d in eModel.Programs_X_Assemblies_X_Builds
on c.AssemblyID equals d.AssemblyID
join p in eModel.Programs
on d.ProgramID equals p.ProgramID
join a in eModel.AssemblyTypes
on c.AssemblyTypeID equals a.AssemblyTypeID
select new AssemblyPrograms
{
AssemblyID = c.AssemblyID
,ProgramID = d.ProgramID
,AssemblyName = c.AssemblyName
,AssemblyPrefixName = c.AssemblyPrefixName
,ProgramName = p.ProgramName
,AssemblyTypeName = a.AssemblyTypeName
,AssemblyTypeID = a.AssemblyTypeID
});
return assembliesList;
}
This is my query and what I need to pull out of the tables
In my XRef table I have AssemblyID, ProgramID and BuildID as my composite PK.
There can be a many-many relationship from AssemblyID to ProgramID. The BuildID is the key that separates them.
I need to pull Distinct AssemblyID to ProgramID relationships for my report, the BuildID can be ignored.
I have tried .Distinct() in my query and a few other things to no avail.
I would appreciate any help anyone could give me.
Thanks
How about a Distinct overload that accepts a custom equality comparer? Something like this:
class AssemblyComparer : EqualityComparer<AssemblyPrograms> {
public override bool Equals(AssemblyPrograms x, AssemblyPrograms y) {
return x.ProgramID == y.ProgramID && x.AssemblyID == y.AssemblyID;
}
public override int GetHashCode(AssemblyPrograms obj) {
return obj.ProgramID.GetHashCode() ^ obj.AssemblyID.GetHashCode();
}
}
How do I handle this SQL query in grails in my ProductsController script? Notice its two tables with a join on the product id.
SELECT p.*,pd.*
FROM products p, products_description pd
WHERE p.products_id=pd.products_id
ORDER BY p.products_date_added DESC
Obviously I can't do this:
def all= {
def a
a = Products.find("FROM products p, products_description pd
WHERE p.products_id=pd.products_id ORDER BY p.products_date_added DESC")
render a as JSON
}
If you are adamant on using a custom sql query instead of any grails dynamic finder, you can use the following code:
def session = sessionFactory.getCurrentSession() // a reference to the sessionFactory is injected in all controllers and services that have a sessionFactory variable defined
Query query = session.createSQLQuery("SELECT p.*,pd.*
FROM products p, products_description pd
WHERE p.products_id=pd.products_id
ORDER BY p.products_date_added DESC");
def result = query.list()
You will have to add a variable named sessionFactory to your controller. Something like this:
class ProductsController = {
def sessionFactory
The result list would be a list of lists. Each element of the main list would be a list of size 2 with the first element as the product and the second as the product description.
You can use a Many-to-one and one-to-one relation and let GORM take care of the association for you.
class Products {
ProductsDescription description
...
static mapping = {
description column: 'products_id'
}
}
You can then access the description by reference:
a = products.listOrderByProductsDateAdded(order: 'desc')
a.description.productsDescription
Example grouping by name of the zones:
def result = User.createCriteria().list{
projections {
roles {
zones{
groupProperty("name")
}
}
}
}
but suppose I want to get the "id" or other attributes. the fact is that i want the object on the representing the group and not the string "name".
result.each{ println it.customFuncion() }
"zones" is a hasMany attribute and then i cant group by itself. What should be done, but doesnt works:
def result = User.createCriteria().list{
projections {
roles {
groupProperty("zones")
}
}
}
Is that possible? Thank you guys!
use hql for complex queries:
def result = User.executeQuery("select u.id, zone.name from User as u inner join u.roles as role inner join role.zones as zone group by u.id, zone.name")
Then you can access the result columns as follows:
result.each { row -> row[0] // u.id } // or access via defined column name
Imagine that i do not know your real hasMany collection names.