![JAR search and dependency download from the Maven repository](/logo.png)
com.jaxio.jpa.querybyexample.DefaultLuceneQueryBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jpa-querybyexample Show documentation
Show all versions of jpa-querybyexample Show documentation
This API allows you to construct Query By Example using the JPA 2 Criteria API.
The newest version!
/*
* Copyright 2015 JAXIO http://www.jaxio.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaxio.jpa.querybyexample;
import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.Query;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.metamodel.SingularAttribute;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Lists.newArrayList;
import static org.apache.commons.lang.StringUtils.*;
import static org.apache.lucene.queryparser.classic.QueryParser.escape;
public class DefaultLuceneQueryBuilder implements LuceneQueryBuilder {
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(DefaultLuceneQueryBuilder.class);
private static final String SPACES_OR_PUNCTUATION = "\\p{Punct}|\\p{Blank}";
@Override
public Query build(FullTextEntityManager fullTextEntityManager, SearchParameters searchParameters, List> availableProperties) {
List clauses = getAllClauses(searchParameters, searchParameters.getTerms(), availableProperties);
StringBuilder query = new StringBuilder();
query.append("+(");
for (String clause : clauses) {
if (query.length() > 2) {
query.append(" AND ");
}
query.append(clause);
}
query.append(")");
if (query.length() == 3) {
return null;
}
log.debug("Lucene query: {}", query);
try {
return new QueryParser(availableProperties.get(0).getName(), fullTextEntityManager.getSearchFactory().getAnalyzer("custom"))
.parse(query.toString());
} catch (Exception e) {
throw propagate(e);
}
}
private List getAllClauses(SearchParameters sp, List terms, List> availableProperties) {
List clauses = newArrayList();
for (TermSelector term : terms) {
if (term.isNotEmpty()) {
String clause = getClause(sp, term.getSelected(), term.getAttribute(), term.isOrMode(), availableProperties);
if (isNotBlank(clause)) {
clauses.add(clause);
}
}
}
return clauses;
}
private String getClause(SearchParameters sp, List terms, SingularAttribute, ?> property, boolean orMode,
List> availableProperties) {
if (property != null) {
checkArgument(availableProperties.contains(property), property + " is not indexed");
StringBuilder subQuery = new StringBuilder();
if (terms != null) {
subQuery.append("(");
for (String wordWithSpacesOrPunctuation : terms) {
if (isBlank(wordWithSpacesOrPunctuation)) {
continue;
}
List wordElements = newArrayList();
for (String str : wordWithSpacesOrPunctuation.split(SPACES_OR_PUNCTUATION)) {
if (isNotBlank(str)) {
wordElements.add(str);
}
}
if (!wordElements.isEmpty()) {
if (subQuery.length() > 1) {
subQuery.append(" ").append(orMode ? "OR" : "AND").append(" ");
}
subQuery.append(buildSubQuery(property, wordElements, sp));
}
}
subQuery.append(")");
}
if (subQuery.length() > 2) {
return subQuery.toString();
}
} else {
return getOnAnyClause(sp, terms, availableProperties, orMode, availableProperties);
}
return null;
}
private String buildSubQuery(SingularAttribute, ?> property, List terms, SearchParameters sp) {
StringBuilder subQuery = new StringBuilder();
subQuery.append("(");
for (String term : terms) {
if (subQuery.length() > 1) {
subQuery.append(" AND ");
}
if (sp.getSearchSimilarity() != null) {
subQuery.append(property.getName() + ":" + escapeForFuzzy(lowerCase(term)) + "~" + sp.getSearchSimilarity());
} else {
subQuery.append(property.getName() + ":" + escape(lowerCase(term)));
}
}
subQuery.append(")");
return subQuery.toString();
}
private String getOnAnyClause(SearchParameters sp, List terms, List> properties, boolean orMode,
List> availableProperties) {
List subClauses = newArrayList();
for (SingularAttribute, ?> property : properties) {
String clause = getClause(sp, terms, property, orMode, availableProperties);
if (isNotBlank(clause)) {
subClauses.add(clause);
}
}
if (subClauses.isEmpty()) {
return null;
}
if (subClauses.size() > 1) {
StringBuilder subQuery = new StringBuilder();
subQuery.append("(");
for (String subClause : subClauses) {
if (subQuery.length() > 1) {
subQuery.append(" OR ");
}
subQuery.append(subClause);
}
subQuery.append(")");
return subQuery.toString();
} else {
return subClauses.get(0);
}
}
/**
* Apply same filtering as "custom" analyzer. Lowercase is done by QueryParser for fuzzy search.
*
* @param word word
* @return word escaped
*/
private String escapeForFuzzy(String word) {
int length = word.length();
char[] tmp = new char[length * 4];
length = ASCIIFoldingFilter.foldToASCII(word.toCharArray(), 0, tmp, 0, length);
return new String(tmp, 0, length);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy