Grails Searchable Plugin(Lucene) - 1 To Many Query - lucene

I am using grail's searchable plugin(0.6.4). I need to search the Members on the basis of privacy settings. Following is the db design.
Member has MemberProfile, and MemberProfile has PrivacySettings
class Member extends {
String firstName
String lastName
static searchable = {
analyzer "simple"
only = ['firstName', 'lastName']
firstName boost: 5.0
profile component: true
profile reference: true
}
static hasOne = [profile: MemberProfile]
}
class MemberProfile {
static searchable = {
analyzer "simple"
only = ['currentCity', 'currentCountry']
privacySettings component: true
}
static hasMany = [privacySettings:PrivacySettings]
String currentCity
String currentCountry
List<PrivacySettings> privacySettings
}
//For instance Privacy Settings contains
//fieldSectionName: firstName , connectionLevel: true , memberLevel: false
// This means firstName will be shown to only members' friends(connection)
class PrivacySettings {
static searchable = {
analyzer "simple"
only = ['fieldSectionName', 'connectionLevel', 'memberLevel']
}
String fieldSectionName
boolean connectionLevel
boolean memberLevel
}
One member profile has many privacy settings for each field.
What will be the query to search only those members which have display_name in fieldsSectionName and connectionLevel true in the privacy settings table.
I am trying something like this
def query="mydisplayname"
def searchResults = Member.search(query + "*" + " +(fieldSectionName:${'display_name'} AND connectionLevel:${true})", params)

I don't know grail but in Lucene the maximum number of clauses in a boolean query is 1024 by default.
You can increase this limit.
There would be performance penalty, though. or you can index the values on the document and search for them (lucene will do an OR operation on different fields with the same name).
You can change the limit using a static property on BooleanQuery class:
BooleanQuery.MaxClauseCount = 99999;
Omri

I had the same issue in my grails application, to resolve it add org.apache.lucene.search.BooleanQuery.maxClauseCount = 99999 to your config.groovy and restart your application

Related

Hibernate Search with Lucene Phone Number Analyzer issues

Our database contains thousands of numbers in various formats and what I am attempting to do is remove all punctuation at index time and store only the digits and then when a user types digits into a keyword field, only match on those digits. I thought that a custom analyzer was the way to go but I think I am missing an important step...
#Override
protected TokenStreamComponents createComponents(String fieldName) {
log.debug("Creating Components for Analyzer...");
final Tokenizer source = new KeywordTokenizer();
LowerCaseFilter lcFilter = new LowerCaseFilter(source);
PatternReplaceFilter prFilter = new PatternReplaceFilter(lcFilter,
Pattern.compile("[^0-9]"), "", true);
TrimFilter trimFilter = new TrimFilter(prFilter);
return new TokenStreamComponents(source, trimFilter);
}
...
#KeywordSearch
#Analyzer(impl = com.jjkane.common.search.analyzer.PhoneNumberAnalyzer.class)
#Field(name = "phone", index = org.hibernate.search.annotations.Index.YES, analyze = Analyze.YES, store = Store.YES)
public String getPhone() {
return this.phone;
}
This may just be ignorance on my part in attempting to do this... From all the documentation, it seems like I am on the right track, but the query never matches unless I submit (555)555-5555 as an exact match to what was in my db. If I put in 5555555555, I get nothing...

How to add prefix and suffix when indexing

How is it possible to add a suffix and prefix to an entity in Hibernate Search during indexing?
I need this to perform exact search.
E.g. if one is searching for "this is a test", then following entries are found:
* this is a test
* this is a test and ...
So I found the idea to add a prefix and suffix to the whole value during indexing, e.g.:
_____ this is a test _____
and if one is searching for "this is a test" and is enabling the checkbox for exact search, I'll change the search string to_
"_____ this is a test _____"
I created a FilterFactory for this, but with this one it adds the prefix and suffix to every term:
public boolean incrementToken() throws IOException {
if (!this.input.incrementToken()) {
return false;
} else {
String input = termAtt.toString();
// add "_____" at the beginning and ending of the phrase for exact match searching
input = "_____ " + input + " _____";
char[] newBuffer = input.toLowerCase().toCharArray();
termAtt.setEmpty();
termAtt.copyBuffer(newBuffer, 0, newBuffer.length);
return true;
}
}
This is not how you should do it.
What you need is that the string you index is considered a unique token. This way, you will only have results having the exact token.
To do so you need to define an analyzer based on the KeywordTokenizer.
#Entity
#AnalyzerDefs({
#AnalyzerDef(name = "keyword",
tokenizer = #TokenizerDef(factory = KeywordTokenizerFactory.class)
)
})
#Indexed
public class YourEntity {
#Fields({
#Field, // your default field with default analyzer if you need it
#Field(name = "propertyKeyword", analyzer = #Analyzer(definition = "keyword"))
})
private String property;
}
Then you should search on the propertyKeyword field. Note that the analyzer definition is global so you only need to declare the definition for one entity for it to be available for all your entities.
Take a look at the documentation about analyzers: http://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#example-analyzer-def .
It's important to understand what an analyzer is for because usually the default one is not exactly the one you are looking for.

Hibernate search boolean filter

I have book entry:
#Entity
#Indexed
public class Book extends BaseEntity {
#Field
private String subtitle;
#DateBridge(resolution = Resolution.DAY)
private Date publicationDate;
#Field
private int score;
#IndexedEmbedded
#ManyToMany(fetch = FetchType.EAGER)
#Cascade(value = {CascadeType.ALL})
private List<Author> authors = new ArrayList<Author>();
#Field
#FieldBridge(impl = BooleanBridge.class)
private boolean prohibited;
And filter by boolean field "phohibited"
public class BFilter extends Filter {
#Override
public DocIdSet getDocIdSet(IndexReader indexReader) throws IOException {
OpenBitSet bitSet = new OpenBitSet(indexReader.maxDoc());
TermDocs termDocs = indexReader.termDocs(new Term("prohibited","false"));
while (termDocs.next()) {
bitSet.set(termDocs.doc());
}
return bitSet;
}
}
Search method
public List<T> findByQuery(Class c, String q) throws InterruptedException {
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.createIndexer().startAndWait();
QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(c).get();
Query luceneQuery = qb
.keyword()
.fuzzy()
.onFields("title", "subtitle", "authors.name", "prohibited", "score")
.matching(q)
.createQuery();
FullTextQuery createFullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, Book.class, BaseEntity.class);
createFullTextQuery.setFilter(new BFilter());
return createFullTextQuery.list();
}
if I apply that filter - search result is empty. Entries in the database 100% there. What am I doing wrong? If you replace the filter field to "score" that all works, and the result is not empty. Do not search it on a Boolean field
The basic approach looks ok. A couple of comments. You are calling the indexer for each findByQuery call. Not sure whether this is just some test code, but you should index before you search and only once or when things change (you can also use automatic index updates). It might also be that depending on your transaction setup, your search cannot see the indexed data. However, you seem to say that all works if you don't use a filter at all. In this case I would add some debug to the filter or debug it to see what's going on and if it gets called at all. Last but not least, you don't need to explicitly set explicitly #FieldBridge(impl = BooleanBridge.class).

Play framework select input validation

I am using Play 2.1
I made a select drop down box using the helper field constructor.
The drop down box have 3 fields, default:"choose a gender", Male, and Female.
How do I ensure that the user choose one of male or female, and not the default value? (A required drop down field)
I am using Play!Framework 2.1.0, below is a simple solution for your problem:
The model should be like this: (Below is simple model for your problem)
package models;
import play.data.validation.Constraints;
public class Gender {
// This field must have a value (not null or not an empty string)
#Constraints.Required
public String gender;
}
The controller should be like this:
/** Render form with select input **/
public static Result selectInput() {
Form<Gender> genderForm = Form.form(Gender.class);
return ok(views.html.validselect.render(genderForm));
}
/** Handle form submit **/
public static Result validateSelectInput() {
Form<Gender> genderForm = Form.form(Gender.class).bindFromRequest();
if (genderForm.hasErrors()) { // check validity
return ok("Gender must be filled!"); // can be bad request or error, etc.
} else {
return ok("Input is valid"); // success validating input
}
}
The template/view should be like this:
#(genderForm: Form[models.Gender])
#import views.html.helper._
#main(title = "Validate Select") {
#form(action = routes.Application.validateSelectInput()) {
#********** The default value for select input should be "" as a value *********#
#select(
field = genderForm("gender"),
options = options("" -> "Select Gender", "M" -> "Male", "F" -> "Female")
)
<input type="submit" value="Post">
}
}
See also this post as reference : Use of option helper in Play Framework 2.0 templates

RavenDB: Indexing documents from multiple collections

I have several document collections that occasionally need to be pulled together into a single index for reporting purposes.
This FAQ provides a solution for writing such an index in Raven Studio: http://ravendb.net/faq/indexing-across-entities
While I understand I won't get full compile-time checking, I'm trying to avoid completely unchecked code like this:
public class Assets_ById : AbstractIndexCreationTask
{
public override IndexDefinition CreateIndexDefinition()
{
return new IndexDefinition
{
Map = #"from doc in docs
where doc[""#metadata""][""Raven-Entity-Name""] == ""Cars"" ||
doc[""#metadata""][""Raven-Entity-Name""] == ""Trains"" ||
doc[""#metadata""][""Raven-Entity-Name""] == ""Boats"" ||
doc[""#metadata""][""Raven-Entity-Name""] == ""Planes""
select new
{
Cost = doc.Cost,
Id = doc.Id,
Name = doc.Name,
Type = doc.Type,
};"
}
}
}
Is there something similar to the generic AbstractIndexCreationTask<T> that will allow me to define a heterogeneous index with lambda expressions?
You can use WhereEntityIs(names), like this:
from doc in docs.WhereEntityIs<Vehicle>("Cars", "Trains", "Boats", "Planes")
select new
{
doc.Cost,
doc.Name,
doc.Type
}
Take a look here: https://groups.google.com/forum/#!topic/ravendb/9wvRY0OiGBs
It's basically the same question and the short answer is:
"right now there isn't a better option, but there will be in the future"