add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / benchmark / src / java / org / apache / lucene / benchmark / byTask / tasks / WriteLineDocTask.java
1 package org.apache.lucene.benchmark.byTask.tasks;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.BufferedWriter;
21 import java.io.File;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.PrintWriter;
25 import java.util.Arrays;
26 import java.util.HashSet;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30 import org.apache.lucene.benchmark.byTask.PerfRunData;
31 import org.apache.lucene.benchmark.byTask.feeds.DocMaker;
32 import org.apache.lucene.benchmark.byTask.utils.Config;
33 import org.apache.lucene.benchmark.byTask.utils.StreamUtils;
34 import org.apache.lucene.document.Document;
35 import org.apache.lucene.document.Field;
36
37 /**
38  * A task which writes documents, one line per document. Each line is in the
39  * following format: title <TAB> date <TAB> body. The output of this
40  * task can be consumed by
41  * {@link org.apache.lucene.benchmark.byTask.feeds.LineDocSource} and is intended
42  * to save the IO overhead of opening a file per document to be indexed.
43  * <p>
44  * The format of the output is set according to the output file extension.
45  * Compression is recommended when the output file is expected to be large.
46  * See info on file extensions in 
47  * {@link org.apache.lucene.benchmark.byTask.utils.StreamUtils.Type}
48  * <p> 
49  * Supports the following parameters:
50  * <ul>
51  * <li><b>line.file.out</b> - the name of the file to write the output to. That
52  * parameter is mandatory. <b>NOTE:</b> the file is re-created.
53  * <li><b>line.fields</b> - which fields should be written in each line.
54  * (optional, default: {@link #DEFAULT_FIELDS}).
55  * <li><b>sufficient.fields</b> - list of field names, separated by comma, which, 
56  * if all of them are missing, the document will be skipped. For example, to require 
57  * that at least one of f1,f2 is not empty, specify: "f1,f2" in this field. To specify
58  * that no field is required, i.e. that even empty docs should be emitted, specify <b>","</b>.    
59  * (optional, default: {@link #DEFAULT_SUFFICIENT_FIELDS}).
60  * </ul>
61  * <b>NOTE:</b> this class is not thread-safe and if used by multiple threads the
62  * output is unspecified (as all will write to the same output file in a
63  * non-synchronized way).
64  */
65 public class WriteLineDocTask extends PerfTask {
66
67   public static final String FIELDS_HEADER_INDICATOR = "FIELDS_HEADER_INDICATOR###";
68
69   public final static char SEP = '\t';
70   
71   /**
72    * Fields to be written by default
73    */
74   public static final String[] DEFAULT_FIELDS = new String[] {
75     DocMaker.TITLE_FIELD,
76     DocMaker.DATE_FIELD,
77     DocMaker.BODY_FIELD,
78   };
79   
80   /**
81    * Default fields which at least one of them is required to not skip the doc.
82    */
83   public static final String DEFAULT_SUFFICIENT_FIELDS = DocMaker.TITLE_FIELD +',' + DocMaker.BODY_FIELD;
84   
85   private int docSize = 0;
86   private PrintWriter lineFileOut = null;
87   private DocMaker docMaker;
88   private ThreadLocal<StringBuilder> threadBuffer = new ThreadLocal<StringBuilder>();
89   private ThreadLocal<Matcher> threadNormalizer = new ThreadLocal<Matcher>();
90   private final String[] fieldsToWrite;;
91   private final boolean[] sufficientFields;
92   private final boolean checkSufficientFields;
93   
94   public WriteLineDocTask(PerfRunData runData) throws Exception {
95     super(runData);
96     Config config = runData.getConfig();
97     String fname = config.get("line.file.out", null);
98     if (fname == null) {
99       throw new IllegalArgumentException("line.file.out must be set");
100     }
101     OutputStream out = StreamUtils.outputStream(new File(fname));
102     lineFileOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), StreamUtils.BUFFER_SIZE));
103     docMaker = runData.getDocMaker();
104     
105     // init fields 
106     String f2r = config.get("line.fields",null);
107     if (f2r == null) {
108       fieldsToWrite = DEFAULT_FIELDS;
109     } else {
110       if (f2r.indexOf(SEP)>=0) {
111         throw new IllegalArgumentException("line.fields "+f2r+" should not contain the separator char: "+SEP);
112       }
113       fieldsToWrite = f2r.split(","); 
114     }
115     
116     // init sufficient fields
117     sufficientFields = new boolean[fieldsToWrite.length];
118     String suff = config.get("sufficient.fields",DEFAULT_SUFFICIENT_FIELDS);
119     if (",".equals(suff)) {
120       checkSufficientFields = false;
121     } else {
122       checkSufficientFields = true;
123       HashSet<String> sf = new HashSet<String>(Arrays.asList(suff.split(",")));
124       for (int i=0; i<fieldsToWrite.length; i++) {
125         if (sf.contains(fieldsToWrite[i])) {
126           sufficientFields[i] = true;
127         }
128       }
129     }
130     
131     writeHeader();
132   }
133
134   /**
135    * Write a header to the lines file - indicating how to read the file later 
136    */
137   private void writeHeader() {
138     StringBuilder sb = threadBuffer.get();
139     if (sb == null) {
140       sb = new StringBuilder();
141       threadBuffer.set(sb);
142     }
143     sb.setLength(0);
144     sb.append(FIELDS_HEADER_INDICATOR);
145     for (String f : fieldsToWrite) {
146       sb.append(SEP).append(f);
147     }
148     lineFileOut.println(sb.toString());
149   }
150
151   @Override
152   protected String getLogMessage(int recsCount) {
153     return "Wrote " + recsCount + " line docs";
154   }
155   
156   @Override
157   public int doLogic() throws Exception {
158     Document doc = docSize > 0 ? docMaker.makeDocument(docSize) : docMaker.makeDocument();
159
160     Matcher matcher = threadNormalizer.get();
161     if (matcher == null) {
162       matcher = Pattern.compile("[\t\r\n]+").matcher("");
163       threadNormalizer.set(matcher);
164     }
165     
166     StringBuilder sb = threadBuffer.get();
167     if (sb == null) {
168       sb = new StringBuilder();
169       threadBuffer.set(sb);
170     }
171     sb.setLength(0);
172
173     boolean sufficient = !checkSufficientFields;
174     for (int i=0; i<fieldsToWrite.length; i++) {
175       Field f = doc.getField(fieldsToWrite[i]);
176       String text = f == null ? "" : matcher.reset(f.stringValue()).replaceAll(" ").trim();
177       sb.append(text).append(SEP);
178       sufficient |= text.length()>0 && sufficientFields[i];
179     }
180     if (sufficient) {
181       sb.setLength(sb.length()-1); // remove redundant last separator
182       // lineFileOut is a PrintWriter, which synchronizes internally in println.
183       lineFileOut.println(sb.toString());
184     }
185
186     return 1;
187   }
188
189   @Override
190   public void close() throws Exception {
191     lineFileOut.close();
192     super.close();
193   }
194   
195   /**
196    * Set the params (docSize only)
197    * @param params docSize, or 0 for no limit.
198    */
199   @Override
200   public void setParams(String params) {
201     super.setParams(params);
202     docSize = (int) Float.parseFloat(params); 
203   }
204
205   @Override
206   public boolean supportsParams() {
207     return true;
208   }
209   
210 }