diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 87cdb01220..34e3259493 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -271,6 +271,13 @@ + + + + + + + diff --git a/config/alfresco/index-recovery-context.xml b/config/alfresco/index-recovery-context.xml index cfd662fa90..ebe7b15b41 100644 --- a/config/alfresco/index-recovery-context.xml +++ b/config/alfresco/index-recovery-context.xml @@ -43,6 +43,22 @@ ${index.recovery.mode} + + + + ${index.recovery.mode} + + + + + + + + + true, i.e. the server will be locked against further updates. + * + * @param lockServer + * true to force the server to be read-only + */ + public void setLockServer(boolean lockServer) + { + this.lockServer = lockServer; + } + + + public void setAvmService(AVMService avmService) + { + this.avmService = avmService; + } + + public void setAvmSnapShotTriggeredIndexingMethodInterceptor( + AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) + { + this.avmSnapShotTriggeredIndexingMethodInterceptor = avmSnapShotTriggeredIndexingMethodInterceptor; + } + + @Override + protected void reindexImpl() + { + processStores(); + } + + private void processStores() + { + List stores = avmService.getStores(); + if(stores.size() == 0) + { + return; + } + int count = 0; + int tracker = -1; + logger.info("Checking indexes for AVM Stores"); + for (AVMStoreDescriptor store : stores) + { + if (isShuttingDown()) + { + return; + } + processStore(store.getName()); + count++; + if (count*10l/stores.size() > tracker) + { + tracker = (int)(count*10l/stores.size()); + logger.info(" Stores "+(tracker*10)+"% complete"); + } + } + logger.info("Finished checking indexes for AVM Stores"); + } + + private void processStore(String store) + { + if (logger.isDebugEnabled()) + { + logger.debug("Performing AVM index recovery for type: " + recoveryMode + " on store " + store); + } + + // do we just ignore + if (recoveryMode == RecoveryMode.NONE) + { + return; + } + // check the level of cover required + boolean fullRecoveryRequired = false; + if (recoveryMode == RecoveryMode.FULL) // no validate required + { + fullRecoveryRequired = true; + } + else + // validate first + { + int lastActualSnapshotId = avmService.getLatestSnapshotID(store); + if (lastActualSnapshotId <= 0) + { + return; + } + int lastIndexedSnapshotId = avmSnapShotTriggeredIndexingMethodInterceptor.getLastIndexedSnapshot(store); + if (lastActualSnapshotId != lastIndexedSnapshotId) + { + logger.warn("Index for avm store " + store + " is out of date"); + // this store isn't up to date + if (recoveryMode == RecoveryMode.VALIDATE) + { + // the store is out of date - validation failed + } + else if (recoveryMode == RecoveryMode.AUTO) + { + fullRecoveryRequired = true; + } + } + } + + // put the server into read-only mode for the duration + boolean allowWrite = !transactionService.isReadOnly(); + try + { + if (lockServer) + { + // set the server into read-only mode + transactionService.setAllowWrite(false); + } + + // do we need to perform a full recovery + if (fullRecoveryRequired) + { + recoverStore(store); + } + } + finally + { + // restore read-only state + transactionService.setAllowWrite(allowWrite); + } + + } + + private void recoverStore(String store) + { + int tracker = -1; + int latest = avmService.getLatestSnapshotID(store); + if(latest <= 0) + { + return; + } + logger.info("Recovery for "+store); + + if(!avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated(store)) + { + avmSnapShotTriggeredIndexingMethodInterceptor.createIndex(store); + } + for (int i = 0; i <= latest; i++) + { + if (isShuttingDown()) + { + return; + } + recoverSnapShot(store, i); + if (i*10l/latest > tracker) + { + tracker = (int)(i*10l/latest); + logger.info(" Store "+store +" "+(tracker*10)+"% complete"); + } + } + logger.info("Recovery for "+store+" done"); + } + + private void recoverSnapShot(final String store, final int id) + { + if (logger.isDebugEnabled()) + { + logger.debug("Reindexing avm store: " + store + " snapshot id " + id); + } + + TransactionWork reindexWork = new TransactionWork() + { + public Object doWork() throws Exception + { + if (!avmSnapShotTriggeredIndexingMethodInterceptor.isSnapshotIndexed(store, id)) + { + avmSnapShotTriggeredIndexingMethodInterceptor.indexSnapshot(store, id - 1, id); + } + // done + return null; + } + }; + TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, reindexWork, true); + // done + } + +} diff --git a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java index 2d7638ede8..f8deaa7e34 100644 --- a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java +++ b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java @@ -115,14 +115,7 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte int before = initialStates.get(store).intValue(); if (after > before) { - - StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); - Indexer indexer = indexerAndSearcher.getIndexer(storeRef); - if (indexer instanceof AVMLuceneIndexer) - { - AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; - avmIndexer.index(store, before, after, getIndexMode(store)); - } + indexSnapshot(store, before, after); } } return returnValue; @@ -144,13 +137,7 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte { String store = (String) mi.getArguments()[0]; Object returnValue = mi.proceed(); - StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); - Indexer indexer = indexerAndSearcher.getIndexer(storeRef); - if (indexer instanceof AVMLuceneIndexer) - { - AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; - avmIndexer.createIndex(store, IndexMode.SYNCHRONOUS); - } + createIndex(store); return returnValue; } else if (mi.getMethod().getName().equals("renameStore")) @@ -246,6 +233,63 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte this.defaultMode = defaultMode; } + + /** + * + * @param store + * @param before + * @param after + */ + public void indexSnapshot(String store, int before, int after) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + avmIndexer.index(store, before, after, getIndexMode(store)); + } + } + + + /** + * + * @param store + * @return + */ + public int getLastIndexedSnapshot(String store) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + return avmIndexer.getLastIndexedSnapshot(store); + } + return -1; + } + + /** + * Is the snapshot applied to the index? + * + * Is there an entry for any node that was added OR have all the nodes in the transaction been deleted as expected? + * + * @param store + * @param id + * @return - true if applied, false if not + */ + public boolean isSnapshotIndexed(String store, int id) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + return avmIndexer.isSnapshotIndexed(store, id); + } + return false; + } + private synchronized IndexMode getIndexMode(String store) { IndexMode mode = modeCache.get(store); @@ -363,4 +407,27 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte { STAGING, STAGING_PREVIEW, AUTHOR, AUTHOR_PREVIEW, WORKFLOW, WORKFLOW_PREVIEW, AUTHOR_WORKFLOW, AUTHOR_WORKFLOW_PREVIEW, UNKNOWN; } + + public boolean hasIndexBeenCreated(String store) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + return avmIndexer.hasIndexBeenCreated(store); + } + return false; + } + + public void createIndex(String store) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + avmIndexer.createIndex(store, IndexMode.SYNCHRONOUS); + } + } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexer.java b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexer.java index 6991c5365c..afbc0eaac4 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexer.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexer.java @@ -61,4 +61,42 @@ public interface AVMLuceneIndexer extends LuceneIndexer, BackgroundIndexerAware * @param mode */ public void createIndex(String store, IndexMode mode); + + /** + * Get the id of the last snapshot added to the index + * @param store + * + * @param mode + * - IndexMode.SYNCHRONOUS - the last searchable snapshop + * - IndexMode.ASYNCHRONOUS - the last pending snapshot to be indexed. It may or may not be searchable. + * @return - the snapshot id + */ + public int getLastIndexedSnapshot(String store); + + /** + * Is the snapshot applied to the index? + * + * Is there an entry for any node that was added OR have all the nodes in the transaction been deleted as expected? + * + * @param store + * @param id + * @return - true if applied, false if not + */ + public boolean isSnapshotIndexed(String store, int id); + + /** + * Is snapshot searchable + * @param store + * @param id + * @return - true if snapshot has been fully indexed, false if pending or unindexed. + */ + public boolean isSnapshotSearchable(String store, int id); + + /** + * Has the index been ceated + * + * @param store + * @return + */ + public boolean hasIndexBeenCreated(String store); } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java index b5b42d78f1..7956d07e92 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java @@ -40,6 +40,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.model.WCMModel; @@ -52,7 +53,6 @@ import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.search.IndexMode; -import org.alfresco.repo.search.impl.lucene.analysis.NumericEncoder; import org.alfresco.repo.search.impl.lucene.fts.FTSIndexerAware; import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; @@ -74,6 +74,9 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.repository.datatype.TypeConversionException; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; import org.alfresco.util.GUID; @@ -82,7 +85,10 @@ import org.alfresco.util.Pair; import org.apache.log4j.Logger; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermDocs; +import org.apache.lucene.index.TermEnum; import org.apache.lucene.search.Hits; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Searcher; @@ -94,9 +100,7 @@ import org.apache.lucene.search.Searcher; */ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl implements AVMLuceneIndexer { - private static String SNAP_SHOT_ID = "SnapShotId_"; - - private static String SNAP_SHOT_STORE = "SnapShotStore"; + private static String SNAP_SHOT_ID = "SnapShot"; static Logger s_logger = Logger.getLogger(AVMLuceneIndexerImpl.class); @@ -116,9 +120,9 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl private FullTextSearchIndexer fullTextSearchIndexer; private int remainingCount; - + private int startVersion = -1; - + private int endVersion = -1; /** @@ -213,29 +217,28 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl } } - public void asynchronousIndex(String store, int srcVersion, int dstVersion) + private void asynchronousIndex(String store, int srcVersion, int dstVersion) { - if(s_logger.isDebugEnabled()) + if (s_logger.isDebugEnabled()) { - s_logger.debug("Async index for "+store + " from " + srcVersion + " to " + dstVersion); + s_logger.debug("Async index for " + store + " from " + srcVersion + " to " + dstVersion); } index("\u0000BG:STORE:" + store + ":" + srcVersion + ":" + dstVersion + ":" + GUID.generate()); fullTextSearchIndexer.requiresIndex(AVMNodeConverter.ToStoreRef(store)); } - public void synchronousIndex(String store, int srcVersion, int dstVersion) + private void synchronousIndex(String store, int srcVersion, int dstVersion) { - if(startVersion == -1) + if (startVersion == -1) { startVersion = srcVersion; } - + endVersion = dstVersion; - - - if(s_logger.isDebugEnabled()) + + if (s_logger.isDebugEnabled()) { - s_logger.debug("Sync index for "+store + " from " + srcVersion + " to "+dstVersion); + s_logger.debug("Sync index for " + store + " from " + srcVersion + " to " + dstVersion); } String path = store + ":/"; List changeList = avmSyncService.compare(srcVersion, path, dstVersion, path, null); @@ -275,12 +278,20 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl // Anything else then we reindex else { - // TODO: Check hen to rebuild aux paths - // Should only reindex if the path has changed - not anything on a directory - reindex(difference.getSourcePath(), srcDesc.isDirectory()); - reindex(difference.getDestinationPath(), dstDesc.isDirectory()); - reindexAllAncestors(difference.getDestinationPath()); - reindexAllAncestors(difference.getDestinationPath()); + if (!difference.getSourcePath().equals(difference.getDestinationPath())) + { + reindex(difference.getSourcePath(), srcDesc.isDirectory()); + reindex(difference.getDestinationPath(), dstDesc.isDirectory()); + reindexAllAncestors(difference.getSourcePath()); + reindexAllAncestors(difference.getDestinationPath()); + } + else + { + // If it is a directory, it is at the same path, + // so no cascade update is required for the bridge table data. + reindex(difference.getDestinationPath(), false); + reindexAllAncestors(difference.getDestinationPath()); + } } break; case AVMDifference.DIRECTORY: @@ -293,7 +304,7 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl } } // record the snap shotid - reindex(SNAP_SHOT_ID + store, false); + reindex(SNAP_SHOT_ID + ":" + store + ":" + srcVersion + ":" + dstVersion, false); } /* @@ -355,11 +366,6 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl { Document sdoc = new Document(); sdoc.add(new Field("ID", stringNodeRef, Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO)); - String storeName = stringNodeRef.substring(SNAP_SHOT_ID.length()); - sdoc.add(new Field(SNAP_SHOT_ID, NumericEncoder.encode(avmService.getLatestSnapshotID(storeName)), - Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO)); - sdoc.add(new Field(SNAP_SHOT_STORE, NumericEncoder.encode(avmService.getLatestSnapshotID(storeName)), - Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO)); docs.add(sdoc); return docs; } @@ -375,25 +381,25 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl } List> allPaths = avmService.getPaths(desc); - List> paths = new ArrayList> (); - for(Pair pair: allPaths) + List> paths = new ArrayList>(); + for (Pair pair : allPaths) { - if(pair.getFirst().intValue() == endVersion) + if (pair.getFirst().intValue() == endVersion) { paths.add(pair); } } - if(paths.size() == 0) + if (paths.size() == 0) { - for(Pair pair: allPaths) + for (Pair pair : allPaths) { - if(pair.getFirst().intValue() == -1) + if (pair.getFirst().intValue() == -1) { paths.add(pair); } } } - + if (paths.size() == 0) { return docs; @@ -437,17 +443,17 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl boolean isAtomic = true; - Map properties = getIndexableProperties(desc, node, nodeRef, endVersion, stringNodeRef); + Map properties = getIndexableProperties(desc, nodeRef, endVersion, stringNodeRef); for (QName propertyName : properties.keySet()) { Serializable value = properties.get(propertyName); if (indexAllProperties) { - indexProperty(node, nodeRef, propertyName, value, xdoc, false); + indexProperty(nodeRef, propertyName, value, xdoc, false, endVersion, stringNodeRef); } else { - isAtomic &= indexProperty(node, nodeRef, propertyName, value, xdoc, true); + isAtomic &= indexProperty(nodeRef, propertyName, value, xdoc, true, endVersion, stringNodeRef); } } @@ -617,10 +623,10 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl } } - private Map getIndexableProperties(AVMNodeDescriptor desc, AVMNode node, NodeRef nodeRef, - Integer version, String path) + private Map getIndexableProperties(AVMNodeDescriptor desc, NodeRef nodeRef, Integer version, + String path) { - Map properties = node.getProperties(); + Map properties = avmService.getNodeProperties(version, path); Map result = new HashMap(); for (QName qName : properties.keySet()) @@ -642,11 +648,14 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl result.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier()); if (desc.isLayeredDirectory()) { - result.put(WCMModel.PROP_AVM_DIR_INDIRECTION, AVMNodeConverter.ToNodeRef(endVersion, desc.getIndirection())); + result + .put(WCMModel.PROP_AVM_DIR_INDIRECTION, AVMNodeConverter.ToNodeRef(endVersion, desc + .getIndirection())); } if (desc.isLayeredFile()) { - result.put(WCMModel.PROP_AVM_FILE_INDIRECTION, AVMNodeConverter.ToNodeRef(endVersion, desc.getIndirection())); + result.put(WCMModel.PROP_AVM_FILE_INDIRECTION, AVMNodeConverter + .ToNodeRef(endVersion, desc.getIndirection())); } if (desc.isFile()) { @@ -696,8 +705,8 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl } } - protected boolean indexProperty(AVMNode node, NodeRef banana, QName propertyName, Serializable value, Document doc, - boolean indexAtomicPropertiesOnly) + protected boolean indexProperty(NodeRef banana, QName propertyName, Serializable value, Document doc, + boolean indexAtomicPropertiesOnly, int version, String path) { String attributeName = "@" + QName.createQName(propertyName.getNamespaceURI(), ISO9075.encode(propertyName.getLocalName())); @@ -937,7 +946,8 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl // TODO: Use the node locale in preferanced to the system locale Locale locale = null; - Serializable localeProperty = node.getProperty(ContentModel.PROP_LOCALE); + Serializable localeProperty = avmService.getNodeProperties(version, path).get( + ContentModel.PROP_LOCALE); if (localeProperty != null) { locale = DefaultTypeConverter.INSTANCE.convert(Locale.class, localeProperty); @@ -1169,20 +1179,20 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl public void syncronousDeleteIndex(String store) { - if(s_logger.isDebugEnabled()) + if (s_logger.isDebugEnabled()) { - s_logger.debug("Sync delete for "+store); + s_logger.debug("Sync delete for " + store); } deleteAll(); } public void asyncronousDeleteIndex(String store) { - if(s_logger.isDebugEnabled()) + if (s_logger.isDebugEnabled()) { - s_logger.debug("Async delete for "+store); + s_logger.debug("Async delete for " + store); } - index("\u0000BG:DELETE:" + store+":"+GUID.generate()); + index("\u0000BG:DELETE:" + store + ":" + GUID.generate()); fullTextSearchIndexer.requiresIndex(AVMNodeConverter.ToStoreRef(store)); } @@ -1214,9 +1224,9 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl public void syncronousCreateIndex(String store) { - if(s_logger.isDebugEnabled()) + if (s_logger.isDebugEnabled()) { - s_logger.debug("Sync create for "+store); + s_logger.debug("Sync create for " + store); } @SuppressWarnings("unused") AVMNodeDescriptor rootDesc = avmService.getStoreRoot(-1, store); @@ -1226,11 +1236,11 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl public void asyncronousCreateIndex(String store) { - if(s_logger.isDebugEnabled()) + if (s_logger.isDebugEnabled()) { - s_logger.debug("Async create for "+store); + s_logger.debug("Async create for " + store); } - index("\u0000BG:CREATE:" + store+":"+GUID.generate()); + index("\u0000BG:CREATE:" + store + ":" + GUID.generate()); fullTextSearchIndexer.requiresIndex(AVMNodeConverter.ToStoreRef(store)); } @@ -1275,15 +1285,15 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl Document doc = hits.doc(0); action = doc.getField("ID").stringValue(); String[] split = action.split(":"); - if(split[1].equals("DELETE")) + if (split[1].equals("DELETE")) { deleteAll("\u0000BG:"); } - else if(split[1].equals("CREATE")) + else if (split[1].equals("CREATE")) { syncronousCreateIndex(split[2]); } - else if(split[1].equals("STORE")) + else if (split[1].equals("STORE")) { synchronousIndex(split[2], Integer.parseInt(split[3]), Integer.parseInt(split[4])); } @@ -1329,4 +1339,177 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl { this.fullTextSearchIndexer = fullTextSearchIndexer; } + + public int getLastIndexedSnapshot(String store) + { + int last = getLastAsynchronousSnapshot(store); + if (last >= 0) + { + return last; + } + return getLastSynchronousSnapshot(store); + } + + public boolean isSnapshotIndexed(String store, int id) + { + return (id <= getLastAsynchronousSnapshot(store)) || (id <= getLastSynchronousSnapshot(store)); + } + + public boolean isSnapshotSearchable(String store, int id) + { + return (id <= getLastSynchronousSnapshot(store)); + } + + private int getLastSynchronousSnapshot(String store) + { + String prefix = SNAP_SHOT_ID + ":" + store; + IndexReader mainReader = null; + int end = -1; + try + { + mainReader = getReader(); + TermEnum terms = null; + try + { + terms = mainReader.terms(new Term("ID", prefix)); + + while (terms.next()) + { + Term term = terms.term(); + if (term.text().startsWith(prefix)) + { + String[] split = term.text().split(":"); + end = Integer.parseInt(split[3]); + } + else + { + break; + } + } + } + finally + { + if (terms != null) + { + terms.close(); + } + } + return end; + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("IO error", e); + } + finally + { + try + { + if (mainReader != null) + { + mainReader.close(); + } + } + catch (IOException e) + { + s_logger.warn("Failed to close main reader", e); + } + } + } + + private int getLastAsynchronousSnapshot(String store) + { + String prefix = "\u0000BG:STORE:" + store + ":"; + IndexReader mainReader = null; + int end = -1; + try + { + mainReader = getReader(); + TermEnum terms = null; + try + { + terms = mainReader.terms(new Term("ID", prefix)); + + while (terms.next()) + { + Term term = terms.term(); + if (term.text().startsWith(prefix)) + { + String[] split = term.text().split(":"); + end = Integer.parseInt(split[4]); + } + else + { + break; + } + } + } + finally + { + if (terms != null) + { + terms.close(); + } + } + return end; + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("IO error", e); + } + finally + { + try + { + if (mainReader != null) + { + mainReader.close(); + } + } + catch (IOException e) + { + s_logger.warn("Failed to close main reader", e); + } + } + } + + public boolean hasIndexBeenCreated(String store) + { + IndexReader mainReader = null; + try + { + mainReader = getReader(); + TermDocs termDocs = null; + try + { + termDocs = mainReader.termDocs(new Term("ISROOT", "T")); + return termDocs.next(); + } + finally + { + if (termDocs != null) + { + termDocs.close(); + } + } + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("IO error", e); + } + finally + { + try + { + if (mainReader != null) + { + mainReader.close(); + } + } + catch (IOException e) + { + s_logger.warn("Failed to close main reader", e); + } + } + + } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java index 3eae3d67de..534a52b03b 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java @@ -215,6 +215,21 @@ public class LuceneQueryParser extends QueryParser TermQuery termQuery = new TermQuery(new Term(field, queryText)); return termQuery; } + else if (field.equals("ISROOT")) + { + TermQuery termQuery = new TermQuery(new Term(field, queryText)); + return termQuery; + } + else if (field.equals("ISCONTAINER")) + { + TermQuery termQuery = new TermQuery(new Term(field, queryText)); + return termQuery; + } + else if (field.equals("ISNODE")) + { + TermQuery termQuery = new TermQuery(new Term(field, queryText)); + return termQuery; + } else if (field.equals("TX")) { TermQuery termQuery = new TermQuery(new Term(field, queryText));