IndexWriter.cs
上传用户:zhangkuixh
上传日期:2013-09-30
资源大小:5473k
文件大小:35k
源码类别:

搜索引擎

开发平台:

C#

  1. /*
  2.  * Copyright 2004 The Apache Software Foundation
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  * 
  8.  * http://www.apache.org/licenses/LICENSE-2.0
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. using System;
  17. using Analyzer = Lucene.Net.Analysis.Analyzer;
  18. using Document = Lucene.Net.Documents.Document;
  19. using Similarity = Lucene.Net.Search.Similarity;
  20. using Directory = Lucene.Net.Store.Directory;
  21. using FSDirectory = Lucene.Net.Store.FSDirectory;
  22. using IndexInput = Lucene.Net.Store.IndexInput;
  23. using IndexOutput = Lucene.Net.Store.IndexOutput;
  24. using Lock = Lucene.Net.Store.Lock;
  25. using RAMDirectory = Lucene.Net.Store.RAMDirectory;
  26. namespace Lucene.Net.Index
  27. {
  28. /// <summary>An IndexWriter creates and maintains an index.
  29. /// The third argument to the 
  30. /// <a href="#IndexWriter(Lucene.Net.store.Directory, Lucene.Net.analysis.Analyzer, boolean)"><b>constructor</b></a>
  31. /// determines whether a new index is created, or whether an existing index is
  32. /// opened for the addition of new documents.
  33. /// In either case, documents are added with the <a
  34. /// href="#addDocument(Lucene.Net.document.Document)"><b>addDocument</b></a> method.  
  35. /// When finished adding documents, <a href="#close()"><b>close</b></a> should be called.
  36. /// <p>If an index will not have more documents added for a while and optimal search
  37. /// performance is desired, then the <a href="#optimize()"><b>optimize</b></a>
  38. /// method should be called before the index is closed.
  39. /// </summary>
  40. /// <summary><p>Opening an IndexWriter creates a lock file for the directory in use. Trying to open
  41. /// another IndexWriter on the same directory will lead to an IOException. The IOException
  42. /// is also thrown if an IndexReader on the same directory is used to delete documents
  43. /// from the index.
  44. /// </summary>
  45. /// <seealso cref="IndexModifier IndexModifier supports the important methods of IndexWriter plus deletion">
  46. /// </seealso>
  47. public class IndexWriter
  48. {
  49. private class AnonymousClassWith : Lock.With
  50. {
  51. private void  InitBlock(bool create, IndexWriter enclosingInstance)
  52. {
  53. this.create = create;
  54. this.enclosingInstance = enclosingInstance;
  55. }
  56. private bool create;
  57. private IndexWriter enclosingInstance;
  58. public IndexWriter Enclosing_Instance
  59. {
  60. get
  61. {
  62. return enclosingInstance;
  63. }
  64. }
  65. internal AnonymousClassWith(bool create, IndexWriter enclosingInstance, Lucene.Net.Store.Lock Param1, long Param2) : base(Param1, Param2)
  66. {
  67. InitBlock(create, enclosingInstance);
  68. }
  69. public override System.Object DoBody()
  70. {
  71. if (create)
  72. Enclosing_Instance.segmentInfos.Write(Enclosing_Instance.directory);
  73. else
  74. Enclosing_Instance.segmentInfos.Read(Enclosing_Instance.directory);
  75. return null;
  76. }
  77. }
  78. private class AnonymousClassWith1 : Lock.With
  79. {
  80. private void  InitBlock(System.Collections.ArrayList segmentsToDelete, IndexWriter enclosingInstance)
  81. {
  82. this.segmentsToDelete = segmentsToDelete;
  83. this.enclosingInstance = enclosingInstance;
  84. }
  85. private System.Collections.ArrayList segmentsToDelete;
  86. private IndexWriter enclosingInstance;
  87. public IndexWriter Enclosing_Instance
  88. {
  89. get
  90. {
  91. return enclosingInstance;
  92. }
  93. }
  94. internal AnonymousClassWith1(System.Collections.ArrayList segmentsToDelete, IndexWriter enclosingInstance, Lucene.Net.Store.Lock Param1, long Param2):base(Param1, Param2)
  95. {
  96. InitBlock(segmentsToDelete, enclosingInstance);
  97. }
  98. public override System.Object DoBody()
  99. {
  100. Enclosing_Instance.segmentInfos.Write(Enclosing_Instance.directory); // commit changes
  101. Enclosing_Instance.DeleteSegments(segmentsToDelete); // delete now-unused segments
  102. return null;
  103. }
  104. }
  105. private class AnonymousClassWith2 : Lock.With
  106. {
  107. private void  InitBlock(System.String mergedName, System.Collections.ArrayList filesToDelete, IndexWriter enclosingInstance)
  108. {
  109. this.mergedName = mergedName;
  110. this.filesToDelete = filesToDelete;
  111. this.enclosingInstance = enclosingInstance;
  112. }
  113. private System.String mergedName;
  114. private System.Collections.ArrayList filesToDelete;
  115. private IndexWriter enclosingInstance;
  116. public IndexWriter Enclosing_Instance
  117. {
  118. get
  119. {
  120. return enclosingInstance;
  121. }
  122. }
  123. internal AnonymousClassWith2(System.String mergedName, System.Collections.ArrayList filesToDelete, IndexWriter enclosingInstance, Lucene.Net.Store.Lock Param1, long Param2):base(Param1, Param2)
  124. {
  125. InitBlock(mergedName, filesToDelete, enclosingInstance);
  126. }
  127. public override System.Object DoBody()
  128. {
  129. // make compound file visible for SegmentReaders
  130. Enclosing_Instance.directory.RenameFile(mergedName + ".tmp", mergedName + ".cfs");
  131. // delete now unused files of segment 
  132. Enclosing_Instance.DeleteFiles(filesToDelete);
  133. return null;
  134. }
  135. }
  136. private class AnonymousClassWith3 : Lock.With
  137. {
  138. private void  InitBlock(System.Collections.ArrayList segmentsToDelete, IndexWriter enclosingInstance)
  139. {
  140. this.segmentsToDelete = segmentsToDelete;
  141. this.enclosingInstance = enclosingInstance;
  142. }
  143. private System.Collections.ArrayList segmentsToDelete;
  144. private IndexWriter enclosingInstance;
  145. public IndexWriter Enclosing_Instance
  146. {
  147. get
  148. {
  149. return enclosingInstance;
  150. }
  151. }
  152. internal AnonymousClassWith3(System.Collections.ArrayList segmentsToDelete, IndexWriter enclosingInstance, Lucene.Net.Store.Lock Param1, long Param2):base(Param1, Param2)
  153. {
  154. InitBlock(segmentsToDelete, enclosingInstance);
  155. }
  156. public override System.Object DoBody()
  157. {
  158. Enclosing_Instance.segmentInfos.Write(Enclosing_Instance.directory); // commit before deleting
  159. Enclosing_Instance.DeleteSegments(segmentsToDelete); // delete now-unused segments
  160. return null;
  161. }
  162. }
  163. private class AnonymousClassWith4 : Lock.With
  164. {
  165. private void  InitBlock(System.String mergedName, System.Collections.ArrayList filesToDelete, IndexWriter enclosingInstance)
  166. {
  167. this.mergedName = mergedName;
  168. this.filesToDelete = filesToDelete;
  169. this.enclosingInstance = enclosingInstance;
  170. }
  171. private System.String mergedName;
  172. private System.Collections.ArrayList filesToDelete;
  173. private IndexWriter enclosingInstance;
  174. public IndexWriter Enclosing_Instance
  175. {
  176. get
  177. {
  178. return enclosingInstance;
  179. }
  180. }
  181. internal AnonymousClassWith4(System.String mergedName, System.Collections.ArrayList filesToDelete, IndexWriter enclosingInstance, Lucene.Net.Store.Lock Param1, long Param2):base(Param1, Param2)
  182. {
  183. InitBlock(mergedName, filesToDelete, enclosingInstance);
  184. }
  185. public override System.Object DoBody()
  186. {
  187. // make compound file visible for SegmentReaders
  188. Enclosing_Instance.directory.RenameFile(mergedName + ".tmp", mergedName + ".cfs");
  189. // delete now unused files of segment 
  190. Enclosing_Instance.DeleteFiles(filesToDelete);
  191. return null;
  192. }
  193. }
  194. private void  InitBlock()
  195. {
  196. similarity = Similarity.GetDefault();
  197. }
  198. /// <summary> Default value is 1,000.</summary>
  199. public const long WRITE_LOCK_TIMEOUT = 1000;
  200. /// <summary> Default value is 10,000.</summary>
  201. public const long COMMIT_LOCK_TIMEOUT = 10000;
  202. public const System.String WRITE_LOCK_NAME = "write.lock";
  203. public const System.String COMMIT_LOCK_NAME = "commit.lock";
  204. /// <summary> Default value is 10. Change using {@link #SetMergeFactor(int)}.</summary>
  205. public const int DEFAULT_MERGE_FACTOR = 10;
  206. /// <summary> Default value is 10. Change using {@link #SetMaxBufferedDocs(int)}.</summary>
  207. public const int DEFAULT_MAX_BUFFERED_DOCS = 10;
  208. /// <deprecated> use {@link #DEFAULT_MAX_BUFFERED_DOCS} instead
  209. /// </deprecated>
  210. public static readonly int DEFAULT_MIN_MERGE_DOCS = DEFAULT_MAX_BUFFERED_DOCS;
  211. /// <summary> Default value is {@link Integer#MAX_VALUE}. Change using {@link #SetMaxMergeDocs(int)}.</summary>
  212. public static readonly int DEFAULT_MAX_MERGE_DOCS = System.Int32.MaxValue;
  213. /// <summary> Default value is 10,000. Change using {@link #SetMaxFieldLength(int)}.</summary>
  214. public const int DEFAULT_MAX_FIELD_LENGTH = 10000;
  215. /// <summary> Default value is 128. Change using {@link #SetTermIndexInterval(int)}.</summary>
  216. public const int DEFAULT_TERM_INDEX_INTERVAL = 128;
  217. private Directory directory; // where this index resides
  218. private Analyzer analyzer; // how to analyze text
  219. private Similarity similarity; // how to normalize
  220. private SegmentInfos segmentInfos = new SegmentInfos(); // the segments
  221. private Directory ramDirectory = new RAMDirectory(); // for temp segs
  222. private Lock writeLock;
  223. private int termIndexInterval = DEFAULT_TERM_INDEX_INTERVAL;
  224. /// <summary>Use compound file setting. Defaults to true, minimizing the number of
  225. /// files used.  Setting this to false may improve indexing performance, but
  226. /// may also cause file handle problems.
  227. /// </summary>
  228. private bool useCompoundFile = true;
  229. private bool closeDir;
  230. /// <summary>Get the current setting of whether to use the compound file format.
  231. /// Note that this just returns the value you set with setUseCompoundFile(boolean)
  232. /// or the default. You cannot use this to query the status of an existing index.
  233. /// </summary>
  234. /// <seealso cref="SetUseCompoundFile(boolean)">
  235. /// </seealso>
  236. public virtual bool GetUseCompoundFile()
  237. {
  238. return useCompoundFile;
  239. }
  240. /// <summary>Setting to turn on usage of a compound file. When on, multiple files
  241. /// for each segment are merged into a single file once the segment creation
  242. /// is finished. This is done regardless of what directory is in use.
  243. /// </summary>
  244. public virtual void  SetUseCompoundFile(bool value_Renamed)
  245. {
  246. useCompoundFile = value_Renamed;
  247. }
  248. /// <summary>Expert: Set the Similarity implementation used by this IndexWriter.
  249. /// 
  250. /// </summary>
  251. /// <seealso cref="Similarity.SetDefault(Similarity)">
  252. /// </seealso>
  253. public virtual void  SetSimilarity(Similarity similarity)
  254. {
  255. this.similarity = similarity;
  256. }
  257. /// <summary>Expert: Return the Similarity implementation used by this IndexWriter.
  258. /// 
  259. /// <p>This defaults to the current value of {@link Similarity#GetDefault()}.
  260. /// </summary>
  261. public virtual Similarity GetSimilarity()
  262. {
  263. return this.similarity;
  264. }
  265. /// <summary>Expert: Set the interval between indexed terms.  Large values cause less
  266. /// memory to be used by IndexReader, but slow random-access to terms.  Small
  267. /// values cause more memory to be used by an IndexReader, and speed
  268. /// random-access to terms.
  269. /// 
  270. /// This parameter determines the amount of computation required per query
  271. /// term, regardless of the number of documents that contain that term.  In
  272. /// particular, it is the maximum number of other terms that must be
  273. /// scanned before a term is located and its frequency and position information
  274. /// may be processed.  In a large index with user-entered query terms, query
  275. /// processing time is likely to be dominated not by term lookup but rather
  276. /// by the processing of frequency and positional data.  In a small index
  277. /// or when many uncommon query terms are generated (e.g., by wildcard
  278. /// queries) term lookup may become a dominant cost.
  279. /// 
  280. /// In particular, <code>numUniqueTerms/interval</code> terms are read into
  281. /// memory by an IndexReader, and, on average, <code>interval/2</code> terms
  282. /// must be scanned for each random term access.
  283. /// 
  284. /// </summary>
  285. /// <seealso cref="DEFAULT_TERM_INDEX_INTERVAL">
  286. /// </seealso>
  287. public virtual void  SetTermIndexInterval(int interval)
  288. {
  289. this.termIndexInterval = interval;
  290. }
  291. /// <summary>Expert: Return the interval between indexed terms.
  292. /// 
  293. /// </summary>
  294. /// <seealso cref="SetTermIndexInterval(int)">
  295. /// </seealso>
  296. public virtual int GetTermIndexInterval()
  297. {
  298. return termIndexInterval;
  299. }
  300. /// <summary> Constructs an IndexWriter for the index in <code>path</code>.
  301. /// Text will be analyzed with <code>a</code>.  If <code>create</code>
  302. /// is true, then a new, empty index will be created in
  303. /// <code>path</code>, replacing the index already there, if any.
  304. /// 
  305. /// </summary>
  306. /// <param name="path">the path to the index directory
  307. /// </param>
  308. /// <param name="a">the analyzer to use
  309. /// </param>
  310. /// <param name="create"><code>true</code> to create the index or overwrite
  311. /// the existing one; <code>false</code> to append to the existing
  312. /// index
  313. /// </param>
  314. /// <throws>  IOException if the directory cannot be read/written to, or </throws>
  315. /// <summary>  if it does not exist, and <code>create</code> is
  316. /// <code>false</code>
  317. /// </summary>
  318. public IndexWriter(System.String path, Analyzer a, bool create) : this(FSDirectory.GetDirectory(path, create), a, create, true)
  319. {
  320. }
  321. /// <summary> Constructs an IndexWriter for the index in <code>path</code>.
  322. /// Text will be analyzed with <code>a</code>.  If <code>create</code>
  323. /// is true, then a new, empty index will be created in
  324. /// <code>path</code>, replacing the index already there, if any.
  325. /// 
  326. /// </summary>
  327. /// <param name="path">the path to the index directory
  328. /// </param>
  329. /// <param name="a">the analyzer to use
  330. /// </param>
  331. /// <param name="create"><code>true</code> to create the index or overwrite
  332. /// the existing one; <code>false</code> to append to the existing
  333. /// index
  334. /// </param>
  335. /// <throws>  IOException if the directory cannot be read/written to, or </throws>
  336. /// <summary>  if it does not exist, and <code>create</code> is
  337. /// <code>false</code>
  338. /// </summary>
  339. public IndexWriter(System.IO.FileInfo path, Analyzer a, bool create) : this(FSDirectory.GetDirectory(path, create), a, create, true)
  340. {
  341. }
  342. /// <summary> Constructs an IndexWriter for the index in <code>d</code>.
  343. /// Text will be analyzed with <code>a</code>.  If <code>create</code>
  344. /// is true, then a new, empty index will be created in
  345. /// <code>d</code>, replacing the index already there, if any.
  346. /// 
  347. /// </summary>
  348. /// <param name="d">the index directory
  349. /// </param>
  350. /// <param name="a">the analyzer to use
  351. /// </param>
  352. /// <param name="create"><code>true</code> to create the index or overwrite
  353. /// the existing one; <code>false</code> to append to the existing
  354. /// index
  355. /// </param>
  356. /// <throws>  IOException if the directory cannot be read/written to, or </throws>
  357. /// <summary>  if it does not exist, and <code>create</code> is
  358. /// <code>false</code>
  359. /// </summary>
  360. public IndexWriter(Directory d, Analyzer a, bool create) : this(d, a, create, false)
  361. {
  362. }
  363. private IndexWriter(Directory d, Analyzer a, bool create, bool closeDir)
  364. {
  365. InitBlock();
  366. this.closeDir = closeDir;
  367. directory = d;
  368. analyzer = a;
  369. Lock writeLock = directory.MakeLock(IndexWriter.WRITE_LOCK_NAME);
  370. if (!writeLock.obtain(WRITE_LOCK_TIMEOUT))
  371. // obtain write lock
  372. {
  373. throw new System.IO.IOException("Index locked for write: " + writeLock);
  374. }
  375. this.writeLock = writeLock; // save it
  376. lock (directory)
  377. {
  378. // in- & inter-process sync
  379. new AnonymousClassWith(create, this, directory.MakeLock(IndexWriter.COMMIT_LOCK_NAME), COMMIT_LOCK_TIMEOUT).Run();
  380. }
  381. }
  382. /// <summary>Determines the largest number of documents ever merged by addDocument().
  383. /// Small values (e.g., less than 10,000) are best for interactive indexing,
  384. /// as this limits the length of pauses while indexing to a few seconds.
  385. /// Larger values are best for batched indexing and speedier searches.
  386. /// 
  387. /// <p>The default value is {@link Integer#MAX_VALUE}.
  388. /// </summary>
  389. public virtual void  SetMaxMergeDocs(int maxMergeDocs)
  390. {
  391. this.maxMergeDocs = maxMergeDocs;
  392. }
  393. /// <seealso cref="setMaxMergeDocs">
  394. /// </seealso>
  395. public virtual int GetMaxMergeDocs()
  396. {
  397. return maxMergeDocs;
  398. }
  399. /// <summary> The maximum number of terms that will be indexed for a single field in a
  400. /// document.  This limits the amount of memory required for indexing, so that
  401. /// collections with very large files will not crash the indexing process by
  402. /// running out of memory.<p/>
  403. /// Note that this effectively truncates large documents, excluding from the
  404. /// index terms that occur further in the document.  If you know your source
  405. /// documents are large, be sure to set this value high enough to accomodate
  406. /// the expected size.  If you set it to Integer.MAX_VALUE, then the only limit
  407. /// is your memory, but you should anticipate an OutOfMemoryError.<p/>
  408. /// By default, no more than 10,000 terms will be indexed for a field.
  409. /// </summary>
  410. public virtual void  SetMaxFieldLength(int maxFieldLength)
  411. {
  412. this.maxFieldLength = maxFieldLength;
  413. }
  414. /// <seealso cref="setMaxFieldLength">
  415. /// </seealso>
  416. public virtual int GetMaxFieldLength()
  417. {
  418. return maxFieldLength;
  419. }
  420. /// <summary>Determines the minimal number of documents required before the buffered
  421. /// in-memory documents are merging and a new Segment is created.
  422. /// Since Documents are merged in a {@link Lucene.Net.store.RAMDirectory},
  423. /// large value gives faster indexing.  At the same time, mergeFactor limits
  424. /// the number of files open in a FSDirectory.
  425. /// 
  426. /// <p> The default value is 10.
  427. /// 
  428. /// </summary>
  429. /// <throws>  IllegalArgumentException if maxBufferedDocs is smaller than 1  </throws>
  430. public virtual void  SetMaxBufferedDocs(int maxBufferedDocs)
  431. {
  432. if (maxBufferedDocs < 1)
  433. throw new System.ArgumentException("maxBufferedDocs must at least be 1");
  434. this.minMergeDocs = maxBufferedDocs;
  435. }
  436. /// <seealso cref="setMaxBufferedDocs">
  437. /// </seealso>
  438. public virtual int GetMaxBufferedDocs()
  439. {
  440. return minMergeDocs;
  441. }
  442. /// <summary>Determines how often segment indices are merged by addDocument().  With
  443. /// smaller values, less RAM is used while indexing, and searches on
  444. /// unoptimized indices are faster, but indexing speed is slower.  With larger
  445. /// values, more RAM is used during indexing, and while searches on unoptimized
  446. /// indices are slower, indexing is faster.  Thus larger values (> 10) are best
  447. /// for batch index creation, and smaller values (< 10) for indices that are
  448. /// interactively maintained.
  449. /// 
  450. /// <p>This must never be less than 2.  The default value is 10.
  451. /// </summary>
  452. public virtual void  SetMergeFactor(int mergeFactor)
  453. {
  454. if (mergeFactor < 2)
  455. throw new System.ArgumentException("mergeFactor cannot be less than 2");
  456. this.mergeFactor = mergeFactor;
  457. }
  458. /// <seealso cref="setMergeFactor">
  459. /// </seealso>
  460. public virtual int GetMergeFactor()
  461. {
  462. return mergeFactor;
  463. }
  464. /// <summary>If non-null, information about merges and a message when
  465. /// maxFieldLength is reached will be printed to this.
  466. /// </summary>
  467. public virtual void  SetInfoStream(System.IO.TextWriter infoStream)
  468. {
  469. this.infoStream = infoStream;
  470. }
  471. /// <seealso cref="setInfoStream">
  472. /// </seealso>
  473. public virtual System.IO.TextWriter GetInfoStream()
  474. {
  475. return infoStream;
  476. }
  477. /// <summary>Flushes all changes to an index and closes all associated files. </summary>
  478. public virtual void  Close()
  479. {
  480. lock (this)
  481. {
  482. FlushRamSegments();
  483. ramDirectory.Close();
  484. if (writeLock != null)
  485. {
  486. writeLock.Release(); // release write lock
  487. writeLock = null;
  488. }
  489. if (closeDir)
  490. directory.Close();
  491.                 System.GC.SuppressFinalize(this);
  492.             }
  493. }
  494. /// <summary>Release the write lock, if needed. </summary>
  495. ~IndexWriter()
  496. {
  497. if (writeLock != null)
  498. {
  499. writeLock.Release(); // release write lock
  500. writeLock = null;
  501. }
  502. }
  503. /// <summary>Returns the Directory used by this index. </summary>
  504. public virtual Directory GetDirectory()
  505. {
  506. return directory;
  507. }
  508. /// <summary>Returns the analyzer used by this index. </summary>
  509. public virtual Analyzer GetAnalyzer()
  510. {
  511. return analyzer;
  512. }
  513. /// <summary>Returns the number of documents currently in this index. </summary>
  514. public virtual int DocCount()
  515. {
  516. lock (this)
  517. {
  518. int count = 0;
  519. for (int i = 0; i < segmentInfos.Count; i++)
  520. {
  521. SegmentInfo si = segmentInfos.Info(i);
  522. count += si.docCount;
  523. }
  524. return count;
  525. }
  526. }
  527. /// <summary> The maximum number of terms that will be indexed for a single field in a
  528. /// document.  This limits the amount of memory required for indexing, so that
  529. /// collections with very large files will not crash the indexing process by
  530. /// running out of memory.<p/>
  531. /// Note that this effectively truncates large documents, excluding from the
  532. /// index terms that occur further in the document.  If you know your source
  533. /// documents are large, be sure to set this value high enough to accomodate
  534. /// the expected size.  If you set it to Integer.MAX_VALUE, then the only limit
  535. /// is your memory, but you should anticipate an OutOfMemoryError.<p/>
  536. /// By default, no more than 10,000 terms will be indexed for a field.
  537. /// 
  538. /// </summary>
  539. /// <deprecated> use {@link #setMaxFieldLength} instead
  540. /// </deprecated>
  541. public int maxFieldLength = DEFAULT_MAX_FIELD_LENGTH;
  542. /// <summary> Adds a document to this index.  If the document contains more than
  543. /// {@link #SetMaxFieldLength(int)} terms for a given field, the remainder are
  544. /// discarded.
  545. /// </summary>
  546. public virtual void  AddDocument(Document doc)
  547. {
  548. AddDocument(doc, analyzer);
  549. }
  550. /// <summary> Adds a document to this index, using the provided analyzer instead of the
  551. /// value of {@link #GetAnalyzer()}.  If the document contains more than
  552. /// {@link #SetMaxFieldLength(int)} terms for a given field, the remainder are
  553. /// discarded.
  554. /// </summary>
  555. public virtual void  AddDocument(Document doc, Analyzer analyzer)
  556. {
  557. DocumentWriter dw = new DocumentWriter(ramDirectory, analyzer, this);
  558. dw.SetInfoStream(infoStream);
  559. System.String segmentName = NewSegmentName();
  560. dw.AddDocument(segmentName, doc);
  561. lock (this)
  562. {
  563. segmentInfos.Add(new SegmentInfo(segmentName, 1, ramDirectory));
  564. MaybeMergeSegments();
  565. }
  566. }
  567. internal int GetSegmentsCounter()
  568. {
  569. return segmentInfos.counter;
  570. }
  571. private System.String NewSegmentName()
  572. {
  573. lock (this)
  574. {
  575.                 return "_" + SupportClass.Number.ToString(segmentInfos.counter++, SupportClass.Number.MAX_RADIX);
  576. }
  577. }
  578. /// <summary>Determines how often segment indices are merged by addDocument().  With
  579. /// smaller values, less RAM is used while indexing, and searches on
  580. /// unoptimized indices are faster, but indexing speed is slower.  With larger
  581. /// values, more RAM is used during indexing, and while searches on unoptimized
  582. /// indices are slower, indexing is faster.  Thus larger values (> 10) are best
  583. /// for batch index creation, and smaller values (< 10) for indices that are
  584. /// interactively maintained.
  585. /// 
  586. /// <p>This must never be less than 2.  The default value is 10.
  587. /// </summary>
  588. /// <deprecated> use {@link #setMergeFactor} instead
  589. /// </deprecated>
  590. public int mergeFactor = DEFAULT_MERGE_FACTOR;
  591. /// <summary>Determines the minimal number of documents required before the buffered
  592. /// in-memory documents are merging and a new Segment is created.
  593. /// Since Documents are merged in a {@link Lucene.Net.store.RAMDirectory},
  594. /// large value gives faster indexing.  At the same time, mergeFactor limits
  595. /// the number of files open in a FSDirectory.
  596. /// 
  597. /// <p> The default value is 10.
  598. /// </summary>
  599. /// <deprecated> use {@link #setMaxBufferedDocs} instead
  600. /// </deprecated>
  601. public int minMergeDocs = DEFAULT_MIN_MERGE_DOCS;
  602. /// <summary>Determines the largest number of documents ever merged by addDocument().
  603. /// Small values (e.g., less than 10,000) are best for interactive indexing,
  604. /// as this limits the length of pauses while indexing to a few seconds.
  605. /// Larger values are best for batched indexing and speedier searches.
  606. /// 
  607. /// <p>The default value is {@link Integer#MAX_VALUE}.
  608. /// </summary>
  609. /// <deprecated> use {@link #setMaxMergeDocs} instead
  610. /// </deprecated>
  611. public int maxMergeDocs = DEFAULT_MAX_MERGE_DOCS;
  612. /// <summary>If non-null, information about merges will be printed to this.</summary>
  613. /// <deprecated> use {@link #setInfoStream} instead 
  614. /// </deprecated>
  615. public System.IO.TextWriter infoStream = null;
  616. /// <summary>Merges all segments together into a single segment, optimizing an index
  617. /// for search. 
  618. /// </summary>
  619. public virtual void  Optimize()
  620. {
  621. lock (this)
  622. {
  623. FlushRamSegments();
  624. while (segmentInfos.Count > 1 || (segmentInfos.Count == 1 && (SegmentReader.HasDeletions(segmentInfos.Info(0)) || segmentInfos.Info(0).dir != directory || (useCompoundFile && (!SegmentReader.UsesCompoundFile(segmentInfos.Info(0)) || SegmentReader.HasSeparateNorms(segmentInfos.Info(0)))))))
  625. {
  626. int minSegment = segmentInfos.Count - mergeFactor;
  627. MergeSegments(minSegment < 0?0:minSegment);
  628. }
  629. }
  630. }
  631. /// <summary>Merges all segments from an array of indexes into this index.
  632. /// 
  633. /// <p>This may be used to parallelize batch indexing.  A large document
  634. /// collection can be broken into sub-collections.  Each sub-collection can be
  635. /// indexed in parallel, on a different thread, process or machine.  The
  636. /// complete index can then be created by merging sub-collection indexes
  637. /// with this method.
  638. /// 
  639. /// <p>After this completes, the index is optimized. 
  640. /// </summary>
  641. public virtual void  AddIndexes(Directory[] dirs)
  642. {
  643. lock (this)
  644. {
  645. Optimize(); // start with zero or 1 seg
  646. int start = segmentInfos.Count;
  647. for (int i = 0; i < dirs.Length; i++)
  648. {
  649. SegmentInfos sis = new SegmentInfos(); // read infos from dir
  650. sis.Read(dirs[i]);
  651. for (int j = 0; j < sis.Count; j++)
  652. {
  653. segmentInfos.Add(sis.Info(j)); // add each info
  654. }
  655. }
  656. // merge newly added segments in log(n) passes
  657. while (segmentInfos.Count > start + mergeFactor)
  658. {
  659. for (int base_Renamed = start + 1; base_Renamed < segmentInfos.Count; base_Renamed++)
  660. {
  661. int end = System.Math.Min(segmentInfos.Count, base_Renamed + mergeFactor);
  662. if (end - base_Renamed > 1)
  663. MergeSegments(base_Renamed, end);
  664. }
  665. }
  666. Optimize(); // final cleanup
  667. }
  668. }
  669. /// <summary>Merges the provided indexes into this index.
  670. /// <p>After this completes, the index is optimized. </p>
  671. /// <p>The provided IndexReaders are not closed.</p>
  672. /// </summary>
  673. public virtual void  AddIndexes(IndexReader[] readers)
  674. {
  675. lock (this)
  676. {
  677. Optimize(); // start with zero or 1 seg
  678. System.String mergedName = NewSegmentName();
  679. SegmentMerger merger = new SegmentMerger(this, mergedName);
  680. System.Collections.ArrayList segmentsToDelete = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  681. IndexReader sReader = null;
  682. if (segmentInfos.Count == 1)
  683. {
  684. // add existing index, if any
  685. sReader = SegmentReader.Get(segmentInfos.Info(0));
  686. merger.Add(sReader);
  687. segmentsToDelete.Add(sReader); // queue segment for deletion
  688. }
  689. for (int i = 0; i < readers.Length; i++)
  690.      // add new indexes
  691. merger.Add(readers[i]);
  692. int docCount = merger.Merge(); // merge 'em
  693. segmentInfos.RemoveRange(0, segmentInfos.Count - 0);  // pop old infos & add new
  694. segmentInfos.Add(new SegmentInfo(mergedName, docCount, directory));
  695. if (sReader != null)
  696. sReader.Close();
  697. lock (directory)
  698. {
  699. // in- & inter-process sync
  700. new AnonymousClassWith1(segmentsToDelete, this, directory.MakeLock(COMMIT_LOCK_NAME), COMMIT_LOCK_TIMEOUT).Run();
  701. }
  702. if (useCompoundFile)
  703. {
  704. System.Collections.ArrayList filesToDelete = merger.CreateCompoundFile(mergedName + ".tmp");
  705. lock (directory)
  706. {
  707. // in- & inter-process sync
  708. new AnonymousClassWith2(mergedName, filesToDelete, this, directory.MakeLock(COMMIT_LOCK_NAME), COMMIT_LOCK_TIMEOUT).Run();
  709. }
  710. }
  711. }
  712. }
  713. /// <summary>Merges all RAM-resident segments. </summary>
  714. private void  FlushRamSegments()
  715. {
  716. int minSegment = segmentInfos.Count - 1;
  717. int docCount = 0;
  718. while (minSegment >= 0 && (segmentInfos.Info(minSegment)).dir == ramDirectory)
  719. {
  720. docCount += segmentInfos.Info(minSegment).docCount;
  721. minSegment--;
  722. }
  723. if (minSegment < 0 || (docCount + segmentInfos.Info(minSegment).docCount) > mergeFactor || !(segmentInfos.Info(segmentInfos.Count - 1).dir == ramDirectory))
  724. minSegment++;
  725. if (minSegment >= segmentInfos.Count)
  726. return ; // none to merge
  727. MergeSegments(minSegment);
  728. }
  729. /// <summary>Incremental segment merger.  </summary>
  730. private void  MaybeMergeSegments()
  731. {
  732. long targetMergeDocs = minMergeDocs;
  733. while (targetMergeDocs <= maxMergeDocs)
  734. {
  735. // find segments smaller than current target size
  736. int minSegment = segmentInfos.Count;
  737. int mergeDocs = 0;
  738. while (--minSegment >= 0)
  739. {
  740. SegmentInfo si = segmentInfos.Info(minSegment);
  741. if (si.docCount >= targetMergeDocs)
  742. break;
  743. mergeDocs += si.docCount;
  744. }
  745. if (mergeDocs >= targetMergeDocs)
  746. // found a merge to do
  747. MergeSegments(minSegment + 1);
  748. else
  749. break;
  750. targetMergeDocs *= mergeFactor; // increase target size
  751. }
  752. }
  753. /// <summary>Pops segments off of segmentInfos stack down to minSegment, merges them,
  754. /// and pushes the merged index onto the top of the segmentInfos stack. 
  755. /// </summary>
  756. private void  MergeSegments(int minSegment)
  757. {
  758. MergeSegments(minSegment, segmentInfos.Count);
  759. }
  760. /// <summary>Merges the named range of segments, replacing them in the stack with a
  761. /// single segment. 
  762. /// </summary>
  763. private void  MergeSegments(int minSegment, int end)
  764. {
  765. System.String mergedName = NewSegmentName();
  766. if (infoStream != null)
  767. infoStream.Write("merging segments");
  768. SegmentMerger merger = new SegmentMerger(this, mergedName);
  769. System.Collections.ArrayList segmentsToDelete = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  770. for (int i = minSegment; i < end; i++)
  771. {
  772. SegmentInfo si = segmentInfos.Info(i);
  773. if (infoStream != null)
  774. infoStream.Write(" " + si.name + " (" + si.docCount + " docs)");
  775. IndexReader reader = SegmentReader.Get(si);
  776. merger.Add(reader);
  777. if ((reader.Directory() == this.directory) || (reader.Directory() == this.ramDirectory))
  778. segmentsToDelete.Add(reader); // queue segment for deletion
  779. }
  780. int mergedDocCount = merger.Merge();
  781. if (infoStream != null)
  782. {
  783. infoStream.WriteLine(" into " + mergedName + " (" + mergedDocCount + " docs)");
  784. }
  785. for (int i = end - 1; i >= minSegment; i--)
  786.      // remove old infos & add new
  787. segmentInfos.RemoveAt(i);
  788. segmentInfos.Add(new SegmentInfo(mergedName, mergedDocCount, directory));
  789. // close readers before we attempt to delete now-obsolete segments
  790. merger.CloseReaders();
  791. lock (directory)
  792. {
  793. // in- & inter-process sync
  794. new AnonymousClassWith3(segmentsToDelete, this, directory.MakeLock(COMMIT_LOCK_NAME), COMMIT_LOCK_TIMEOUT).Run();
  795. }
  796. if (useCompoundFile)
  797. {
  798. System.Collections.ArrayList filesToDelete = merger.CreateCompoundFile(mergedName + ".tmp");
  799. lock (directory)
  800. {
  801. // in- & inter-process sync
  802. new AnonymousClassWith4(mergedName, filesToDelete, this, directory.MakeLock(COMMIT_LOCK_NAME), COMMIT_LOCK_TIMEOUT).Run();
  803. }
  804. }
  805. }
  806. /*
  807. * Some operating systems (e.g. Windows) don't permit a file to be deleted
  808. * while it is opened for read (e.g. by another process or thread). So we
  809. * assume that when a delete fails it is because the file is open in another
  810. * process, and queue the file for subsequent deletion.
  811. */
  812. private void  DeleteSegments(System.Collections.ArrayList segments)
  813. {
  814. System.Collections.ArrayList deletable = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  815. DeleteFiles(ReadDeleteableFiles(), deletable); // try to delete deleteable
  816. for (int i = 0; i < segments.Count; i++)
  817. {
  818. SegmentReader reader = (SegmentReader) segments[i];
  819. if (reader.Directory() == this.directory)
  820. DeleteFiles(reader.Files(), deletable);
  821. // try to delete our files
  822. else
  823. DeleteFiles(reader.Files(), reader.Directory()); // delete other files
  824. }
  825. WriteDeleteableFiles(deletable); // note files we can't delete
  826. }
  827. private void  DeleteFiles(System.Collections.ArrayList files)
  828. {
  829. System.Collections.ArrayList deletable = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  830. DeleteFiles(ReadDeleteableFiles(), deletable); // try to delete deleteable
  831. DeleteFiles(files, deletable); // try to delete our files
  832. WriteDeleteableFiles(deletable); // note files we can't delete
  833. }
  834. private void  DeleteFiles(System.Collections.ArrayList files, Directory directory)
  835. {
  836. for (int i = 0; i < files.Count; i++)
  837. directory.DeleteFile((System.String) files[i]);
  838. }
  839. private void  DeleteFiles(System.Collections.ArrayList files, System.Collections.ArrayList deletable)
  840. {
  841. for (int i = 0; i < files.Count; i++)
  842. {
  843. System.String file = (System.String) files[i];
  844. try
  845. {
  846. directory.DeleteFile(file); // try to delete each file
  847. }
  848. catch (System.IO.IOException e)
  849. {
  850. // if delete fails
  851. if (directory.FileExists(file))
  852. {
  853. if (infoStream != null)
  854. {
  855. infoStream.WriteLine(e.ToString() + "; Will re-try later.");
  856. }
  857. deletable.Add(file); // add to deletable
  858. }
  859. }
  860. }
  861. }
  862. private System.Collections.ArrayList ReadDeleteableFiles()
  863. {
  864. System.Collections.ArrayList result = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
  865. if (!directory.FileExists(IndexFileNames.DELETABLE))
  866. return result;
  867. IndexInput input = directory.OpenInput(IndexFileNames.DELETABLE);
  868. try
  869. {
  870. for (int i = input.ReadInt(); i > 0; i--)
  871. // read file names
  872. result.Add(input.ReadString());
  873. }
  874. finally
  875. {
  876. input.Close();
  877. }
  878. return result;
  879. }
  880. private void  WriteDeleteableFiles(System.Collections.ArrayList files)
  881. {
  882. IndexOutput output = directory.CreateOutput("deleteable.new");
  883. try
  884. {
  885. output.WriteInt(files.Count);
  886. for (int i = 0; i < files.Count; i++)
  887. output.WriteString((System.String) files[i]);
  888. }
  889. finally
  890. {
  891. output.Close();
  892. }
  893. directory.RenameFile("deleteable.new", IndexFileNames.DELETABLE);
  894. }
  895. }
  896. }