Grails searchable - grails

Im trying to 'get my toes wet' with grails, and have decided to make a recipe site as a first project.
Im using grails 2.0.1 and using mongoDB GORM for persistence, which is working fine, and am using static Searchable = true in my models for searching.
Making a simple search utility, I have managed to find recipes by name using:
def recipes = Recipe.withCriteria
{
ilike('name', params.name)
}
The recipes can be found by name. My question is, how can I search the ingredient names to flag as results in the search (regarding the models below)?
Coming from PHP and MySQL, this would be as easy as modifying the query with a join or something
my models are as follows:
class Recipe
{
String name;
String method;
Date dateAdded;
static hasMany = [ingredients:Ingredient];
static Searchable = true;
static constraints =
{
name(blank:false, maxSize: 255)
method(blank:false)
}
static mapping =
{
sort dateAdded: "desc"
}
}
class Ingredient
{
String name;
static hasMany = [recipes:Recipe];
static belongsTo = [Recipe]
static constraints =
{
name blank:false;
}
String toString()
{
return name;
}
}

That should be static searchable = true with a lowercase "s" - see http://grails.org/Searchable+Plugin+-+Mapping
But the Searchable plugin doesn't work with Mongo or other NoSQL datastores. This is because it is implemented using Hibernate events to listen for events corresponding to inserting, deleting, and updating database rows and updating the Lucene index based on those changes. Since there's no Hibernate in the mix, the Searchable isn't aware of any changes.

your .withCriteria search has not much to do with the searchable plugin - it's just a normal SQL search.
Try something like
def recipes = Recipe.withCriteria
{
or {
ilike('name', params.name)
ingredients {
ilike('name', params.name)
}
}
}
in order to search in Recipe and Ingredient names.
see http://grails.org/doc/2.0.x/guide/GORM.html#criteria for more help.

Searchable Plugin works with mongodb but you need to configure and reindex it manually as the default behavior is using hibernate.
change your config.groovy mirrorchanges = false and bulkIndexOnStartup = false
add mapwith attribute to all your domain classes that connects to mongodb.
static mapWith="mongo"
static searchable=true
reindex manually by calling reindex() from bootstrap.groovy and everytime your domain has update events.
def domainList = DomainName.list()
DomainName.reindex(domainList)
this is a helpful link from which i referred to though I did not implement the rabbit mq part as I don't need it at the moment. Hope this helps. http://spring.io/blog/2011/08/29/rabbitmq-enabling-grails-full-text-search-on-cloud-foundry/

Related

Grails many to many with join table + extra columns

I needed to create a custom join table to re-model a many-to-many mapping and following some great posts on here came up with the model below.
Now my question is, if I have either a Course or Journey object in a GSP, how do I access the extra column data belonging to the join table.
In this example, I want to access the field named extraColumn1 in the CourseJourneyDetail within my GSP if I have either a journey or course instance
I've tried the following :
${course.courseJourneyDetail.extraColumn1}
but it didn't work.
Here are (relevant parts of) my domain classes :
class Course {
static hasMany = [journies: CourseJourneyDetail]
String courseName
String organisersDescription
Set<Journey> getJournies() {
return CourseJourneyDetail.findAllByCourse(this)*.journey
}
}
class Journey {
static hasMany = [courses: CourseJourneyDetail]
java.util.Date dateCreated
java.util.Date lastUpdated
boolean enabled = true
User user
Set<Course> getCourses() {
return CourseJourneyDetail.findAllByJourney(this)*.course
}
}
class CourseJourneyDetail implements Serializable {
String extraColumn1
static belongsTo = [course: Course, journey: Journey]
boolean equals(other) {
if (!(other instanceof CourseJourneyDetail)) {
return false
}
other.journey?.id == journey?.id &&
other.course?.id == course?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (course) builder.append(course.id)
if (journey) builder.append(journey.id)
builder.toHashCode()
}
static constraints = {
}
static mapping = {
version false
id composite: ['course', 'journey']
}
}
Since you've established that each Course/Journey has a collection of CourseJourneyDetail's rather than a single instance, ${course.courseJourneyDetail.extraColumn1} won't work (as you've discovered).
If you break down your groovy expression into this: course.courseJourneyDetail, it doesn't really make sense based on the relationships you have created. The reason being, Course doesn't have a single CourseJourneyDetail but rather a collection.
If your desire is to have Course and Journey in a one-to-one relationship, but with a join table with additional columns, then your domain structure needs to change to reflect this: rather than using static hasHany in each class, you would switch to a single instance.
If your desire is to keep the many-to-many relationship, then you need to think about how to fetch the appropriate join object that represents the association. One example:
CourseJourneyDetail.findAllByCourseAndJourney(<courseInstance>, <journeyInstance>)
If you want the additional columns collected for all of the many-to-many associations, you can use a syntax that you are already using in your convience methods (getJournies and getCourses):
course.journies*.extraColumn1
This would output an array of Strings, so its usage makes less sense within a ${} expression; and more sense within a g:each. It entirely depends on how you plan on using this data.

how not to load hasMany entities from database using Grails

I've got 3 classes:
class Author {
static hasMany = [books: Book]
static belongsTo = [company: Company]
String name
}
class Book {
static mapping = {
collection "documents"
id generator: 'assigned',index: true, indexAttributes:[background:true, unique:true, dropDups:true]
}
String id
String name
}
class Company {
static mapping = {
collection "documents"
id generator: 'assigned',index: true, indexAttributes:[background:true, unique:true, dropDups:true]
}
String id
String name
}
I want to use
Author author = Author.getByCompanyAndBook(1,1);
but when running this Grails retrieves all the Book objects from the database.
I need those Books only as identifiers for the Author and I am not going to use those objects.
Is there a way for me to force Grails not to fetch the Books and Companies from the database?
I tried to use:
static mapping = {
books lazy: true
}
but still all of the Books and the Company were loaded.
Edit:
I am using mongo db as my database.
That's weird - collections are lazy by default, so getting an author shouldn't retrieve anything in the books collection until you access the books property. But regardless, even if it worked as expected I recommend that you avoid mapped collections. See this talk which shows why collections are unnecessarily expensive and provides workarounds to minimize the costs.

How can I change the default Grails/GORM lookup?

I'm new to Grails. I'm developing a web app that handles the records of a gymnasium, to make routines, exercises, etc.
I have this domain class Ejercicios:
class Ejercicios {
String nombreEjercicio
String idHoja
String descripcion
List<String> descripcionO
static hasMany = [descripcionO: Descripciones]
static transients = ['descripcionTrans']
String descripcionTrans
static mapping = {
id column: "idHoja"
version false
}
static constraints = {
nombreEjercicio maxSize: 45
idHoja blank: false
}
The database table has the default Grails id named "idHoja", and another attribute named "id_hoja"
The thing here is that when I make a JSON parse from the rest API, I need GORM to look for exercises via the "id_hoja" attribute, not the "idHoja" because it'll cause a mismatch.
I found the solution by myself!
The only thing I needed to was to make the JSON call with the name "idHoja" and that was it.

Lucene /Searchable Plugin - Grails not reindexing has many

I have a searchable model setup as:
class Tag{
def searchable = true
String name
}
class PersonTag{
static belongsTo = [person: Person]
static searchable = {
tag(component: true)
person(component: true)
}
static PersonTag addTag(String name, Person person){
if(person && person.id){
def tag = Tag.findOrCreate(name)
def t = new PersonTag(tag:tag, person:person)
t.save(flush:true)
return t
}
}
}
class Person{
static searchable = {
name boost: 2.0
tags component:true
}
}
What I am working on is searching "Persons" by tags. When my server starts it indexes everything and all existing tags on people work. If I add a new tag, I can not search for it until a server restart. However if I change a simple property like the persons name, I am able to search for it w/o a restart. All of my changes for tags are going through a PeopleAdminController.
Any suggestions on why searching by tags is not working? I have even tried to manually index/reindex Person, Tag, and PersonTag via the domainInstance.reindex(), as well as in the controller using the searchableService.
I am searching for People in a different controller PeopleController:
def search(){
def result = People.search("${params.q}")
render (view: '/searchable/search.gsp' , model:[searchResult: result])
}
Well I found a way to "fix the issue" but i dont like it(because its a reindexAll()). After I add the tag if I call:
searchableService.reindexAll()
It will work. I dont understand why the following wouldn't work:
static PersonTag addTag(String name, Person person){
if(person && person.id){
def tag = Tag.findOrCreate(name)
def t = new PersonTag(tag:tag, person:person)
t.save(flush:true)
t.index()
tag.reindex()
person.reindex()
return t
}
}

Grails GORM Query with Multiple Objects?

I am trying to write a query in Grails to return a set of results from a domain class, but within those return the relevant results of a separate class whom have the parentId of the main class.
def query = Cars.where {
(colour == 'red')
}
And then within each list item include the set of parts relating to that CAR ID (as an example of what I'm trying to achieve, I know the code is incorrect though....
query.each{
this car. add(Parts.whereCarID{it.id})
}
If you define your domain model properly, you should get it without a criteria involved.
As far as I understand you need to add static hasMany = [parts: Parts] in your Cars domain class, and static belongsTo = [car:Cars] in your Parts class.
So for example, here how it might look:
class Cars {
string colour
static hasMany = [parts:Parts]
// ... rest of your properties
}
class Parts {
static belongsTo = [car:Cars]
// ... rest of your properties
}
And to get your result just do this:
def cars = Cars.findAllByColour('red')
Then you can do:
cars.each { car->
println car.parts // <-- all the parts for each car is here
}

Resources