add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / misc / src / java / org / apache / lucene / index / IndexSorter.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.apache.lucene.index;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.Arrays;
23 import java.util.logging.Logger;
24
25 import org.apache.lucene.analysis.WhitespaceAnalyzer;
26 import org.apache.lucene.document.*;
27 import org.apache.lucene.index.IndexWriter;       // javadocs
28 import org.apache.lucene.store.*;
29 import org.apache.lucene.util.Version;
30
31 /** Sort an index by document importance factor. Higher scoring documents are
32  * assigned smaller document numbers. Document weights are obtained from a
33  * specified field, which has to be single-valued and stored, with string value
34  * that represents a float number. Stored fields in the output index remain
35  * consistent, i.e. both stored fields and postings are renumbered in sync.
36  *
37  * <p><b>NOTE</b>: this tool is unaware of documents added
38  * atomically via {@link IndexWriter#addDocuments} or {@link
39  * IndexWriter#updateDocuments}, which means it can easily
40  * break up such document groups.
41  */
42 public class IndexSorter {
43   private static final Logger LOG = Logger.getLogger(IndexSorter.class.getName());
44   
45   private static class PostingMap implements Comparable<PostingMap> {
46     private int newDoc;
47     private long offset;
48
49     public int compareTo(PostingMap pm) {              // order by newDoc id
50       return this.newDoc - pm.newDoc;
51     }
52   }
53
54   private static class SortedTermPositions implements TermPositions {
55     private TermPositions original;
56     private int[] oldToNew;
57
58     private int docFreq;
59
60     private PostingMap[] postingMaps = new PostingMap[0];
61     private int pointer;
62
63     private int freq;
64     private int position;
65
66     private static final String TEMP_FILE = "temp";
67     private final RAMDirectory tempDir = new RAMDirectory();
68     private RAMOutputStream out;
69     private IndexInput in;
70
71     public SortedTermPositions(TermPositions original, int[] oldToNew) {
72       this.original = original;
73       this.oldToNew = oldToNew;
74       try {
75         out = (RAMOutputStream)tempDir.createOutput(TEMP_FILE);
76       } catch (IOException ioe) {
77         LOG.warning("Error creating temporary output: " + ioe);
78       }
79     }
80
81     public void seek(Term term) throws IOException {
82       throw new UnsupportedOperationException();
83     }
84
85     public void seek(TermEnum terms) throws IOException {
86       original.seek(terms);
87
88       docFreq = terms.docFreq();
89       pointer = -1;
90
91       if (docFreq > postingMaps.length) {         // grow postingsMap
92         PostingMap[] newMap = new PostingMap[docFreq];
93         System.arraycopy(postingMaps, 0, newMap, 0, postingMaps.length);
94         for (int i = postingMaps.length; i < docFreq; i++) {
95           newMap[i] = new PostingMap();
96         }
97         postingMaps = newMap;
98       }
99
100       out.reset();
101
102       int i = 0;
103       while (original.next()) {
104         PostingMap map = postingMaps[i++];
105         map.newDoc = oldToNew[original.doc()];    // remap the newDoc id
106         map.offset = out.getFilePointer();        // save pointer to buffer
107
108         final int tf = original.freq();           // buffer tf & positions
109         out.writeVInt(tf);
110         int prevPosition = 0;
111         for (int j = tf; j > 0; j--) {            // delta encode positions
112           int p = original.nextPosition();
113           out.writeVInt(p - prevPosition);
114           prevPosition = p;
115         }
116       }
117       out.flush();
118       docFreq = i;                                // allow for deletions
119       
120       Arrays.sort(postingMaps, 0, docFreq);       // resort by mapped doc ids
121
122       // NOTE: this might be substantially faster if RAMInputStream were public
123       // and supported a reset() operation.
124       in = tempDir.openInput(TEMP_FILE);
125     }
126         
127     public boolean next() throws IOException {
128       pointer++;
129       if (pointer < docFreq) {
130         in.seek(postingMaps[pointer].offset);
131         freq = in.readVInt();
132         position = 0;
133         return true;
134       }
135       return false;
136     }
137       
138     public int doc() { return postingMaps[pointer].newDoc; }
139     public int freq() { return freq; }
140
141     public int nextPosition() throws IOException {
142       int positionIncrement = in.readVInt();
143       position += positionIncrement;
144       return position;
145     }
146
147     public int read(int[] docs, int[] freqs) {
148       throw new UnsupportedOperationException();
149     }
150     public boolean skipTo(int target) {
151       throw new UnsupportedOperationException();
152     }
153
154     public byte[] getPayload(byte[] data, int offset) throws IOException {
155       return null;
156     }
157
158     public int getPayloadLength() {
159       return 0;
160     }
161
162     public boolean isPayloadAvailable() {
163       return false;
164     }
165
166     public void close() throws IOException {
167       original.close();
168     }
169
170   }
171
172   private static class SortingReader extends FilterIndexReader {
173     
174     private int[] oldToNew;
175     private int[] newToOld;
176
177     public SortingReader(IndexReader oldReader, int[] oldToNew) {
178       super(oldReader);
179       this.oldToNew = oldToNew;
180       
181       this.newToOld = new int[oldReader.maxDoc()];
182       int oldDoc = 0;
183       while (oldDoc < oldToNew.length) {
184         int newDoc = oldToNew[oldDoc];
185         if (newDoc != -1) {
186           newToOld[newDoc] = oldDoc;
187         }
188         oldDoc++;
189       }
190     }
191
192     @Override
193     public IndexReader[] getSequentialSubReaders() {
194       return null;
195     }
196
197     @Override
198     public Document document(int n) throws IOException {
199       return document(n, null);
200     }
201
202     @Override
203     public Document document(int n, FieldSelector fieldSelector)
204         throws CorruptIndexException, IOException {
205       return super.document(newToOld[n], fieldSelector);
206     }
207
208     @Override
209     public boolean isDeleted(int n) {
210       return false;
211     }
212
213     @Override
214     public byte[] norms(String f) throws IOException {
215       throw new UnsupportedOperationException();
216     }
217
218     @Override
219     public void norms(String f, byte[] norms, int offset) throws IOException {
220       byte[] oldNorms = super.norms(f);
221       int oldDoc = 0;
222       while (oldDoc < oldNorms.length) {
223         int newDoc = oldToNew[oldDoc];
224         if (newDoc != -1) {
225           norms[newDoc] = oldNorms[oldDoc];
226         }
227         oldDoc++;
228       }
229     }
230
231     @Override
232     protected void doSetNorm(int d, String f, byte b) throws IOException {
233       throw new UnsupportedOperationException();
234     }
235
236     @Override
237     public TermDocs termDocs() throws IOException {
238       throw new UnsupportedOperationException();
239     }
240     
241     @Override
242     public TermPositions termPositions() throws IOException {
243       return new SortedTermPositions(super.termPositions(), oldToNew);
244     }
245
246     @Override
247     public TermFreqVector[] getTermFreqVectors(int docNumber)
248             throws IOException {
249       return super.getTermFreqVectors(newToOld[docNumber]);
250     }
251
252     @Override
253     protected void doDelete(int n) throws IOException { 
254       throw new UnsupportedOperationException();
255     }
256
257   }
258
259   private static class DocScore implements Comparable<DocScore> {
260     private int oldDoc;
261     private float score;
262
263     public int compareTo(DocScore that) {            // order by score, oldDoc
264       if (this.score == that.score) {
265         return this.oldDoc - that.oldDoc;
266       } else {
267         return this.score < that.score ? 1 : -1 ;
268       }
269     }
270     
271     @Override
272     public String toString() {
273       return "oldDoc=" + oldDoc + ",score=" + score;
274     }
275   }
276
277   public IndexSorter() {
278     
279   }
280   
281   public void sort(Directory input, Directory output, String field) throws IOException {
282     LOG.info("IndexSorter: starting.");
283     long start = System.currentTimeMillis();
284     IndexReader reader = IndexReader.open(input, true);
285
286     SortingReader sorter = new SortingReader(reader, oldToNew(reader, field));
287     IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_31, new WhitespaceAnalyzer(Version.LUCENE_31));
288     IndexWriter writer = new IndexWriter(output, cfg);
289     writer.addIndexes(new IndexReader[] { sorter });
290     writer.close();
291     long end = System.currentTimeMillis();
292     LOG.info("IndexSorter: done, " + (end - start)
293         + " total milliseconds");
294   }
295
296   private static int[] oldToNew(IndexReader reader, String field) throws IOException {
297     int readerMax = reader.maxDoc();
298     DocScore[] newToOld = new DocScore[readerMax];
299     FieldSelector fSel = new MapFieldSelector(field);
300
301     for (int oldDoc = 0; oldDoc < readerMax; oldDoc++) {
302       float score;
303       if (reader.isDeleted(oldDoc)) {
304         score = 0.0f;
305       } else {
306         Document d = reader.document(oldDoc, fSel);
307         try {
308           score = Float.parseFloat(d.get(field));
309         } catch (Exception e) {
310           score = 0.0f;
311         }
312       }
313       DocScore docScore = new DocScore();
314       docScore.oldDoc = oldDoc;
315       docScore.score = score;
316       newToOld[oldDoc] = docScore;
317     }
318     Arrays.sort(newToOld);
319
320     int[] oldToNew = new int[readerMax];
321     for (int newDoc = 0; newDoc < readerMax; newDoc++) {
322       DocScore docScore = newToOld[newDoc];
323       oldToNew[docScore.oldDoc] = newDoc;
324     }    
325     return oldToNew;
326   }
327
328   /** */
329   public static void main(String[] args) throws Exception {
330     Directory input, output;
331     String field;
332       
333     String usage = "IndexSorter <input> <output> <field>";
334
335     if (args.length < 3) {
336       System.err.println("Usage: " + usage);
337       System.exit(-1);
338     }
339
340     input = FSDirectory.open(new File(args[0]));
341     File out = new File(args[1]);
342     if (!out.exists()) out.mkdirs();
343     output = FSDirectory.open(out);
344     field = args[2];
345     IndexSorter sorter = new IndexSorter();
346     try {
347       sorter.sort(input, output, field);
348     } catch (Exception e) {
349       LOG.warning("IndexSorter: " + e);
350     }
351   }
352 }