From 1b188c5d4b017233fa158e9613701882dfa01f80 Mon Sep 17 00:00:00 2001 From: lgrignon Date: Tue, 18 Oct 2016 14:35:19 +0200 Subject: [PATCH 1/2] publish doc --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 17f7c80..4823b09 100644 --- a/README.md +++ b/README.md @@ -645,6 +645,11 @@ Support for indexing inherited properties + https://github.com/lgrignon +Publish with: +``` +gradlew clean bintrayUpload --stacktrace +``` + ## License Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 From 3ec93ad6fa9c50cac48a193d79be762c8852e25c Mon Sep 17 00:00:00 2001 From: lgrignon Date: Tue, 18 Oct 2016 20:56:50 +0200 Subject: [PATCH 2/2] option to ignore empty query for analyzers with stop words --- README.md | 10 ++ build.gradle | 2 +- .../search/HibernateSearchConfig.groovy | 12 ++ .../search/HibernateSearchGrailsPlugin.groovy | 9 +- .../search/HibernateSearchQueryBuilder.groovy | 109 +++++++++++------- 5 files changed, 98 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4823b09..d6baaea 100644 --- a/README.md +++ b/README.md @@ -613,6 +613,16 @@ MyDomainClass.search().list { } ``` +### Options + +```groovy +grails.plugins.hibernatesearch = { + rebuildIndexOnStart false // see related section above + throwOnEmptyQuery false // throw or not exception when Hibernate Search raises an EmptyQueryException + fullTextFilter /* ... */ // see related section above +} +``` + ## Change log ### v2.0.2 diff --git a/build.gradle b/build.gradle index 5f26e91..740733d 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } } -version "2.0.2" +version "2.0.3" group "org.grails.plugins" apply plugin:"eclipse" diff --git a/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchConfig.groovy b/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchConfig.groovy index d49c666..7adc131 100644 --- a/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchConfig.groovy +++ b/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchConfig.groovy @@ -17,6 +17,8 @@ class HibernateSearchConfig { private final FullTextSession fullTextSession private static final List MASS_INDEXER_METHODS = MassIndexer.methods.findAll { it.returnType == MassIndexer }*.name + boolean throwExceptionOnEmptyQuery + HibernateSearchConfig( Session session ) { this.fullTextSession = Search.getFullTextSession( session ) } @@ -55,6 +57,16 @@ class HibernateSearchConfig { massIndexer = fullTextSession.createIndexer().startAndWait() } + + /** + * Throws exception if Hibernate Search raises an EmptyQueryException, (could occur if analyzer has stop words) default false + */ + def throwOnEmptyQuery( boolean throwException ) { + + log.debug "throwExceptionOnEmptyQuery = " + throwException + + throwExceptionOnEmptyQuery = throwException + } Object invokeMethod( String name, Object args ) { if ( name in MASS_INDEXER_METHODS ) { diff --git a/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchGrailsPlugin.groovy b/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchGrailsPlugin.groovy index 5308920..75806f2 100644 --- a/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchGrailsPlugin.groovy +++ b/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchGrailsPlugin.groovy @@ -17,6 +17,8 @@ class HibernateSearchGrailsPlugin extends Plugin { private final static Logger log = LoggerFactory.getLogger(this) + public static HibernateSearchConfig pluginConfig; + def grailsVersion = "3.1.12 > *" def profiles = ['web'] @@ -70,12 +72,12 @@ class HibernateSearchGrailsPlugin extends Plugin { log.info "* " + clazz.getSimpleName() + " is indexed" grailsClass.metaClass.static.search = { - new HibernateSearchQueryBuilder( clazz, applicationContext.sessionFactory.getCurrentSession() ) + new HibernateSearchQueryBuilder( clazz, applicationContext.sessionFactory.getCurrentSession(), pluginConfig ) } grailsClass.metaClass.search = { def instance = delegate - new HibernateSearchQueryBuilder( clazz, instance, applicationContext.sessionFactory.getCurrentSession() ) + new HibernateSearchQueryBuilder( clazz, instance, applicationContext.sessionFactory.getCurrentSession(), pluginConfig ) } } } @@ -88,7 +90,8 @@ class HibernateSearchGrailsPlugin extends Plugin { log.debug 'hibernatesearch config found' Session session = applicationContext.sessionFactory.openSession(); - hibernateSearchConfig.delegate = new HibernateSearchConfig( session ) + pluginConfig = new HibernateSearchConfig( session ) + hibernateSearchConfig.delegate = pluginConfig hibernateSearchConfig.resolveStrategy = Closure.DELEGATE_FIRST hibernateSearchConfig.call() } diff --git a/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchQueryBuilder.groovy b/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchQueryBuilder.groovy index e3e768d..fa539e4 100644 --- a/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchQueryBuilder.groovy +++ b/src/main/groovy/grails/plugins/hibernate/search/HibernateSearchQueryBuilder.groovy @@ -26,6 +26,7 @@ import org.hibernate.search.FullTextQuery import org.hibernate.search.FullTextSession import org.hibernate.search.MassIndexer import org.hibernate.search.Search +import org.hibernate.search.exception.EmptyQueryException; import org.hibernate.search.query.dsl.FieldCustomization import org.hibernate.search.query.dsl.QueryBuilder import org.slf4j.Logger; @@ -65,6 +66,38 @@ class HibernateSearchQueryBuilder { children << component } + /** + * @return true if composite contains at least one valid (not empty) query + */ + protected boolean forEachQuery( Closure action ) { + + boolean notEmpty = false; + if (children) { + + for (child in children) { + try { + + Query subQuery = child.createQuery(); + + action.delegate = subQuery + action.resolveStrategy = Closure.DELEGATE_FIRST + action.call(subQuery) + notEmpty = true; + + } catch (EmptyQueryException e) { + if (HibernateSearchGrailsPlugin.pluginConfig.throwExceptionOnEmptyQuery) { + throw e + } else { + log.warn 'empty Hibernate search query ignored! ' + child, e + } + } + } + + } + + return notEmpty + } + def toString( indent ) { [( "-" * indent ) + this.class.simpleName, children.collect { it.toString( indent + 1 ) }].flatten().findAll {it}.join( "\n" ) } @@ -100,54 +133,47 @@ class HibernateSearchQueryBuilder { private static class MustNotComponent extends Composite { Query createQuery( ) { - if ( children ) { - def query = queryBuilder.bool() + def query = queryBuilder.bool() + boolean notEmpty = forEachQuery { subQuery -> + query = query.must( subQuery ).not() + } - children*.createQuery().each { - query = query.must( it ).not() - } - - query.createQuery() - - } else { - queryBuilder.all().createQuery() - } + if (notEmpty) { + return query.createQuery() + } else { + return queryBuilder.all().createQuery() + } + } } private static class MustComponent extends Composite { Query createQuery( ) { - if ( children ) { - - def query = queryBuilder.bool() - - children*.createQuery().each { - query = query.must( it ) - } - - query.createQuery() - - } else { - queryBuilder.all().createQuery() - } + def query = queryBuilder.bool() + boolean notEmpty = forEachQuery { subQuery -> + query = query.must( subQuery ) + } + + if (notEmpty) { + return query.createQuery() + } else { + return queryBuilder.all().createQuery() + } } } private static class ShouldComponent extends Composite { Query createQuery( ) { - if ( children ) { - - def query = queryBuilder.bool() - - children*.createQuery().each { - query = query.should( it ) - } - - query.createQuery() - - } else { - queryBuilder.all().createQuery() - } + def query = queryBuilder.bool() + boolean notEmpty = forEachQuery { subQuery -> + query = query.should( subQuery ) + } + + if (notEmpty) { + return query.createQuery() + } else { + return queryBuilder.all().createQuery() + } } } @@ -219,6 +245,8 @@ class HibernateSearchQueryBuilder { private static final List MASS_INDEXER_METHODS = MassIndexer.methods.findAll { it.returnType == MassIndexer }*.name + private final HibernateSearchConfig pluginConfig; + private final FullTextSession fullTextSession private final clazz private final instance @@ -241,15 +269,16 @@ class HibernateSearchQueryBuilder { Filter filter - HibernateSearchQueryBuilder( clazz, instance, Session session ) { + HibernateSearchQueryBuilder( clazz, instance, Session session, HibernateSearchConfig pluginConfig ) { this.clazz = clazz this.fullTextSession = Search.getFullTextSession( session ) this.instance = instance this.staticContext = instance == null + this.pluginConfig = pluginConfig; } - HibernateSearchQueryBuilder( clazz, Session session ) { - this( clazz, null, session ) + HibernateSearchQueryBuilder( clazz, Session session, HibernateSearchConfig pluginConfig ) { + this( clazz, null, session, pluginConfig ) } private FullTextQuery createFullTextQuery( ) {