/* * This is a common dao with basic CRUD operations and is not limited to any * persistent layer implementation * * Copyright (C) 2010 Imran M Yousuf (imyousuf@smartitengineering.com) * * This library 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 3 of the License, or (at your option) any later version. * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.smartitengineering.dao.search.solr; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; import com.google.inject.name.Names; import com.smartitengineering.common.dao.search.CommonFreeTextPersistentDao; import com.smartitengineering.common.dao.search.CommonFreeTextSearchDao; import com.smartitengineering.common.dao.search.solr.SolrFreeTextPersistentDao; import com.smartitengineering.common.dao.search.solr.SolrFreeTextSearchDao; import com.smartitengineering.common.dao.search.solr.spi.ObjectIdentifierQuery; import com.smartitengineering.dao.common.queryparam.QueryParameterFactory; import com.smartitengineering.dao.solr.MultivalueMap; import com.smartitengineering.dao.solr.ServerConfiguration; import com.smartitengineering.dao.solr.ServerFactory; import com.smartitengineering.dao.solr.SolrQueryDao; import com.smartitengineering.dao.solr.SolrWriteDao; import com.smartitengineering.dao.solr.impl.MultivalueMapImpl; import com.smartitengineering.dao.solr.impl.ServerConfigurationImpl; import com.smartitengineering.dao.solr.impl.SingletonRemoteServerFactory; import com.smartitengineering.dao.solr.impl.SolrDao; import com.smartitengineering.util.bean.adapter.AbstractAdapterHelper; import com.smartitengineering.util.bean.adapter.GenericAdapter; import com.smartitengineering.util.bean.adapter.GenericAdapterImpl; import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author imyousuf */ public class SolrSearchDaoTest { private static final int PORT = 40080; private static Server jettyServer; private static CommonFreeTextPersistentDao<Domain> writeDao; private static CommonFreeTextSearchDao<Domain> readDao; private static final Logger LOGGER = LoggerFactory.getLogger(SolrSearchDaoTest.class); @BeforeClass public static void globalSetup() throws Exception { System.setProperty("solr.solr.home", "./target/sample-conf/"); jettyServer = new Server(PORT); final String webapp = "./target/solr/"; if (!new File(webapp).exists()) { throw new IllegalStateException("WebApp file/dir does not exist!"); } WebAppContext webAppHandler = new WebAppContext(webapp, "/"); jettyServer.setHandler(webAppHandler); jettyServer.setSendDateHeader(true); jettyServer.start(); Injector injector = Guice.createInjector(new SearchModule()); writeDao = injector.getInstance(Key.get(new TypeLiteral<CommonFreeTextPersistentDao<Domain>>() { })); readDao = injector.getInstance(Key.get(new TypeLiteral<CommonFreeTextSearchDao<Domain>>() { })); } @AfterClass public static void globalTearDown() throws Exception { jettyServer.stop(); } @Test public void testSimpleAdd() { Domain domain = new Domain(); domain.id = Long.toString(1l); domain.name = "Test Domain 1"; domain.features = new String[]{"sports", "soccer"}; writeDao.save(domain); Collection<Domain> domains = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: 1")); Assert.assertNotNull(domains); Assert.assertEquals(1, domains.size()); domain = domains.iterator().next(); LOGGER.info("Domain retrieved from search is: " + domain); Assert.assertEquals(new Long(1), Long.valueOf(domain.id)); Assert.assertEquals("Test Domain 1", domain.name); Assert.assertTrue(Arrays.equals(new String[]{"sports", "soccer"}, domain.features)); } @Test public void testUpadte() { Domain domain = new Domain(); domain.id = Long.toString(1l); domain.name = "Test Domain 2"; domain.features = new String[]{"sports", "cricket"}; writeDao.update(domain); Collection<Domain> domains = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: 1")); Assert.assertNotNull(domains); Assert.assertEquals(1, domains.size()); domain = domains.iterator().next(); LOGGER.info("Domain retrieved from search is: " + domain); Assert.assertEquals(new Long(1), Long.valueOf(domain.id)); Assert.assertEquals("Test Domain 2", domain.name); Assert.assertTrue(Arrays.equals(new String[]{"sports", "cricket"}, domain.features)); } @Test public void deletTest() { Domain domain = new Domain(); domain.id = Long.toString(1l); domain.name = "Test Domain 2"; domain.features = new String[]{"sports", "cricket"}; LOGGER.info("Domain retrieved from delete is: " + domain); writeDao.delete(domain); Collection<Domain> domains = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: 1")); Assert.assertEquals(0, domains.size()); } @Test public void testPagination() { final Domain[] domains = new Domain[20]; for (int i = 0; i < domains.length; ++i) { final Domain domain = new Domain(); domain.id = Long.toString(i); domain.name = "Test Domain " + i; domain.features = new String[]{"sports", "cricket"}; domains[i] = domain; } writeDao.save(domains); Collection<Domain> result = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: *"), QueryParameterFactory. getMaxResultsParam(domains.length)); LOGGER.info(result.toString()); Assert.assertEquals(domains.length, result.size()); final int newSize = domains.length / 2; result = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: *"), QueryParameterFactory. getMaxResultsParam(newSize)); LOGGER.info(result.toString()); Iterator<Domain> iter = result.iterator(); for (int i = 0; i < newSize && iter.hasNext(); ++i) { Assert.assertEquals(Integer.toString(i), iter.next().id); } Assert.assertEquals(newSize, result.size()); result = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: *"), QueryParameterFactory. getMaxResultsParam(newSize), QueryParameterFactory.getFirstResultParam(newSize)); LOGGER.info(result.toString()); Assert.assertEquals(newSize, result.size()); iter = result.iterator(); for (int i = newSize; i < domains.length && iter.hasNext(); ++i) { Assert.assertEquals(Integer.toString(i), iter.next().id); } writeDao.delete(domains); result = readDao.search(QueryParameterFactory.getStringLikePropertyParam("q", "id: *")); Assert.assertEquals(0, result.size()); } private static class SearchModule extends AbstractModule { @Override protected void configure() { bind(Long.class).annotatedWith(Names.named("waitTime")).toInstance(new Long(10)); bind(TimeUnit.class).annotatedWith(Names.named("waitTimeUnit")).toInstance(TimeUnit.SECONDS); bind(ExecutorService.class).toInstance(Executors.newCachedThreadPool()); bind(new TypeLiteral<ObjectIdentifierQuery<Domain>>() { }).to(DomainIdentifierQuery.class).in(Scopes.SINGLETON); bind(new TypeLiteral<GenericAdapter<Domain, MultivalueMap<String, Object>>>() { }).to(new TypeLiteral<GenericAdapterImpl<Domain, MultivalueMap<String, Object>>>() { }).in(Scopes.SINGLETON); bind(new TypeLiteral<AbstractAdapterHelper<Domain, MultivalueMap<String, Object>>>() { }).to(DomainAdapterHelper.class).in(Scopes.SINGLETON); bind(SolrQueryDao.class).to(SolrDao.class).in(Scopes.SINGLETON); bind(SolrWriteDao.class).to(SolrDao.class).in(Scopes.SINGLETON); bind(ServerFactory.class).to(SingletonRemoteServerFactory.class).in(Scopes.SINGLETON); bind(ServerConfiguration.class).to(ServerConfigurationImpl.class).in(Scopes.SINGLETON); bind(String.class).annotatedWith(Names.named("uri")).toInstance("http://localhost:" + PORT + "/"); bind(new TypeLiteral<CommonFreeTextPersistentDao<Domain>>() { }).to(new TypeLiteral<SolrFreeTextPersistentDao<Domain>>() { }).in(Scopes.SINGLETON); bind(new TypeLiteral<CommonFreeTextSearchDao<Domain>>() { }).to(new TypeLiteral<SolrFreeTextSearchDao<Domain>>() { }).in(Scopes.SINGLETON); } } private static class Domain { String id; String name; String[] features; @Override public String toString() { return "Domain{" + "id=" + id + ", name=" + name + ", features=" + Arrays.toString(features) + '}'; } } private static class DomainAdapterHelper extends AbstractAdapterHelper<Domain, MultivalueMap<String, Object>> { @Override protected MultivalueMap<String, Object> newTInstance() { return new MultivalueMapImpl<String, Object>(); } @Override protected void mergeFromF2T(Domain fromBean, MultivalueMap<String, Object> toBean) { toBean.addValue("id", fromBean.id); toBean.addValue("name", fromBean.name); for (String feature : fromBean.features) { toBean.addValue("features", feature); } } @Override protected Domain convertFromT2F(MultivalueMap<String, Object> toBean) { Domain domain = new Domain(); domain.id = toBean.getFirst("id").toString(); domain.name = toBean.getFirst("name").toString(); domain.features = toBean.get("features").toArray(new String[0]); return domain; } } private static class DomainIdentifierQuery implements ObjectIdentifierQuery<Domain> { @Override public String getQuery(Domain object) { return "id: " + object.id; } } }