Java tutorial
/* * #%L * Alfresco Legacy Lucene * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.search.impl.lucene; import java.io.IOException; import java.io.StringReader; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.dictionary.IndexTokenisationMode; import org.alfresco.repo.search.MLAnalysisMode; import org.alfresco.repo.search.adaptor.lucene.AnalysisMode; import org.alfresco.repo.search.adaptor.lucene.LuceneFunction; import org.alfresco.repo.search.adaptor.lucene.QueryConstants; import org.alfresco.repo.search.impl.QueryParserUtils; import org.alfresco.repo.search.impl.lucene.analysis.DateTimeAnalyser; import org.alfresco.repo.search.impl.lucene.analysis.MLTokenDuplicator; import org.alfresco.repo.search.impl.parsers.FTSQueryException; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; import org.alfresco.util.CachingDateFormat; import org.alfresco.util.ISO9075; import org.alfresco.util.Pair; import org.alfresco.util.SearchLanguageConversion; import org.antlr.misc.OrderedHashSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Token; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.CharStream; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.queryParser.QueryParserTokenManager; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreRangeQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.WildcardTermEnum; import org.apache.lucene.search.regex.RegexQuery; import org.apache.lucene.search.spans.SpanNearQuery; import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.search.spans.SpanTermQuery; import org.jaxen.saxpath.SAXPathException; import org.springframework.extensions.surf.util.I18NUtil; /** * Extensions to the standard lucene query parser. * <p> * Covers: * <ul> * <li>special fields; * <li>range expansion; * <li>adds wild card support for phrases; * <li>exposes more helper methods to build lucene queries and request tokneisation bahviour. * </ul> * TODO: Locale loop should not include tokenisation expansion * * @author andyh */ public abstract class AbstractLuceneQueryParser extends QueryParser implements QueryConstants { @SuppressWarnings("unused") private static Log s_logger = LogFactory.getLog(AbstractLuceneQueryParser.class); protected NamespacePrefixResolver namespacePrefixResolver; protected DictionaryService dictionaryService; private TenantService tenantService; private SearchParameters searchParameters; private MLAnalysisMode defaultSearchMLAnalysisMode; private IndexReader indexReader; private int internalSlop = 0; private AbstractAnalyzer luceneAnalyser; /** * @param defaultSearchMLAnalysisMode MLAnalysisMode */ public void setDefaultSearchMLAnalysisMode(MLAnalysisMode defaultSearchMLAnalysisMode) { this.defaultSearchMLAnalysisMode = defaultSearchMLAnalysisMode; } /** * @param indexReader IndexReader */ public void setIndexReader(IndexReader indexReader) { this.indexReader = indexReader; } /** * @param searchParameters SearchParameters */ public void setSearchParameters(SearchParameters searchParameters) { this.searchParameters = searchParameters; } /** * @param namespacePrefixResolver NamespacePrefixResolver */ public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) { this.namespacePrefixResolver = namespacePrefixResolver; } /** * @param tenantService TenantService */ public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } public SearchParameters getSearchParameters() { return searchParameters; } public IndexReader getIndexReader() { return indexReader; } public MLAnalysisMode getDefaultSearchMLAnalysisMode() { return defaultSearchMLAnalysisMode; } public abstract boolean addContentCrossLocaleWildcards(); /** * Lucene default constructor * * @param arg0 String * @param arg1 Analyzer */ public AbstractLuceneQueryParser(String arg0, Analyzer arg1) { super(arg0, arg1); if (arg1 instanceof AbstractAnalyzer) { luceneAnalyser = (AbstractAnalyzer) arg1; } } /** * Lucene default constructor * * @param arg0 CharStream */ public AbstractLuceneQueryParser(CharStream arg0) { super(arg0); } /** * Lucene default constructor * * @param arg0 QueryParserTokenManager */ public AbstractLuceneQueryParser(QueryParserTokenManager arg0) { super(arg0); } protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException { try { internalSlop = slop; Query query = getFieldQuery(field, queryText); return query; } finally { internalSlop = 0; } } /** * @param field String * @param queryText String * @param analysisMode AnalysisMode * @param slop int * @param luceneFunction LuceneFunction * @return the query * @throws ParseException */ public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, int slop, LuceneFunction luceneFunction) throws ParseException { try { internalSlop = slop; Query query = getFieldQuery(field, queryText, analysisMode, luceneFunction); return query; } finally { internalSlop = 0; } } /** * @param field String * @param sqlLikeClause String * @param analysisMode AnalysisMode * @return the query * @throws ParseException */ public Query getLikeQuery(String field, String sqlLikeClause, AnalysisMode analysisMode) throws ParseException { String luceneWildCardExpression = translate(sqlLikeClause); return getWildcardQuery(field, luceneWildCardExpression, AnalysisMode.LIKE); } private String translate(String string) { StringBuilder builder = new StringBuilder(string.length()); boolean lastWasEscape = false; for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (lastWasEscape) { builder.append(c); lastWasEscape = false; } else { if (c == '\\') { lastWasEscape = true; } else if (c == '%') { builder.append('*'); } else if (c == '_') { builder.append('?'); } else if (c == '*') { builder.append('\\'); builder.append(c); } else if (c == '?') { builder.append('\\'); builder.append(c); } else { builder.append(c); } } } if (lastWasEscape) { throw new FTSQueryException("Escape character at end of string " + string); } return builder.toString(); } /** * @param field String * @param queryText String * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @return the query * @throws ParseException */ public Query getDoesNotMatchFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { BooleanQuery query = new BooleanQuery(); Query allQuery = new MatchAllDocsQuery(); Query matchQuery = getFieldQuery(field, queryText, analysisMode, luceneFunction); if ((matchQuery != null)) { query.add(allQuery, Occur.MUST); query.add(matchQuery, Occur.MUST_NOT); } else { throw new UnsupportedOperationException(); } return query; } public Query getFieldQuery(String field, String queryText) throws ParseException { return getFieldQuery(field, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } /** * @param field String * @param first String * @param last String * @param slop int * @param inOrder boolean * @return the query */ public Query getSpanQuery(String field, String first, String last, int slop, boolean inOrder) { if (field.equals(FIELD_PATH)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATH); } else if (field.equals(FIELD_PATHWITHREPEATS)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_PATHWITHREPEATS); } else if (field.equals(FIELD_TEXT)) { Set<String> text = searchParameters.getTextAttributes(); if ((text == null) || (text.size() == 0)) { Query query = getSpanQuery(PROPERTY_FIELD_PREFIX + ContentModel.PROP_CONTENT.toString(), first, last, slop, inOrder); return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : text) { Query part = getSpanQuery(fieldName, first, last, slop, inOrder); query.add(part, Occur.SHOULD); } return query; } } else if (field.equals(FIELD_CLASS)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_CLASS); } else if (field.equals(FIELD_TYPE)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_TYPE); } else if (field.equals(FIELD_EXACTTYPE)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTTYPE); } else if (field.equals(FIELD_ASPECT)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_ASPECT); } else if (field.equals(FIELD_EXACTASPECT)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_EXACTASPECT); } else if (field.startsWith(PROPERTY_FIELD_PREFIX)) { return spanQueryBuilder(field, first, last, slop, inOrder); } else if (field.equals(FIELD_ALL)) { Set<String> all = searchParameters.getAllAttributes(); if ((all == null) || (all.size() == 0)) { Collection<QName> contentAttributes = dictionaryService.getAllProperties(null); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); query.add(part, Occur.SHOULD); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : all) { Query part = getSpanQuery(fieldName, first, last, slop, inOrder); query.add(part, Occur.SHOULD); } return query; } } else if (field.equals(FIELD_ISUNSET)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISUNSET); } else if (field.equals(FIELD_ISNULL)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNULL); } else if (field.equals(FIELD_ISNOTNULL)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_ISNOTNULL); } else if (matchDataTypeDefinition(field) != null) { Collection<QName> contentAttributes = dictionaryService .getAllProperties(matchDataTypeDefinition(field).getName()); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { Query part = getSpanQuery(PROPERTY_FIELD_PREFIX + qname.toString(), first, last, slop, inOrder); query.add(part, Occur.SHOULD); } return query; } else if (field.equals(FIELD_FTSSTATUS)) { throw new UnsupportedOperationException("Span is not supported for " + FIELD_FTSSTATUS); } else if (field.equals(FIELD_TAG)) { return null; } else { // Default behaviour for the following fields // FIELD_ID // FIELD_DBID // FIELD_ISROOT // FIELD_ISCONTAINER // FIELD_ISNODE // FIELD_TX // FIELD_PARENT // FIELD_PRIMARYPARENT // FIELD_QNAME // FIELD_PRIMARYASSOCTYPEQNAME // FIELD_ASSOCTYPEQNAME // // SpanQuery firstTerm = new SpanTermQuery(new Term(field, first)); SpanQuery lastTerm = new SpanTermQuery(new Term(field, last)); return new SpanNearQuery(new SpanQuery[] { firstTerm, lastTerm }, slop, inOrder); } } private DataTypeDefinition matchDataTypeDefinition(String string) { QName search = QName.createQName( QueryParserUtils.expandQName(searchParameters.getNamespace(), namespacePrefixResolver, string)); DataTypeDefinition dataTypeDefinition = dictionaryService.getDataType(QName.createQName( QueryParserUtils.expandQName(searchParameters.getNamespace(), namespacePrefixResolver, string))); QName match = null; if (dataTypeDefinition == null) { for (QName definition : dictionaryService.getAllDataTypes()) { if (definition.getNamespaceURI().equalsIgnoreCase(search.getNamespaceURI())) { if (definition.getLocalName().equalsIgnoreCase(search.getLocalName())) { if (match == null) { match = definition; } else { throw new LuceneQueryParserException("Ambiguous data datype " + string); } } } } } else { return dataTypeDefinition; } if (match == null) { return null; } else { return dictionaryService.getDataType(match); } } private PropertyDefinition matchPropertyDefinition(String string) { QName search = QName.createQName( QueryParserUtils.expandQName(searchParameters.getNamespace(), namespacePrefixResolver, string)); PropertyDefinition propertyDefinition = dictionaryService.getProperty(QName.createQName( QueryParserUtils.expandQName(searchParameters.getNamespace(), namespacePrefixResolver, string))); QName match = null; if (propertyDefinition == null) { for (QName definition : dictionaryService.getAllProperties(null)) { if (definition.getNamespaceURI().equalsIgnoreCase(search.getNamespaceURI())) { if (definition.getLocalName().equalsIgnoreCase(search.getLocalName())) { if (match == null) { match = definition; } else { throw new LuceneQueryParserException("Ambiguous data datype " + string); } } } } } else { return propertyDefinition; } if (match == null) { return null; } else { return dictionaryService.getProperty(match); } } /** * @param field String * @param queryText String * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @return the query * @throws ParseException */ public Query getFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { try { if (field.equals(FIELD_PATH)) { return createPathQuery(queryText, false); } else if (field.equals(FIELD_PATHWITHREPEATS)) { return createPathQuery(queryText, true); } else if (field.equals(FIELD_TEXT)) { return createTextQuery(queryText, analysisMode, luceneFunction); } else if (field.equals(FIELD_ID)) { return createIdQuery(queryText); } else if (field.equals(FIELD_DBID)) { return createDbidQuery(queryText); } else if (field.equals(FIELD_ACLID)) { return createAclIdQuery(queryText); } else if (field.equals(FIELD_OWNER)) { return createOwnerQuery(queryText); } else if (field.equals(FIELD_OWNERSET)) { return createOwnerSetQuery(queryText); } else if (field.equals(FIELD_READER)) { return createReaderQuery(queryText); } else if (field.equals(FIELD_READERSET)) { return createReaderSetQuery(queryText); } else if (field.equals(FIELD_AUTHORITY)) { return createAuthorityQuery(queryText); } else if (field.equals(FIELD_AUTHORITYSET)) { return createAuthoritySetQuery(queryText); } else if (field.equals(FIELD_ISROOT)) { return createIsRootQuery(queryText); } else if (field.equals(FIELD_ISCONTAINER)) { return createIsContainerQuery(queryText); } else if (field.equals(FIELD_ISNODE)) { return createIsNodeQuery(queryText); } else if (field.equals(FIELD_TX)) { return createTransactionQuery(queryText); } else if (field.equals(FIELD_INTXID)) { return createInTxIdQuery(queryText); } else if (field.equals(FIELD_INACLTXID)) { return createInAclTxIdQuery(queryText); } else if (field.equals(FIELD_PARENT)) { return createParentQuery(queryText); } else if (field.equals(FIELD_PRIMARYPARENT)) { return createPrimaryParentQuery(queryText); } else if (field.equals(FIELD_QNAME)) { return createQNameQuery(queryText); } else if (field.equals(FIELD_PRIMARYASSOCQNAME)) { return createPrimaryAssocQNameQuery(queryText); } else if (field.equals(FIELD_PRIMARYASSOCTYPEQNAME)) { return createPrimaryAssocTypeQNameQuery(queryText); } else if (field.equals(FIELD_ASSOCTYPEQNAME)) { return createAssocTypeQNameQuery(queryText); } else if (field.equals(FIELD_CLASS)) { ClassDefinition target = QueryParserUtils.matchClassDefinition(searchParameters.getNamespace(), namespacePrefixResolver, dictionaryService, queryText); if (target == null) { throw new LuceneQueryParserException("Invalid type: " + queryText); } return getFieldQuery(target.isAspect() ? FIELD_ASPECT : FIELD_TYPE, queryText, analysisMode, luceneFunction); } else if (field.equals(FIELD_TYPE)) { return createTypeQuery(queryText, false); } else if (field.equals(FIELD_EXACTTYPE)) { return createTypeQuery(queryText, true); } else if (field.equals(FIELD_ASPECT)) { return createAspectQuery(queryText, false); } else if (field.equals(FIELD_EXACTASPECT)) { return createAspectQuery(queryText, true); } else if (field.startsWith(PROPERTY_FIELD_PREFIX)) { Query query = attributeQueryBuilder(field, queryText, new FieldQuery(), analysisMode, luceneFunction); return query; } else if (field.equals(FIELD_ALL)) { return createAllQuery(queryText, analysisMode, luceneFunction); } else if (field.equals(FIELD_ISUNSET)) { return createIsUnsetQuery(queryText, analysisMode, luceneFunction); } else if (field.equals(FIELD_ISNULL)) { return createIsNullQuery(queryText, analysisMode, luceneFunction); } else if (field.equals(FIELD_ISNOTNULL)) { return createIsNotNull(queryText, analysisMode, luceneFunction); } else if (matchDataTypeDefinition(field) != null) { return createDataTypeDefinitionQuery(field, queryText, analysisMode, luceneFunction); } else if (field.equals(FIELD_FTSSTATUS)) { return createTermQuery(field, queryText); } else if (field.equals(FIELD_TXID)) { return createTxIdQuery(queryText); } else if (field.equals(FIELD_ACLTXID)) { return createAclTxIdQuery(queryText); } else if (field.equals(FIELD_TXCOMMITTIME)) { return createTxCommitTimeQuery(queryText); } else if (field.equals(FIELD_ACLTXCOMMITTIME)) { return createAclTxCommitTimeQuery(queryText); } else if (field.equals(FIELD_TAG)) { return createTagQuery(queryText); } else if (field.equals(FIELD_SITE)) { return createSiteQuery(queryText); } else if (field.equals(FIELD_TENANT)) { return createTenantQuery(queryText); } else if (field.equals(FIELD_ANCESTOR)) { return createAncestorQuery(queryText); } else { return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); } } catch (SAXPathException e) { throw new ParseException("Failed to parse XPath...\n" + e.getMessage()); } } protected Query createTenantQuery(String queryText) throws ParseException { if (queryText.length() > 0) { return getFieldQueryImpl(FIELD_TENANT, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } else { return getFieldQueryImpl(FIELD_TENANT, "_DEFAULT_", AnalysisMode.DEFAULT, LuceneFunction.FIELD); } } protected Query createAncestorQuery(String queryText) throws ParseException { return createNodeRefQuery(FIELD_ANCESTOR, queryText); } /** * @param tag (which will then be ISO9075 encoded) * @return Query * @throws ParseException */ protected Query createTagQuery(String tag) throws ParseException { return getFieldQuery(FIELD_PATH, "/cm:taggable/cm:" + ISO9075.encode(tag.toLowerCase()) + "/member"); } protected Query createSiteQuery(String site) throws ParseException { if (site.equals("_EVERYTHING_")) { return createTermQuery(FIELD_ISNODE, "T"); } else if (site.equals("_ALL_SITES_")) { return getFieldQuery(FIELD_PATH, "/app:company_home/st:sites/*//*"); } else if (site.equals("_REPOSITORY_")) { return createTermQuery(FIELD_ISNODE, "T"); } else { return getFieldQuery(FIELD_PATH, "/app:company_home/st:sites/cm:" + ISO9075.encode(site) + "//*"); } } /** * @param queryText String * @return Query */ protected abstract Query createAclIdQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected abstract Query createOwnerQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected abstract Query createReaderQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected abstract Query createAuthorityQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected abstract Query createOwnerSetQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected abstract Query createReaderSetQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected abstract Query createAuthoritySetQuery(String queryText) throws ParseException; /** * @param queryText String * @return Query */ protected Query createDbidQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_DBID, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createTxIdQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_TXID, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createAclTxIdQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_ACLTXID, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createTxCommitTimeQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_TXCOMMITTIME, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createAclTxCommitTimeQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_ACLTXCOMMITTIME, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createDataTypeDefinitionQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { Collection<QName> contentAttributes = dictionaryService .getAllProperties(matchDataTypeDefinition(field).getName()); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), queryText, analysisMode, luceneFunction); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } protected Query createIsNotNull(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { PropertyDefinition pd = matchPropertyDefinition(queryText); if (pd != null) { ClassDefinition containerClass = pd.getContainerClass(); QName container = containerClass.getName(); BooleanQuery query = new BooleanQuery(); String classType = containerClass.isAspect() ? FIELD_ASPECT : FIELD_TYPE; Query typeQuery = getFieldQuery(classType, container.toString(), analysisMode, luceneFunction); Query presenceQuery = getWildcardQuery(PROPERTY_FIELD_PREFIX + pd.getName().toString(), "*"); if ((typeQuery != null) && (presenceQuery != null)) { // query.add(typeQuery, Occur.MUST); query.add(presenceQuery, Occur.MUST); } return query; } else { return getFieldQueryImpl(FIELD_ISNOTNULL, queryText, analysisMode, luceneFunction); } } protected Query createIsNullQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { PropertyDefinition pd = matchPropertyDefinition(queryText); if (pd != null) { BooleanQuery query = new BooleanQuery(); Query presenceQuery = getWildcardQuery(PROPERTY_FIELD_PREFIX + pd.getName().toString(), "*"); if (presenceQuery != null) { query.add(new MatchAllDocsQuery(), Occur.MUST); query.add(presenceQuery, Occur.MUST_NOT); } return query; } else { return getFieldQueryImpl(FIELD_ISNULL, queryText, analysisMode, luceneFunction); } } protected Query createIsUnsetQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { PropertyDefinition pd = matchPropertyDefinition(queryText); if (pd != null) { ClassDefinition containerClass = pd.getContainerClass(); QName container = containerClass.getName(); BooleanQuery query = new BooleanQuery(); String classType = containerClass.isAspect() ? FIELD_ASPECT : FIELD_TYPE; Query typeQuery = getFieldQuery(classType, container.toString(), analysisMode, luceneFunction); Query presenceQuery = getWildcardQuery(PROPERTY_FIELD_PREFIX + pd.getName().toString(), "*"); if ((typeQuery != null) && (presenceQuery != null)) { query.add(typeQuery, Occur.MUST); query.add(presenceQuery, Occur.MUST_NOT); } return query; } else { return getFieldQueryImpl(FIELD_ISUNSET, queryText, analysisMode, luceneFunction); } } protected Query createAllQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { Set<String> all = searchParameters.getAllAttributes(); if ((all == null) || (all.size() == 0)) { Collection<QName> contentAttributes = dictionaryService.getAllProperties(null); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getFieldQuery(PROPERTY_FIELD_PREFIX + qname.toString(), queryText, analysisMode, luceneFunction); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : all) { Query part = getFieldQuery(fieldName, queryText, analysisMode, luceneFunction); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } protected Query createAspectQuery(String queryText, boolean exactOnly) { AspectDefinition target = QueryParserUtils.matchAspectDefinition(searchParameters.getNamespace(), namespacePrefixResolver, dictionaryService, queryText); if (target == null) { // failed to find the aspect in the dictionary throw new AlfrescoRuntimeException("Unknown aspect specified in query: " + queryText); } if (exactOnly) { QName targetQName = target.getName(); TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, targetQName.toString())); return termQuery; } else { Collection<QName> subclasses = dictionaryService.getSubAspects(target.getName(), true); BooleanQuery booleanQuery = new BooleanQuery(); for (QName qname : subclasses) { AspectDefinition current = dictionaryService.getAspect(qname); if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) { TermQuery termQuery = new TermQuery(new Term(FIELD_ASPECT, qname.toString())); if (termQuery != null) { booleanQuery.add(termQuery, Occur.SHOULD); } } } return booleanQuery; } } protected Query createTypeQuery(String queryText, boolean exactOnly) { TypeDefinition target = QueryParserUtils.matchTypeDefinition(searchParameters.getNamespace(), namespacePrefixResolver, dictionaryService, queryText); if (target == null) { throw new LuceneQueryParserException("Invalid type: " + queryText); } if (exactOnly) { QName targetQName = target.getName(); TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, targetQName.toString())); return termQuery; } else { Collection<QName> subclasses = dictionaryService.getSubTypes(target.getName(), true); BooleanQuery booleanQuery = new BooleanQuery(); for (QName qname : subclasses) { TypeDefinition current = dictionaryService.getType(qname); if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) { TermQuery termQuery = new TermQuery(new Term(FIELD_TYPE, qname.toString())); if (termQuery != null) { booleanQuery.add(termQuery, Occur.SHOULD); } } } return booleanQuery; } } protected abstract Query createAssocTypeQNameQuery(String queryText) throws SAXPathException; protected abstract Query createPrimaryAssocTypeQNameQuery(String queryText) throws SAXPathException; protected abstract Query createPrimaryAssocQNameQuery(String queryText) throws SAXPathException; protected abstract Query createQNameQuery(String queryText) throws SAXPathException; protected Query createInTxIdQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_INTXID, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createInAclTxIdQuery(String queryText) throws ParseException { return getFieldQueryImpl(FIELD_INACLTXID, queryText, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } protected Query createTransactionQuery(String queryText) { return createTermQuery(FIELD_TX, queryText); } protected Query createIsNodeQuery(String queryText) { return createTermQuery(FIELD_ISNODE, queryText); } protected Query createIsContainerQuery(String queryText) { return createTermQuery(FIELD_ISCONTAINER, queryText); } protected Query createIsRootQuery(String queryText) { return createTermQuery(FIELD_ISROOT, queryText); } protected Query createTermQuery(String field, String queryText) { TermQuery termQuery = new TermQuery(new Term(field, queryText)); return termQuery; } protected Query createPrimaryParentQuery(String queryText) { return createNodeRefQuery(FIELD_PRIMARYPARENT, queryText); } protected Query createParentQuery(String queryText) { return createNodeRefQuery(FIELD_PARENT, queryText); } protected Query createIdQuery(String queryText) { return createNodeRefQuery(FIELD_ID, queryText); } protected Query createNodeRefQuery(String field, String queryText) { if (tenantService.isTenantUser() && (queryText.contains(StoreRef.URI_FILLER))) { // assume NodeRef, since it contains StorRef URI filler queryText = tenantService.getName(new NodeRef(queryText)).toString(); } return createTermQuery(field, queryText); } abstract protected Query createPathQuery(String queryText, boolean withRepeats) throws SAXPathException; protected Query createTextQuery(String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { Set<String> text = searchParameters.getTextAttributes(); if ((text == null) || (text.size() == 0)) { Query query = getFieldQuery(PROPERTY_FIELD_PREFIX + ContentModel.PROP_CONTENT.toString(), queryText, analysisMode, luceneFunction); if (query == null) { return createNoMatchQuery(); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : text) { Query part = getFieldQuery(fieldName, queryText, analysisMode, luceneFunction); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } @SuppressWarnings("unchecked") protected Query getFieldQueryImpl(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { // Use the analyzer to get all the tokens, and then build a TermQuery, // PhraseQuery, or noth // TODO: Untokenised columns with functions require special handling if (luceneFunction != LuceneFunction.FIELD) { throw new UnsupportedOperationException( "Field queries are not supported on lucene functions (UPPER, LOWER, etc)"); } // if the incoming string already has a language identifier we strip it iff and addit back on again String localePrefix = ""; String toTokenise = queryText; if (queryText.startsWith("{")) { int position = queryText.indexOf("}"); String language = queryText.substring(0, position + 1); Locale locale = new Locale(queryText.substring(1, position)); String token = queryText.substring(position + 1); boolean found = false; if (!locale.toString().isEmpty()) { for (Locale current : Locale.getAvailableLocales()) { if (current.toString().equalsIgnoreCase(locale.toString())) { found = true; break; } } } if (found) { localePrefix = language; toTokenise = token; } else { toTokenise = token; } } String testText = toTokenise; boolean requiresMLTokenDuplication = false; String localeString = null; if (field.startsWith(PROPERTY_FIELD_PREFIX) && (localePrefix.length() == 0)) { if ((queryText.length() > 0) && (queryText.charAt(0) == '\u0000')) { int position = queryText.indexOf("\u0000", 1); testText = queryText.substring(position + 1); requiresMLTokenDuplication = true; localeString = queryText.substring(1, position); } } // find the positions of any escaped * and ? and ignore them Set<Integer> wildcardPoistions = getWildcardPositions(testText); TokenStream source; if ((localePrefix.length() == 0) || (wildcardPoistions.size() > 0) || (analysisMode == AnalysisMode.IDENTIFIER)) { source = getAnalyzer().tokenStream(field, new StringReader(toTokenise), analysisMode); } else { source = getAnalyzer().tokenStream(field, new StringReader( "\u0000" + localePrefix.substring(1, localePrefix.length() - 1) + "\u0000" + toTokenise), analysisMode); localePrefix = ""; } ArrayList<org.apache.lucene.analysis.Token> list = new ArrayList<org.apache.lucene.analysis.Token>(); org.apache.lucene.analysis.Token reusableToken = new org.apache.lucene.analysis.Token(); org.apache.lucene.analysis.Token nextToken; int positionCount = 0; boolean severalTokensAtSamePosition = false; while (true) { try { nextToken = source.next(reusableToken); } catch (IOException e) { nextToken = null; } if (nextToken == null) break; list.add((org.apache.lucene.analysis.Token) nextToken.clone()); if (nextToken.getPositionIncrement() != 0) positionCount += nextToken.getPositionIncrement(); else severalTokensAtSamePosition = true; } try { source.close(); } catch (IOException e) { // ignore } // add any alpha numeric wildcards that have been missed // Fixes most stop word and wild card issues for (int index = 0; index < testText.length(); index++) { char current = testText.charAt(index); if (((current == '*') || (current == '?')) && wildcardPoistions.contains(index)) { StringBuilder pre = new StringBuilder(10); if (index == 0) { // "*" and "?" at the start boolean found = false; for (int j = 0; j < list.size(); j++) { org.apache.lucene.analysis.Token test = list.get(j); if ((test.startOffset() <= 0) && (0 < test.endOffset())) { found = true; break; } } if (!found && (testText.length() == 1)) { // Add new token followed by * not given by the tokeniser org.apache.lucene.analysis.Token newToken = new org.apache.lucene.analysis.Token(0, 0); newToken.setTermBuffer(""); newToken.setType("ALPHANUM"); if (requiresMLTokenDuplication) { Locale locale = I18NUtil.parseLocale(localeString); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, mlAnalysisMode); Iterator<org.apache.lucene.analysis.Token> it = duplicator.buildIterator(newToken); if (it != null) { int count = 0; while (it.hasNext()) { list.add(it.next()); count++; if (count > 1) { severalTokensAtSamePosition = true; } } } } // content else { list.add(newToken); } } } else if (index > 0) { // Add * and ? back into any tokens from which it has been removed boolean tokenFound = false; for (int j = 0; j < list.size(); j++) { org.apache.lucene.analysis.Token test = list.get(j); if ((test.startOffset() <= index) && (index < test.endOffset())) { if (requiresMLTokenDuplication) { String termText = new String(test.termBuffer(), 0, test.termLength()); int position = termText.indexOf("}"); String language = termText.substring(0, position + 1); String token = termText.substring(position + 1); if (index >= test.startOffset() + token.length()) { test.setTermBuffer(language + token + current); } } else { if (index >= test.startOffset() + test.termLength()) { test.setTermBuffer(test.term() + current); } } tokenFound = true; break; } } if (!tokenFound) { for (int i = index - 1; i >= 0; i--) { char c = testText.charAt(i); if (Character.isLetterOrDigit(c)) { boolean found = false; for (int j = 0; j < list.size(); j++) { org.apache.lucene.analysis.Token test = list.get(j); if ((test.startOffset() <= i) && (i < test.endOffset())) { found = true; break; } } if (found) { break; } else { pre.insert(0, c); } } else { break; } } if (pre.length() > 0) { // Add new token followed by * not given by the tokeniser org.apache.lucene.analysis.Token newToken = new org.apache.lucene.analysis.Token( index - pre.length(), index); newToken.setTermBuffer(pre.toString()); newToken.setType("ALPHANUM"); if (requiresMLTokenDuplication) { Locale locale = I18NUtil.parseLocale(localeString); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, mlAnalysisMode); Iterator<org.apache.lucene.analysis.Token> it = duplicator.buildIterator(newToken); if (it != null) { int count = 0; while (it.hasNext()) { list.add(it.next()); count++; if (count > 1) { severalTokensAtSamePosition = true; } } } } // content else { list.add(newToken); } } } } StringBuilder post = new StringBuilder(10); if (index > 0) { for (int i = index + 1; i < testText.length(); i++) { char c = testText.charAt(i); if (Character.isLetterOrDigit(c)) { boolean found = false; for (int j = 0; j < list.size(); j++) { org.apache.lucene.analysis.Token test = list.get(j); if ((test.startOffset() <= i) && (i < test.endOffset())) { found = true; break; } } if (found) { break; } else { post.append(c); } } else { break; } } if (post.length() > 0) { // Add new token followed by * not given by the tokeniser org.apache.lucene.analysis.Token newToken = new org.apache.lucene.analysis.Token(index + 1, index + 1 + post.length()); newToken.setTermBuffer(post.toString()); newToken.setType("ALPHANUM"); if (requiresMLTokenDuplication) { Locale locale = I18NUtil.parseLocale(localeString); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); MLTokenDuplicator duplicator = new MLTokenDuplicator(locale, mlAnalysisMode); Iterator<org.apache.lucene.analysis.Token> it = duplicator.buildIterator(newToken); if (it != null) { int count = 0; while (it.hasNext()) { list.add(it.next()); count++; if (count > 1) { severalTokensAtSamePosition = true; } } } } // content else { list.add(newToken); } } } } } Collections.sort(list, new Comparator<org.apache.lucene.analysis.Token>() { public int compare(Token o1, Token o2) { int dif = o1.startOffset() - o2.startOffset(); if (dif != 0) { return dif; } else { return o2.getPositionIncrement() - o1.getPositionIncrement(); } } }); // Combined * and ? based strings - should redo the tokeniser // Build tokens by position LinkedList<LinkedList<org.apache.lucene.analysis.Token>> tokensByPosition = new LinkedList<LinkedList<org.apache.lucene.analysis.Token>>(); LinkedList<org.apache.lucene.analysis.Token> currentList = null; for (org.apache.lucene.analysis.Token c : list) { if (c.getPositionIncrement() == 0) { if (currentList == null) { currentList = new LinkedList<org.apache.lucene.analysis.Token>(); tokensByPosition.add(currentList); } currentList.add(c); } else { currentList = new LinkedList<org.apache.lucene.analysis.Token>(); tokensByPosition.add(currentList); currentList.add(c); } } // Build all the token sequences and see which ones get strung together LinkedList<LinkedList<org.apache.lucene.analysis.Token>> allTokenSequences = new LinkedList<LinkedList<org.apache.lucene.analysis.Token>>(); for (LinkedList<org.apache.lucene.analysis.Token> tokensAtPosition : tokensByPosition) { if (allTokenSequences.size() == 0) { for (org.apache.lucene.analysis.Token t : tokensAtPosition) { LinkedList<org.apache.lucene.analysis.Token> newEntry = new LinkedList<org.apache.lucene.analysis.Token>(); newEntry.add(t); allTokenSequences.add(newEntry); } } else { LinkedList<LinkedList<org.apache.lucene.analysis.Token>> newAllTokeSequences = new LinkedList<LinkedList<org.apache.lucene.analysis.Token>>(); FOR_FIRST_TOKEN_AT_POSITION_ONLY: for (org.apache.lucene.analysis.Token t : tokensAtPosition) { boolean tokenFoundSequence = false; for (LinkedList<org.apache.lucene.analysis.Token> tokenSequence : allTokenSequences) { LinkedList<org.apache.lucene.analysis.Token> newEntry = new LinkedList<org.apache.lucene.analysis.Token>(); newEntry.addAll(tokenSequence); if (newEntry.getLast().endOffset() <= t.startOffset()) { newEntry.add(t); tokenFoundSequence = true; } newAllTokeSequences.add(newEntry); } if (false == tokenFoundSequence) { LinkedList<org.apache.lucene.analysis.Token> newEntry = new LinkedList<org.apache.lucene.analysis.Token>(); newEntry.add(t); newAllTokeSequences.add(newEntry); } // Limit the max number of permutations we consider if (newAllTokeSequences.size() > 64) { break FOR_FIRST_TOKEN_AT_POSITION_ONLY; } } allTokenSequences = newAllTokeSequences; } } // build the uniquie LinkedList<LinkedList<org.apache.lucene.analysis.Token>> fixedTokenSequences = new LinkedList<LinkedList<org.apache.lucene.analysis.Token>>(); for (LinkedList<org.apache.lucene.analysis.Token> tokenSequence : allTokenSequences) { LinkedList<org.apache.lucene.analysis.Token> fixedTokenSequence = new LinkedList<org.apache.lucene.analysis.Token>(); fixedTokenSequences.add(fixedTokenSequence); org.apache.lucene.analysis.Token replace = null; for (org.apache.lucene.analysis.Token c : tokenSequence) { if (replace == null) { StringBuilder prefix = new StringBuilder(); for (int i = c.startOffset() - 1; i >= 0; i--) { char test = testText.charAt(i); if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) { prefix.insert(0, test); } else { break; } } String pre = prefix.toString(); if (requiresMLTokenDuplication) { String termText = new String(c.termBuffer(), 0, c.termLength()); int position = termText.indexOf("}"); String language = termText.substring(0, position + 1); String token = termText.substring(position + 1); replace = new org.apache.lucene.analysis.Token(c.startOffset() - pre.length(), c.endOffset()); replace.setTermBuffer(language + pre + token); replace.setType(c.type()); replace.setPositionIncrement(c.getPositionIncrement()); } else { String termText = new String(c.termBuffer(), 0, c.termLength()); replace = new org.apache.lucene.analysis.Token(c.startOffset() - pre.length(), c.endOffset()); replace.setTermBuffer(pre + termText); replace.setType(c.type()); replace.setPositionIncrement(c.getPositionIncrement()); } } else { StringBuilder prefix = new StringBuilder(); StringBuilder postfix = new StringBuilder(); StringBuilder builder = prefix; for (int i = c.startOffset() - 1; i >= replace.endOffset(); i--) { char test = testText.charAt(i); if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) { builder.insert(0, test); } else { builder = postfix; postfix.setLength(0); } } String pre = prefix.toString(); String post = postfix.toString(); // Does it bridge? if ((pre.length() > 0) && (replace.endOffset() + pre.length()) == c.startOffset()) { String termText = new String(c.termBuffer(), 0, c.termLength()); if (requiresMLTokenDuplication) { int position = termText.indexOf("}"); @SuppressWarnings("unused") String language = termText.substring(0, position + 1); String token = termText.substring(position + 1); int oldPositionIncrement = replace.getPositionIncrement(); String replaceTermText = new String(replace.termBuffer(), 0, replace.termLength()); replace = new org.apache.lucene.analysis.Token(replace.startOffset(), c.endOffset()); replace.setTermBuffer(replaceTermText + pre + token); replace.setType(replace.type()); replace.setPositionIncrement(oldPositionIncrement); } else { int oldPositionIncrement = replace.getPositionIncrement(); String replaceTermText = new String(replace.termBuffer(), 0, replace.termLength()); replace = new org.apache.lucene.analysis.Token(replace.startOffset(), c.endOffset()); replace.setTermBuffer(replaceTermText + pre + termText); replace.setType(replace.type()); replace.setPositionIncrement(oldPositionIncrement); } } else { String termText = new String(c.termBuffer(), 0, c.termLength()); if (requiresMLTokenDuplication) { int position = termText.indexOf("}"); String language = termText.substring(0, position + 1); String token = termText.substring(position + 1); String replaceTermText = new String(replace.termBuffer(), 0, replace.termLength()); org.apache.lucene.analysis.Token last = new org.apache.lucene.analysis.Token( replace.startOffset(), replace.endOffset() + post.length()); last.setTermBuffer(replaceTermText + post); last.setType(replace.type()); last.setPositionIncrement(replace.getPositionIncrement()); fixedTokenSequence.add(last); replace = new org.apache.lucene.analysis.Token(c.startOffset() - pre.length(), c.endOffset()); replace.setTermBuffer(language + pre + token); replace.setType(c.type()); replace.setPositionIncrement(c.getPositionIncrement()); } else { String replaceTermText = new String(replace.termBuffer(), 0, replace.termLength()); org.apache.lucene.analysis.Token last = new org.apache.lucene.analysis.Token( replace.startOffset(), replace.endOffset() + post.length()); last.setTermBuffer(replaceTermText + post); last.setType(replace.type()); last.setPositionIncrement(replace.getPositionIncrement()); fixedTokenSequence.add(last); replace = new org.apache.lucene.analysis.Token(c.startOffset() - pre.length(), c.endOffset()); replace.setTermBuffer(pre + termText); replace.setType(c.type()); replace.setPositionIncrement(c.getPositionIncrement()); } } } } // finish last if (replace != null) { StringBuilder postfix = new StringBuilder(); if ((replace.endOffset() >= 0) && (replace.endOffset() < testText.length())) { for (int i = replace.endOffset(); i < testText.length(); i++) { char test = testText.charAt(i); if (((test == '*') || (test == '?')) && wildcardPoistions.contains(i)) { postfix.append(test); } else { break; } } } String post = postfix.toString(); int oldPositionIncrement = replace.getPositionIncrement(); String replaceTermText = new String(replace.termBuffer(), 0, replace.termLength()); replace = new org.apache.lucene.analysis.Token(replace.startOffset(), replace.endOffset() + post.length()); replace.setTermBuffer(replaceTermText + post); replace.setType(replace.type()); replace.setPositionIncrement(oldPositionIncrement); fixedTokenSequence.add(replace); } } // rebuild fixed list ArrayList<org.apache.lucene.analysis.Token> fixed = new ArrayList<org.apache.lucene.analysis.Token>(); for (LinkedList<org.apache.lucene.analysis.Token> tokenSequence : fixedTokenSequences) { for (org.apache.lucene.analysis.Token token : tokenSequence) { fixed.add(token); } } // reorder by start position and increment Collections.sort(fixed, new Comparator<org.apache.lucene.analysis.Token>() { public int compare(Token o1, Token o2) { int dif = o1.startOffset() - o2.startOffset(); if (dif != 0) { return dif; } else { return o1.getPositionIncrement() - o2.getPositionIncrement(); } } }); // make sure we remove any tokens we have duplicated @SuppressWarnings("rawtypes") OrderedHashSet unique = new OrderedHashSet(); unique.addAll(fixed); fixed = new ArrayList<org.apache.lucene.analysis.Token>(unique); list = fixed; // add any missing locales back to the tokens if (localePrefix.length() > 0) { for (int j = 0; j < list.size(); j++) { org.apache.lucene.analysis.Token currentToken = list.get(j); String termText = new String(currentToken.termBuffer(), 0, currentToken.termLength()); currentToken.setTermBuffer(localePrefix + termText); } } if (list.size() == 0) return null; else if (list.size() == 1) { nextToken = list.get(0); String termText = new String(nextToken.termBuffer(), 0, nextToken.termLength()); if (termText.contains("*") || termText.contains("?")) { return newWildcardQuery( new Term(field, getLowercaseExpandedTerms() ? termText.toLowerCase() : termText)); } else { return newTermQuery(new Term(field, termText)); } } else { if (severalTokensAtSamePosition) { if (positionCount == 1) { // no phrase query: BooleanQuery q = newBooleanQuery(true); for (int i = 0; i < list.size(); i++) { Query currentQuery; nextToken = list.get(i); String termText = new String(nextToken.termBuffer(), 0, nextToken.termLength()); if (termText.contains("*") || termText.contains("?")) { currentQuery = newWildcardQuery(new Term(field, getLowercaseExpandedTerms() ? termText.toLowerCase() : termText)); } else { currentQuery = newTermQuery(new Term(field, termText)); } q.add(currentQuery, BooleanClause.Occur.SHOULD); } return q; } // Consider if we can use a multi-phrase query (e.g for synonym use rather then WordDelimiterFilterFactory) else if (canUseMultiPhraseQuery(fixedTokenSequences)) { // phrase query: MultiPhraseQuery mpq = newMultiPhraseQuery(); mpq.setSlop(internalSlop); ArrayList<Term> multiTerms = new ArrayList<Term>(); int position = 0; for (int i = 0; i < list.size(); i++) { nextToken = list.get(i); String termText = new String(nextToken.termBuffer(), 0, nextToken.termLength()); Term term = new Term(field, termText); if ((termText != null) && (termText.contains("*") || termText.contains("?"))) { addWildcardTerms(multiTerms, term); } else { multiTerms.add(term); } if (nextToken.getPositionIncrement() > 0 && multiTerms.size() > 0) { if (getEnablePositionIncrements()) { mpq.add(multiTerms.toArray(new Term[0]), position); } else { mpq.add(multiTerms.toArray(new Term[0])); } checkTermCount(field, queryText, mpq); multiTerms.clear(); } position += nextToken.getPositionIncrement(); } if (getEnablePositionIncrements()) { if (multiTerms.size() > 0) { mpq.add(multiTerms.toArray(new Term[0]), position); } // else // { // mpq.add(new Term[] { new Term(field, "\u0000") }, position); // } } else { if (multiTerms.size() > 0) { mpq.add(multiTerms.toArray(new Term[0])); } // else // { // mpq.add(new Term[] { new Term(field, "\u0000") }); // } } checkTermCount(field, queryText, mpq); return mpq; } // Word delimiter factory and other odd things generate complex token patterns // Smart skip token sequences with small tokens that generate toomany wildcards // Fall back to the larger pattern // e.g Site1* will not do (S ite 1*) or (Site 1*) if 1* matches too much (S ite1*) and (Site1*) will still be OK // If we skip all (for just 1* in the input) this is still an issue. else { boolean skippedTokens = false; BooleanQuery q = newBooleanQuery(true); TOKEN_SEQUENCE: for (LinkedList<org.apache.lucene.analysis.Token> tokenSequence : fixedTokenSequences) { // phrase query: MultiPhraseQuery mpq = newMultiPhraseQuery(); mpq.setSlop(internalSlop); int position = 0; for (int i = 0; i < tokenSequence.size(); i++) { nextToken = (org.apache.lucene.analysis.Token) tokenSequence.get(i); String termText = new String(nextToken.termBuffer(), 0, nextToken.termLength()); Term term = new Term(field, termText); if (getEnablePositionIncrements()) { if ((termText != null) && (termText.contains("*") || termText.contains("?"))) { mpq.add(getMatchingTerms(field, term), position); } else { mpq.add(new Term[] { term }, position); } if (exceedsTermCount(mpq)) { // We could duplicate the token sequence without the failing wildcard expansion and try again ?? skippedTokens = true; continue TOKEN_SEQUENCE; } if (nextToken.getPositionIncrement() > 0) { position += nextToken.getPositionIncrement(); } else { position++; } } else { if ((termText != null) && (termText.contains("*") || termText.contains("?"))) { mpq.add(getMatchingTerms(field, term)); } else { mpq.add(term); } if (exceedsTermCount(mpq)) { skippedTokens = true; continue TOKEN_SEQUENCE; } } } q.add(mpq, BooleanClause.Occur.SHOULD); } if (skippedTokens && (q.clauses().size() == 0)) { throw new LuceneQueryParserException( "Query skipped all token sequences as wildcards generated too many clauses: " + field + " " + queryText); } return q; } } else { MultiPhraseQuery q = new MultiPhraseQuery(); q.setSlop(internalSlop); int position = 0; for (int i = 0; i < list.size(); i++) { nextToken = list.get(i); String termText = new String(nextToken.termBuffer(), 0, nextToken.termLength()); Term term = new Term(field, termText); if (getEnablePositionIncrements()) { if ((termText != null) && (termText.contains("*") || termText.contains("?"))) { q.add(getMatchingTerms(field, term), position); } else { q.add(new Term[] { term }, position); } checkTermCount(field, queryText, q); if (nextToken.getPositionIncrement() > 0) { position += nextToken.getPositionIncrement(); } else { position++; } } else { if ((termText != null) && (termText.contains("*") || termText.contains("?"))) { q.add(getMatchingTerms(field, term)); } else { q.add(term); } checkTermCount(field, queryText, q); } } return q; } } } /** * @param field String * @param queryText String * @param mpq MultiPhraseQuery */ private void checkTermCount(String field, String queryText, MultiPhraseQuery mpq) { if (exceedsTermCount(mpq)) { throw new LuceneQueryParserException( "Wildcard has generated too many clauses: " + field + " " + queryText); } } /** * * @param mpq MultiPhraseQuery * @return boolean */ private boolean exceedsTermCount(MultiPhraseQuery mpq) { int termCount = 0; for (Iterator<?> iter = mpq.getTermArrays().iterator(); iter.hasNext(); /**/) { Term[] arr = (Term[]) iter.next(); termCount += arr.length; if (termCount > BooleanQuery.getMaxClauseCount()) { return true; } } return false; } /** * @param fixedTokenSequences LinkedList<LinkedList<Token>> * @return boolean */ private boolean canUseMultiPhraseQuery(LinkedList<LinkedList<Token>> fixedTokenSequences) { if (fixedTokenSequences.size() <= 1) { return true; } LinkedList<Token> first = fixedTokenSequences.get(0); for (int i = 1; i < fixedTokenSequences.size(); i++) { LinkedList<Token> current = fixedTokenSequences.get(i); if (first.size() != current.size()) { return false; } for (int j = 0; j < first.size(); j++) { Token fromFirst = first.get(j); Token fromCurrent = current.get(j); if (fromFirst.startOffset() != fromCurrent.startOffset()) { return false; } } } return true; } private Set<Integer> getWildcardPositions(String string) { HashSet<Integer> wildcardPositions = new HashSet<Integer>(); boolean lastWasEscape = false; for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (lastWasEscape) { lastWasEscape = false; } else { if (c == '\\') { lastWasEscape = true; } else if (c == '*') { wildcardPositions.add(i); } else if (c == '?') { wildcardPositions.add(i); } } } return wildcardPositions; } private Term[] getMatchingTerms(String field, Term term) throws ParseException { ArrayList<Term> terms = new ArrayList<Term>(); addWildcardTerms(terms, term); if (terms.size() == 0) { return new Term[] { new Term(field, "\u0000") }; } else { return terms.toArray(new Term[0]); } } private void addWildcardTerms(ArrayList<Term> terms, Term term) throws ParseException { try { Term searchTerm = term; if (getLowercaseExpandedTerms()) { searchTerm = new Term(term.field(), term.text().toLowerCase()); } WildcardTermEnum wcte = new WildcardTermEnum(indexReader, searchTerm); do { Term current = wcte.term(); if (current != null) { if ((current.text() != null) && (current.text().length() > 0) && (current.text().charAt(0) == '{')) { if ((searchTerm != null) && (searchTerm.text().length() > 0) && (searchTerm.text().charAt(0) == '{')) { terms.add(current); } // If not, we cod not add so wildcards do not match the locale prefix } else { terms.add(current); } } } while (wcte.next()); } catch (IOException e) { throw new ParseException("IO error generating phares wildcards " + e.getMessage()); } } /** * @exception ParseException * throw in overridden method to disallow */ protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException { return getRangeQuery(field, part1, part2, inclusive, inclusive, AnalysisMode.DEFAULT, LuceneFunction.FIELD); } /** * @param field String * @param part1 String * @param part2 String * @param includeLower boolean * @param includeUpper boolean * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @return the query * @exception ParseException * throw in overridden method to disallow */ public Query getRangeQuery(String field, String part1, String part2, boolean includeLower, boolean includeUpper, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { if (field.equals(FIELD_PATH)) { throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATH); } else if (field.equals(FIELD_PATHWITHREPEATS)) { throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_PATHWITHREPEATS); } else if (field.equals(FIELD_TEXT)) { Set<String> text = searchParameters.getTextAttributes(); if ((text == null) || (text.size() == 0)) { Query query = getRangeQuery(PROPERTY_FIELD_PREFIX + ContentModel.PROP_CONTENT.toString(), part1, part2, includeLower, includeUpper, analysisMode, luceneFunction); if (query == null) { return createNoMatchQuery(); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : text) { Query part = getRangeQuery(fieldName, part1, part2, includeLower, includeUpper, analysisMode, luceneFunction); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } // FIELD_ID uses the default // FIELD_DBID uses the default // FIELD_ISROOT uses the default // FIELD_ISCONTAINER uses the default // FIELD_ISNODE uses the default // FIELD_TX uses the default // FIELD_PARENT uses the default // FIELD_PRIMARYPARENT uses the default // FIELD_QNAME uses the default // FIELD_PRIMARYASSOCTYPEQNAME uses the default // FIELD_ASSOCTYPEQNAME uses the default // FIELD_CLASS uses the default // FIELD_TYPE uses the default // FIELD_EXACTTYPE uses the default // FIELD_ASPECT uses the default // FIELD_EXACTASPECT uses the default // FIELD_TYPE uses the default // FIELD_TYPE uses the default if (field.startsWith(PROPERTY_FIELD_PREFIX)) { String fieldName; PropertyDefinition propertyDef = matchPropertyDefinition(field.substring(1)); if (propertyDef != null) { fieldName = PROPERTY_FIELD_PREFIX + propertyDef.getName(); } else { fieldName = expandAttributeFieldName(field); } IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; if (propertyDef != null) { tokenisationMode = propertyDef.getIndexTokenisationMode(); if (tokenisationMode == null) { tokenisationMode = IndexTokenisationMode.TRUE; } } if (propertyDef != null) { // LOWER AND UPPER if (luceneFunction != LuceneFunction.FIELD) { if (luceneFunction == LuceneFunction.LOWER) { if ((false == part1.toLowerCase().equals(part1)) || (false == part2.toLowerCase().equals(part2))) { return createNoMatchQuery(); } } if (luceneFunction == LuceneFunction.UPPER) { if ((false == part1.toUpperCase().equals(part1)) || (false == part2.toUpperCase().equals(part2))) { return createNoMatchQuery(); } } if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) { BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addLocaleSpecificUntokenisedTextRangeFunction(fieldName, part1, part2, includeLower, includeUpper, luceneFunction, booleanQuery, mlAnalysisMode, locale, tokenisationMode); } return booleanQuery; } else { throw new UnsupportedOperationException("Lucene Function"); } } if (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT)) { throw new UnsupportedOperationException("Range is not supported against ml-text"); } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)) { throw new UnsupportedOperationException("Range is not supported against content"); } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT)) { BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addTextRange(field, part1, part2, includeLower, includeUpper, analysisMode, fieldName, propertyDef, tokenisationMode, booleanQuery, mlAnalysisMode, locale); } return booleanQuery; } else if (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME)) { String analyserClassName = propertyDef.resolveAnalyserClassName(); boolean usesDateTimeAnalyser = analyserClassName .equals(DateTimeAnalyser.class.getCanonicalName()); // Expand query for internal date time format if (usesDateTimeAnalyser) { Calendar start = Calendar.getInstance(); int startResolution; Calendar end = Calendar.getInstance(); int endResolution; try { Pair<Date, Integer> result = CachingDateFormat.lenientParse(part1, Calendar.YEAR); start.setTime(result.getFirst()); startResolution = result.getSecond(); } catch (java.text.ParseException e) { SimpleDateFormat oldDf = CachingDateFormat.getDateFormat(); try { Date date = oldDf.parse(part1); start.setTime(date); start.set(Calendar.MILLISECOND, 0); startResolution = Calendar.SECOND; } catch (java.text.ParseException ee) { if (part1.equalsIgnoreCase("min")) { start.set(Calendar.YEAR, start.getMinimum(Calendar.YEAR)); start.set(Calendar.DAY_OF_YEAR, start.getMinimum(Calendar.DAY_OF_YEAR)); start.set(Calendar.HOUR_OF_DAY, start.getMinimum(Calendar.HOUR_OF_DAY)); start.set(Calendar.MINUTE, start.getMinimum(Calendar.MINUTE)); start.set(Calendar.SECOND, start.getMinimum(Calendar.SECOND)); start.set(Calendar.MILLISECOND, start.getMinimum(Calendar.MILLISECOND)); startResolution = Calendar.MILLISECOND; } else if (part1.equalsIgnoreCase("now")) { start.setTime(new Date()); startResolution = Calendar.MILLISECOND; } else if (part1.equalsIgnoreCase("today")) { start.setTime(new Date()); start.set(Calendar.HOUR_OF_DAY, start.getMinimum(Calendar.HOUR_OF_DAY)); start.set(Calendar.MINUTE, start.getMinimum(Calendar.MINUTE)); start.set(Calendar.SECOND, start.getMinimum(Calendar.SECOND)); start.set(Calendar.MILLISECOND, start.getMinimum(Calendar.MILLISECOND)); startResolution = Calendar.DAY_OF_MONTH; } else { return createNoMatchQuery(); } } } try { Pair<Date, Integer> result = CachingDateFormat.lenientParse(part2, Calendar.YEAR); end.setTime(result.getFirst()); endResolution = result.getSecond(); } catch (java.text.ParseException e) { SimpleDateFormat oldDf = CachingDateFormat.getDateFormat(); try { Date date = oldDf.parse(part2); end.setTime(date); end.set(Calendar.MILLISECOND, 0); endResolution = Calendar.SECOND; } catch (java.text.ParseException ee) { if (part2.equalsIgnoreCase("max")) { end.set(Calendar.YEAR, end.getMaximum(Calendar.YEAR)); end.set(Calendar.DAY_OF_YEAR, end.getMaximum(Calendar.DAY_OF_YEAR)); end.set(Calendar.HOUR_OF_DAY, end.getMaximum(Calendar.HOUR_OF_DAY)); end.set(Calendar.MINUTE, end.getMaximum(Calendar.MINUTE)); end.set(Calendar.SECOND, end.getMaximum(Calendar.SECOND)); end.set(Calendar.MILLISECOND, end.getMaximum(Calendar.MILLISECOND)); endResolution = Calendar.MILLISECOND; } else if (part2.equalsIgnoreCase("now")) { end.setTime(new Date()); endResolution = Calendar.MILLISECOND; } else if (part2.equalsIgnoreCase("today")) { end.setTime(new Date()); end.set(Calendar.HOUR_OF_DAY, end.getMinimum(Calendar.HOUR_OF_DAY)); end.set(Calendar.MINUTE, end.getMinimum(Calendar.MINUTE)); end.set(Calendar.SECOND, end.getMinimum(Calendar.SECOND)); end.set(Calendar.MILLISECOND, end.getMinimum(Calendar.MILLISECOND)); endResolution = Calendar.DAY_OF_MONTH; } else { return createNoMatchQuery(); } } } // Build a composite query for all the bits Query rq = buildDateTimeRange(fieldName, start, startResolution, end, endResolution, includeLower, includeUpper); return rq; } else { // Old Date time String first = getToken(fieldName, part1, AnalysisMode.DEFAULT); String last = getToken(fieldName, part2, AnalysisMode.DEFAULT); return new ConstantScoreRangeQuery(fieldName, first, last, includeLower, includeUpper); } } else { // Default property behaviour String first = getToken(fieldName, part1, AnalysisMode.DEFAULT); String last = getToken(fieldName, part2, AnalysisMode.DEFAULT); return new ConstantScoreRangeQuery(fieldName, first, last, includeLower, includeUpper); } } else { // No DD def String first = getToken(fieldName, part1, AnalysisMode.DEFAULT); String last = getToken(fieldName, part2, AnalysisMode.DEFAULT); return new ConstantScoreRangeQuery(fieldName, first, last, includeLower, includeUpper); } } else if (field.equals(FIELD_ALL)) { Set<String> all = searchParameters.getAllAttributes(); if ((all == null) || (all.size() == 0)) { Collection<QName> contentAttributes = dictionaryService.getAllProperties(null); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, includeUpper, analysisMode, luceneFunction); query.add(part, Occur.SHOULD); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : all) { Query part = getRangeQuery(fieldName, part1, part2, includeLower, includeUpper, analysisMode, luceneFunction); query.add(part, Occur.SHOULD); } return query; } } // FIELD_ISUNSET uses the default // FIELD_ISNULL uses the default // FIELD_ISNOTNULL uses the default else if (matchDataTypeDefinition(field) != null) { Collection<QName> contentAttributes = dictionaryService .getAllProperties(matchDataTypeDefinition(field).getName()); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { Query part = getRangeQuery(PROPERTY_FIELD_PREFIX + qname.toString(), part1, part2, includeLower, includeUpper, analysisMode, luceneFunction); query.add(part, Occur.SHOULD); } return query; } // FIELD_FTSSTATUS uses the default if (field.equals(FIELD_TAG)) { throw new UnsupportedOperationException("Range Queries are not support for " + FIELD_TAG); } else { // None property - leave alone if (getLowercaseExpandedTerms()) { part1 = part1.toLowerCase(); part2 = part2.toLowerCase(); } return new ConstantScoreRangeQuery(field, part1, part2, includeLower, includeUpper); } } /** * @param field String * @param part1 String * @param part2 String * @param includeLower boolean * @param includeUpper boolean * @param analysisMode AnalysisMode * @param fieldName String * @param propertyDef PropertyDefinition * @param tokenisationMode IndexTokenisationMode * @param booleanQuery BooleanQuery * @param mlAnalysisMode MLAnalysisMode * @param locale Locale * @throws ParseException */ protected abstract void addTextRange(String field, String part1, String part2, boolean includeLower, boolean includeUpper, AnalysisMode analysisMode, String fieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale) throws ParseException; protected abstract void addLocaleSpecificUntokenisedTextRangeFunction(String expandedFieldName, String lower, String upper, boolean includeLower, boolean includeUpper, LuceneFunction luceneFunction, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale, IndexTokenisationMode tokenisationMode) throws ParseException; // private Query buildRangeFunctionQuery(String expandedFieldName, String lowerTermText, String upperTermText, // boolean includeLower, boolean includeUpper, // LuceneFunction luceneFunction) // { // String testLowerTermText = lowerTermText; // if (testLowerTermText.startsWith("{")) // { // int index = lowerTermText.indexOf("}"); // testLowerTermText = lowerTermText.substring(index + 1); // } // // String testUpperTermText = upperTermText; // if (testUpperTermText.startsWith("{")) // { // int index = upperTermText.indexOf("}"); // testUpperTermText = upperTermText.substring(index + 1); // } // // switch (luceneFunction) // { // case LOWER: // if (testLowerTermText.equals(testLowerTermText.toLowerCase()) && // testUpperTermText.equals(testUpperTermText.toLowerCase())) // { // return new CaseInsensitiveFieldRangeQuery(expandedFieldName, lowerTermText, upperTermText, includeLower, // includeUpper); // } // else // { // // No match // return createNoMatchQuery(); // } // case UPPER: // if (testLowerTermText.equals(testLowerTermText.toUpperCase()) && // testUpperTermText.equals(testUpperTermText.toUpperCase())) // { // return new CaseInsensitiveFieldRangeQuery(expandedFieldName, lowerTermText, upperTermText, includeLower, // includeUpper); // } // else // { // // No match // return createNoMatchQuery(); // } // default: // throw new UnsupportedOperationException("Unsupported Lucene Function " + luceneFunction); // // } // } // private void addLocaleSpecificTokenisedTextRange(String part1, String part2, boolean includeLower, boolean // includeUpper, AnalysisMode analysisMode, String fieldName, // BooleanQuery booleanQuery, Locale locale, String textFieldName) throws ParseException // { // StringBuilder builder = new StringBuilder(); // builder.append("\u0000").append(locale.toString()).append("\u0000").append(part1); // String first = getToken(fieldName, builder.toString(), analysisMode); // // builder = new StringBuilder(); // builder.append("\u0000").append(locale.toString()).append("\u0000").append(part2); // String last = getToken(fieldName, builder.toString(), analysisMode); // // Query query = new ConstantScoreRangeQuery(textFieldName, first, last, includeLower, includeUpper); // booleanQuery.add(query, Occur.SHOULD); // } // // private void addLocaleSpecificUntokenisedTextRange(String field, String part1, String part2, boolean // includeLower, boolean includeUpper, BooleanQuery booleanQuery, // MLAnalysisMode mlAnalysisMode, Locale locale, String textFieldName) // { // String lower = part1; // String upper = part2; // if (locale.toString().length() > 0) // { // lower = "{" + locale + "}" + part1; // upper = "{" + locale + "}" + part2; // } // // Query subQuery = new ConstantScoreRangeQuery(textFieldName, lower, upper, includeLower, includeUpper); // booleanQuery.add(subQuery, Occur.SHOULD); // // if (booleanQuery.getClauses().length == 0) // { // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); // } // } protected Query buildDateTimeRange(String field, Calendar startIn, int startResolution, Calendar endIn, int endResolution, boolean includeLower, boolean includeUpper) throws ParseException { int minResolution = (startResolution <= endResolution) ? startResolution : endResolution; // fix start and end dates and treat all as inclusive ranges Calendar start = Calendar.getInstance(); start.setTime(startIn.getTime()); if (!includeLower) { start.add(startResolution, 1); } Calendar end = Calendar.getInstance(); end.setTime(endIn.getTime()); if (!includeUpper) { end.add(endResolution, -1); } // Calendar comparison does not work for MAX .... joy if (start.get(Calendar.YEAR) > end.get(Calendar.YEAR)) { return createNoMatchQuery(); } else if (start.get(Calendar.YEAR) == end.get(Calendar.YEAR)) { if (start.get(Calendar.MONTH) > end.get(Calendar.MONTH)) { return createNoMatchQuery(); } else if (start.get(Calendar.MONTH) == end.get(Calendar.MONTH)) { if (start.get(Calendar.DAY_OF_MONTH) > end.get(Calendar.DAY_OF_MONTH)) { return createNoMatchQuery(); } else if (start.get(Calendar.DAY_OF_MONTH) == end.get(Calendar.DAY_OF_MONTH)) { if (start.get(Calendar.HOUR_OF_DAY) > end.get(Calendar.HOUR_OF_DAY)) { return createNoMatchQuery(); } else if (start.get(Calendar.HOUR_OF_DAY) == end.get(Calendar.HOUR_OF_DAY)) { if (start.get(Calendar.MINUTE) > end.get(Calendar.MINUTE)) { return createNoMatchQuery(); } else if (start.get(Calendar.MINUTE) == end.get(Calendar.MINUTE)) { if (start.get(Calendar.SECOND) > end.get(Calendar.SECOND)) { return createNoMatchQuery(); } else if (start.get(Calendar.SECOND) == end.get(Calendar.SECOND)) { if (start.get(Calendar.MILLISECOND) > end.get(Calendar.MILLISECOND)) { return createNoMatchQuery(); } else if (start.get(Calendar.MILLISECOND) == end.get(Calendar.MILLISECOND)) { // continue } } } } } } } BooleanQuery query = new BooleanQuery(); Query part; if ((minResolution > Calendar.YEAR) && (start.get(Calendar.YEAR) == end.get(Calendar.YEAR))) { part = new TermQuery(new Term(field, "YE" + start.get(Calendar.YEAR))); query.add(part, Occur.MUST); if ((minResolution > Calendar.MONTH) && (start.get(Calendar.MONTH) == end.get(Calendar.MONTH))) { part = new TermQuery(new Term(field, build2SF("MO", start.get(Calendar.MONTH)))); query.add(part, Occur.MUST); if ((minResolution > Calendar.DAY_OF_MONTH) && (start.get(Calendar.DAY_OF_MONTH) == end.get(Calendar.DAY_OF_MONTH))) { part = new TermQuery(new Term(field, build2SF("DA", start.get(Calendar.DAY_OF_MONTH)))); query.add(part, Occur.MUST); if ((minResolution > Calendar.HOUR_OF_DAY) && (start.get(Calendar.HOUR_OF_DAY) == end.get(Calendar.HOUR_OF_DAY))) { part = new TermQuery(new Term(field, build2SF("HO", start.get(Calendar.HOUR_OF_DAY)))); query.add(part, Occur.MUST); if ((minResolution > Calendar.MINUTE) && (start.get(Calendar.MINUTE) == end.get(Calendar.MINUTE))) { part = new TermQuery(new Term(field, build2SF("MI", start.get(Calendar.MINUTE)))); query.add(part, Occur.MUST); if ((minResolution > Calendar.SECOND) && (start.get(Calendar.SECOND) == end.get(Calendar.SECOND))) { part = new TermQuery(new Term(field, build2SF("SE", start.get(Calendar.SECOND)))); query.add(part, Occur.MUST); if (minResolution >= Calendar.MILLISECOND) { if (start.get(Calendar.MILLISECOND) == end.get(Calendar.MILLISECOND)) { part = new TermQuery( new Term(field, build3SF("MS", start.get(Calendar.MILLISECOND)))); query.add(part, Occur.MUST); } else { part = new ConstantScoreRangeQuery(field, build3SF("MS", start.get(Calendar.MILLISECOND)), build3SF("MS", end.get(Calendar.MILLISECOND)), true, true); query.add(part, Occur.MUST); } } else { return createNoMatchQuery(); } } else { // s + ms BooleanQuery subQuery = new BooleanQuery(); Query subPart; for (int i : new int[] { Calendar.MILLISECOND }) { subPart = buildStart(field, start, Calendar.SECOND, i, startResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.SECOND < minResolution) { if ((end.get(Calendar.SECOND) - start.get(Calendar.SECOND)) > 1) { subPart = new ConstantScoreRangeQuery(field, build2SF("SE", start.get(Calendar.SECOND)), build2SF("SE", end.get(Calendar.SECOND)), false, false); subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.SECOND == minResolution) { if (start.get(Calendar.SECOND) == end.get(Calendar.SECOND)) { if (includeLower && includeUpper) { part = new TermQuery( new Term(field, build2SF("SE", start.get(Calendar.SECOND)))); query.add(part, Occur.MUST); } else { return createNoMatchQuery(); } } else { subPart = new ConstantScoreRangeQuery(field, build2SF("SE", start.get(Calendar.SECOND)), build2SF("SE", end.get(Calendar.SECOND)), includeLower, includeUpper); subQuery.add(subPart, Occur.SHOULD); } } for (int i : new int[] { Calendar.MILLISECOND }) { subPart = buildEnd(field, end, Calendar.SECOND, i, endResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (subQuery.clauses().size() > 0) { query.add(subQuery, Occur.MUST); } } } else { // min + s + ms BooleanQuery subQuery = new BooleanQuery(); Query subPart; for (int i : new int[] { Calendar.MILLISECOND, Calendar.SECOND }) { subPart = buildStart(field, start, Calendar.MINUTE, i, startResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.MINUTE < minResolution) { if ((end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE)) > 1) { subPart = new ConstantScoreRangeQuery(field, build2SF("MI", start.get(Calendar.MINUTE)), build2SF("MI", end.get(Calendar.MINUTE)), false, false); subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.MINUTE == minResolution) { if (start.get(Calendar.MINUTE) == end.get(Calendar.MINUTE)) { if (includeLower && includeUpper) { part = new TermQuery( new Term(field, build2SF("MI", start.get(Calendar.MINUTE)))); query.add(part, Occur.MUST); } else { return createNoMatchQuery(); } } else { subPart = new ConstantScoreRangeQuery(field, build2SF("MI", start.get(Calendar.MINUTE)), build2SF("MI", end.get(Calendar.MINUTE)), includeLower, includeUpper); subQuery.add(subPart, Occur.SHOULD); } } for (int i : new int[] { Calendar.SECOND, Calendar.MILLISECOND }) { subPart = buildEnd(field, end, Calendar.MINUTE, i, endResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (subQuery.clauses().size() > 0) { query.add(subQuery, Occur.MUST); } } } else { // hr + min + s + ms BooleanQuery subQuery = new BooleanQuery(); Query subPart; for (int i : new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE }) { subPart = buildStart(field, start, Calendar.HOUR_OF_DAY, i, startResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.HOUR_OF_DAY < minResolution) { if ((end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY)) > 1) { subPart = new ConstantScoreRangeQuery(field, build2SF("HO", start.get(Calendar.HOUR_OF_DAY)), build2SF("HO", end.get(Calendar.HOUR_OF_DAY)), false, false); subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.HOUR_OF_DAY == minResolution) { if (start.get(Calendar.HOUR_OF_DAY) == end.get(Calendar.HOUR_OF_DAY)) { if (includeLower && includeUpper) { part = new TermQuery( new Term(field, build2SF("HO", start.get(Calendar.HOUR_OF_DAY)))); query.add(part, Occur.MUST); } else { return createNoMatchQuery(); } } else { subPart = new ConstantScoreRangeQuery(field, build2SF("HO", start.get(Calendar.HOUR_OF_DAY)), build2SF("HO", end.get(Calendar.HOUR_OF_DAY)), includeLower, includeUpper); subQuery.add(subPart, Occur.SHOULD); } } for (int i : new int[] { Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) { subPart = buildEnd(field, end, Calendar.HOUR_OF_DAY, i, endResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (subQuery.clauses().size() > 0) { query.add(subQuery, Occur.MUST); } } } else { // day + hr + min + s + ms BooleanQuery subQuery = new BooleanQuery(); Query subPart; for (int i : new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY }) { subPart = buildStart(field, start, Calendar.DAY_OF_MONTH, i, startResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.DAY_OF_MONTH < minResolution) { if ((end.get(Calendar.DAY_OF_MONTH) - start.get(Calendar.DAY_OF_MONTH)) > 1) { subPart = new ConstantScoreRangeQuery(field, build2SF("DA", start.get(Calendar.DAY_OF_MONTH)), build2SF("DA", end.get(Calendar.DAY_OF_MONTH)), false, false); subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.DAY_OF_MONTH == minResolution) { if (start.get(Calendar.DAY_OF_MONTH) == end.get(Calendar.DAY_OF_MONTH)) { if (includeLower && includeUpper) { part = new TermQuery( new Term(field, build2SF("DA", start.get(Calendar.DAY_OF_MONTH)))); query.add(part, Occur.MUST); } else { return createNoMatchQuery(); } } else { subPart = new ConstantScoreRangeQuery(field, build2SF("DA", start.get(Calendar.DAY_OF_MONTH)), build2SF("DA", end.get(Calendar.DAY_OF_MONTH)), includeLower, includeUpper); subQuery.add(subPart, Occur.SHOULD); } } for (int i : new int[] { Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) { subPart = buildEnd(field, end, Calendar.DAY_OF_MONTH, i, endResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (subQuery.clauses().size() > 0) { query.add(subQuery, Occur.MUST); } } } else { // month + day + hr + min + s + ms BooleanQuery subQuery = new BooleanQuery(); Query subPart; for (int i : new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH }) { subPart = buildStart(field, start, Calendar.MONTH, i, startResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.MONTH < minResolution) { if ((end.get(Calendar.MONTH) - start.get(Calendar.MONTH)) > 1) { subPart = new ConstantScoreRangeQuery(field, build2SF("MO", start.get(Calendar.MONTH)), build2SF("MO", end.get(Calendar.MONTH)), false, false); subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.MONTH == minResolution) { if (start.get(Calendar.MONTH) == end.get(Calendar.MONTH)) { if (includeLower && includeUpper) { part = new TermQuery(new Term(field, build2SF("MO", start.get(Calendar.MONTH)))); query.add(part, Occur.MUST); } else { return createNoMatchQuery(); } } else { subPart = new ConstantScoreRangeQuery(field, build2SF("MO", start.get(Calendar.MONTH)), build2SF("MO", end.get(Calendar.MONTH)), includeLower, includeUpper); subQuery.add(subPart, Occur.SHOULD); } } for (int i : new int[] { Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) { subPart = buildEnd(field, end, Calendar.MONTH, i, endResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (subQuery.clauses().size() > 0) { query.add(subQuery, Occur.MUST); } } } else { // year + month + day + hr + min + s + ms BooleanQuery subQuery = new BooleanQuery(); Query subPart; for (int i : new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH }) { subPart = buildStart(field, start, Calendar.YEAR, i, startResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.YEAR < minResolution) { if ((end.get(Calendar.YEAR) - start.get(Calendar.YEAR)) > 1) { subPart = new ConstantScoreRangeQuery(field, "YE" + start.get(Calendar.YEAR), "YE" + end.get(Calendar.YEAR), false, false); subQuery.add(subPart, Occur.SHOULD); } } if (Calendar.YEAR == minResolution) { if (start.get(Calendar.YEAR) == end.get(Calendar.YEAR)) { if (includeLower && includeUpper) { part = new TermQuery(new Term(field, "YE" + start.get(Calendar.YEAR))); query.add(part, Occur.MUST); } else { return createNoMatchQuery(); } } else { subPart = new ConstantScoreRangeQuery(field, "YE" + start.get(Calendar.YEAR), "YE" + end.get(Calendar.YEAR), includeLower, includeUpper); subQuery.add(subPart, Occur.SHOULD); } } for (int i : new int[] { Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) { subPart = buildEnd(field, end, Calendar.YEAR, i, endResolution); if (subPart != null) { subQuery.add(subPart, Occur.SHOULD); } } if (subQuery.clauses().size() > 0) { query.add(subQuery, Occur.MUST); } } return query; } private Query buildStart(String field, Calendar cal, int startField, int padField, int resolutionField) { BooleanQuery range = new BooleanQuery(); // only ms difference Query part; switch (startField) { case Calendar.YEAR: if ((cal.get(Calendar.YEAR) == 1) && (cal.get(Calendar.MONTH) == 0) && (cal.get(Calendar.DAY_OF_MONTH) == 1) && (cal.get(Calendar.HOUR_OF_DAY) == 0) && (cal.get(Calendar.MINUTE) == 0) && (cal.get(Calendar.SECOND) == 0) && (cal.get(Calendar.MILLISECOND) == 0)) { if (padField == Calendar.MONTH) { if (Calendar.YEAR <= resolutionField) { part = new TermQuery(new Term(field, "YE" + cal.get(Calendar.YEAR))); range.add(part, Occur.MUST); } else { return null; } } break; } else if (padField == Calendar.YEAR) { return null; } else { if (Calendar.YEAR <= resolutionField) { part = new TermQuery(new Term(field, "YE" + cal.get(Calendar.YEAR))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.MONTH: if ((cal.get(Calendar.MONTH) == 0) && (cal.get(Calendar.DAY_OF_MONTH) == 1) && (cal.get(Calendar.HOUR_OF_DAY) == 0) && (cal.get(Calendar.MINUTE) == 0) && (cal.get(Calendar.SECOND) == 0) && (cal.get(Calendar.MILLISECOND) == 0)) { if (padField == Calendar.DAY_OF_MONTH) { if (Calendar.MONTH <= resolutionField) { part = new TermQuery(new Term(field, build2SF("MO", cal.get(Calendar.MONTH)))); range.add(part, Occur.MUST); } else { return null; } } break; } else if (padField == Calendar.MONTH) { if (Calendar.MONTH < resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("MO", (cal.get(Calendar.MONTH) + 1)), "MO" + cal.getMaximum(Calendar.MONTH), true, true); range.add(part, Occur.MUST); } else if (Calendar.MONTH == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("MO", (cal.get(Calendar.MONTH))), "MO" + cal.getMaximum(Calendar.MONTH), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.MONTH <= resolutionField) { part = new TermQuery(new Term(field, build2SF("MO", cal.get(Calendar.MONTH)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.DAY_OF_MONTH: if ((cal.get(Calendar.DAY_OF_MONTH) == 1) && (cal.get(Calendar.HOUR_OF_DAY) == 0) && (cal.get(Calendar.MINUTE) == 0) && (cal.get(Calendar.SECOND) == 0) && (cal.get(Calendar.MILLISECOND) == 0)) { if (padField == Calendar.HOUR_OF_DAY) { if (Calendar.DAY_OF_MONTH <= resolutionField) { part = new TermQuery(new Term(field, build2SF("DA", cal.get(Calendar.DAY_OF_MONTH)))); range.add(part, Occur.MUST); } else { return null; } } break; } else if (padField == Calendar.DAY_OF_MONTH) { if (Calendar.DAY_OF_MONTH < resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("DA", (cal.get(Calendar.DAY_OF_MONTH) + 1)), "DA" + cal.getMaximum(Calendar.DAY_OF_MONTH), true, true); range.add(part, Occur.MUST); } else if (Calendar.DAY_OF_MONTH == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("DA", (cal.get(Calendar.DAY_OF_MONTH))), "DA" + cal.getMaximum(Calendar.DAY_OF_MONTH), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.DAY_OF_MONTH <= resolutionField) { part = new TermQuery(new Term(field, build2SF("DA", cal.get(Calendar.DAY_OF_MONTH)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.HOUR_OF_DAY: if ((cal.get(Calendar.HOUR_OF_DAY) == 0) && (cal.get(Calendar.MINUTE) == 0) && (cal.get(Calendar.SECOND) == 0) && (cal.get(Calendar.MILLISECOND) == 0)) { if (padField == Calendar.MINUTE) { if (Calendar.HOUR_OF_DAY <= resolutionField) { part = new TermQuery(new Term(field, build2SF("HO", cal.get(Calendar.HOUR_OF_DAY)))); range.add(part, Occur.MUST); } else { return null; } } break; } else if (padField == Calendar.HOUR_OF_DAY) { if (Calendar.HOUR_OF_DAY < resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("HO", (cal.get(Calendar.HOUR_OF_DAY) + 1)), "HO" + cal.getMaximum(Calendar.HOUR_OF_DAY), true, true); range.add(part, Occur.MUST); } else if (Calendar.HOUR_OF_DAY == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("HO", (cal.get(Calendar.HOUR_OF_DAY))), "HO" + cal.getMaximum(Calendar.HOUR_OF_DAY), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.HOUR_OF_DAY <= resolutionField) { part = new TermQuery(new Term(field, build2SF("HO", cal.get(Calendar.HOUR_OF_DAY)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.MINUTE: if ((cal.get(Calendar.MINUTE) == 0) && (cal.get(Calendar.SECOND) == 0) && (cal.get(Calendar.MILLISECOND) == 0)) { if (padField == Calendar.SECOND) { if (Calendar.MINUTE <= resolutionField) { part = new TermQuery(new Term(field, build2SF("MI", cal.get(Calendar.MINUTE)))); range.add(part, Occur.MUST); } else { return null; } } break; } else if (padField == Calendar.MINUTE) { if (Calendar.MINUTE < resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("MI", (cal.get(Calendar.MINUTE) + 1)), "MI" + cal.getMaximum(Calendar.MINUTE), true, true); range.add(part, Occur.MUST); } else if (Calendar.MINUTE == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("MI", (cal.get(Calendar.MINUTE))), "MI" + cal.getMaximum(Calendar.MINUTE), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.MINUTE <= resolutionField) { part = new TermQuery(new Term(field, build2SF("MI", cal.get(Calendar.MINUTE)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.SECOND: if ((cal.get(Calendar.SECOND) == 0) && (cal.get(Calendar.MILLISECOND) == 0)) { if (padField == Calendar.MILLISECOND) { if (Calendar.SECOND <= resolutionField) { part = new TermQuery(new Term(field, build2SF("SE", cal.get(Calendar.SECOND)))); range.add(part, Occur.MUST); } else { return null; } } break; } else if (padField == Calendar.SECOND) { if (Calendar.SECOND < resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("SE", (cal.get(Calendar.SECOND) + 1)), "SE" + cal.getMaximum(Calendar.SECOND), true, true); range.add(part, Occur.MUST); } else if (Calendar.SECOND == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("SE", (cal.get(Calendar.SECOND))), "SE" + cal.getMaximum(Calendar.SECOND), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.SECOND <= resolutionField) { part = new TermQuery(new Term(field, build2SF("SE", cal.get(Calendar.SECOND)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.MILLISECOND: if ((cal.get(Calendar.MILLISECOND) > 0) && (cal.get(Calendar.MILLISECOND) <= cal.getMaximum(Calendar.MILLISECOND))) { if (Calendar.MILLISECOND <= resolutionField) { part = new ConstantScoreRangeQuery(field, build3SF("MS", cal.get(Calendar.MILLISECOND)), "MS" + cal.getMaximum(Calendar.MILLISECOND), true, true); range.add(part, Occur.MUST); } else { break; } } default: } return getNonEmptyBooleanQuery(range); } private Query buildEnd(String field, Calendar cal, int startField, int padField, int resolutionField) { BooleanQuery range = new BooleanQuery(); Query part; switch (startField) { case Calendar.YEAR: if (padField == Calendar.YEAR) { if (Calendar.YEAR < resolutionField) { if (cal.get(Calendar.YEAR) > cal.getMinimum(Calendar.YEAR)) { part = new ConstantScoreRangeQuery(field, "YE" + cal.getMinimum(Calendar.YEAR), "YE" + (cal.get(Calendar.YEAR) - 1), true, true); range.add(part, Occur.MUST); } else { return null; } } else if (Calendar.YEAR == resolutionField) { part = new ConstantScoreRangeQuery(field, "YE" + cal.getMinimum(Calendar.YEAR), "YE" + (cal.get(Calendar.YEAR)), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.YEAR <= resolutionField) { part = new TermQuery(new Term(field, "YE" + cal.get(Calendar.YEAR))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.MONTH: if (padField == Calendar.MONTH) { if (Calendar.MONTH < resolutionField) { if (cal.get(Calendar.MONTH) > cal.getMinimum(Calendar.MONTH)) { part = new ConstantScoreRangeQuery(field, build2SF("MO", cal.getMinimum(Calendar.MONTH)), build2SF("MO", (cal.get(Calendar.MONTH) - 1)), true, true); range.add(part, Occur.MUST); } else { return null; } } else if (Calendar.MONTH == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("MO", cal.getMinimum(Calendar.MONTH)), build2SF("MO", (cal.get(Calendar.MONTH))), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.MONTH <= resolutionField) { part = new TermQuery(new Term(field, build2SF("MO", cal.get(Calendar.MONTH)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.DAY_OF_MONTH: if (padField == Calendar.DAY_OF_MONTH) { if (Calendar.DAY_OF_MONTH < resolutionField) { if (cal.get(Calendar.DAY_OF_MONTH) > cal.getMinimum(Calendar.DAY_OF_MONTH)) { part = new ConstantScoreRangeQuery(field, build2SF("DA", cal.getMinimum(Calendar.DAY_OF_MONTH)), build2SF("DA", (cal.get(Calendar.DAY_OF_MONTH) - 1)), true, true); range.add(part, Occur.MUST); } else { return null; } } else if (Calendar.DAY_OF_MONTH == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("DA", cal.getMinimum(Calendar.DAY_OF_MONTH)), build2SF("DA", (cal.get(Calendar.DAY_OF_MONTH))), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.DAY_OF_MONTH <= resolutionField) { part = new TermQuery(new Term(field, build2SF("DA", cal.get(Calendar.DAY_OF_MONTH)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.HOUR_OF_DAY: if (padField == Calendar.HOUR_OF_DAY) { if (Calendar.HOUR_OF_DAY < resolutionField) { if (cal.get(Calendar.HOUR_OF_DAY) > cal.getMinimum(Calendar.HOUR_OF_DAY)) { part = new ConstantScoreRangeQuery(field, build2SF("HO", cal.getMinimum(Calendar.HOUR_OF_DAY)), build2SF("HO", (cal.get(Calendar.HOUR_OF_DAY) - 1)), true, true); range.add(part, Occur.MUST); } else { return null; } } else if (Calendar.HOUR_OF_DAY == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("HO", cal.getMinimum(Calendar.HOUR_OF_DAY)), build2SF("HO", (cal.get(Calendar.HOUR_OF_DAY))), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.HOUR_OF_DAY <= resolutionField) { part = new TermQuery(new Term(field, build2SF("HO", cal.get(Calendar.HOUR_OF_DAY)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.MINUTE: if (padField == Calendar.MINUTE) { if (Calendar.MINUTE < resolutionField) { if (cal.get(Calendar.MINUTE) > cal.getMinimum(Calendar.MINUTE)) { part = new ConstantScoreRangeQuery(field, build2SF("MI", cal.getMinimum(Calendar.MINUTE)), build2SF("MI", (cal.get(Calendar.MINUTE) - 1)), true, true); range.add(part, Occur.MUST); } else { return null; } } else if (Calendar.MINUTE == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("MI", cal.getMinimum(Calendar.MINUTE)), build2SF("MI", (cal.get(Calendar.MINUTE))), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.MINUTE <= resolutionField) { part = new TermQuery(new Term(field, build2SF("MI", cal.get(Calendar.MINUTE)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.SECOND: if (padField == Calendar.SECOND) { if (Calendar.SECOND < resolutionField) { if (cal.get(Calendar.SECOND) > cal.getMinimum(Calendar.SECOND)) { part = new ConstantScoreRangeQuery(field, build2SF("SE", cal.getMinimum(Calendar.SECOND)), build2SF("SE", (cal.get(Calendar.SECOND) - 1)), true, true); range.add(part, Occur.MUST); } else { return null; } } else if (Calendar.SECOND == resolutionField) { part = new ConstantScoreRangeQuery(field, build2SF("SE", cal.getMinimum(Calendar.SECOND)), build2SF("SE", (cal.get(Calendar.SECOND))), true, true); range.add(part, Occur.MUST); } else { return null; } break; } else { if (Calendar.SECOND <= resolutionField) { part = new TermQuery(new Term(field, build2SF("SE", cal.get(Calendar.SECOND)))); range.add(part, Occur.MUST); } else { return null; } } case Calendar.MILLISECOND: if ((cal.get(Calendar.MILLISECOND) >= cal.getMinimum(Calendar.MILLISECOND)) && (cal.get(Calendar.MILLISECOND) < cal.getMaximum(Calendar.MILLISECOND))) { if (Calendar.MILLISECOND <= resolutionField) { part = new ConstantScoreRangeQuery(field, build3SF("MS", cal.getMinimum(Calendar.MILLISECOND)), build3SF("MS", cal.get(Calendar.MILLISECOND)), true, true); range.add(part, Occur.MUST); } else { break; } } default: } return getNonEmptyBooleanQuery(range); } private String build2SF(String prefix, int value) { if (value < 10) { return prefix + "0" + value; } else { return prefix + value; } } private String build3SF(String prefix, int value) { if (value < 10) { return prefix + "00" + value; } else if (value < 100) { return prefix + "0" + value; } else { return prefix + value; } } private String expandAttributeFieldName(String field) { return PROPERTY_FIELD_PREFIX + QueryParserUtils.expandQName(searchParameters.getNamespace(), namespacePrefixResolver, field.substring(1)); } protected String getToken(String field, String value, AnalysisMode analysisMode) throws ParseException { TokenStream source = getAnalyzer().tokenStream(field, new StringReader(value), analysisMode); org.apache.lucene.analysis.Token reusableToken = new org.apache.lucene.analysis.Token(); org.apache.lucene.analysis.Token nextToken; String tokenised = null; while (true) { try { nextToken = source.next(reusableToken); } catch (IOException e) { nextToken = null; } if (nextToken == null) break; tokenised = new String(nextToken.termBuffer(), 0, nextToken.termLength()); } try { source.close(); } catch (IOException e) { } return tokenised; } @Override public Query getPrefixQuery(String field, String termStr) throws ParseException { return getPrefixQuery(field, termStr, AnalysisMode.PREFIX); } public Query getPrefixQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException { if (field.equals(FIELD_PATH)) { throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATH); } else if (field.equals(FIELD_PATHWITHREPEATS)) { throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_PATHWITHREPEATS); } else if (field.equals(FIELD_TEXT)) { Set<String> text = searchParameters.getTextAttributes(); if ((text == null) || (text.size() == 0)) { Query query = getPrefixQuery(PROPERTY_FIELD_PREFIX + ContentModel.PROP_CONTENT.toString(), termStr, analysisMode); if (query == null) { return createNoMatchQuery(); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : text) { Query part = getPrefixQuery(fieldName, termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) { boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); try { setLowercaseExpandedTerms(false); return super.getPrefixQuery(field, termStr); } finally { setLowercaseExpandedTerms(lowercaseExpandedTerms); } } else if (field.equals(FIELD_CLASS)) { return super.getPrefixQuery(field, termStr); // throw new UnsupportedOperationException("Prefix Queries are not support for "+FIELD_CLASS); } else if (field.equals(FIELD_TYPE)) { return super.getPrefixQuery(field, termStr); // throw new UnsupportedOperationException("Prefix Queries are not support for "+FIELD_TYPE); } else if (field.equals(FIELD_EXACTTYPE)) { return super.getPrefixQuery(field, termStr); // throw new UnsupportedOperationException("Prefix Queries are not support for "+FIELD_EXACTTYPE); } else if (field.equals(FIELD_ASPECT)) { return super.getPrefixQuery(field, termStr); // throw new UnsupportedOperationException("Prefix Queries are not support for "+FIELD_ASPECT); } else if (field.equals(FIELD_EXACTASPECT)) { return super.getPrefixQuery(field, termStr); // throw new UnsupportedOperationException("Prefix Queries are not support for "+FIELD_EXACTASPECT); } else if (field.startsWith(PROPERTY_FIELD_PREFIX)) { return attributeQueryBuilder(field, termStr, new PrefixQuery(), analysisMode, LuceneFunction.FIELD); } else if (field.equals(FIELD_ALL)) { Set<String> all = searchParameters.getAllAttributes(); if ((all == null) || (all.size() == 0)) { Collection<QName> contentAttributes = dictionaryService.getAllProperties(null); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : all) { Query part = getPrefixQuery(fieldName, termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } else if (field.equals(FIELD_ISUNSET)) { throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISUNSET); } else if (field.equals(FIELD_ISNULL)) { throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNULL); } else if (field.equals(FIELD_ISNOTNULL)) { throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_ISNOTNULL); } else if (matchDataTypeDefinition(field) != null) { Collection<QName> contentAttributes = dictionaryService .getAllProperties(matchDataTypeDefinition(field).getName()); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getPrefixQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else if (field.equals(FIELD_FTSSTATUS)) { throw new UnsupportedOperationException("Prefix Queries are not support for " + FIELD_FTSSTATUS); } else if (field.equals(FIELD_TAG)) { return null; } else { return super.getPrefixQuery(field, termStr); } } @Override public Query getWildcardQuery(String field, String termStr) throws ParseException { return getWildcardQuery(field, termStr, AnalysisMode.WILD); } public Query getWildcardQuery(String field, String termStr, AnalysisMode analysisMode) throws ParseException { if (field.equals(FIELD_PATH)) { throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_PATH); } else if (field.equals(FIELD_PATHWITHREPEATS)) { throw new UnsupportedOperationException( "Wildcard Queries are not support for " + FIELD_PATHWITHREPEATS); } else if (field.equals(FIELD_TEXT)) { Set<String> text = searchParameters.getTextAttributes(); if ((text == null) || (text.size() == 0)) { Query query = getWildcardQuery(PROPERTY_FIELD_PREFIX + ContentModel.PROP_CONTENT.toString(), termStr, analysisMode); if (query == null) { return createNoMatchQuery(); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : text) { Query part = getWildcardQuery(fieldName, termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) { boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); try { setLowercaseExpandedTerms(false); return super.getWildcardQuery(field, termStr); } finally { setLowercaseExpandedTerms(lowercaseExpandedTerms); } } else if (field.equals(FIELD_CLASS)) { return super.getWildcardQuery(field, termStr); // throw new UnsupportedOperationException("Wildcard Queries are not support for "+FIELD_CLASS); } else if (field.equals(FIELD_TYPE)) { return super.getWildcardQuery(field, termStr); // throw new UnsupportedOperationException("Wildcard Queries are not support for "+FIELD_TYPE); } else if (field.equals(FIELD_EXACTTYPE)) { return super.getWildcardQuery(field, termStr); // throw new UnsupportedOperationException("Wildcard Queries are not support for "+FIELD_EXACTTYPE); } else if (field.equals(FIELD_ASPECT)) { return super.getWildcardQuery(field, termStr); // throw new UnsupportedOperationException("Wildcard Queries are not support for "+FIELD_ASPECT); } else if (field.equals(FIELD_EXACTASPECT)) { return super.getWildcardQuery(field, termStr); // throw new UnsupportedOperationException("Wildcard Queries are not support for "+FIELD_EXACTASPECT); } else if (field.startsWith(PROPERTY_FIELD_PREFIX)) { return attributeQueryBuilder(field, termStr, new WildcardQuery(), analysisMode, LuceneFunction.FIELD); } else if (field.equals(FIELD_ALL)) { Set<String> all = searchParameters.getAllAttributes(); if ((all == null) || (all.size() == 0)) { Collection<QName> contentAttributes = dictionaryService.getAllProperties(null); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : all) { Query part = getWildcardQuery(fieldName, termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } else if (field.equals(FIELD_ISUNSET)) { throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISUNSET); } else if (field.equals(FIELD_ISNULL)) { throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNULL); } else if (field.equals(FIELD_ISNOTNULL)) { throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_ISNOTNULL); } else if (matchDataTypeDefinition(field) != null) { Collection<QName> contentAttributes = dictionaryService .getAllProperties(matchDataTypeDefinition(field).getName()); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getWildcardQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, analysisMode); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else if (field.equals(FIELD_FTSSTATUS)) { throw new UnsupportedOperationException("Wildcard Queries are not support for " + FIELD_FTSSTATUS); } else if (field.equals(FIELD_TAG)) { return null; } else { return super.getWildcardQuery(field, termStr); } } @Override public Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException { if (field.equals(FIELD_PATH)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATH); } else if (field.equals(FIELD_PATHWITHREPEATS)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_PATHWITHREPEATS); } else if (field.equals(FIELD_TEXT)) { Set<String> text = searchParameters.getTextAttributes(); if ((text == null) || (text.size() == 0)) { Query query = getFuzzyQuery(PROPERTY_FIELD_PREFIX + ContentModel.PROP_CONTENT.toString(), termStr, minSimilarity); if (query == null) { return createNoMatchQuery(); } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : text) { Query part = getFuzzyQuery(fieldName, termStr, minSimilarity); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } else if (field.equals(FIELD_ID) || field.equals(FIELD_DBID) || field.equals(FIELD_ISROOT) || field.equals(FIELD_ISCONTAINER) || field.equals(FIELD_ISNODE) || field.equals(FIELD_TX) || field.equals(FIELD_PARENT) || field.equals(FIELD_PRIMARYPARENT) || field.equals(FIELD_QNAME) || field.equals(FIELD_PRIMARYASSOCTYPEQNAME) || field.equals(FIELD_ASSOCTYPEQNAME)) { boolean lowercaseExpandedTerms = getLowercaseExpandedTerms(); try { setLowercaseExpandedTerms(false); return super.getFuzzyQuery(field, termStr, minSimilarity); } finally { setLowercaseExpandedTerms(lowercaseExpandedTerms); } } else if (field.equals(FIELD_CLASS)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_CLASS); } else if (field.equals(FIELD_TYPE)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_TYPE); } else if (field.equals(FIELD_EXACTTYPE)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTTYPE); } else if (field.equals(FIELD_ASPECT)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ASPECT); } else if (field.equals(FIELD_EXACTASPECT)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_EXACTASPECT); } else if (field.startsWith(PROPERTY_FIELD_PREFIX)) { return attributeQueryBuilder(field, termStr, new FuzzyQuery(minSimilarity), AnalysisMode.FUZZY, LuceneFunction.FIELD); } else if (field.equals(FIELD_ALL)) { Set<String> all = searchParameters.getAllAttributes(); if ((all == null) || (all.size() == 0)) { Collection<QName> contentAttributes = dictionaryService.getAllProperties(null); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else { BooleanQuery query = new BooleanQuery(); for (String fieldName : all) { Query part = getFuzzyQuery(fieldName, termStr, minSimilarity); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } } else if (field.equals(FIELD_ISUNSET)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISUNSET); } else if (field.equals(FIELD_ISNULL)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNULL); } else if (field.equals(FIELD_ISNOTNULL)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_ISNOTNULL); } else if (matchDataTypeDefinition(field) != null) { Collection<QName> contentAttributes = dictionaryService .getAllProperties(matchDataTypeDefinition(field).getName()); BooleanQuery query = new BooleanQuery(); for (QName qname : contentAttributes) { // The super implementation will create phrase queries etc if required Query part = getFuzzyQuery(PROPERTY_FIELD_PREFIX + qname.toString(), termStr, minSimilarity); if (part != null) { query.add(part, Occur.SHOULD); } else { query.add(createNoMatchQuery(), Occur.SHOULD); } } return query; } else if (field.equals(FIELD_FTSSTATUS)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_FTSSTATUS); } else if (field.equals(FIELD_TAG)) { throw new UnsupportedOperationException("Fuzzy Queries are not support for " + FIELD_TAG); } else { return super.getFuzzyQuery(field, termStr, minSimilarity); } } /** * @param dictionaryService DictionaryService */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } /** * @param field String * @param queryText String * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @return the query * @throws ParseException */ public Query getSuperFieldQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { return getFieldQueryImpl(field, queryText, analysisMode, luceneFunction); } /** * @param field String * @param termStr String * @param minSimilarity float * @return the query * @throws ParseException */ public Query getSuperFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException { return super.getFuzzyQuery(field, termStr, minSimilarity); } /** * @param field String * @param termStr String * @return the query * @throws ParseException */ public Query getSuperPrefixQuery(String field, String termStr) throws ParseException { return super.getPrefixQuery(field, termStr); } /** * @param field String * @param termStr String * @return the query * @throws ParseException */ public Query getSuperWildcardQuery(String field, String termStr) throws ParseException { return super.getWildcardQuery(field, termStr); } /* * (non-Javadoc) * @see org.apache.lucene.queryParser.QueryParser#newWildcardQuery(org.apache.lucene.index.Term) */ @Override protected Query newWildcardQuery(Term t) { if (t.text().contains("\\")) { String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, SearchLanguageConversion.DEF_REGEX, t.text()); return new RegexQuery(new Term(t.field(), regexp)); } else { return super.newWildcardQuery(t); } } /* * (non-Javadoc) * @see org.apache.lucene.queryParser.QueryParser#newPrefixQuery(org.apache.lucene.index.Term) */ @Override protected Query newPrefixQuery(Term prefix) { if (prefix.text().contains("\\")) { String regexp = SearchLanguageConversion.convert(SearchLanguageConversion.DEF_LUCENE, SearchLanguageConversion.DEF_REGEX, prefix.text()); return new RegexQuery(new Term(prefix.field(), regexp)); } else { return super.newPrefixQuery(prefix); } } public interface SubQuery { /** * @param field String * @param queryText String * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @return the query * @throws ParseException */ Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException; } class FieldQuery implements SubQuery { public Query getQuery(String field, String queryText, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { return getSuperFieldQuery(field, queryText, analysisMode, luceneFunction); } } class FuzzyQuery implements SubQuery { float minSimilarity; FuzzyQuery(float minSimilarity) { this.minSimilarity = minSimilarity; } public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { return getSuperFuzzyQuery(field, translateLocale(termStr), minSimilarity); } } private String translateLocale(String localised) { if (localised.startsWith("\u0000")) { if (localised.startsWith("\u0000\u0000")) { if (localised.length() < 3) { return ""; } else { return localised.substring(2); } } else { int end = localised.indexOf('\u0000', 1); if (end == -1) { return localised; } else { StringBuilder buffer = new StringBuilder(); buffer.append("{"); buffer.append(localised.substring(1, end)); buffer.append("}"); buffer.append(localised.substring(end + 1)); return buffer.toString(); } } } else { return localised; } } class PrefixQuery implements SubQuery { public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { StringBuilder builder = new StringBuilder(termStr.length() + 1); builder.append(termStr); builder.append("*"); return getSuperFieldQuery(field, builder.toString(), analysisMode, luceneFunction); //return getSuperPrefixQuery(field, termStr); } } class WildcardQuery implements SubQuery { public Query getQuery(String field, String termStr, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { return getSuperFieldQuery(field, termStr, analysisMode, luceneFunction); //return getSuperWildcardQuery(field, termStr); } } private Query spanQueryBuilder(String field, String first, String last, int slop, boolean inOrder) { String propertyFieldName = field.substring(1); String expandedFieldName; PropertyDefinition propertyDef = matchPropertyDefinition(propertyFieldName); IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; if (propertyDef != null) { tokenisationMode = propertyDef.getIndexTokenisationMode(); if (tokenisationMode == null) { tokenisationMode = IndexTokenisationMode.TRUE; } QName propertyQName = propertyDef.getName(); expandedFieldName = PROPERTY_FIELD_PREFIX + propertyQName; } else { expandedFieldName = expandAttributeFieldName(field); } if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) { // Build a sub query for each locale and or the results together - the analysis will take care of // cross language matching for each entry BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addMLTextSpanQuery(field, first, last, slop, inOrder, expandedFieldName, propertyDef, tokenisationMode, booleanQuery, mlAnalysisMode, locale); } return booleanQuery; } // Content else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll( MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); } return addContentSpanQuery(field, first, last, slop, inOrder, expandedFieldName, expandedLocales, mlAnalysisMode); } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) { BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addTextSpanQuery(field, first, last, slop, inOrder, expandedFieldName, tokenisationMode, booleanQuery, mlAnalysisMode, locale); } return booleanQuery; } else { throw new UnsupportedOperationException( "Span queries are only supported for d:text, d:mltext and d:content data types"); } // need to build each term for the span //SpanQuery firstTerm = new SpanTermQuery(new Term(field, first)); //SpanQuery lastTerm = new SpanTermQuery(new Term(field, last)); //return new SpanNearQuery(new SpanQuery[] { firstTerm, lastTerm }, slop, inOrder); } /** * @param field String * @param first String * @param last String * @param slop int * @param inOrder boolean * @param expandedFieldName String * @param tokenisationMode IndexTokenisationMode * @param booleanQuery BooleanQuery * @param mlAnalysisMode MLAnalysisMode * @param locale Locale */ protected abstract void addTextSpanQuery(String field, String first, String last, int slop, boolean inOrder, String expandedFieldName, IndexTokenisationMode tokenisationMode, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale); /** * @param field String * @param first String * @param last String * @param slop int * @param inOrder boolean * @param expandedFieldName String * @param mlAnalysisMode MLAnalysisMode * @return org.apache.lucene.search.Query */ protected abstract org.apache.lucene.search.Query addContentSpanQuery(String field, String first, String last, int slop, boolean inOrder, String expandedFieldName, List<Locale> expandedLocales, MLAnalysisMode mlAnalysisMode); /** * @param field String * @param first String * @param last String * @param slop int * @param inOrder boolean * @param expandedFieldName String * @param propertyDef PropertyDefinition * @param tokenisationMode IndexTokenisationMode * @param booleanQuery BooleanQuery * @param mlAnalysisMode MLAnalysisMode * @param locale Locale */ protected abstract void addMLTextSpanQuery(String field, String first, String last, int slop, boolean inOrder, String expandedFieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale); private Query attributeQueryBuilder(String field, String queryText, SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction) throws ParseException { // TODO: Fix duplicate token generation for mltext, content and text. // -locale expansion here and in tokeisation -> duplicates // Get type info etc // TODO: additional suffixes String propertyFieldName = null; String ending = ""; if (field.endsWith(FIELD_MIMETYPE_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_MIMETYPE_SUFFIX.length()); ending = FIELD_MIMETYPE_SUFFIX; } else if (field.endsWith(FIELD_SIZE_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_SIZE_SUFFIX.length()); ending = FIELD_SIZE_SUFFIX; } else if (field.endsWith(FIELD_LOCALE_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_LOCALE_SUFFIX.length()); ending = FIELD_LOCALE_SUFFIX; } else if (field.endsWith(FIELD_ENCODING_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_ENCODING_SUFFIX.length()); ending = FIELD_ENCODING_SUFFIX; } else if (field.endsWith(FIELD_CONTENT_DOC_ID_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_CONTENT_DOC_ID_SUFFIX.length()); ending = FIELD_CONTENT_DOC_ID_SUFFIX; } else if (field.endsWith(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_TRANSFORMATION_EXCEPTION_SUFFIX.length()); ending = FIELD_TRANSFORMATION_EXCEPTION_SUFFIX; } else if (field.endsWith(FIELD_TRANSFORMATION_TIME_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_TRANSFORMATION_TIME_SUFFIX.length()); ending = FIELD_TRANSFORMATION_TIME_SUFFIX; } else if (field.endsWith(FIELD_TRANSFORMATION_STATUS_SUFFIX)) { propertyFieldName = field.substring(1, field.length() - FIELD_TRANSFORMATION_STATUS_SUFFIX.length()); ending = FIELD_TRANSFORMATION_STATUS_SUFFIX; } else { propertyFieldName = field.substring(1); } String expandedFieldName; QName propertyQName; PropertyDefinition propertyDef = matchPropertyDefinition(propertyFieldName); IndexTokenisationMode tokenisationMode = IndexTokenisationMode.TRUE; if (propertyDef != null) { tokenisationMode = propertyDef.getIndexTokenisationMode(); if (tokenisationMode == null) { tokenisationMode = IndexTokenisationMode.TRUE; } expandedFieldName = PROPERTY_FIELD_PREFIX + propertyDef.getName() + ending; propertyQName = propertyDef.getName(); } else { expandedFieldName = expandAttributeFieldName(field); propertyQName = QName.createQName(propertyFieldName); } if (luceneFunction != LuceneFunction.FIELD) { if ((tokenisationMode == IndexTokenisationMode.FALSE) || (tokenisationMode == IndexTokenisationMode.BOTH)) { if (luceneFunction == LuceneFunction.LOWER) { if (false == queryText.toLowerCase().equals(queryText)) { return createNoMatchQuery(); } } if (luceneFunction == LuceneFunction.UPPER) { if (false == queryText.toUpperCase().equals(queryText)) { return createNoMatchQuery(); } } return functionQueryBuilder(expandedFieldName, propertyQName, propertyDef, tokenisationMode, queryText, luceneFunction); } } // Mime type if (expandedFieldName.endsWith(FIELD_MIMETYPE_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } else if (expandedFieldName.endsWith(FIELD_SIZE_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } else if (expandedFieldName.endsWith(FIELD_LOCALE_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } else if (expandedFieldName.endsWith(FIELD_ENCODING_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } else if (expandedFieldName.endsWith(FIELD_TRANSFORMATION_STATUS_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } else if (expandedFieldName.endsWith(FIELD_TRANSFORMATION_TIME_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } else if (expandedFieldName.endsWith(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } // Already in expanded form // ML if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) { // Build a sub query for each locale and or the results together - the analysis will take care of // cross language matching for each entry BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addMLTextAttributeQuery(field, queryText, subQueryBuilder, analysisMode, luceneFunction, expandedFieldName, propertyDef, tokenisationMode, booleanQuery, mlAnalysisMode, locale); } return getNonEmptyBooleanQuery(booleanQuery); } // Content else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { // Identifier request are ignored for content // Build a sub query for each locale and or the results together - // - add an explicit condition for the locale MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll( MLAnalysisMode.getLocales(mlAnalysisMode, locale, addContentCrossLocaleWildcards())); } return addContentAttributeQuery(queryText, subQueryBuilder, analysisMode, luceneFunction, expandedFieldName, expandedLocales, mlAnalysisMode); } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) { if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) { // nasty work around for solr support for user and group look up as we can not support lowercased identifiers in the model if (isLucene()) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } } boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addTextAttributeQuery(field, queryText, subQueryBuilder, analysisMode, luceneFunction, expandedFieldName, tokenisationMode, booleanQuery, mlAnalysisMode, locale); } return getNonEmptyBooleanQuery(booleanQuery); } else { // Date does not support like if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.DATETIME))) { if (analysisMode == AnalysisMode.LIKE) { throw new UnsupportedOperationException("Wild cards are not supported for the datetime type"); } } if ((propertyDef != null) && (tenantService.isTenantUser()) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) && (queryText.contains(StoreRef.URI_FILLER))) { // ALF-6202 queryText = tenantService.getName(new NodeRef(queryText)).toString(); } // Sort and id is only special for MLText, text, and content // Dates are not special in this case Query query = subQueryBuilder.getQuery(expandedFieldName, queryText, AnalysisMode.DEFAULT, luceneFunction); if (query != null) { return query; } else { return createNoMatchQuery(); } } } /** * @return boolean */ protected abstract boolean isLucene(); /** * @param field String * @param queryText String * @param subQueryBuilder SubQuery * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @param expandedFieldName String * @param tokenisationMode IndexTokenisationMode * @param booleanQuery BooleanQuery * @param mlAnalysisMode MLAnalysisMode * @param locale Locale * @throws ParseException */ protected abstract void addTextAttributeQuery(String field, String queryText, SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, IndexTokenisationMode tokenisationMode, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale) throws ParseException; /** * @param queryText String * @param subQueryBuilder SubQuery * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @param expandedFieldName String * @return Query * @throws ParseException */ protected abstract Query addContentAttributeQuery(String queryText, SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, List<Locale> expandedLocales, MLAnalysisMode mlAnalysisMode) throws ParseException; /** * @param field String * @param queryText String * @param subQueryBuilder SubQuery * @param analysisMode AnalysisMode * @param luceneFunction LuceneFunction * @param expandedFieldName String * @param propertyDef PropertyDefinition * @param tokenisationMode IndexTokenisationMode * @param booleanQuery BooleanQuery * @param mlAnalysisMode MLAnalysisMode * @param locale Locale * @throws ParseException */ protected abstract void addMLTextAttributeQuery(String field, String queryText, SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, String expandedFieldName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale) throws ParseException; // private void addLocaleSpecificUntokenisedMLOrTextAttribute(String sourceField, String queryText, SubQuery subQueryBuilder, AnalysisMode analysisMode, // LuceneFunction luceneFunction, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale, String actualField) throws ParseException // { // // String termText = queryText; // if (locale.toString().length() > 0) // { // termText = "{" + locale + "}" + queryText; // } // Query subQuery = subQueryBuilder.getQuery(actualField, termText, analysisMode, luceneFunction); // booleanQuery.add(subQuery, Occur.SHOULD); // // if (booleanQuery.getClauses().length == 0) // { // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); // } // } // private void addLocaleSpecificTokenisedMLOrTextAttribute(String queryText, SubQuery subQueryBuilder, AnalysisMode analysisMode, LuceneFunction luceneFunction, // BooleanQuery booleanQuery, Locale locale, String actualField) throws ParseException // { // StringBuilder builder = new StringBuilder(queryText.length() + 10); // builder.append("\u0000").append(locale.toString()).append("\u0000").append(queryText); // Query subQuery = subQueryBuilder.getQuery(actualField, builder.toString(), analysisMode, luceneFunction); // if (subQuery != null) // { // booleanQuery.add(subQuery, Occur.SHOULD); // } // else // { // booleanQuery.add(createNoMatchQuery(), Occur.SHOULD); // } // } protected Query functionQueryBuilder(String expandedFieldName, QName propertyQName, PropertyDefinition propertyDef, IndexTokenisationMode tokenisationMode, String queryText, LuceneFunction luceneFunction) throws ParseException { // Mime type if (expandedFieldName.endsWith(FIELD_MIMETYPE_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_SIZE_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_LOCALE_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_ENCODING_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_CONTENT_DOC_ID_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_TRANSFORMATION_EXCEPTION_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_TRANSFORMATION_TIME_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } else if (expandedFieldName.endsWith(FIELD_TRANSFORMATION_STATUS_SUFFIX)) { if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene Function"); } } // Already in expanded form // ML if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.MLTEXT))) { // Build a sub query for each locale and or the results together - the analysis will take care of // cross language matching for each entry BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, false)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, queryText, luceneFunction, booleanQuery, mlAnalysisMode, locale, tokenisationMode); } return booleanQuery; } // Content else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))) { throw new UnsupportedOperationException("Lucene functions not supported for content"); } else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) { if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) { throw new UnsupportedOperationException("Functions are not supported agaisnt special text fields"); } boolean withWildCards = propertyQName.equals(ContentModel.PROP_USER_USERNAME) || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME); BooleanQuery booleanQuery = new BooleanQuery(); MLAnalysisMode mlAnalysisMode = searchParameters.getMlAnalaysisMode() == null ? defaultSearchMLAnalysisMode : searchParameters.getMlAnalaysisMode(); List<Locale> locales = searchParameters.getLocales(); List<Locale> expandedLocales = new ArrayList<Locale>(); for (Locale locale : (((locales == null) || (locales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : locales)) { expandedLocales.addAll(MLAnalysisMode.getLocales(mlAnalysisMode, locale, withWildCards)); } for (Locale locale : (((expandedLocales == null) || (expandedLocales.size() == 0)) ? Collections.singletonList(I18NUtil.getLocale()) : expandedLocales)) { addLocaleSpecificUntokenisedMLOrTextFunction(expandedFieldName, queryText, luceneFunction, booleanQuery, mlAnalysisMode, locale, tokenisationMode); } return booleanQuery; } else { throw new UnsupportedOperationException("Lucene Function"); } } protected abstract void addLocaleSpecificUntokenisedMLOrTextFunction(String expandedFieldName, String queryText, LuceneFunction luceneFunction, BooleanQuery booleanQuery, MLAnalysisMode mlAnalysisMode, Locale locale, IndexTokenisationMode tokenisationMode); // private Query buildFunctionQuery(String expandedFieldName, String termText, LuceneFunction luceneFunction) // { // String testText = termText; // if (termText.startsWith("{")) // { // int index = termText.indexOf("}"); // testText = termText.substring(index + 1); // } // switch (luceneFunction) // { // case LOWER: // if (testText.equals(testText.toLowerCase())) // { // return new CaseInsensitiveFieldQuery(new Term(expandedFieldName, termText)); // } // else // { // // No match // return createNoMatchQuery(); // } // case UPPER: // if (testText.equals(testText.toUpperCase())) // { // return new CaseInsensitiveFieldQuery(new Term(expandedFieldName, termText)); // } // else // { // // No match // return createNoMatchQuery(); // } // default: // throw new UnsupportedOperationException("Unsupported Lucene Function " + luceneFunction); // // } // } protected TermQuery createNoMatchQuery() { return new TermQuery(new Term("NO_TOKENS", "__")); } public static void main(String[] args) throws ParseException, java.text.ParseException { //Query query; Calendar start = Calendar.getInstance(); Calendar end = Calendar.getInstance(); SimpleDateFormat df = CachingDateFormat.getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", false); Date date = df.parse("2011-09-31T00:00:00.000"); System.out.println(date); start.setTime(date); System.out.println(start); date = df.parse("2011-10-28T00:00:00.000"); System.out.println(date); end.setTime(date); System.out.println(end); LuceneQueryParser lqp = new LuceneQueryParser(null, null); Query query = lqp.buildDateTimeRange("WOOF", start, Calendar.DAY_OF_MONTH, end, Calendar.DAY_OF_MONTH, true, true); System.out.println("Query is " + query); } @Override public AbstractAnalyzer getAnalyzer() { return luceneAnalyser; } /** * Returns null if all clause words were filtered away by the analyzer * @param booleanQuery - initial BooleanQuery * @return BooleanQuery or <code>null</code> if booleanQuery has no clauses */ protected BooleanQuery getNonEmptyBooleanQuery(BooleanQuery booleanQuery) { if (booleanQuery.clauses().size() > 0) { return booleanQuery; } else { return null; } } }