Prevent criteria filter inside conjunctive statement - grails

An example of what I'm trying to do is:
def authorName = "John Smith"
def books = Book.createCriteria().list() {
eq('genre', 'fiction')
eq('publishDate', '2007')
if(authorName != null){
Author author = Author.findWhere(name: authorName)
if( author == null ) //what do I do here?
else { eq('authorId', author.id }
}
}
If there is no author for the given id, then the author doesn't exist (assuming it wasn't removed) and thus there are no books written by the author. The evaluation should stop there and not return any results. What can I use to accomplish this?

I am not really 100% what you are trying to do. If you only want to execute Book query if the author exists, you could so something like this...
def authorName = "John Smith"
Author author = Author.findWhere(name: authorName)
def books
if(author) {
books = Book.withCriteria {
eq('genre', 'fiction')
eq('publishDate', '2007')
// I can't tell if this is the right thing because
// I don't know what your model looks like, but I will
// assume this part is valid because it is what you had
// in your example.
eq 'authorId', author.id
}
}
Depending on what your model looks like, you could also just make the authorName part of the criteria so now you don't have to execute 2 queries...
def authorName = "John Smith"
def books = Book.withCriteria {
eq('genre', 'fiction')
eq('publishDate', '2007')
// this assumes that Book has a property named
// "author" which points to the Author
author {
eq 'name', authorName
}
}

Related

GORM findAll doesn't work

A have class Product:
class Product {
static hasMany = [attributeValues: ProductAttributeValue]
String productId
String manufacturer
BigDecimal price
static constraints = {
productId unique: true, blank: false
manufacturer blank: false
price min: BigDecimal.ZERO
}
}
I want to find all products, which productId contains substring 'filter'.
I wrote next code:
Product.findAll {it.productId.contains(filter)}
But it doesn't work. Why?
this should't work at all!
you have 2 options here:
1) you use propper GORM techniques like criteria query or HQL, the would look like:
Product.findAllByProductIdIlike( "%${filter}%" ) // dyn-finders
Product.withCriteria{ ilike 'productId', "%${filter}%" } // criteria
Product.findAll{ ilike 'productId', "%${filter}%" } // criteria
Product.findAll( "from Product where productId like '%?%'", [ filter ] ) // hql`
2) or use filtering on the whole dataset in grails app's memory (instead of the db) - NOT recommended:
Product.list().findAll{ it.productId.contains(filter) }
You can use regex,
Try this :
def yourProductIsWithFilter = '123filter456'
def matchingPattern = 'filter'
//def patternToMatch = /\b${matchingPattern}/\b
//def patternToMatch = /[A-Z_0-9${matchingPattern}/]
def patternToMatch = ~/.${matchingPattern}/
Product.findAll{it.productId =~ patternToMatch }
Note: I haven't tested the code.
Hope it gives you a heads up.
Regards

How can I get a set of field values in groovy?

class Book {
String title
}
def book = new Book(title: 'title1')
def book = new Book(title: 'title2')
def book = new Book(title: 'title3')
How can I get the set of titles? Something like titleSet = ['title1', 'title2', 'title3']
I thought maybe something like def titleSet = Book.findTitles(); would work but I can't find anything like that.
I know I could do:
def books = Book.list()
def titleSet
for(def book : books)
titleSet.add(book.title)
But I'm looking for a groovier way.
This goes through all the books for their title and creates a Set instead of a List.
Book.all.title as Set
UPDATE
The above will fetch all Book instances which might be heavy if you only need title. You can also try using criteria or HQL to get only list of titles.
def titleSet = Book.createCriteria().listDistinct {
projections {
property('title')
}
}
Try
Set titleSet = books*.title
Or
Set titleSet = books.collect { it.title }

How to use the withCriteria results in a new withCriteria query in Grails?

Consider the first query:
def books = Book.withCriteria {
eq("category", "fiction")
}
How can I use the result of this query in the next query to get all the authors who wrote these books?
I tried:
def authors = Author.withCriteria {
'in'("books", books)
}
but this is not working. What can I do to fix it?
Can't you just query the association directly?
def authors = Author.withCriteria {
books {
eq("category", "fiction")
}
}

Load children dynamically using Grails Fixture plugin

I'm trying to load data in a Grails application using the Fixture plugin.
class Author {
String name
}
class Book {
String title
hasMany = [authors:Author]
}
I load Author in a separate file and include it in the Book one.
//author fixture
fixture {
author1(Author) {
name = 'Ken Follett'
}
author2(Author) {
name = 'Martin Fowler'
}
}
//book fixture
fixture {
include 'author'
"book"(Book) {
title = 'Your favorite book'
authors = [author1, author2]
}
}
This all works fine. What I can't do, is to replace [author1, author2] so I can dynamically (and randomly) assign authors. Something like:
def dynamicallyBuiltAuthorList = "author1, author2, author100"
authors = ["${dynamicallyBuiltAuthorList}"]
Pretty much everything I tried so far gave me a no matching editors or conversion strategy found error.
Thanks in advance Grails gurus!
Based on your answer below (and the previous edit to this answer), this is probably a better way:
def dynamicallyBuiltAuthorList = [ 'author1', 'author2', 'author100' ].collect {
ref( "$it" )
}
authors = dynamicallyBuiltAuthorList
In the end the answer is simply:
def authorList = []
def dynamicallyBuiltAuthorList = [ 'author1', 'author2', 'author100' ].collect {
authorList.add(ref("$it"))
}
authors = authorList
Thanks to Tim for the help!

Groovy way of doing it in Grails

Article {
String categoryName
static hasMany = [
tags: Tag
]
}
Tag {
String name
}
Now I want to find the list of all related articles. Related meaning, all articles that have the same category name as myArticle or any of the same tags as myArtcle.
With only matching categoryName, here is how I would get the relatedArticles using closures.
def relatedArticles = Article.list().find {it.categoryName == myArticle.categoryName }
Anyone wants to give a shot to find all articles by CategoryName or Tag Name (in a groovy way)?
Any solutions using Criteria or custom queries is appreciated too.
This would work:
def myArticle = // the article you want to match
def relatedArticles = Article.list().findAll {
(it.categoryName == myArticle.categoryName ||
it.tags.name.intersect(myArticle.tags.name)) &&
it.id != myArticle.id)
}
However, if you have a reasonably large number of articles and only a small number of them are expected to match it would be horribly inefficient, as this will load all articles then iterate through them all looking for matches.
A better way would be to simply write a query that only loads matching articles (instead of calling Article.list())
I think that you need to use a separate query per article tag:
// Use a LinkedHashSet to retain the result order if that is important
def results = new LinkedHashSet()
results.addAll(Article.findAll("from Article as article \
where article.categoryName = :categoryName \
and article.id != :id",
[
categoryName:myArticle.categoryName,
id:myArticle.id,
])
myArticle.tags.each {
results.addAll(Article.executeQuery(
"select distinct article from Article as article, \
Tag as tag \
where tag.name = :tag \
and tag in elements(article.tags) \
and article.id != :id",
[
tag:it.name,
id:myArticle.id,
]))
}
def relatedArticles = results as List
This is obviously worthwhile doing when you have a lot of content in the system and wish to avoid loading the entire database for a single page request. Other improvements include specifying max and offset parameters to the queries.
Ideally, you'd use Criteria Query, bug since you said you are not concerned about performance, something like this should work:
def category =
def tagName
def relatedArticles = Article.list().findAll {
(it.categoryName == myArticle.categoryName) || ( it.tags.contains(tagName) )
}

Resources