/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ package org.apache.solr; import org.apache.commons.io.FileUtils; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.request.CoreAdminRequest; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ShardParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.BinaryResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.junit.AfterClass; import org.junit.BeforeClass; import java.io.File; import java.io.IOException; import java.io.OutputStream; public class TestTolerantSearch extends SolrJettyTestBase { private static SolrClient collection1; private static SolrClient collection2; private static String shard1; private static String shard2; private static File solrHome; private static File createSolrHome() throws Exception { File workDir = createTempDir().toFile(); setupJettyTestHome(workDir, "collection1"); FileUtils.copyFile(new File(SolrTestCaseJ4.TEST_HOME() + "/collection1/conf/solrconfig-tolerant-search.xml"), new File(workDir, "/collection1/conf/solrconfig.xml")); FileUtils.copyDirectory(new File(workDir, "collection1"), new File(workDir, "collection2")); return workDir; } @BeforeClass public static void createThings() throws Exception { solrHome = createSolrHome(); createJetty(solrHome.getAbsolutePath()); String url = jetty.getBaseUrl().toString(); collection1 = getHttpSolrClient(url + "/collection1"); collection2 = getHttpSolrClient(url + "/collection2"); String urlCollection1 = jetty.getBaseUrl().toString() + "/" + "collection1"; String urlCollection2 = jetty.getBaseUrl().toString() + "/" + "collection2"; shard1 = urlCollection1.replaceAll("https?://", ""); shard2 = urlCollection2.replaceAll("https?://", ""); //create second core try (HttpSolrClient nodeClient = getHttpSolrClient(url)) { CoreAdminRequest.Create req = new CoreAdminRequest.Create(); req.setCoreName("collection2"); req.setConfigSet("collection1"); nodeClient.request(req); } SolrInputDocument doc = new SolrInputDocument(); doc.setField("id", "1"); doc.setField("subject", "batman"); doc.setField("title", "foo bar"); collection1.add(doc); collection1.commit(); doc.setField("id", "2"); doc.setField("subject", "superman"); collection2.add(doc); collection2.commit(); doc = new SolrInputDocument(); doc.setField("id", "3"); doc.setField("subject", "aquaman"); doc.setField("title", "foo bar"); collection1.add(doc); collection1.commit(); } @AfterClass public static void destroyThings() throws Exception { collection1.close(); collection2.close(); collection1 = null; collection2 = null; jetty.stop(); jetty=null; resetExceptionIgnores(); } @SuppressWarnings("unchecked") public void testGetFieldsPhaseError() throws SolrServerException, IOException { BadResponseWriter.failOnGetFields = true; BadResponseWriter.failOnGetTopIds = false; SolrQuery query = new SolrQuery(); query.setQuery("subject:batman OR subject:superman"); query.addField("id"); query.addField("subject"); query.set("distrib", "true"); query.set("shards", shard1 + "," + shard2); query.set(ShardParams.SHARDS_INFO, "true"); query.set("debug", "true"); query.set("stats", "true"); query.set("stats.field", "id"); query.set("mlt", "true"); query.set("mlt.fl", "title"); query.set("mlt.count", "1"); query.set("mlt.mintf", "0"); query.set("mlt.mindf", "0"); query.setHighlight(true); query.addFacetField("id"); query.setFacet(true); ignoreException("Dummy exception in BadResponseWriter"); try { collection1.query(query); fail("Should get an exception"); } catch (Exception e) { //expected } query.set(ShardParams.SHARDS_TOLERANT, "true"); QueryResponse response = collection1.query(query); assertTrue(response.getResponseHeader().getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY)); NamedList<Object> shardsInfo = ((NamedList<Object>)response.getResponse().get(ShardParams.SHARDS_INFO)); boolean foundError = false; for (int i = 0; i < shardsInfo.size(); i++) { if (shardsInfo.getName(i).contains("collection2")) { assertNotNull(((NamedList<Object>)shardsInfo.getVal(i)).get("error")); foundError = true; break; } } assertTrue(foundError); assertEquals(1, response.getResults().get(0).getFieldValue("id")); assertEquals("batman", response.getResults().get(0).getFirstValue("subject")); unIgnoreException("Dummy exception in BadResponseWriter"); } @SuppressWarnings("unchecked") public void testGetTopIdsPhaseError() throws SolrServerException, IOException { BadResponseWriter.failOnGetTopIds = true; BadResponseWriter.failOnGetFields = false; SolrQuery query = new SolrQuery(); query.setQuery("subject:batman OR subject:superman"); query.addField("id"); query.addField("subject"); query.set("distrib", "true"); query.set("shards", shard1 + "," + shard2); query.set(ShardParams.SHARDS_INFO, "true"); query.set("debug", "true"); query.set("stats", "true"); query.set("stats.field", "id"); query.set("mlt", "true"); query.set("mlt.fl", "title"); query.set("mlt.count", "1"); query.set("mlt.mintf", "0"); query.set("mlt.mindf", "0"); query.setHighlight(true); query.addFacetField("id"); query.setFacet(true); ignoreException("Dummy exception in BadResponseWriter"); try { collection1.query(query); fail("Should get an exception"); } catch (Exception e) { //expected } query.set(ShardParams.SHARDS_TOLERANT, "true"); QueryResponse response = collection1.query(query); assertTrue(response.getResponseHeader().getBooleanArg(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY)); NamedList<Object> shardsInfo = ((NamedList<Object>)response.getResponse().get(ShardParams.SHARDS_INFO)); boolean foundError = false; for (int i = 0; i < shardsInfo.size(); i++) { if (shardsInfo.getName(i).contains("collection2")) { assertNotNull(((NamedList<Object>)shardsInfo.getVal(i)).get("error")); foundError = true; break; } } assertTrue(foundError); assertEquals(1, response.getResults().get(0).getFieldValue("id")); assertEquals("batman", response.getResults().get(0).getFirstValue("subject")); unIgnoreException("Dummy exception in BadResponseWriter"); } public static class BadResponseWriter extends BinaryResponseWriter { private static boolean failOnGetFields = false; private static boolean failOnGetTopIds = false; public BadResponseWriter() { super(); } @Override public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) throws IOException { // I want to fail on the shard request, not the original user request, and only on the // GET_FIELDS phase if (failOnGetFields && "collection2".equals(req.getCore().getName()) && "subject:batman OR subject:superman".equals(req.getParams().get("q", "")) && req.getParams().get("ids") != null) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Dummy exception in BadResponseWriter"); } else if (failOnGetTopIds && "collection2".equals(req.getCore().getName()) && "subject:batman OR subject:superman".equals(req.getParams().get("q", "")) && req.getParams().get("ids") == null && req.getParams().getBool("isShard", false) == true) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Dummy exception in BadResponseWriter"); } super.write(out, req, response); } } }