Index: src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (revision 568963) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (working copy) @@ -146,7 +146,7 @@ // build lucene query Query query = LuceneQueryBuilder.createQuery(root, session, index.getContext().getItemStateManager(), index.getNamespaceMappings(), - index.getTextAnalyzer(), propReg, index.getSynonymProvider()); + index.getTextAnalyzer(), propReg, index.getSynonymProvider(), index.getNewIndexFormat()); OrderQueryNode orderNode = root.getOrderNode(); Index: src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java (revision 568963) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java (working copy) @@ -96,6 +96,11 @@ protected boolean supportHighlighting = false; /** + * Indicates if the index format is new or old. + */ + protected boolean newIndexFormat = true; + + /** * Creates a new node indexer. * * @param node the node state to index. @@ -132,6 +137,15 @@ } /** + * Sets wether the index is of new format. + * + * @param format when true, format is new. + */ + public void setNewIndexFormat(boolean format) { + this.newIndexFormat = format; + } + + /** * Sets the indexing configuration for this node indexer. * * @param config the indexing configuration. @@ -189,6 +203,13 @@ PropertyId id = new PropertyId(node.getNodeId(), propName); try { PropertyState propState = (PropertyState) stateProvider.getItemState(id); + + // add each property to the _PROPERTIES_SET for searching + // if index format is new + if(newIndexFormat) { + addPropertyName(doc,propState.getName()); + } + InternalValue[] values = propState.getValues(); for (int i = 0; i < values.length; i++) { addValue(doc, values[i], propState.getName()); @@ -313,6 +334,22 @@ } /** + * Adds the propertyname to the lucene _PROPERTIES_SET field. + * + * @param doc the document. + * @param name the name of the property. + */ + private void addPropertyName(Document doc, QName name) { + String fieldName = name.getLocalName(); + try { + fieldName = NameFormat.format(name, mappings); + } catch (NoPrefixDeclaredException e) { + // will never happen + } + doc.add(new Field(FieldNames.PROPERTIES_SET,fieldName,Field.Store.NO,Field.Index.NO_NORMS)); + } + + /** * Adds the binary value to the document as the named field. *

* This implementation checks if this {@link #node} is of type nt:resource Index: src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java (revision 568963) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java (working copy) @@ -71,6 +71,12 @@ public static final String PROPERTIES = "_:PROPERTIES".intern(); /** + * Name of the field that contains all available properties that are available + * for this indexed node. + */ + public static final String PROPERTIES_SET = "_:PROPERTIES_SET".intern(); + + /** * Name of the field that contains the UUIDs of the aggregated nodes. The * terms are not tokenized and not stored, only indexed. */ Index: src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (revision 568963) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (working copy) @@ -16,58 +16,60 @@ */ package org.apache.jackrabbit.core.query.lucene; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jcr.RepositoryException; +import javax.jcr.query.InvalidQueryException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.collections.iterators.AbstractIteratorDecorator; import org.apache.jackrabbit.core.ItemManager; -import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.NodeId; import org.apache.jackrabbit.core.NodeIdIterator; +import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.query.AbstractQueryHandler; import org.apache.jackrabbit.core.query.ExecutableQuery; +import org.apache.jackrabbit.core.query.QueryHandler; import org.apache.jackrabbit.core.query.QueryHandlerContext; -import org.apache.jackrabbit.core.query.QueryHandler; +import org.apache.jackrabbit.core.state.ItemStateManager; import org.apache.jackrabbit.core.state.NodeState; import org.apache.jackrabbit.core.state.NodeStateIterator; -import org.apache.jackrabbit.core.state.ItemStateManager; import org.apache.jackrabbit.extractor.DefaultTextExtractor; import org.apache.jackrabbit.extractor.TextExtractor; +import org.apache.jackrabbit.name.NameFormat; import org.apache.jackrabbit.name.NoPrefixDeclaredException; import org.apache.jackrabbit.name.QName; -import org.apache.jackrabbit.name.NameFormat; import org.apache.jackrabbit.uuid.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; +import org.apache.lucene.index.IndexReader.FieldOption; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.commons.collections.iterators.AbstractIteratorDecorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; import org.xml.sax.SAXException; -import org.w3c.dom.Element; -import javax.jcr.RepositoryException; -import javax.jcr.query.InvalidQueryException; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.File; -import java.util.Iterator; -import java.util.List; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - /** * Implements a {@link org.apache.jackrabbit.core.query.QueryHandler} using * Lucene. @@ -302,6 +304,13 @@ private boolean closed = false; /** + * Indicates if this SearchIndex index format is new. + * If you have multiple workspaces, some indices might + * be in the old format, while others in the new. + */ + private boolean newIndexFormat = true; + + /** * Default constructor. */ public SearchIndex() { @@ -375,6 +384,14 @@ log.warn("Failed to run consistency check on index: " + e); } } + + newIndexFormat = index.getIndexReader().getFieldNames( + FieldOption.ALL).contains(FieldNames.PROPERTIES_SET) + || index.getIndexReader().numDocs() == 0; + + if(!newIndexFormat)log.warn("Index is in old format. This might imply slower queries. " + + "Re-index if possible"); + log.info("Index initialized: " + path); } @@ -703,6 +720,7 @@ getContext().getItemStateManager(), nsMappings, extractor); indexer.setSupportHighlighting(supportHighlighting); indexer.setIndexingConfiguration(indexingConfig); + indexer.setNewIndexFormat(newIndexFormat); Document doc = indexer.createDoc(); mergeAggregatedNodeIndexes(node, doc); return doc; @@ -1470,7 +1488,15 @@ public boolean getEnableConsistencyCheck() { return consistencyCheckEnabled; } - + + /** + * @return true if the index format is in the new format; + * false otherwise. + */ + public boolean getNewIndexFormat() { + return newIndexFormat; + } + //----------------------------< internal >---------------------------------- /** Index: src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (revision 568963) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (working copy) @@ -130,6 +130,11 @@ private SynonymProvider synonymProvider; /** + * The wether the index format is new or old. + */ + private boolean newIndexFormat; + + /** * Exceptions thrown during tree translation */ private List exceptions = new ArrayList(); @@ -155,7 +160,8 @@ NamespaceMappings nsMappings, Analyzer analyzer, PropertyTypeRegistry propReg, - SynonymProvider synonymProvider) { + SynonymProvider synonymProvider, + boolean newIndexFormat) { this.root = root; this.session = session; this.sharedItemMgr = sharedItemMgr; @@ -164,6 +170,7 @@ this.analyzer = analyzer; this.propRegistry = propReg; this.synonymProvider = synonymProvider; + this.newIndexFormat = newIndexFormat; } /** @@ -189,7 +196,7 @@ PropertyTypeRegistry propReg) throws RepositoryException { return createQuery(root, session, sharedItemMgr, - nsMappings, analyzer, propReg, null); + nsMappings, analyzer, propReg, null, true); } /** @@ -215,7 +222,8 @@ NamespaceMappings nsMappings, Analyzer analyzer, PropertyTypeRegistry propReg, - SynonymProvider synonymProvider) + SynonymProvider synonymProvider, + boolean newIndexFormat) throws RepositoryException { NodeId id = ((NodeImpl) session.getRootNode()).getNodeId(); @@ -223,7 +231,7 @@ id, sharedItemMgr, session); LuceneQueryBuilder builder = new LuceneQueryBuilder( root, session, sharedItemMgr, hmgr, nsMappings, - analyzer, propReg, synonymProvider); + analyzer, propReg, synonymProvider, newIndexFormat); Query q = builder.createLuceneQuery(); if (builder.exceptions.size() > 0) { @@ -569,7 +577,7 @@ // todo this will traverse the whole index, optimize! Query subQuery = null; try { - subQuery = new MatchAllQuery(NameFormat.format(QName.JCR_PRIMARYTYPE, nsMappings)); + subQuery = getMatchAllQuery(NameFormat.format(QName.JCR_PRIMARYTYPE, nsMappings)); } catch (NoPrefixDeclaredException e) { // will never happen, prefixes are created when unknown } @@ -610,7 +618,7 @@ } if (node.getIncludeDescendants()) { - Query refPropQuery = new MatchAllQuery(refProperty); + Query refPropQuery = getMatchAllQuery(refProperty); context = new DescendantSelfAxisQuery(context, refPropQuery, false); } @@ -815,7 +823,7 @@ // the like operation always has one string value. // no coercing, see above if (stringValues[0].equals("%")) { - query = new MatchAllQuery(field); + query = getMatchAllQuery(field); } else { query = new WildcardQuery(FieldNames.PROPERTIES, field, stringValues[0], transform[0]); } @@ -836,7 +844,7 @@ case QueryConstants.OPERATION_NE_VALUE: // != // match nodes with property 'field' that includes svp and mvp BooleanQuery notQuery = new BooleanQuery(); - notQuery.add(new MatchAllQuery(field), Occur.SHOULD); + notQuery.add(getMatchAllQuery(field), Occur.SHOULD); // exclude all nodes where 'field' has the term in question for (int i = 0; i < stringValues.length; i++) { Term t = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i])); @@ -862,7 +870,7 @@ // minus the nodes that have a multi-valued property 'field' and // all values are equal to term in question notQuery = new BooleanQuery(); - notQuery.add(new MatchAllQuery(field), Occur.SHOULD); + notQuery.add(getMatchAllQuery(field), Occur.SHOULD); for (int i = 0; i < stringValues.length; i++) { // exclude the nodes that have the term and are single valued Term t = new Term(FieldNames.PROPERTIES, FieldNames.createNamedValue(field, stringValues[i])); @@ -885,7 +893,7 @@ query = notQuery; break; case QueryConstants.OPERATION_NULL: - query = new NotQuery(new MatchAllQuery(field)); + query = new NotQuery(getMatchAllQuery(field)); break; case QueryConstants.OPERATION_SIMILAR: String uuid = "x"; @@ -900,7 +908,7 @@ query = new SimilarityQuery(uuid, analyzer); break; case QueryConstants.OPERATION_NOT_NULL: - query = new MatchAllQuery(field); + query = getMatchAllQuery(field); break; default: throw new IllegalArgumentException("Unknown relation operation: " @@ -1108,4 +1116,19 @@ } return (String[]) values.toArray(new String[values.size()]); } + + /** + * Depeding wether the index is of the new index format this method + * returns a query that matches all nodes that have property 'field' + * + * @param field + * @return Query that matches all nodes that contain the property field + */ + private final Query getMatchAllQuery(String field){ + if(this.newIndexFormat) { + return new TermQuery(new Term(FieldNames.PROPERTIES_SET,field)); + } else { + return new MatchAllQuery(field); + } + } }