Как сохранить индекс Lucene без удаленных документов

Это мой первый вопрос о переполнении стека, так что пожелайте мне удачи.

Я делаю процесс классификации по индексу Lucene с Java, и мне нужно обновить поле документа с именем категории. Для этой цели я использовал Lucene 4.2 с функцией средства записи индекса updateDocument(), и она работает очень хорошо, за исключением части удаления. Даже если я использую функцию forceMergeDeletes() после обновления, индекс покажет мне некоторые уже удаленные документы. Например, если я выполню классификацию по индексу с 1000 документов, то итоговое количество документов в индексе останется прежним и будет работать, как и ожидалось, но когда я увеличу индексные документы до 10000, индекс покажет некоторые уже удаленные документы, но не все. Итак, как я могу на самом деле удалить эти удаленные документы из индекса?

Вот некоторые фрагменты моего кода:

public static void main(String[] args) throws IOException, ParseException {
    ///////////////////////Preparing config data////////////////////////////
    File indexDir = new File("/indexDir");
    Directory fsDir = FSDirectory.open(indexDir);

    IndexWriterConfig iwConf = new IndexWriterConfig(Version.LUCENE_42, new WhitespaceSpanishAnalyzer());
    iwConf.setOpenMode(IndexWriterConfig.OpenMode.APPEND);
    IndexWriter indexWriter = new IndexWriter(fsDir, iwConf);

    IndexReader reader = DirectoryReader.open(fsDir);
    IndexSearcher indexSearcher = new IndexSearcher(reader);
    KNearestNeighborClassifier classifier = new KNearestNeighborClassifier(100);
    AtomicReader ar = new SlowCompositeReaderWrapper((CompositeReader) reader);

    classifier.train(ar, "text", "category", new WhitespaceSpanishAnalyzer());

    System.out.println("***Before***");
    showIndexedDocuments(reader);
    System.out.println("***Before***");

    int maxdoc = reader.maxDoc();
    int j = 0;
    for (int i = 0; i < maxdoc; i++) {
        Document doc = reader.document(i);
        String clusterClasif = doc.get("category");
        String text = doc.get("text");
        String docid = doc.get("doc_id");
        ClassificationResult<BytesRef> result = classifier.assignClass(text);
        String classified = result.getAssignedClass().utf8ToString();

        if (!classified.isEmpty() && clusterClasif.compareTo(classified) != 0) {
            Term term = new Term("doc_id", docid);
            doc.removeField("category");
            doc.add(new StringField("category",
                    classified, Field.Store.YES));
            indexWriter.updateDocument(term,doc);
            j++;
        }
    }
    indexWriter.forceMergeDeletes(true);
    indexWriter.close();
    System.out.println("Classified documents count: " + j);        
    System.out.println();
    reader.close();

    reader = DirectoryReader.open(fsDir);
    System.out.println("Deleted docs: " + reader.numDeletedDocs());
    System.out.println("***After***");
    showIndexedDocuments(reader);
}

private static void showIndexedDocuments(IndexReader reader) throws IOException {
    int maxdoc = reader.maxDoc();
    for (int i = 0; i < maxdoc; i++) {
        Document doc = reader.document(i);
        String idDoc = doc.get("doc_id");
        String text = doc.get("text");
        String category = doc.get("category");

        System.out.println("Id Doc: " + idDoc);
        System.out.println("Category: " + category);
        System.out.println("Text: " + text);
        System.out.println();
    }
    System.out.println("Total: " + maxdoc);
}

Я потратил много часов на поиски решения этой проблемы, кто-то говорит, что удаленные документы в индексе не важны и что в конечном итоге они будут удалены, когда мы продолжим добавлять документы в индекс, но мне нужно каким-то образом контролировать этот процесс. Я могу перебирать индексные документы в любое время, и что документы, которые я получаю, на самом деле являются живыми. Версии Lucene, предшествовавшие 4.0, имели функцию в классе IndexReader с именем isDeleted (docId), которая выдает, если документ помечен как удаленный, что может быть только половиной решения моей проблемы, но я не нашел способа сделать это с помощью версия 4.2 Lucene. Если вы знаете, как это сделать, я очень ценю, если вы поделитесь этим.

1 ответ

Решение

Вы можете проверить, удален ли документ в классе MultiFields, например:

Bits liveDocs = MultiFields.getLiveDocs(reader);
if (!liveDocs.get(docID)) ...

Итак, добавив это в ваш код, возможно, что-то вроде:

int maxdoc = reader.maxDoc();
Bits liveDocs = MultiFields.getLiveDocs(reader);
for (int i = 0; i < maxdoc; i++) {
    if (!liveDocs.get(docID)) continue;
    Document doc = reader.document(i);
    String idDoc = doc.get("doc_id");
    ....
}

Кстати, звучит так, как будто вы раньше работали с 3.X, а сейчас - с 4.X. Руководство по миграции Lucene очень полезно для понимания такого рода изменений между версиями и способов их устранения.

Другие вопросы по тегам