Распечатать люцен в перевернутом индексном формате
Насколько я понимаю, Lucene использует инвертированные индексы. Есть ли способ извлечь / напечатать индекс люцена (люцен 6) в инвертированном формате индекса:
term1 <doc1, doc100, ..., doc555>
term1 <doc1, ..., doc100, ..., do89>
term1 <doc3, doc2, doc5, ...>
.
.
.
termn <doc10, doc43, ..., dock>
3 ответа
Вы можете использовать TermEnum для перебора терминов в вашем инвертированном индексе. Затем для каждого термина вы должны использовать его PostingsEnum для перебора записей. Следующий код будет работать, если у вас есть индекс с одним сегментом (версия Lucene: 6_5_1):
String indexPath = "your_index_path"
String field = "your_index_field"
try (FSDirectory directory = FSDirectory.open(Paths.get(indexPath));
IndexReader reader = DirectoryReader.open(directory)) {
Terms terms = MultiFields.getTerms(reader, field);
final TermsEnum it = terms.iterator();
BytesRef term = it.next();
while (term != null) {
String termString = term.utf8ToString();
System.out.print(termStirng + ": ");
for (LeafReaderContext lrc : reader.leaves()) {
LeafReader lr = lrc.reader();
PostingsEnum pe = lr.postings(new Term(field, termString));
int docId = pe.nextDoc();
while (docId != PostingsEnum.NO_MORE_DOCS) {
postingSize++;
Document doc = lr.document(docId);
// here you can print your document title, id, etc
docId = pe.nextDoc();
}
}
term = it.next();
}
} catch (IOException e) {
e.printStackTrace();
}
Если ваш индекс имеет более одного сегмента, то $reader.leaves()$ вернет читатели, у которых в качестве листьев будут другие читатели (представьте дерево читателей индекса). В этом случае вам нужно пройти по дереву, чтобы добраться до листьев, и повторить код внутри цикла for для каждого листа.
Я использую Lucene 6.xx, и я не уверен ни в каком простом пути, но решение лучше, чем вообще никакого. Нечто подобное работает для меня, используя - MatchAllDocsQuery
,
private static void printWholeIndex(IndexSearcher searcher) throws IOException{
MatchAllDocsQuery query = new MatchAllDocsQuery();
TopDocs hits = searcher.search(query, Integer.MAX_VALUE);
Map<String, Set<Integer>> invertedIndex = new HashMap<>();
if (null == hits.scoreDocs || hits.scoreDocs.length <= 0) {
System.out.println("No Hits Found with MatchAllDocsQuery");
return;
}
for (ScoreDoc hit : hits.scoreDocs) {
Document doc = searcher.doc(hit.doc);
List<IndexableField> allFields = doc.getFields();
for(IndexableField field:allFields){
//Single document inverted index
Terms terms = searcher.getIndexReader().getTermVector(hit.doc,field.name());
if (terms != null ) {
TermsEnum termsEnum = terms.iterator();
while(termsEnum.next() != null){
if(invertedIndex.containsKey(termsEnum.term().utf8ToString())){
Set<Integer> existingDocs = invertedIndex.get(termsEnum.term().utf8ToString());
existingDocs.add(hit.doc);
invertedIndex.put(termsEnum.term().utf8ToString(),existingDocs);
}else{
Set<Integer> docs = new TreeSet<>();
docs.add(hit.doc);
invertedIndex.put(termsEnum.term().utf8ToString(), docs);
}
}
}
}
}
System.out.println("Printing Inverted Index:");
invertedIndex.forEach((key , value) -> {System.out.println(key+":"+value);
});
}
Две точки,
1. максимум документов поддерживается - Integer.MAX_VALUE
, Я не пробовал, но, вероятно, этот предел можно устранить с помощью searchAfter
метод поиска и выполнения нескольких поисков.
2.doc.getFields()
возвращает только те поля, которые хранятся Возможно, вы можете сохранить массив статических полей, если все ваши проиндексированные поля не сохраняются после строки, Terms terms = searcher.getIndexReader().getTermVector(hit.doc,field.name());
работает и для не сохраненных полей.
Разработана версия, которая печатает docId:tokenPos для Lucene 6.6.
Directory directory = new RAMDirectory();
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
iwc.setOpenMode(OpenMode.CREATE);
IndexWriter writer = new IndexWriter(directory, iwc);
FieldType type = new FieldType();
type.setStoreTermVectors(true);
type.setStoreTermVectorPositions(true);
type.setStoreTermVectorOffsets(true);
type.setIndexOptions(IndexOptions.DOCS);
Field fieldStore = new Field("text", "We hold that proof beyond a reasonable doubt is required.", type);
Document doc = new Document();
doc.add(fieldStore);
writer.addDocument(doc);
fieldStore = new Field("text", "We hold that proof requires reasoanble preponderance of the evidenceb.", type);
doc = new Document();
doc.add(fieldStore);
writer.addDocument(doc);
writer.close();
DirectoryReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
MatchAllDocsQuery query = new MatchAllDocsQuery();
TopDocs hits = searcher.search(query, Integer.MAX_VALUE);
Map<String, Set<String>> invertedIndex = new HashMap<>();
BiFunction<Integer, Integer, Set<String>> mergeValue =
(docId, pos)-> {TreeSet<String> s = new TreeSet<>(); s.add((docId+1)+":"+pos); return s;};
for ( ScoreDoc scoreDoc: hits.scoreDocs ) {
Fields termVs = reader.getTermVectors(scoreDoc.doc);
Terms terms = termVs.terms("text");
TermsEnum termsIt = terms.iterator();
PostingsEnum docsAndPosEnum = null;
BytesRef bytesRef;
while ( (bytesRef = termsIt.next()) != null ) {
docsAndPosEnum = termsIt.postings(docsAndPosEnum, PostingsEnum.ALL);
docsAndPosEnum.nextDoc();
int pos = docsAndPosEnum.nextPosition();
String term = bytesRef.utf8ToString();
invertedIndex.merge(
term,
mergeValue.apply(scoreDoc.doc, pos),
(s1,s2)->{s1.addAll(s2); return s1;}
);
}
}
System.out.println( invertedIndex);