Index: src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java (revision 569308) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java (working copy) @@ -206,6 +206,12 @@ private boolean reindexing = false; /** + * Flag indicating wether the current workspace did + * have a persistent index already or not. + */ + private boolean newWorkSpaceIndex = false; + + /** * Creates a new MultiIndex. * * @param indexDir the base file system @@ -312,6 +318,7 @@ // do an initial index if there are no indexes at all if (indexNames.size() == 0) { reindexing = true; + newWorkSpaceIndex = true; // traverse and index workspace executeAndLog(new Start(Action.INTERNAL_TRANSACTION)); NodeState rootState = (NodeState) stateMgr.getItemState(rootId); @@ -837,6 +844,13 @@ attemptDelete(); } + /** + * @return wether this is the first index for the workspace + */ + public boolean getNewWorkSpaceIndex() { + return newWorkSpaceIndex; + } + //-------------------------< internal >------------------------------------- /** @@ -1870,4 +1884,5 @@ return logLine.toString(); } } + } Index: src/main/java/org/apache/jackrabbit/core/query/lucene/IndexFormatVersion.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/IndexFormatVersion.java (revision 0) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/IndexFormatVersion.java (revision 0) @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.core.query.lucene; + +/** + * This class indicates the lucene index format that is used. Old formats + * do not have the PROPERTIES_SET lucene fieldname. Old formats + * are compatible. When the index is recreated from scratch, the new + * format will automatically be used. This format is faster certain queries, so + * if the index does not contain PROPERTIES_SET fieldname and re-indexing + * is an option, this is advisable. + */ +public class IndexFormatVersion { + /** + * V1 is the old index format + */ + public static final IndexFormatVersion V1 = new IndexFormatVersion("1"); + + /** + * V2 is the new index format + */ + public static final IndexFormatVersion V2 = new IndexFormatVersion("2"); + + /** + * The used version of the index format + */ + private final String version; + + /** + * Creates a index format version. + * + * @param version The version of the index. + */ + private IndexFormatVersion(String version) { + this.version = version; + } + + /** + * Returns the index format version + * @return the index format version. + */ + public String getVersion(){ + return version; + } +} Property changes on: src\main\java\org\apache\jackrabbit\core\query\lucene\IndexFormatVersion.java ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/query/lucene/QueryImpl.java (revision 569308) +++ 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.getIndexFormatVersion()); 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 569308) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java (working copy) @@ -96,6 +96,11 @@ protected boolean supportHighlighting = false; /** + * Indicates if this index format is new or old + */ + protected IndexFormatVersion indexFormatVersion; + + /** * 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 indexFormatVersion when IndexFormatVersion.V2, index format is new. + */ + public void setIndexFormatVersion(IndexFormatVersion indexFormatVersion) { + this.indexFormatVersion = indexFormatVersion; + } + + /** * Sets the indexing configuration for this node indexer. * * @param config the indexing configuration. @@ -148,6 +162,7 @@ * values from the ItemStateProvider. */ protected Document createDoc() throws RepositoryException { + System.out.println("create doc"); Document doc = new Document(); doc.setBoost(getNodeBoost()); @@ -189,6 +204,14 @@ 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(indexFormatVersion == IndexFormatVersion.V2) { + addPropertyName(doc,propState.getName()); + } + InternalValue[] values = propState.getValues(); for (int i = 0; i < values.length; i++) { addValue(doc, values[i], propState.getName()); @@ -313,6 +336,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 569308) +++ 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 569308) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (working copy) @@ -48,6 +48,7 @@ import org.apache.lucene.search.SortField; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; +import org.apache.lucene.index.IndexReader.FieldOption; import org.apache.commons.collections.iterators.AbstractIteratorDecorator; import org.xml.sax.SAXException; import org.w3c.dom.Element; @@ -67,6 +68,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Collection; /** * Implements a {@link org.apache.jackrabbit.core.query.QueryHandler} using @@ -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 IndexFormatVersion indexFormatVersion; + + /** * Default constructor. */ public SearchIndex() { @@ -316,6 +325,7 @@ * @throws IOException if an error occurs while initializing this handler. */ protected void doInit() throws IOException { + QueryHandlerContext context = getContext(); if (path == null) { throw new IOException("SearchIndex requires 'path' parameter in configuration!"); @@ -375,6 +385,34 @@ log.warn("Failed to run consistency check on index: " + e); } } + + /* + * The index is in the new format if either the index already contains + * the field FieldNames.PROPERTIES_SET in any document, or if the index + * is empty, or if index.getNewWorkSpaceIndex() is true. + * If the format is old, old style indexing and searching is used + */ + + IndexReader indexReader = index.getIndexReader(); + Collection allFieldNames = indexReader.getFieldNames(FieldOption.ALL); + int numDocs = indexReader.numDocs(); + + if(allFieldNames.contains(FieldNames.PROPERTIES_SET) + || numDocs == 0 + || index.getNewWorkSpaceIndex()) { + // new style index formant + setIndexFormatVersion(IndexFormatVersion.V2); + } else { + // old style index format + setIndexFormatVersion(IndexFormatVersion.V1); + } + indexReader.close(); + + if(!(indexFormatVersion == IndexFormatVersion.V2) ) { + log.warn("Index is in old format. This might imply slower queries. " + + "Re-index if possible"); + } + System.out.println("indexFormatVersion " + indexFormatVersion.getVersion()); log.info("Index initialized: " + path); } @@ -703,6 +741,7 @@ getContext().getItemStateManager(), nsMappings, extractor); indexer.setSupportHighlighting(supportHighlighting); indexer.setIndexingConfiguration(indexingConfig); + indexer.setIndexFormatVersion(indexFormatVersion); Document doc = indexer.createDoc(); mergeAggregatedNodeIndexes(node, doc); return doc; @@ -1470,6 +1509,25 @@ public boolean getEnableConsistencyCheck() { return consistencyCheckEnabled; } + + /** + * Sets the index formant version used for this index + * + * @param indexFormatVersion which can be #IndexFormatVersion.V1 (old format) or + * #IndexFormatVersion.V2 (new format). + */ + private void setIndexFormatVersion(IndexFormatVersion indexFormatVersion){ + this.indexFormatVersion = indexFormatVersion; + } + + /** + * + * @return the used index format version for this index, #IndexFormatVersion.V1 + * or #IndexFormatVersion.V2 + */ + public IndexFormatVersion getIndexFormatVersion(){ + return indexFormatVersion; + } //----------------------------< 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 569328) +++ src/main/java/org/apache/jackrabbit/core/query/lucene/LuceneQueryBuilder.java (working copy) @@ -130,6 +130,11 @@ private SynonymProvider synonymProvider; /** + * Wether the index format is new or old. + */ + private IndexFormatVersion indexFormatVersion; + + /** * Exceptions thrown during tree translation */ private List exceptions = new ArrayList(); @@ -155,7 +160,8 @@ NamespaceMappings nsMappings, Analyzer analyzer, PropertyTypeRegistry propReg, - SynonymProvider synonymProvider) { + SynonymProvider synonymProvider, + IndexFormatVersion indexFormatVersion) { this.root = root; this.session = session; this.sharedItemMgr = sharedItemMgr; @@ -164,6 +170,7 @@ this.analyzer = analyzer; this.propRegistry = propReg; this.synonymProvider = synonymProvider; + this.indexFormatVersion = indexFormatVersion; } /** @@ -180,6 +187,7 @@ * information. * @param synonymProvider the synonym provider or null if node * is configured. + * @param indexFormatVersion the index format version to be used * @return the lucene query tree. * @throws RepositoryException if an error occurs during the translation. */ @@ -189,7 +197,8 @@ NamespaceMappings nsMappings, Analyzer analyzer, PropertyTypeRegistry propReg, - SynonymProvider synonymProvider) + SynonymProvider synonymProvider, + IndexFormatVersion indexFormatVersion) throws RepositoryException { NodeId id = ((NodeImpl) session.getRootNode()).getNodeId(); @@ -197,7 +206,7 @@ id, sharedItemMgr, session); LuceneQueryBuilder builder = new LuceneQueryBuilder( root, session, sharedItemMgr, hmgr, nsMappings, - analyzer, propReg, synonymProvider); + analyzer, propReg, synonymProvider, indexFormatVersion); Query q = builder.createLuceneQuery(); if (builder.exceptions.size() > 0) { @@ -543,7 +552,7 @@ // todo this will traverse the whole index, optimize! Query subQuery = null; try { - subQuery = new MatchAllQuery(NameFormat.format(QName.JCR_PRIMARYTYPE, nsMappings)); + subQuery = createMatchAllQuery(NameFormat.format(QName.JCR_PRIMARYTYPE, nsMappings)); } catch (NoPrefixDeclaredException e) { // will never happen, prefixes are created when unknown } @@ -584,7 +593,7 @@ } if (node.getIncludeDescendants()) { - Query refPropQuery = new MatchAllQuery(refProperty); + Query refPropQuery = createMatchAllQuery(refProperty); context = new DescendantSelfAxisQuery(context, refPropQuery, false); } @@ -789,7 +798,7 @@ // the like operation always has one string value. // no coercing, see above if (stringValues[0].equals("%")) { - query = new MatchAllQuery(field); + query = createMatchAllQuery(field); } else { query = new WildcardQuery(FieldNames.PROPERTIES, field, stringValues[0], transform[0]); } @@ -810,7 +819,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(createMatchAllQuery(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])); @@ -836,7 +845,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(createMatchAllQuery(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])); @@ -859,7 +868,7 @@ query = notQuery; break; case QueryConstants.OPERATION_NULL: - query = new NotQuery(new MatchAllQuery(field)); + query = new NotQuery(createMatchAllQuery(field)); break; case QueryConstants.OPERATION_SIMILAR: String uuid = "x"; @@ -874,7 +883,7 @@ query = new SimilarityQuery(uuid, analyzer); break; case QueryConstants.OPERATION_NOT_NULL: - query = new MatchAllQuery(field); + query = createMatchAllQuery(field); break; default: throw new IllegalArgumentException("Unknown relation operation: " @@ -1082,4 +1091,23 @@ } 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 createMatchAllQuery(String field){ + if(indexFormatVersion == indexFormatVersion.V2) { + // new index format style + return new TermQuery(new Term(FieldNames.PROPERTIES_SET,field)); + } else { + // old index format style : might be slow + log.debug("You are using old index format style. If performance issues, " + + "re-index if possible"); + return new MatchAllQuery(field); + } + } }