ch.entwine.weblounge.contentrepository.index.SearchIndexFulltextTest.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.contentrepository.index.SearchIndexFulltextTest.java

Source

/*
 *  Weblounge: Web Content Management System
 *  Copyright (c) 2003 - 2011 The Weblounge Team
 *  http://entwinemedia.com/weblounge
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software Foundation
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package ch.entwine.weblounge.contentrepository.index;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import ch.entwine.weblounge.common.content.Resource;
import ch.entwine.weblounge.common.content.SearchQuery;
import ch.entwine.weblounge.common.content.page.Page;
import ch.entwine.weblounge.common.content.page.PageTemplate;
import ch.entwine.weblounge.common.content.page.Pagelet;
import ch.entwine.weblounge.common.impl.content.SearchQueryImpl;
import ch.entwine.weblounge.common.impl.content.page.PageReader;
import ch.entwine.weblounge.common.impl.language.LanguageUtils;
import ch.entwine.weblounge.common.language.Language;
import ch.entwine.weblounge.common.repository.ContentRepositoryException;
import ch.entwine.weblounge.common.site.Site;
import ch.entwine.weblounge.common.url.PathUtils;
import ch.entwine.weblounge.contentrepository.impl.FileResourceSerializer;
import ch.entwine.weblounge.contentrepository.impl.ImageResourceSerializer;
import ch.entwine.weblounge.contentrepository.impl.MovieResourceSerializer;
import ch.entwine.weblounge.contentrepository.impl.PageSerializer;
import ch.entwine.weblounge.contentrepository.impl.ResourceSerializerServiceImpl;
import ch.entwine.weblounge.contentrepository.impl.index.SearchIndex;
import ch.entwine.weblounge.contentrepository.impl.index.elasticsearch.ElasticSearchUtils;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.easymock.EasyMock;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * Test case for {@link SearchIndex}.
 */
public class SearchIndexFulltextTest {

    /** The search index */
    protected static SearchIndex idx = null;

    /** The index root directory */
    protected static File idxRoot = null;

    /** Flag to indicate read only index */
    protected static boolean isReadOnly = false;

    /** Page template */
    protected static PageTemplate template = null;

    /** The mock site */
    protected static Site site = null;

    /** The sample page */
    protected static String pageFile = "/page.xml";

    /** The sample live page */
    protected static Page livePage = null;

    /** The sample work page */
    protected static Page workPage = null;

    /** The sample pagelet */
    protected static Pagelet pagelet = null;

    /** The resource serializer */
    private static ResourceSerializerServiceImpl serializer = null;

    /**
     * Sets up the solr search index. Since solr sometimes has a hard time
     * shutting down cleanly, it's done only once for all the tests.
     * 
     * @throws Exception
     */
    @BeforeClass
    public static void setupClass() throws Exception {
        // Template
        template = EasyMock.createNiceMock(PageTemplate.class);
        EasyMock.expect(template.getIdentifier()).andReturn("templateid").anyTimes();
        EasyMock.expect(template.getStage()).andReturn("non-existing").anyTimes();
        EasyMock.replay(template);

        Set<Language> languages = new HashSet<Language>();
        languages.add(LanguageUtils.getLanguage("en"));
        languages.add(LanguageUtils.getLanguage("de"));

        // Site
        site = EasyMock.createNiceMock(Site.class);
        EasyMock.expect(site.getIdentifier()).andReturn("test").anyTimes();
        EasyMock.expect(site.getTemplate((String) EasyMock.anyObject())).andReturn(template).anyTimes();
        EasyMock.expect(site.getDefaultTemplate()).andReturn(template).anyTimes();
        EasyMock.expect(site.getLanguages()).andReturn(languages.toArray(new Language[languages.size()]))
                .anyTimes();
        EasyMock.replay(site);

        // Resource serializer
        serializer = new ResourceSerializerServiceImpl();
        serializer.addSerializer(new PageSerializer());
        serializer.addSerializer(new FileResourceSerializer());
        serializer.addSerializer(new ImageResourceSerializer());
        serializer.addSerializer(new MovieResourceSerializer());

        String rootPath = PathUtils.concat(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
        idxRoot = new File(rootPath);
        System.setProperty("weblounge.home", rootPath);
        ElasticSearchUtils.createIndexConfigurationAt(idxRoot);
        idx = new SearchIndex(site, serializer, isReadOnly);

        // Prepare the pages
        PageReader pageReader = new PageReader();
        InputStream is = null;

        // Add the live page
        try {
            is = SearchIndexFulltextTest.class.getResourceAsStream(pageFile);
            livePage = pageReader.read(is, site);
            pagelet = livePage.getPagelets()[0];
            idx.add(livePage);
        } finally {
            IOUtils.closeQuietly(is);
        }

        // Add the work page
        try {
            is = SearchIndexFulltextTest.class.getResourceAsStream(pageFile);
            workPage = pageReader.read(is, site);
            workPage.setVersion(Resource.WORK);
            idx.add(workPage);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Does the cleanup after the test suite.
     */
    @AfterClass
    public static void tearDownClass() {
        try {
            idx.close();
            FileUtils.deleteQuietly(idxRoot);
        } catch (IOException e) {
            fail("Error closing search index: " + e.getMessage());
        }
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPath() throws Exception {
        String path = livePage.getURI().getPath();
        SearchQuery q = new SearchQueryImpl(site).withPathPrefix(path);
        assertEquals(2, idx.getByQuery(q).getItems().length);
        q.withText(path);
        assertEquals(1, idx.getByQuery(q).getItems().length);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPathPrefix() throws Exception {
        String path = livePage.getURI().getPath();

        // Check the full path
        String pathPrefix = path.substring(0, path.indexOf('/', 1));

        assertSearchResult(pathPrefix, true, 2, 1);

        // Check path elements
        for (String pathElement : StringUtils.split(path, "/")) {
            if (pathElement.length() > 2)
                assertSearchResult(pathElement, true, 2, 1);
        }

    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithTitle() throws Exception {
        assertSearchResult(livePage.getTitle(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithDescription() throws Exception {
        assertSearchResult(livePage.getDescription(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithSubject() throws Exception {
        SearchQuery q = new SearchQueryImpl(site).withFulltext(livePage.getSubjects()[0]);
        assertEquals(2, idx.getByQuery(q).getItems().length);
        q.withText(livePage.getSubjects()[0]);
        assertEquals(0, idx.getByQuery(q).getItems().length);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithCoverage() throws Exception {
        SearchQuery q = new SearchQueryImpl(site).withFulltext(livePage.getCoverage());
        assertEquals(2, idx.getByQuery(q).getItems().length);
        q.withText(livePage.getCoverage());
        assertEquals(0, idx.getByQuery(q).getItems().length);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithOwner() throws Exception {
        assertSearchResult(livePage.getOwner().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithCreator() throws Exception {
        assertSearchResult(livePage.getCreator().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithModifier() throws Exception {
        assertSearchResult(livePage.getModifier().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPublisher() throws Exception {
        assertSearchResult(livePage.getPublisher().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithLockOwner() throws Exception {
        assertSearchResult(livePage.getLockOwner().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithSubjects() throws Exception {
        String subject = "subject";
        SearchQuery q = new SearchQueryImpl(site).withFulltext(subject);
        assertEquals(2, idx.getByQuery(q).getItems().length);
        q.withText(subject);
        assertEquals(0, idx.getByQuery(q).getItems().length);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithContent() throws Exception {
        assertSearchResult("text", true, 2, 1);
        assertSearchResult("titre", true, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPageletOwner() throws Exception {
        assertSearchResult(pagelet.getOwner().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPageletCreator() throws Exception {
        assertSearchResult(pagelet.getCreator().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPageletModifier() throws Exception {
        assertSearchResult(pagelet.getModifier().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPageletPublisher() throws Exception {
        assertSearchResult(pagelet.getPublisher().getName(), false, 2, 0);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithPageletContent() throws Exception {
        assertSearchResult(pagelet.getContent("textid"), false, 2, 1);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithWildcardPageletContent() throws Exception {
        assertSearchResult(pagelet.getContent("textid").substring(0, 5), true, 2, 1);
    }

    /**
     * Test method for
     * {@link ch.entwine.weblounge.contentrepository.impl.index.SearchIndex#getByQuery(ch.entwine.weblounge.common.content.SearchQuery)}
     * .
     */
    @Test
    public void testGetWithProperty() throws Exception {
        SearchQuery q = new SearchQueryImpl(site).withFulltext(pagelet.getProperty("propertyid"));
        assertEquals(2, idx.getByQuery(q).getItems().length);
        q.withText(pagelet.getProperty("propertyid"));
        assertEquals(0, idx.getByQuery(q).getItems().length);
    }

    /**
     * Helper method to test existence of search text and parts thereof in the
     * fulltext and in the text index.
     * 
     * @param searchText
     *          the text to search for
     * @param fuzzy
     *          whether to do a fuzzy search only
     * @param expectedInFulltext
     *          expected number of hits in the fulltext index
     * @param expectedInText
     *          expected number of hits in the text index
     * @throws ContentRepositoryException
     *           if searching fails
     */
    private void assertSearchResult(String searchText, boolean fuzzy, int expectedInFulltext, int expectedInText)
            throws ContentRepositoryException {
        SearchQuery q = new SearchQueryImpl(site).withFulltext(fuzzy, searchText);
        assertEquals(expectedInFulltext, idx.getByQuery(q).getItems().length);
        q = new SearchQueryImpl(site).withText(fuzzy, searchText);
        assertEquals(expectedInText, idx.getByQuery(q).getItems().length);

        // Lowercase match
        q = new SearchQueryImpl(site).withFulltext(true, searchText.toLowerCase());
        assertEquals(2, idx.getByQuery(q).getItems().length);

        // Partial matches
        for (String part : StringUtils.split(searchText)) {
            q = new SearchQueryImpl(site).withFulltext(true, part);
            assertTrue(idx.getByQuery(q).getItems().length >= expectedInFulltext);
            q.withText(true, part);
            assertTrue(idx.getByQuery(q).getItems().length >= expectedInText);
        }
    }

}