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

搜索引擎

开发平台:

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 Token = Lucene.Net.Analysis.Token;
  19. using TokenStream = Lucene.Net.Analysis.TokenStream;
  20. using Document = Lucene.Net.Documents.Document;
  21. using Field = Lucene.Net.Documents.Field;
  22. using Similarity = Lucene.Net.Search.Similarity;
  23. using Directory = Lucene.Net.Store.Directory;
  24. using IndexOutput = Lucene.Net.Store.IndexOutput;
  25. namespace Lucene.Net.Index
  26. {
  27. public sealed class DocumentWriter
  28. {
  29. private void  InitBlock()
  30. {
  31. termIndexInterval = IndexWriter.DEFAULT_TERM_INDEX_INTERVAL;
  32. }
  33. private Analyzer analyzer;
  34. private Directory directory;
  35. private Similarity similarity;
  36. private FieldInfos fieldInfos;
  37. private int maxFieldLength;
  38. private int termIndexInterval;
  39. private System.IO.TextWriter infoStream;
  40. /// <summary>This ctor used by test code only.
  41. /// 
  42. /// </summary>
  43. /// <param name="directory">The directory to write the document information to
  44. /// </param>
  45. /// <param name="analyzer">The analyzer to use for the document
  46. /// </param>
  47. /// <param name="similarity">The Similarity function
  48. /// </param>
  49. /// <param name="maxFieldLength">The maximum number of tokens a field may have
  50. /// </param>
  51. internal DocumentWriter(Directory directory, Analyzer analyzer, Similarity similarity, int maxFieldLength)
  52. {
  53. InitBlock();
  54. this.directory = directory;
  55. this.analyzer = analyzer;
  56. this.similarity = similarity;
  57. this.maxFieldLength = maxFieldLength;
  58. }
  59. internal DocumentWriter(Directory directory, Analyzer analyzer, IndexWriter writer)
  60. {
  61. InitBlock();
  62. this.directory = directory;
  63. this.analyzer = analyzer;
  64. this.similarity = writer.GetSimilarity();
  65. this.maxFieldLength = writer.GetMaxFieldLength();
  66. this.termIndexInterval = writer.GetTermIndexInterval();
  67. }
  68. public /*internal*/ void  AddDocument(System.String segment, Document doc)
  69. {
  70. // write field names
  71. fieldInfos = new FieldInfos();
  72. fieldInfos.Add(doc);
  73. fieldInfos.Write(directory, segment + ".fnm");
  74. // write field values
  75. FieldsWriter fieldsWriter = new FieldsWriter(directory, segment, fieldInfos);
  76. try
  77. {
  78. fieldsWriter.AddDocument(doc);
  79. }
  80. finally
  81. {
  82. fieldsWriter.Close();
  83. }
  84. // invert doc into postingTable
  85. postingTable.Clear(); // clear postingTable
  86. fieldLengths = new int[fieldInfos.Size()]; // init fieldLengths
  87. fieldPositions = new int[fieldInfos.Size()]; // init fieldPositions
  88. fieldOffsets = new int[fieldInfos.Size()]; // init fieldOffsets
  89. fieldBoosts = new float[fieldInfos.Size()]; // init fieldBoosts
  90.             float boost = doc.GetBoost();             for (int i = 0; i < fieldBoosts.Length; i++)             {                 fieldBoosts[i] = boost;             }
  91. InvertDocument(doc);
  92. // sort postingTable into an array
  93. Posting[] postings = SortPostingTable();
  94. /*
  95. for (int i = 0; i < postings.length; i++) {
  96. Posting posting = postings[i];
  97. System.out.print(posting.term);
  98. System.out.print(" freq=" + posting.freq);
  99. System.out.print(" pos=");
  100. System.out.print(posting.positions[0]);
  101. for (int j = 1; j < posting.freq; j++)
  102. System.out.print("," + posting.positions[j]);
  103. System.out.println("");
  104. }
  105. */
  106. // write postings
  107. WritePostings(postings, segment);
  108. // write norms of indexed fields
  109. WriteNorms(segment);
  110. }
  111. // Keys are Terms, values are Postings.
  112. // Used to buffer a document before it is written to the index.
  113. private System.Collections.Hashtable postingTable = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());
  114. private int[] fieldLengths;
  115. private int[] fieldPositions;
  116. private int[] fieldOffsets;
  117. private float[] fieldBoosts;
  118. // Tokenizes the fields of a document into Postings.
  119. private void  InvertDocument(Document doc)
  120. {
  121. System.Collections.IEnumerator fields = doc.Fields();
  122. while (fields.MoveNext())
  123. {
  124. Field field = (Field) fields.Current;
  125. System.String fieldName = field.Name();
  126. int fieldNumber = fieldInfos.FieldNumber(fieldName);
  127. int length = fieldLengths[fieldNumber]; // length of field
  128. int position = fieldPositions[fieldNumber]; // position in field
  129. if (length > 0)
  130. position += analyzer.GetPositionIncrementGap(fieldName);
  131. int offset = fieldOffsets[fieldNumber]; // offset field
  132. if (field.IsIndexed())
  133. {
  134. if (!field.IsTokenized())
  135. {
  136. // un-tokenized field
  137. System.String stringValue = field.StringValue();
  138. if (field.IsStoreOffsetWithTermVector())
  139. AddPosition(fieldName, stringValue, position++, new TermVectorOffsetInfo(offset, offset + stringValue.Length));
  140. else
  141. AddPosition(fieldName, stringValue, position++, null);
  142. offset += stringValue.Length;
  143. length++;
  144. }
  145. else
  146. {
  147. System.IO.TextReader reader; // find or make Reader
  148. if (field.ReaderValue() != null)
  149. reader = field.ReaderValue();
  150. else if (field.StringValue() != null)
  151. reader = new System.IO.StringReader(field.StringValue());
  152. else
  153. throw new System.ArgumentException("field must have either String or Reader value");
  154. // Tokenize field and add to postingTable
  155. TokenStream stream = analyzer.TokenStream(fieldName, reader);
  156. try
  157. {
  158. Token lastToken = null;
  159. for (Token t = stream.Next(); t != null; t = stream.Next())
  160. {
  161. position += (t.GetPositionIncrement() - 1);
  162. if (field.IsStoreOffsetWithTermVector())
  163. AddPosition(fieldName, t.TermText(), position++, new TermVectorOffsetInfo(offset + t.StartOffset(), offset + t.EndOffset()));
  164. else
  165. AddPosition(fieldName, t.TermText(), position++, null);
  166. lastToken = t;
  167. if (++length > maxFieldLength)
  168. {
  169. if (infoStream != null)
  170. infoStream.WriteLine("maxFieldLength " + maxFieldLength + " reached, ignoring following tokens");
  171. break;
  172. }
  173. }
  174. if (lastToken != null)
  175. offset += lastToken.EndOffset() + 1;
  176. }
  177. finally
  178. {
  179. stream.Close();
  180. }
  181. }
  182. fieldLengths[fieldNumber] = length; // save field length
  183. fieldPositions[fieldNumber] = position; // save field position
  184. fieldBoosts[fieldNumber] *= field.GetBoost();
  185. fieldOffsets[fieldNumber] = offset;
  186. }
  187. }
  188. }
  189. private Term termBuffer = new Term("", ""); // avoid consing
  190. private void  AddPosition(System.String field, System.String text, int position, TermVectorOffsetInfo offset)
  191. {
  192. termBuffer.Set(field, text);
  193. //System.out.println("Offset: " + offset);
  194. Posting ti = (Posting) postingTable[termBuffer];
  195. if (ti != null)
  196. {
  197. // word seen before
  198. int freq = ti.freq;
  199. if (ti.positions.Length == freq)
  200. {
  201. // positions array is full
  202. int[] newPositions = new int[freq * 2]; // double size
  203. int[] positions = ti.positions;
  204. for (int i = 0; i < freq; i++)
  205. // copy old positions to new
  206. newPositions[i] = positions[i];
  207. ti.positions = newPositions;
  208. }
  209. ti.positions[freq] = position; // add new position
  210. if (offset != null)
  211. {
  212. if (ti.offsets.Length == freq)
  213. {
  214. TermVectorOffsetInfo[] newOffsets = new TermVectorOffsetInfo[freq * 2];
  215. TermVectorOffsetInfo[] offsets = ti.offsets;
  216. for (int i = 0; i < freq; i++)
  217. {
  218. newOffsets[i] = offsets[i];
  219. }
  220. ti.offsets = newOffsets;
  221. }
  222. ti.offsets[freq] = offset;
  223. }
  224. ti.freq = freq + 1; // update frequency
  225. }
  226. else
  227. {
  228. // word not seen before
  229. Term term = new Term(field, text, false);
  230. postingTable[term] = new Posting(term, position, offset);
  231. }
  232. }
  233. private Posting[] SortPostingTable()
  234. {
  235. // copy postingTable into an array
  236. Posting[] array = new Posting[postingTable.Count];
  237. System.Collections.IEnumerator postings = postingTable.Values.GetEnumerator();
  238. for (int i = 0; postings.MoveNext(); i++)
  239. {
  240. array[i] = (Posting) postings.Current;
  241. }
  242. // sort the array
  243. QuickSort(array, 0, array.Length - 1);
  244. return array;
  245. }
  246. private static void  QuickSort(Posting[] postings, int lo, int hi)
  247. {
  248. if (lo >= hi)
  249. return ;
  250. int mid = (lo + hi) / 2;
  251. if (postings[lo].term.CompareTo(postings[mid].term) > 0)
  252. {
  253. Posting tmp = postings[lo];
  254. postings[lo] = postings[mid];
  255. postings[mid] = tmp;
  256. }
  257. if (postings[mid].term.CompareTo(postings[hi].term) > 0)
  258. {
  259. Posting tmp = postings[mid];
  260. postings[mid] = postings[hi];
  261. postings[hi] = tmp;
  262. if (postings[lo].term.CompareTo(postings[mid].term) > 0)
  263. {
  264. Posting tmp2 = postings[lo];
  265. postings[lo] = postings[mid];
  266. postings[mid] = tmp2;
  267. }
  268. }
  269. int left = lo + 1;
  270. int right = hi - 1;
  271. if (left >= right)
  272. return ;
  273. Term partition = postings[mid].term;
  274. for (; ; )
  275. {
  276. while (postings[right].term.CompareTo(partition) > 0)
  277. --right;
  278. while (left < right && postings[left].term.CompareTo(partition) <= 0)
  279. ++left;
  280. if (left < right)
  281. {
  282. Posting tmp = postings[left];
  283. postings[left] = postings[right];
  284. postings[right] = tmp;
  285. --right;
  286. }
  287. else
  288. {
  289. break;
  290. }
  291. }
  292. QuickSort(postings, lo, left);
  293. QuickSort(postings, left + 1, hi);
  294. }
  295. private void  WritePostings(Posting[] postings, System.String segment)
  296. {
  297. IndexOutput freq = null, prox = null;
  298. TermInfosWriter tis = null;
  299. TermVectorsWriter termVectorWriter = null;
  300. try
  301. {
  302. //open files for inverse index storage
  303. freq = directory.CreateOutput(segment + ".frq");
  304. prox = directory.CreateOutput(segment + ".prx");
  305. tis = new TermInfosWriter(directory, segment, fieldInfos, termIndexInterval);
  306. TermInfo ti = new TermInfo();
  307. System.String currentField = null;
  308. for (int i = 0; i < postings.Length; i++)
  309. {
  310. Posting posting = postings[i];
  311. // add an entry to the dictionary with pointers to prox and freq files
  312. ti.Set(1, freq.GetFilePointer(), prox.GetFilePointer(), - 1);
  313. tis.Add(posting.term, ti);
  314. // add an entry to the freq file
  315. int postingFreq = posting.freq;
  316. if (postingFreq == 1)
  317. // optimize freq=1
  318. freq.WriteVInt(1);
  319. // set low bit of doc num.
  320. else
  321. {
  322. freq.WriteVInt(0); // the document number
  323. freq.WriteVInt(postingFreq); // frequency in doc
  324. }
  325. int lastPosition = 0; // write positions
  326. int[] positions = posting.positions;
  327. for (int j = 0; j < postingFreq; j++)
  328. {
  329. // use delta-encoding
  330. int position = positions[j];
  331. prox.WriteVInt(position - lastPosition);
  332. lastPosition = position;
  333. }
  334. // check to see if we switched to a new field
  335. System.String termField = posting.term.Field();
  336. if (currentField != termField)
  337. {
  338. // changing field - see if there is something to save
  339. currentField = termField;
  340. FieldInfo fi = fieldInfos.FieldInfo(currentField);
  341. if (fi.storeTermVector)
  342. {
  343. if (termVectorWriter == null)
  344. {
  345. termVectorWriter = new TermVectorsWriter(directory, segment, fieldInfos);
  346. termVectorWriter.OpenDocument();
  347. }
  348. termVectorWriter.OpenField(currentField);
  349. }
  350. else if (termVectorWriter != null)
  351. {
  352. termVectorWriter.CloseField();
  353. }
  354. }
  355. if (termVectorWriter != null && termVectorWriter.IsFieldOpen())
  356. {
  357. termVectorWriter.AddTerm(posting.term.Text(), postingFreq, posting.positions, posting.offsets);
  358. }
  359. }
  360. if (termVectorWriter != null)
  361. termVectorWriter.CloseDocument();
  362. }
  363. finally
  364. {
  365. // make an effort to close all streams we can but remember and re-throw
  366. // the first exception encountered in this process
  367. System.IO.IOException keep = null;
  368. if (freq != null)
  369. try
  370. {
  371. freq.Close();
  372. }
  373. catch (System.IO.IOException e)
  374. {
  375. if (keep == null)
  376. keep = e;
  377. }
  378. if (prox != null)
  379. try
  380. {
  381. prox.Close();
  382. }
  383. catch (System.IO.IOException e)
  384. {
  385. if (keep == null)
  386. keep = e;
  387. }
  388. if (tis != null)
  389. try
  390. {
  391. tis.Close();
  392. }
  393. catch (System.IO.IOException e)
  394. {
  395. if (keep == null)
  396. keep = e;
  397. }
  398. if (termVectorWriter != null)
  399. try
  400. {
  401. termVectorWriter.Close();
  402. }
  403. catch (System.IO.IOException e)
  404. {
  405. if (keep == null)
  406. keep = e;
  407. }
  408. if (keep != null)
  409. {
  410. throw new System.IO.IOException(keep.StackTrace);
  411. }
  412. }
  413. }
  414. private void  WriteNorms(System.String segment)
  415. {
  416. for (int n = 0; n < fieldInfos.Size(); n++)
  417. {
  418. FieldInfo fi = fieldInfos.FieldInfo(n);
  419. if (fi.isIndexed && !fi.omitNorms)
  420. {
  421. float norm = fieldBoosts[n] * similarity.LengthNorm(fi.name, fieldLengths[n]);
  422. IndexOutput norms = directory.CreateOutput(segment + ".f" + n);
  423. try
  424. {
  425. norms.WriteByte(Similarity.EncodeNorm(norm));
  426. }
  427. finally
  428. {
  429. norms.Close();
  430. }
  431. }
  432. }
  433. }
  434. /// <summary>If non-null, a message will be printed to this if maxFieldLength is reached.</summary>
  435. internal void  SetInfoStream(System.IO.TextWriter infoStream)
  436. {
  437. this.infoStream = infoStream;
  438. }
  439. }
  440. sealed class Posting
  441. {
  442. // info about a Term in a doc
  443. internal Term term; // the Term
  444. internal int freq; // its frequency in doc
  445. internal int[] positions; // positions it occurs at
  446. internal TermVectorOffsetInfo[] offsets;
  447. internal Posting(Term t, int position, TermVectorOffsetInfo offset)
  448. {
  449. term = t;
  450. freq = 1;
  451. positions = new int[1];
  452. positions[0] = position;
  453. if (offset != null)
  454. {
  455. offsets = new TermVectorOffsetInfo[1];
  456. offsets[0] = offset;
  457. }
  458. else
  459. offsets = null;
  460. }
  461. }
  462. }