eu.interedition.web.index.IndexController.java Source code

Java tutorial

Introduction

Here is the source code for eu.interedition.web.index.IndexController.java

Source

/*
 * #%L
 * Text Repository: Datastore for texts based on Interedition's model.
 * %%
 * Copyright (C) 2010 - 2011 The Interedition Development Group
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package eu.interedition.web.index;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
import eu.interedition.text.Text;
import eu.interedition.web.metadata.DublinCoreMetadata;
import eu.interedition.web.metadata.MetadataController;
import eu.interedition.web.text.TextController;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="http://gregor.middell.net/" title="Homepage">Gregor Middell</a>
 */
@Controller
@RequestMapping("/index")
public class IndexController implements InitializingBean, DisposableBean {

    @Autowired
    @Qualifier("dataDirectory")
    private File dataDirectory;

    @Autowired
    private MetadataController metadataController;

    @Autowired
    private SessionFactory sessionFactory;

    private FSDirectory directory;
    private StandardAnalyzer analyzer;
    private IndexWriter indexWriter;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;

    @RequestMapping(produces = "text/html")
    public ModelAndView search(@ModelAttribute("query") IndexQuery query) throws IOException, ParseException {
        final List<IndexQueryResult> searchResults = query(query);
        return (searchResults.size() == 1)
                ? new ModelAndView(TextController.redirectTo(searchResults.get(0).getText()))
                : new ModelAndView("search", "results", searchResults);
    }

    @RequestMapping(produces = "application/json")
    @ResponseBody
    public List<IndexQueryResult> query(@ModelAttribute("query") IndexQuery query)
            throws IOException, ParseException {
        if (Strings.isNullOrEmpty(query.getQuery())) {
            return Collections.emptyList();
        }

        final Query parsed = new QueryParser(Version.LUCENE_30, "content", analyzer).parse(query.getQuery());
        final int pageSize = query.getPageSize();
        final int offset = query.getPage() * pageSize;

        final Map<Long, Integer> scores = Maps.newLinkedHashMap();
        final IndexSearcher searcher = indexSearcher();
        final TopDocs searchResult = searcher.search(parsed, offset + pageSize);
        for (int rc = offset; rc < searchResult.scoreDocs.length; rc++) {
            final ScoreDoc scoreDocument = searchResult.scoreDocs[rc];
            final Document document = searcher.doc(scoreDocument.doc);
            scores.put(Long.parseLong(document.get("id")), Math.round(scoreDocument.score * 100));
        }

        final List<IndexQueryResult> results = Lists.newArrayListWithExpectedSize(scores.size());
        for (DublinCoreMetadata metadata : metadataController.read(scores.keySet())) {
            results.add(new IndexQueryResult(metadata, scores.get(metadata.getText())));
        }
        return results;
    }

    public void update(DublinCoreMetadata metadata) throws IOException {
        final Text text = (Text) sessionFactory.getCurrentSession().load(Text.class, metadata.getText());
        Reader textReader = null;
        try {
            final Document document = new Document();
            document.add(new Field("id", Long.toString(text.getId()), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.add(new Field("type", text.getType().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.add(new Field("content_length", Long.toString(text.getLength()), Field.Store.YES,
                    Field.Index.NOT_ANALYZED));
            document.add(new Field("content", textReader = text.read().getInput()));

            metadata.addTo(document);

            indexWriter.updateDocument(idTerm(metadata), document);
            commit();

        } finally {
            Closeables.close(textReader, false);
        }
    }

    public void delete(DublinCoreMetadata metadata) throws IOException {
        indexWriter.deleteDocuments(new TermQuery(idTerm(metadata)));
        commit();
    }

    protected Term idTerm(DublinCoreMetadata metadata) {
        return new Term("id", Long.toString(metadata.getText()));
    }

    public void index() throws IOException {
        indexWriter.deleteAll();
        metadataController.index();
        commit();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        final File indexHome = new File(dataDirectory, "index");
        Assert.isTrue(indexHome.isDirectory() || indexHome.mkdirs(), //
                "Fulltext index directory '" + indexHome + "' does not exist and could not be created");

        this.directory = FSDirectory.open(indexHome);
        this.analyzer = new StandardAnalyzer(Version.LUCENE_30);

        this.indexWriter = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_33, analyzer));
        this.indexReader = IndexReader.open(this.indexWriter, false);
    }

    @Override
    public void destroy() throws Exception {
        Closeables.close(this.indexReader, false);
        Closeables.close(this.indexWriter, false);
        Closeables.close(this.directory, false);
    }

    protected synchronized IndexSearcher indexSearcher() throws IOException {
        if (this.indexSearcher == null) {
            this.indexSearcher = new IndexSearcher(indexReader.reopen());
        }
        return indexSearcher;
    }

    protected synchronized void commit() throws IOException {
        this.indexWriter.commit();

        Closeables.closeQuietly(this.indexSearcher);
        this.indexSearcher = null;
    }
}