net.sf.mmm.search.indexer.impl.lucene.LuceneSearchIndexer.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.mmm.search.indexer.impl.lucene.LuceneSearchIndexer.java

Source

/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.search.indexer.impl.lucene;

import java.io.IOException;

import net.sf.mmm.search.api.SearchEntry;
import net.sf.mmm.search.api.SearchException;
import net.sf.mmm.search.base.SearchDependencies;
import net.sf.mmm.search.engine.api.ManagedSearchEngine;
import net.sf.mmm.search.engine.api.SearchEngine;
import net.sf.mmm.search.engine.impl.lucene.LuceneFieldManager;
import net.sf.mmm.search.engine.impl.lucene.LuceneSearchEngineBuilder;
import net.sf.mmm.search.indexer.api.MutableSearchEntry;
import net.sf.mmm.search.indexer.base.AbstractSearchIndexer;
import net.sf.mmm.search.indexer.base.SearchAddFailedException;
import net.sf.mmm.search.indexer.base.SearchRemoveFailedException;
import net.sf.mmm.util.exception.api.NlsIllegalArgumentException;
import net.sf.mmm.util.exception.api.NlsIllegalStateException;
import net.sf.mmm.util.exception.api.NlsNullPointerException;
import net.sf.mmm.util.io.api.IoMode;
import net.sf.mmm.util.io.api.RuntimeIoException;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.util.NumericUtils;

/**
 * This is the implementation of {@link net.sf.mmm.search.indexer.api.SearchIndexer} using lucene as
 * underlying search-engine.
 *
 * @author Joerg Hohwiller (hohwille at users.sourceforge.net)
 */
public class LuceneSearchIndexer extends AbstractSearchIndexer {

    /** @see #getSearchEngine() */
    private final LuceneSearchEngineBuilder searchEngineBuilder;

    /** The {@link LuceneFieldManager}. */
    private final LuceneFieldManager fieldManager;

    /**
     * The {@link SearchDependencies}.
     *
     * @see #createEntry()
     */
    private final SearchDependencies searchDependencies;

    /**
     * The base-ID for this session. It is combined with {@link #addCounter} to create
     * {@link SearchEntry#getId() IDs}.
     */
    private final long sessionId;

    /**
     * The number of {@link SearchEntry entries} {@link #add(MutableSearchEntry) added} in this session.
     */
    private int addCounter;

    /** The {@link IndexWriter}. */
    private IndexWriter indexWriter;

    /** @see #getSearchEngine() */
    private ManagedSearchEngine searchEngine;

    /**
     * The constructor.
     *
     * @param indexWriter is the index modifier to use.
     * @param searchEngineBuilder is the {@link LuceneSearchEngineBuilder} required for
     *        {@link #getSearchEngine()}.
     * @param fieldManager is the {@link LuceneFieldManager}.
     * @param searchDependencies are the {@link SearchDependencies}.
     */
    public LuceneSearchIndexer(IndexWriter indexWriter, LuceneSearchEngineBuilder searchEngineBuilder,
            LuceneFieldManager fieldManager, SearchDependencies searchDependencies) {

        super();
        this.searchEngineBuilder = searchEngineBuilder;
        this.fieldManager = fieldManager;
        this.searchDependencies = searchDependencies;
        this.sessionId = System.currentTimeMillis() << 24;
        this.indexWriter = indexWriter;
        this.addCounter = 0;
    }

    /**
     * This method gets the {@link IndexWriter}.
     *
     * @return the {@link IndexWriter}.
     */
    protected IndexWriter getIndexWriter() {

        if (this.indexWriter == null) {
            // already closed...
            throw new NlsIllegalStateException();
        }
        return this.indexWriter;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void add(MutableSearchEntry entry) throws SearchException {

        NlsNullPointerException.checkNotNull(MutableSearchEntry.class, entry);
        this.addCounter++;
        Long id = Long.valueOf(this.sessionId | this.addCounter);
        add(entry, id);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void add(MutableSearchEntry entry, Long id) throws SearchException {

        LuceneMutableSearchEntry luceneEntry = (LuceneMutableSearchEntry) entry;
        try {
            Document luceneDocument = luceneEntry.getLuceneDocument();
            NumericField idField = new NumericField(SearchEntry.FIELD_ID, NumericUtils.PRECISION_STEP_DEFAULT,
                    Store.YES, true);
            idField.setLongValue(id.longValue());
            luceneDocument.add(idField);
            getIndexWriter().addDocument(luceneDocument);
        } catch (IOException e) {
            throw new SearchAddFailedException(e, entry);
        } finally {
            luceneEntry.dispose();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close() {

        try {
            if (this.indexWriter != null) {
                this.indexWriter.close();
                this.indexWriter = null;
            }
            if (this.searchEngine != null) {
                this.searchEngine.close();
                this.searchEngine = null;
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e, IoMode.CLOSE);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void flush() throws SearchException {

        try {
            if (this.indexWriter != null) {
                this.indexWriter.commit();
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e, IoMode.FLUSH);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MutableSearchEntry createEntry() {

        return new LuceneMutableSearchEntry(new Document(), this.fieldManager, this.searchDependencies);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void optimize() throws SearchException {

        try {
            getIndexWriter().optimize();
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }

    /**
     * This method performs the actual remove by {@link Term}.
     *
     * @see #removeByCustumId(String)
     * @see #removeByUri(String, String)
     * @see IndexWriter#deleteDocuments(Term)
     *
     * @param term is the {@link Term} identifying the {@link SearchEntry entry/entries} to remove.
     * @return the number of removed {@link SearchEntry entries}.
     */
    protected int remove(Term term) {

        try {
            IndexWriter writer = getIndexWriter();
            int count = writer.getReader().docFreq(term);
            if (count > 0) {
                writer.deleteDocuments(term);
            }
            return count;
        } catch (IOException e) {
            throw new SearchRemoveFailedException(e, term.field(), term.text());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int remove(String field, Object value) throws SearchException {

        NlsNullPointerException.checkNotNull("value", value);
        if (value == null) {
            throw new NlsNullPointerException("value [" + field + "]");
        }
        Term term = this.fieldManager.createTerm(field, value);
        if (term.text().length() == 0) {
            throw new NlsIllegalArgumentException(value.toString(), "value [" + field + "]");
        }
        return remove(term);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SearchEngine getSearchEngine() {

        try {
            if (this.searchEngine == null) {
                this.searchEngine = this.searchEngineBuilder.createSearchEngine(this.indexWriter.getReader(),
                        this.fieldManager.getConfigurationHolder(), null);
            }
            return this.searchEngine;
        } catch (IOException e) {
            throw new RuntimeIoException(e, IoMode.READ);
        }
    }

}