Lucene /Searchable Plugin - Grails not reindexing has many - grails

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
}
}

Related

Grails - findAll joining two tables

I'm new to Grails. I'm having problem with retrieving data from DB. I have domains with classes, something like...
class Lightbox {
String name = ''
String link = ''
static hasMany = [users: LightboxUserAccount]
}
class LightboxUserAccount {
UserAccount userAccount
static belongsTo = [lightbox: Lightbox]
}
class UserAccount {
String username
...
}
I want to list all "Lightboxes" owned by user with ID=4. I was trying
def my_lb = Lightbox.findAll("from Lightbox as lb where lb.users=:userAccount", [userAccount: springSecurityService.getCurrentUser()])
or
def my_lb = Lightbox.findAllByUsers(4)
None of those work for me. What am I doing wrong? Thx
Try this one:
Lightbox.findAll("from Lightbox as lb where :userAccount in (lb.users)", [userAccount: springSecurityService.getCurrentUser()])
So I did it slightly different, using criteria instead. Take a look if interested
static getAllByUserAccount(UserAccount userAccount) {
def criteria = Lightbox.createCriteria()
def my_lb = criteria.list {
users {
eq('userAccount', userAccount)
}
}
return my_lb
}
It seems to be working. Thanks for responses though

How to add default values to hasMany in Grails?

Say I have a domain Books.
static hasMany = [reader:Reader]
And Class Reader
String fullName
Now I want to add Readers with fullName: "PersonA", "PersonB", "PersonC" by defualt in the Books domain.
Please tell how do I accomplish this? I am pretty new with Grails.
You can create class named BootStrap in Configuration folder of your App andd add init() method to it. This method will be executed before App starts. Example:
class BootStrap {
def init() {
def readerA = Reader.findOrCreateByFullName("PersonA")
def readerB = Reader.findOrCreateByFullName("PersonB")
def readerC = Reader.findOrCreateByFullName("PersonC")
def readers = [readerA, readerB, readerC]
def book = new Book()
for(reader in readers){
book.addToReader(reader)
}
book.save(flush: true)
}
}

Grails: querying hasMany association

I know there are several questions on this subject but none of them seem to work for me. I have a Grails app with the following Domain objects:
class Tag {
String name
}
class SystemTag extends Tag {
// Will have additional properties here...just placeholder for now
}
class Location {
String name
Set<Tag> tags = []
static hasMany = [tags: Tag]
}
I am trying to query for all Location objects that have been tagged by 1 or more tags:
class LocationQueryTests {
#Test
public void testTagsQuery() {
def tag = new SystemTag(name: "My Locations").save(failOnError: true)
def locationNames = ["L1","L2","L3","L4","L5"]
def locations = []
locationNames.each {
locations << new Location(name: it).save(failOnError: true)
}
(2..4).each {
locations[it].tags << tag
locations[it].save(failOnError: true)
}
def results = Location.withCriteria {
tags {
'in'('name', [tag.name])
}
}
assertEquals(3, results.size()) // Returning 0 results
}
}
I have validated that the data is being created/setup correctly...5 Location objects created and the last 3 of them are tagged.
I don't see what's wrong with the above query. I would really like to stay away from HQL and I believe that should be possible here.
Welcome to hibernate.
The save method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush argument is used
if you do not use flush it does the saves in batches so when you setup your query right after the save it appears that the data is not there.
you need to add
locations[it].save(failOnError: true, flush:true)
You should use addTo* for adds a domain class relationship for one-to-many or many-to-many relationship.
(2..4).each {
locations[it].addToTags(tag)
}

Grails searchable

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/

grails - how to save ArrayList in db

I use grails-1.3.2 and hbase plugin.
I have some difficulty in creating one-to-Many association with
hbase (i can work with hibernate), so
i decided to try create one-to-Many association with using ArrayList.
Here are my domain classes and controllers:
class Contacts {
String con
static constraints = {}
}
class ContactsController {
def create = {
def contact = new Contacts()
contact.con = params.con
contact.save(flush:true)
}
}
class User {
String firstname
String lastname
// static hasMany = [contact: Contacts]
static ArrayList<Contacts> contact
static constraints = {}
}
class UserController{
def create = {
def user = new User()
user.properties = params
user.save(flush: true)
}
def addContact = {
def user = User.get(params.userID)
def contact = Contacts.get(params.contactID)
user.contact.add(contact)
user.save(flush:true)
}
}
In addContact action user.contact = null, so it can not work.
In user does nor appear contact field.
Can someone help me understand what i have to do for saving ArrayList in db?
I don't know anything about hbase, but the static contact property of the User class looks very suspicious. The fact that this property is static, implies that every user has the same contact list, which seems unlikely to be the desired behaviour.
In a standard GORM domain model - assuming you want each User to have their own contact list - this would be defined
class User {
String firstname
String lastname
static hasMany = [contact: Contacts]
}
Although it looks like we're also defining a static property here, it's actually just the definition of how the Contact and User classes are related (AKA mapping) that is static. The contact property that is dynamically added to the User class is non-static.
Aside
I recommend renaming the Contacts class to Contact and the contact property to contacts. The GORM mapping would then look like this:
class User {
String firstname
String lastname
static hasMany = [contacts: Contact]
}

Resources