comparison src/luan/modules/lucene/LuceneIndex.java @ 1345:6f8988830098

unique LuceneIndex per dir
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 25 Feb 2019 11:00:10 -0700
parents dc2af9d5463b
children efd1c6380f2c
comparison
equal deleted inserted replaced
1344:dc2af9d5463b 1345:6f8988830098
3 import java.io.Closeable; 3 import java.io.Closeable;
4 import java.io.File; 4 import java.io.File;
5 import java.io.FileOutputStream; 5 import java.io.FileOutputStream;
6 import java.io.FileInputStream; 6 import java.io.FileInputStream;
7 import java.io.IOException; 7 import java.io.IOException;
8 import java.util.Arrays;
8 import java.util.Iterator; 9 import java.util.Iterator;
9 import java.util.Map; 10 import java.util.Map;
11 import java.util.HashMap;
10 import java.util.List; 12 import java.util.List;
11 import java.util.ArrayList; 13 import java.util.ArrayList;
12 import java.util.Set; 14 import java.util.Set;
13 import java.util.HashSet; 15 import java.util.HashSet;
14 import java.util.Collections; 16 import java.util.Collections;
15 import java.util.concurrent.ConcurrentMap;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.atomic.AtomicInteger; 17 import java.util.concurrent.atomic.AtomicInteger;
18 import java.util.concurrent.locks.Lock; 18 import java.util.concurrent.locks.Lock;
19 import java.util.concurrent.locks.ReentrantLock; 19 import java.util.concurrent.locks.ReentrantLock;
20 import java.util.zip.ZipOutputStream; 20 import java.util.zip.ZipOutputStream;
21 import java.util.zip.ZipEntry; 21 import java.util.zip.ZipEntry;
22 import org.apache.lucene.analysis.Analyzer; 22 import org.apache.lucene.analysis.Analyzer;
23 import org.apache.lucene.analysis.TokenStream; 23 import org.apache.lucene.analysis.TokenStream;
24 import org.apache.lucene.analysis.core.KeywordAnalyzer; 24 import org.apache.lucene.analysis.core.KeywordAnalyzer;
25 import org.apache.lucene.analysis.en.EnglishAnalyzer;
25 import org.apache.lucene.document.Document; 26 import org.apache.lucene.document.Document;
26 import org.apache.lucene.document.Field; 27 import org.apache.lucene.document.Field;
27 import org.apache.lucene.document.StoredField; 28 import org.apache.lucene.document.StoredField;
28 import org.apache.lucene.document.StringField; 29 import org.apache.lucene.document.StringField;
29 import org.apache.lucene.document.TextField; 30 import org.apache.lucene.document.TextField;
76 import luan.LuanRuntimeException; 77 import luan.LuanRuntimeException;
77 import luan.lib.logging.Logger; 78 import luan.lib.logging.Logger;
78 import luan.lib.logging.LoggerFactory; 79 import luan.lib.logging.LoggerFactory;
79 80
80 81
81 public final class LuceneIndex implements Closeable { 82 public final class LuceneIndex {
82 private static final Logger logger = LoggerFactory.getLogger(LuceneIndex.class); 83 private static final Logger logger = LoggerFactory.getLogger(LuceneIndex.class);
83 84
85 private static final class Closer implements Closeable {
86 final LuceneIndex li;
87 boolean isClosed = false;
88 private final Exception created = new Exception("created");
89
90 Closer(Luan luan,LuceneIndex li) {
91 this.li = li;
92 luan.onClose(this);
93 }
94
95 public void close() throws IOException {
96 if( !isClosed ) {
97 li.close();
98 isClosed = true;
99 }
100 }
101
102 protected void finalize() throws Throwable {
103 if( !isClosed ) {
104 logger.error("not closed",created);
105 close();
106 }
107 super.finalize();
108 }
109 }
110
111 private static Map<String,LuceneIndex> indexes = new HashMap<String,LuceneIndex>();
112
113 public static Object[] getLuceneIndex(Luan luan,String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields)
114 throws LuanException, IOException
115 {
116 String key = new File(indexDirStr).getCanonicalPath();
117 synchronized(indexes) {
118 LuceneIndex li = indexes.get(key);
119 if( li == null ) {
120 li = new LuceneIndex(indexDirStr,defaultFieldParser,defaultFields,key);
121 li.openCount = 1;
122 indexes.put(key,li);
123 } else {
124 if( defaultFieldParser != li.defaultFieldParser )
125 throw new LuanException("default_type doesn't match previous use");
126 if( !Arrays.equals(defaultFields,li.defaultFields) )
127 throw new LuanException("default_fields don't match previous use");
128 li.openCount++;
129 }
130 return new Object[]{li,new Closer(luan,li)};
131 }
132 }
133
134 private static final Version version = Version.LUCENE_4_9;
84 private static final String FLD_NEXT_ID = "nextId"; 135 private static final String FLD_NEXT_ID = "nextId";
85 public static final StringFieldParser STRING_FIELD_PARSER = new StringFieldParser(new KeywordAnalyzer()); 136 public static final StringFieldParser STRING_FIELD_PARSER = new StringFieldParser(new KeywordAnalyzer());
86 137 public static final StringFieldParser ENGLISH_FIELD_PARSER = new StringFieldParser(new EnglishAnalyzer(version));
87 private static final Version version = Version.LUCENE_4_9; 138
88 private final ReentrantLock writeLock = new ReentrantLock(); 139 private final ReentrantLock writeLock = new ReentrantLock();
89 private final File indexDir; 140 private final File indexDir;
90 private SnapshotDeletionPolicy snapshotDeletionPolicy; 141 private SnapshotDeletionPolicy snapshotDeletionPolicy;
91 private IndexWriter writer; 142 private IndexWriter writer;
92 private DirectoryReader reader; 143 private DirectoryReader reader;
93 private IndexSearcher searcher; 144 private IndexSearcher searcher;
94 private final ThreadLocal<IndexSearcher> threadLocalSearcher = new ThreadLocal<IndexSearcher>(); 145 private final ThreadLocal<IndexSearcher> threadLocalSearcher = new ThreadLocal<IndexSearcher>();
95 private boolean isClosed = true;
96 private final MultiFieldParser mfp; 146 private final MultiFieldParser mfp;
97 private final Analyzer analyzer; 147 private final Analyzer analyzer;
98 private final Exception created = new Exception("created"); 148
99
100 private static ConcurrentMap<File,AtomicInteger> globalWriteCounters = new ConcurrentHashMap<File,AtomicInteger>();
101 private File fileDir; 149 private File fileDir;
102 private int writeCount; 150 private int writeCount;
151 private AtomicInteger writeCounter = new AtomicInteger();
103 152
104 private Set<String> indexOnly = new HashSet<String>(); 153 private Set<String> indexOnly = new HashSet<String>();
105 154
106 public LuceneIndex(Luan luan,String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields) 155 private int openCount;
156 private final String key;
157 private final FieldParser defaultFieldParser;
158 private final String[] defaultFields;
159
160 private LuceneIndex(String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields,String key)
107 throws LuanException, IOException 161 throws LuanException, IOException
108 { 162 {
163 this.key = key;
164 this.defaultFieldParser = defaultFieldParser;
165 this.defaultFields = defaultFields;
109 mfp = defaultFieldParser==null ? new MultiFieldParser() : new MultiFieldParser(defaultFieldParser,defaultFields); 166 mfp = defaultFieldParser==null ? new MultiFieldParser() : new MultiFieldParser(defaultFieldParser,defaultFields);
110 mfp.fields.put( "type", STRING_FIELD_PARSER ); 167 mfp.fields.put( "type", STRING_FIELD_PARSER );
111 mfp.fields.put( "id", NumberFieldParser.LONG ); 168 mfp.fields.put( "id", NumberFieldParser.LONG );
112 File indexDir = new File(indexDirStr); 169 File indexDir = new File(indexDirStr);
113 this.indexDir = indexDir; 170 this.indexDir = indexDir;
115 if( defaultFieldParser instanceof StringFieldParser ) { 172 if( defaultFieldParser instanceof StringFieldParser ) {
116 StringFieldParser sfp = (StringFieldParser)defaultFieldParser; 173 StringFieldParser sfp = (StringFieldParser)defaultFieldParser;
117 analyzer = sfp.analyzer; 174 analyzer = sfp.analyzer;
118 } 175 }
119 this.analyzer = analyzer; 176 this.analyzer = analyzer;
120 luan.onClose(this);
121 reopen(); 177 reopen();
122 } 178 }
123 179
124 public void reopen() throws LuanException, IOException { 180 public void reopen() throws LuanException, IOException {
125 if( !isClosed ) throw new RuntimeException();
126 isClosed = false;
127 IndexWriterConfig conf = new IndexWriterConfig(version,analyzer); 181 IndexWriterConfig conf = new IndexWriterConfig(version,analyzer);
128 snapshotDeletionPolicy = new SnapshotDeletionPolicy(conf.getIndexDeletionPolicy()); 182 snapshotDeletionPolicy = new SnapshotDeletionPolicy(conf.getIndexDeletionPolicy());
129 conf.setIndexDeletionPolicy(snapshotDeletionPolicy); 183 conf.setIndexDeletionPolicy(snapshotDeletionPolicy);
130 FSDirectory dir = FSDirectory.open(indexDir); 184 FSDirectory dir = FSDirectory.open(indexDir);
131 fileDir = dir.getDirectory(); 185 fileDir = dir.getDirectory();
132 globalWriteCounters.putIfAbsent(fileDir,new AtomicInteger());
133 writer = new IndexWriter(dir,conf); 186 writer = new IndexWriter(dir,conf);
134 writer.commit(); // commit index creation 187 writer.commit(); // commit index creation
135 reader = DirectoryReader.open(dir); 188 reader = DirectoryReader.open(dir);
136 searcher = new IndexSearcher(reader); 189 searcher = new IndexSearcher(reader);
137 initId(); 190 initId();
138 } 191 }
139 192
140 private int globalWriteCount() {
141 return globalWriteCounters.get(fileDir).get();
142 }
143
144 private void wrote() { 193 private void wrote() {
145 globalWriteCounters.get(fileDir).incrementAndGet(); 194 writeCounter.incrementAndGet();
146 } 195 }
147 196
148 public void delete_all() throws IOException { 197 public void delete_all() throws IOException {
149 boolean commit = !writeLock.isHeldByCurrentThread(); 198 boolean commit = !writeLock.isHeldByCurrentThread();
150 writeLock.lock(); 199 writeLock.lock();
310 359
311 public String to_string() { 360 public String to_string() {
312 return writer.getDirectory().toString(); 361 return writer.getDirectory().toString();
313 } 362 }
314 363
315 public void close() throws IOException { 364 private synchronized void close() throws IOException {
316 if( !isClosed ) { 365 if( openCount > 0 ) {
317 writer.close(); 366 if( --openCount == 0 ) {
318 reader.close(); 367 doClose();
319 isClosed = true; 368 synchronized(indexes) {
320 } 369 indexes.remove(key);
321 } 370 }
322 371 }
323 protected void finalize() throws Throwable { 372 }
324 if( !isClosed ) { 373 }
325 logger.error("not closed",created); 374
326 close(); 375 public void doClose() throws IOException {
327 } 376 writer.close();
328 super.finalize(); 377 reader.close();
329 } 378 }
330
331 379
332 380
333 private static class DocFn extends LuanFunction { 381 private static class DocFn extends LuanFunction {
334 final IndexSearcher searcher; 382 final IndexSearcher searcher;
335 final Query query; 383 final Query query;
368 return true; 416 return true;
369 } 417 }
370 } 418 }
371 419
372 private synchronized IndexSearcher openSearcher() throws IOException { 420 private synchronized IndexSearcher openSearcher() throws IOException {
373 int gwc = globalWriteCount(); 421 int gwc = writeCounter.get();
374 if( writeCount != gwc ) { 422 if( writeCount != gwc ) {
375 writeCount = gwc; 423 writeCount = gwc;
376 DirectoryReader newReader = DirectoryReader.openIfChanged(reader); 424 DirectoryReader newReader = DirectoryReader.openIfChanged(reader);
377 if( newReader != null ) { 425 if( newReader != null ) {
378 reader.decRef(); 426 reader.decRef();