/* * 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. */ using System; using System.Collections.Generic; using System.IO; using Lucene.Net.Support; using AbstractField = Lucene.Net.Documents.AbstractField; using Document = Lucene.Net.Documents.Document; using Directory = Lucene.Net.Store.Directory; using FSDirectory = Lucene.Net.Store.FSDirectory; using IndexInput = Lucene.Net.Store.IndexInput; namespace Lucene.Net.Index { /// Basic tool and API to check the health of an index and /// write a new segments file that removes reference to /// problematic segments. /// ///

As this tool checks every byte in the index, on a large /// index it can take quite a long time to run. /// ///

WARNING: this tool and API is new and /// experimental and is subject to suddenly change in the /// next release. Please make a complete backup of your /// index before using this to fix your index! ///

public class CheckIndex { private StreamWriter infoStream; private readonly Directory dir; /// Returned from detailing the health and status of the index. /// ///

WARNING: this API is new and experimental and is /// subject to suddenly change in the next release. /// ///

public class Status { /// True if no problems were found with the index. public bool clean; /// True if we were unable to locate and load the segments_N file. public bool missingSegments; /// True if we were unable to open the segments_N file. public bool cantOpenSegments; /// True if we were unable to read the version number from segments_N file. public bool missingSegmentVersion; /// Name of latest segments_N file in the index. public System.String segmentsFileName; /// Number of segments in the index. public int numSegments; /// String description of the version of the index. public System.String segmentFormat; /// Empty unless you passed specific segments list to check as optional 3rd argument. /// /// CheckIndex.CheckIndex_Renamed_Method(System.Collections.IList) /// public List segmentsChecked = new List(); /// True if the index was created with a newer version of Lucene than the CheckIndex tool. public bool toolOutOfDate; /// List of instances, detailing status of each segment. public IList segmentInfos = new List(); /// Directory index is in. public Directory dir; /// SegmentInfos instance containing only segments that /// had no problems (this is used with the /// method to repair the index. /// internal SegmentInfos newSegments; /// How many documents will be lost to bad segments. public int totLoseDocCount; /// How many bad segments were found. public int numBadSegments; /// True if we checked only specific segments () /// was called with non-null /// argument). /// public bool partial; /// Holds the userData of the last commit in the index public IDictionary userData; /// Holds the status of each segment in the index. /// See . /// ///

WARNING: this API is new and experimental and is /// subject to suddenly change in the next release. ///

public class SegmentInfoStatus { /// Name of the segment. public System.String name; /// Document count (does not take deletions into account). public int docCount; /// True if segment is compound file format. public bool compound; /// Number of files referenced by this segment. public int numFiles; /// Net size (MB) of the files referenced by this /// segment. /// public double sizeMB; /// Doc store offset, if this segment shares the doc /// store files (stored fields and term vectors) with /// other segments. This is -1 if it does not share. /// public int docStoreOffset = - 1; /// String of the shared doc store segment, or null if /// this segment does not share the doc store files. /// public System.String docStoreSegment; /// True if the shared doc store files are compound file /// format. /// public bool docStoreCompoundFile; /// True if this segment has pending deletions. public bool hasDeletions; /// Name of the current deletions file name. public System.String deletionsFileName; /// Number of deleted documents. public int numDeleted; /// True if we were able to open a SegmentReader on this /// segment. /// public bool openReaderPassed; /// Number of fields in this segment. internal int numFields; /// True if at least one of the fields in this segment /// does not omitTermFreqAndPositions. /// /// /// public bool hasProx; /// Map<String, String> that includes certain /// debugging details that IndexWriter records into /// each segment it creates /// public IDictionary diagnostics; /// Status for testing of field norms (null if field norms could not be tested). public FieldNormStatus fieldNormStatus; /// Status for testing of indexed terms (null if indexed terms could not be tested). public TermIndexStatus termIndexStatus; /// Status for testing of stored fields (null if stored fields could not be tested). public StoredFieldStatus storedFieldStatus; /// Status for testing of term vectors (null if term vectors could not be tested). public TermVectorStatus termVectorStatus; } /// Status from testing field norms. public sealed class FieldNormStatus { /// Number of fields successfully tested public long totFields = 0L; /// Exception thrown during term index test (null on success) public System.Exception error = null; } /// Status from testing term index. public sealed class TermIndexStatus { /// Total term count public long termCount = 0L; /// Total frequency across all terms. public long totFreq = 0L; /// Total number of positions. public long totPos = 0L; /// Exception thrown during term index test (null on success) public System.Exception error = null; } /// Status from testing stored fields. public sealed class StoredFieldStatus { /// Number of documents tested. public int docCount = 0; /// Total number of stored fields tested. public long totFields = 0; /// Exception thrown during stored fields test (null on success) public System.Exception error = null; } /// Status from testing stored fields. public sealed class TermVectorStatus { /// Number of documents tested. public int docCount = 0; /// Total number of term vectors tested. public long totVectors = 0; /// Exception thrown during term vector test (null on success) public System.Exception error = null; } } /// Create a new CheckIndex on the directory. public CheckIndex(Directory dir) { this.dir = dir; infoStream = null; } /// Set infoStream where messages should go. If null, no /// messages are printed /// public virtual void SetInfoStream(StreamWriter @out) { infoStream = @out; } private void Msg(System.String msg) { if (infoStream != null) infoStream.WriteLine(msg); } private class MySegmentTermDocs:SegmentTermDocs { internal int delCount; internal MySegmentTermDocs(SegmentReader p):base(p) { } public override void Seek(Term term) { base.Seek(term); delCount = 0; } protected internal override void SkippingDoc() { delCount++; } } /// Returns a instance detailing /// the state of the index. /// ///

As this method checks every byte in the index, on a large /// index it can take quite a long time to run. /// ///

WARNING: make sure /// you only call this when the index is not opened by any /// writer. ///

public virtual Status CheckIndex_Renamed_Method() { return CheckIndex_Renamed_Method(null); } /// Returns a instance detailing /// the state of the index. /// /// /// list of specific segment names to check /// ///

As this method checks every byte in the specified /// segments, on a large index it can take quite a long /// time to run. /// ///

WARNING: make sure /// you only call this when the index is not opened by any /// writer. /// public virtual Status CheckIndex_Renamed_Method(List onlySegments) { System.Globalization.NumberFormatInfo nf = System.Globalization.CultureInfo.CurrentCulture.NumberFormat; SegmentInfos sis = new SegmentInfos(); Status result = new Status(); result.dir = dir; try { sis.Read(dir); } catch (System.Exception t) { Msg("ERROR: could not read any segments file in directory"); result.missingSegments = true; if (infoStream != null) infoStream.WriteLine(t.StackTrace); return result; } int numSegments = sis.Count; var segmentsFileName = sis.GetCurrentSegmentFileName(); IndexInput input = null; try { input = dir.OpenInput(segmentsFileName); } catch (System.Exception t) { Msg("ERROR: could not open segments file in directory"); if (infoStream != null) infoStream.WriteLine(t.StackTrace); result.cantOpenSegments = true; return result; } int format = 0; try { format = input.ReadInt(); } catch (System.Exception t) { Msg("ERROR: could not read segment file version in directory"); if (infoStream != null) infoStream.WriteLine(t.StackTrace); result.missingSegmentVersion = true; return result; } finally { if (input != null) input.Close(); } System.String sFormat = ""; bool skip = false; if (format == SegmentInfos.FORMAT) sFormat = "FORMAT [Lucene Pre-2.1]"; if (format == SegmentInfos.FORMAT_LOCKLESS) sFormat = "FORMAT_LOCKLESS [Lucene 2.1]"; else if (format == SegmentInfos.FORMAT_SINGLE_NORM_FILE) sFormat = "FORMAT_SINGLE_NORM_FILE [Lucene 2.2]"; else if (format == SegmentInfos.FORMAT_SHARED_DOC_STORE) sFormat = "FORMAT_SHARED_DOC_STORE [Lucene 2.3]"; else { if (format == SegmentInfos.FORMAT_CHECKSUM) sFormat = "FORMAT_CHECKSUM [Lucene 2.4]"; else if (format == SegmentInfos.FORMAT_DEL_COUNT) sFormat = "FORMAT_DEL_COUNT [Lucene 2.4]"; else if (format == SegmentInfos.FORMAT_HAS_PROX) sFormat = "FORMAT_HAS_PROX [Lucene 2.4]"; else if (format == SegmentInfos.FORMAT_USER_DATA) sFormat = "FORMAT_USER_DATA [Lucene 2.9]"; else if (format == SegmentInfos.FORMAT_DIAGNOSTICS) sFormat = "FORMAT_DIAGNOSTICS [Lucene 2.9]"; else if (format < SegmentInfos.CURRENT_FORMAT) { sFormat = "int=" + format + " [newer version of Lucene than this tool]"; skip = true; } else { sFormat = format + " [Lucene 1.3 or prior]"; } } result.segmentsFileName = segmentsFileName; result.numSegments = numSegments; result.segmentFormat = sFormat; result.userData = sis.UserData; System.String userDataString; if (sis.UserData.Count > 0) { userDataString = " userData=" + CollectionsHelper.CollectionToString(sis.UserData); } else { userDataString = ""; } Msg("Segments file=" + segmentsFileName + " numSegments=" + numSegments + " version=" + sFormat + userDataString); if (onlySegments != null) { result.partial = true; if (infoStream != null) infoStream.Write("\nChecking only these segments:"); foreach(string s in onlySegments) { if (infoStream != null) { infoStream.Write(" " + s); } } result.segmentsChecked.AddRange(onlySegments); Msg(":"); } if (skip) { Msg("\nERROR: this index appears to be created by a newer version of Lucene than this tool was compiled on; please re-compile this tool on the matching version of Lucene; exiting"); result.toolOutOfDate = true; return result; } result.newSegments = (SegmentInfos) sis.Clone(); result.newSegments.Clear(); for (int i = 0; i < numSegments; i++) { SegmentInfo info = sis.Info(i); if (onlySegments != null && !onlySegments.Contains(info.name)) continue; var segInfoStat = new Status.SegmentInfoStatus(); result.segmentInfos.Add(segInfoStat); Msg(" " + (1 + i) + " of " + numSegments + ": name=" + info.name + " docCount=" + info.docCount); segInfoStat.name = info.name; segInfoStat.docCount = info.docCount; int toLoseDocCount = info.docCount; SegmentReader reader = null; try { Msg(" compound=" + info.GetUseCompoundFile()); segInfoStat.compound = info.GetUseCompoundFile(); Msg(" hasProx=" + info.HasProx); segInfoStat.hasProx = info.HasProx; Msg(" numFiles=" + info.Files().Count); segInfoStat.numFiles = info.Files().Count; Msg(System.String.Format(nf, " size (MB)={0:f}", new System.Object[] { (info.SizeInBytes() / (1024.0 * 1024.0)) })); segInfoStat.sizeMB = info.SizeInBytes() / (1024.0 * 1024.0); IDictionary diagnostics = info.Diagnostics; segInfoStat.diagnostics = diagnostics; if (diagnostics.Count > 0) { Msg(" diagnostics = " + CollectionsHelper.CollectionToString(diagnostics)); } int docStoreOffset = info.DocStoreOffset; if (docStoreOffset != - 1) { Msg(" docStoreOffset=" + docStoreOffset); segInfoStat.docStoreOffset = docStoreOffset; Msg(" docStoreSegment=" + info.DocStoreSegment); segInfoStat.docStoreSegment = info.DocStoreSegment; Msg(" docStoreIsCompoundFile=" + info.DocStoreIsCompoundFile); segInfoStat.docStoreCompoundFile = info.DocStoreIsCompoundFile; } System.String delFileName = info.GetDelFileName(); if (delFileName == null) { Msg(" no deletions"); segInfoStat.hasDeletions = false; } else { Msg(" has deletions [delFileName=" + delFileName + "]"); segInfoStat.hasDeletions = true; segInfoStat.deletionsFileName = delFileName; } if (infoStream != null) infoStream.Write(" test: open reader........."); reader = SegmentReader.Get(true, info, IndexReader.DEFAULT_TERMS_INDEX_DIVISOR); segInfoStat.openReaderPassed = true; int numDocs = reader.NumDocs(); toLoseDocCount = numDocs; if (reader.HasDeletions) { if (reader.deletedDocs.Count() != info.GetDelCount()) { throw new System.SystemException("delete count mismatch: info=" + info.GetDelCount() + " vs deletedDocs.count()=" + reader.deletedDocs.Count()); } if (reader.deletedDocs.Count() > reader.MaxDoc) { throw new System.SystemException("too many deleted docs: MaxDoc=" + reader.MaxDoc + " vs deletedDocs.count()=" + reader.deletedDocs.Count()); } if (info.docCount - numDocs != info.GetDelCount()) { throw new System.SystemException("delete count mismatch: info=" + info.GetDelCount() + " vs reader=" + (info.docCount - numDocs)); } segInfoStat.numDeleted = info.docCount - numDocs; Msg("OK [" + (segInfoStat.numDeleted) + " deleted docs]"); } else { if (info.GetDelCount() != 0) { throw new System.SystemException("delete count mismatch: info=" + info.GetDelCount() + " vs reader=" + (info.docCount - numDocs)); } Msg("OK"); } if (reader.MaxDoc != info.docCount) throw new System.SystemException("SegmentReader.MaxDoc " + reader.MaxDoc + " != SegmentInfos.docCount " + info.docCount); // Test getFieldNames() if (infoStream != null) { infoStream.Write(" test: fields.............."); } ICollection fieldNames = reader.GetFieldNames(IndexReader.FieldOption.ALL); Msg("OK [" + fieldNames.Count + " fields]"); segInfoStat.numFields = fieldNames.Count; // Test Field Norms segInfoStat.fieldNormStatus = TestFieldNorms(fieldNames, reader); // Test the Term Index segInfoStat.termIndexStatus = TestTermIndex(info, reader); // Test Stored Fields segInfoStat.storedFieldStatus = TestStoredFields(info, reader, nf); // Test Term Vectors segInfoStat.termVectorStatus = TestTermVectors(info, reader, nf); // Rethrow the first exception we encountered // This will cause stats for failed segments to be incremented properly if (segInfoStat.fieldNormStatus.error != null) { throw new SystemException("Field Norm test failed"); } else if (segInfoStat.termIndexStatus.error != null) { throw new SystemException("Term Index test failed"); } else if (segInfoStat.storedFieldStatus.error != null) { throw new SystemException("Stored Field test failed"); } else if (segInfoStat.termVectorStatus.error != null) { throw new System.SystemException("Term Vector test failed"); } Msg(""); } catch (System.Exception t) { Msg("FAILED"); const string comment = "fixIndex() would remove reference to this segment"; Msg(" WARNING: " + comment + "; full exception:"); if (infoStream != null) infoStream.WriteLine(t.StackTrace); Msg(""); result.totLoseDocCount += toLoseDocCount; result.numBadSegments++; continue; } finally { if (reader != null) reader.Close(); } // Keeper result.newSegments.Add((SegmentInfo)info.Clone()); } if (0 == result.numBadSegments) { result.clean = true; Msg("No problems were detected with this index.\n"); } else Msg("WARNING: " + result.numBadSegments + " broken segments (containing " + result.totLoseDocCount + " documents) detected"); return result; } ///

Test field norms. private Status.FieldNormStatus TestFieldNorms(IEnumerable fieldNames, SegmentReader reader) { var status = new Status.FieldNormStatus(); try { // Test Field Norms if (infoStream != null) { infoStream.Write(" test: field norms........."); } var b = new byte[reader.MaxDoc]; foreach(string fieldName in fieldNames) { if (reader.HasNorms(fieldName)) { reader.Norms(fieldName, b, 0); ++status.totFields; } } Msg("OK [" + status.totFields + " fields]"); } catch (System.Exception e) { Msg("ERROR [" + System.Convert.ToString(e.Message) + "]"); status.error = e; if (infoStream != null) { infoStream.WriteLine(e.StackTrace); } } return status; } /// Test the term index. private Status.TermIndexStatus TestTermIndex(SegmentInfo info, SegmentReader reader) { var status = new Status.TermIndexStatus(); try { if (infoStream != null) { infoStream.Write(" test: terms, freq, prox..."); } TermEnum termEnum = reader.Terms(); TermPositions termPositions = reader.TermPositions(); // Used only to count up # deleted docs for this term var myTermDocs = new MySegmentTermDocs(reader); int maxDoc = reader.MaxDoc; while (termEnum.Next()) { status.termCount++; Term term = termEnum.Term; int docFreq = termEnum.DocFreq(); termPositions.Seek(term); int lastDoc = - 1; int freq0 = 0; status.totFreq += docFreq; while (termPositions.Next()) { freq0++; int doc = termPositions.Doc; int freq = termPositions.Freq; if (doc <= lastDoc) { throw new System.SystemException("term " + term + ": doc " + doc + " <= lastDoc " + lastDoc); } if (doc >= maxDoc) { throw new System.SystemException("term " + term + ": doc " + doc + " >= maxDoc " + maxDoc); } lastDoc = doc; if (freq <= 0) { throw new System.SystemException("term " + term + ": doc " + doc + ": freq " + freq + " is out of bounds"); } int lastPos = - 1; status.totPos += freq; for (int j = 0; j < freq; j++) { int pos = termPositions.NextPosition(); if (pos < - 1) { throw new System.SystemException("term " + term + ": doc " + doc + ": pos " + pos + " is out of bounds"); } if (pos < lastPos) { throw new System.SystemException("term " + term + ": doc " + doc + ": pos " + pos + " < lastPos " + lastPos); } lastPos = pos; } } // Now count how many deleted docs occurred in // this term: int delCount; if (reader.HasDeletions) { myTermDocs.Seek(term); while (myTermDocs.Next()) { } delCount = myTermDocs.delCount; } else { delCount = 0; } if (freq0 + delCount != docFreq) { throw new System.SystemException("term " + term + " docFreq=" + docFreq + " != num docs seen " + freq0 + " + num docs deleted " + delCount); } } Msg("OK [" + status.termCount + " terms; " + status.totFreq + " terms/docs pairs; " + status.totPos + " tokens]"); } catch (System.Exception e) { Msg("ERROR [" + System.Convert.ToString(e.Message) + "]"); status.error = e; if (infoStream != null) { infoStream.WriteLine(e.StackTrace); } } return status; } /// Test stored fields for a segment. private Status.StoredFieldStatus TestStoredFields(SegmentInfo info, SegmentReader reader, System.Globalization.NumberFormatInfo format) { var status = new Status.StoredFieldStatus(); try { if (infoStream != null) { infoStream.Write(" test: stored fields......."); } // Scan stored fields for all documents for (int j = 0; j < info.docCount; ++j) { if (!reader.IsDeleted(j)) { status.docCount++; Document doc = reader.Document(j); status.totFields += doc.GetFields().Count; } } // Validate docCount if (status.docCount != reader.NumDocs()) { throw new System.SystemException("docCount=" + status.docCount + " but saw " + status.docCount + " undeleted docs"); } Msg(string.Format(format, "OK [{0:d} total field count; avg {1:f} fields per doc]", new object[] { status.totFields, (((float) status.totFields) / status.docCount) })); } catch (System.Exception e) { Msg("ERROR [" + System.Convert.ToString(e.Message) + "]"); status.error = e; if (infoStream != null) { infoStream.WriteLine(e.StackTrace); } } return status; } /// Test term vectors for a segment. private Status.TermVectorStatus TestTermVectors(SegmentInfo info, SegmentReader reader, System.Globalization.NumberFormatInfo format) { var status = new Status.TermVectorStatus(); try { if (infoStream != null) { infoStream.Write(" test: term vectors........"); } for (int j = 0; j < info.docCount; ++j) { if (!reader.IsDeleted(j)) { status.docCount++; ITermFreqVector[] tfv = reader.GetTermFreqVectors(j); if (tfv != null) { status.totVectors += tfv.Length; } } } Msg(System.String.Format(format, "OK [{0:d} total vector count; avg {1:f} term/freq vector fields per doc]", new object[] { status.totVectors, (((float) status.totVectors) / status.docCount) })); } catch (System.Exception e) { Msg("ERROR [" + System.Convert.ToString(e.Message) + "]"); status.error = e; if (infoStream != null) { infoStream.WriteLine(e.StackTrace); } } return status; } /// Repairs the index using previously returned result /// from . Note that this does not /// remove any of the unreferenced files after it's done; /// you must separately open an , which /// deletes unreferenced files when it's created. /// ///

WARNING: this writes a /// new segments file into the index, effectively removing /// all documents in broken segments from the index. /// BE CAREFUL. /// ///

WARNING: Make sure you only call this when the /// index is not opened by any writer. ///

public virtual void FixIndex(Status result) { if (result.partial) throw new System.ArgumentException("can only fix an index that was fully checked (this status checked a subset of segments)"); result.newSegments.Commit(result.dir); } private static bool assertsOn; private static bool TestAsserts() { assertsOn = true; return true; } private static bool AssertsOn() { System.Diagnostics.Debug.Assert(TestAsserts()); return assertsOn; } /// Command-line interface to check and fix an index. ///

/// Run it like this: /// /// java -ea:Lucene.Net... Lucene.Net.Index.CheckIndex pathToIndex [-fix] [-segment X] [-segment Y] /// /// /// -fix: actually write a new segments_N file, removing any problematic segments /// -segment X: only check the specified /// segment(s). This can be specified multiple times, /// to check more than one segment, eg -segment _2 /// -segment _a. You can't use this with the -fix /// option. /// ///

WARNING: -fix should only be used on an emergency basis as it will cause /// documents (perhaps many) to be permanently removed from the index. Always make /// a backup copy of your index before running this! Do not run this tool on an index /// that is actively being written to. You have been warned! ///

Run without -fix, this tool will open the index, report version information /// and report any exceptions it hits and what action it would take if -fix were /// specified. With -fix, this tool will remove any segments that have issues and /// write a new segments_N file. This means all documents contained in the affected /// segments will be removed. ///

/// This tool exits with exit code 1 if the index cannot be opened or has any /// corruption, else 0. ///

[STAThread] public static void Main(System.String[] args) { bool doFix = false; var onlySegments = new List(); System.String indexPath = null; int i = 0; while (i < args.Length) { if (args[i].Equals("-fix")) { doFix = true; i++; } else if (args[i].Equals("-segment")) { if (i == args.Length - 1) { System.Console.Out.WriteLine("ERROR: missing name for -segment option"); System.Environment.Exit(1); } onlySegments.Add(args[i + 1]); i += 2; } else { if (indexPath != null) { System.Console.Out.WriteLine("ERROR: unexpected extra argument '" + args[i] + "'"); System.Environment.Exit(1); } indexPath = args[i]; i++; } } if (indexPath == null) { System.Console.Out.WriteLine("\nERROR: index path not specified"); System.Console.Out.WriteLine("\nUsage: java Lucene.Net.Index.CheckIndex pathToIndex [-fix] [-segment X] [-segment Y]\n" + "\n" + " -fix: actually write a new segments_N file, removing any problematic segments\n" + " -segment X: only check the specified segments. This can be specified multiple\n" + " times, to check more than one segment, eg '-segment _2 -segment _a'.\n" + " You can't use this with the -fix option\n" + "\n" + "**WARNING**: -fix should only be used on an emergency basis as it will cause\n" + "documents (perhaps many) to be permanently removed from the index. Always make\n" + "a backup copy of your index before running this! Do not run this tool on an index\n" + "that is actively being written to. You have been warned!\n" + "\n" + "Run without -fix, this tool will open the index, report version information\n" + "and report any exceptions it hits and what action it would take if -fix were\n" + "specified. With -fix, this tool will remove any segments that have issues and\n" + "write a new segments_N file. This means all documents contained in the affected\n" + "segments will be removed.\n" + "\n" + "This tool exits with exit code 1 if the index cannot be opened or has any\n" + "corruption, else 0.\n"); System.Environment.Exit(1); } if (!AssertsOn()) System.Console.Out.WriteLine("\nNOTE: testing will be more thorough if you run java with '-ea:Lucene.Net...', so assertions are enabled"); if (onlySegments.Count == 0) onlySegments = null; else if (doFix) { System.Console.Out.WriteLine("ERROR: cannot specify both -fix and -segment"); System.Environment.Exit(1); } System.Console.Out.WriteLine("\nOpening index @ " + indexPath + "\n"); Directory dir = null; try { dir = FSDirectory.Open(new System.IO.DirectoryInfo(indexPath)); } catch (Exception t) { Console.Out.WriteLine("ERROR: could not open directory \"" + indexPath + "\"; exiting"); Console.Out.WriteLine(t.StackTrace); Environment.Exit(1); } var checker = new CheckIndex(dir); var tempWriter = new System.IO.StreamWriter(System.Console.OpenStandardOutput(), System.Console.Out.Encoding) {AutoFlush = true}; checker.SetInfoStream(tempWriter); Status result = checker.CheckIndex_Renamed_Method(onlySegments); if (result.missingSegments) { System.Environment.Exit(1); } if (!result.clean) { if (!doFix) { System.Console.Out.WriteLine("WARNING: would write new segments file, and " + result.totLoseDocCount + " documents would be lost, if -fix were specified\n"); } else { Console.Out.WriteLine("WARNING: " + result.totLoseDocCount + " documents will be lost\n"); Console.Out.WriteLine("NOTE: will write new segments file in 5 seconds; this will remove " + result.totLoseDocCount + " docs from the index. THIS IS YOUR LAST CHANCE TO CTRL+C!"); for (var s = 0; s < 5; s++) { System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * 1000)); System.Console.Out.WriteLine(" " + (5 - s) + "..."); } Console.Out.WriteLine("Writing..."); checker.FixIndex(result); Console.Out.WriteLine("OK"); Console.Out.WriteLine("Wrote new segments file \"" + result.newSegments.GetCurrentSegmentFileName() + "\""); } } System.Console.Out.WriteLine(""); int exitCode; if (result != null && result.clean == true) exitCode = 0; else exitCode = 1; System.Environment.Exit(exitCode); } } }