/* * 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.handler.component; import org.apache.lucene.index.IndexReader; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.IOUtils; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.GroupParams; import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.params.QueryElevationParams; import org.apache.solr.util.FileUtils; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.component.QueryElevationComponent.ElevationObj; import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; import org.junit.Before; import org.junit.Test; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; public class QueryElevationComponentTest extends SolrTestCaseJ4 { @Before @Override public void setUp() throws Exception { super.setUp(); } private void init(String schema) throws Exception { init("solrconfig-elevate.xml", schema); } private void init(String config, String schema) throws Exception { //write out elevate-data.xml to the Data dir first by copying it from conf, which we know exists, this way we can test both conf and data configurations createTempDir(); File parent = new File(TEST_HOME() + "/collection1", "conf"); File elevateFile = new File(parent, "elevate.xml"); File elevateDataFile = new File(dataDir, "elevate-data.xml"); FileUtils.copyFile(elevateFile, elevateDataFile); initCore(config,schema); clearIndex(); assertU(commit()); } private void delete() throws Exception { deleteCore(); } @Test public void testFieldType() throws Exception { try { init("schema11.xml"); clearIndex(); assertU(commit()); assertU(adoc("id", "1", "text", "XXXX XXXX", "str_s", "a")); assertU(adoc("id", "2", "text", "YYYY", "str_s", "b")); assertU(adoc("id", "3", "text", "ZZZZ", "str_s", "c")); assertU(adoc("id", "4", "text", "XXXX XXXX", "str_s", "x")); assertU(adoc("id", "5", "text", "YYYY YYYY", "str_s", "y")); assertU(adoc("id", "6", "text", "XXXX XXXX", "str_s", "z")); assertU(adoc("id", "7", "text", "AAAA", "str_s", "a")); assertU(adoc("id", "8", "text", "AAAA", "str_s", "a")); assertU(adoc("id", "9", "text", "AAAA AAAA", "str_s", "a")); assertU(commit()); assertQ("", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.FL, "id, score, [elevated]") , "//*[@numFound='3']" , "//result/doc[1]/float[@name='id'][.='7.0']" , "//result/doc[2]/float[@name='id'][.='8.0']" , "//result/doc[3]/float[@name='id'][.='9.0']", "//result/doc[1]/bool[@name='[elevated]'][.='true']", "//result/doc[2]/bool[@name='[elevated]'][.='false']", "//result/doc[3]/bool[@name='[elevated]'][.='false']" ); } finally { delete(); } } @Test public void testGroupedQuery() throws Exception { try { init("schema11.xml"); clearIndex(); assertU(commit()); assertU(adoc("id", "1", "text", "XXXX XXXX", "str_s", "a")); assertU(adoc("id", "2", "text", "XXXX AAAA", "str_s", "b")); assertU(adoc("id", "3", "text", "ZZZZ", "str_s", "c")); assertU(adoc("id", "4", "text", "XXXX ZZZZ", "str_s", "d")); assertU(adoc("id", "5", "text", "ZZZZ ZZZZ", "str_s", "e")); assertU(adoc("id", "6", "text", "AAAA AAAA AAAA", "str_s", "f")); assertU(adoc("id", "7", "text", "AAAA AAAA ZZZZ", "str_s", "g")); assertU(adoc("id", "8", "text", "XXXX", "str_s", "h")); assertU(adoc("id", "9", "text", "YYYY ZZZZ", "str_s", "i")); assertU(adoc("id", "22", "text", "XXXX ZZZZ AAAA", "str_s", "b")); assertU(adoc("id", "66", "text", "XXXX ZZZZ AAAA", "str_s", "f")); assertU(adoc("id", "77", "text", "XXXX ZZZZ AAAA", "str_s", "g")); assertU(commit()); final String groups = "//arr[@name='groups']"; assertQ("non-elevated group query", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", GroupParams.GROUP_FIELD, "str_s", GroupParams.GROUP, "true", GroupParams.GROUP_TOTAL_COUNT, "true", GroupParams.GROUP_LIMIT, "100", QueryElevationParams.ENABLE, "false", CommonParams.FL, "id, score, [elevated]") , "//*[@name='ngroups'][.='3']" , "//*[@name='matches'][.='6']" , groups +"/lst[1]//doc[1]/float[@name='id'][.='6.0']" , groups +"/lst[1]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[1]//doc[2]/float[@name='id'][.='66.0']" , groups +"/lst[1]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[1]/float[@name='id'][.='7.0']" , groups +"/lst[2]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[2]/float[@name='id'][.='77.0']" , groups +"/lst[2]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[1]/float[@name='id'][.='2.0']" , groups +"/lst[3]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[2]/float[@name='id'][.='22.0']" , groups +"/lst[3]//doc[2]/bool[@name='[elevated]'][.='false']" ); assertQ("elevated group query", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", GroupParams.GROUP_FIELD, "str_s", GroupParams.GROUP, "true", GroupParams.GROUP_TOTAL_COUNT, "true", GroupParams.GROUP_LIMIT, "100", CommonParams.FL, "id, score, [elevated]") , "//*[@name='ngroups'][.='3']" , "//*[@name='matches'][.='6']" , groups +"/lst[1]//doc[1]/float[@name='id'][.='7.0']" , groups +"/lst[1]//doc[1]/bool[@name='[elevated]'][.='true']" , groups +"/lst[1]//doc[2]/float[@name='id'][.='77.0']" , groups +"/lst[1]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[1]/float[@name='id'][.='6.0']" , groups +"/lst[2]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[2]/float[@name='id'][.='66.0']" , groups +"/lst[2]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[1]/float[@name='id'][.='2.0']" , groups +"/lst[3]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[2]/float[@name='id'][.='22.0']" , groups +"/lst[3]//doc[2]/bool[@name='[elevated]'][.='false']" ); assertQ("non-elevated because sorted group query", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.SORT, "id asc", GroupParams.GROUP_FIELD, "str_s", GroupParams.GROUP, "true", GroupParams.GROUP_TOTAL_COUNT, "true", GroupParams.GROUP_LIMIT, "100", CommonParams.FL, "id, score, [elevated]") , "//*[@name='ngroups'][.='3']" , "//*[@name='matches'][.='6']" , groups +"/lst[1]//doc[1]/float[@name='id'][.='2.0']" , groups +"/lst[1]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[1]//doc[2]/float[@name='id'][.='22.0']" , groups +"/lst[1]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[1]/float[@name='id'][.='6.0']" , groups +"/lst[2]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[2]/float[@name='id'][.='66.0']" , groups +"/lst[2]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[1]/float[@name='id'][.='7.0']" , groups +"/lst[3]//doc[1]/bool[@name='[elevated]'][.='true']" , groups +"/lst[3]//doc[2]/float[@name='id'][.='77.0']" , groups +"/lst[3]//doc[2]/bool[@name='[elevated]'][.='false']" ); assertQ("force-elevated sorted group query", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.SORT, "id asc", QueryElevationParams.FORCE_ELEVATION, "true", GroupParams.GROUP_FIELD, "str_s", GroupParams.GROUP, "true", GroupParams.GROUP_TOTAL_COUNT, "true", GroupParams.GROUP_LIMIT, "100", CommonParams.FL, "id, score, [elevated]") , "//*[@name='ngroups'][.='3']" , "//*[@name='matches'][.='6']" , groups +"/lst[1]//doc[1]/float[@name='id'][.='7.0']" , groups +"/lst[1]//doc[1]/bool[@name='[elevated]'][.='true']" , groups +"/lst[1]//doc[2]/float[@name='id'][.='77.0']" , groups +"/lst[1]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[1]/float[@name='id'][.='2.0']" , groups +"/lst[2]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[2]/float[@name='id'][.='22.0']" , groups +"/lst[2]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[1]/float[@name='id'][.='6.0']" , groups +"/lst[3]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[2]/float[@name='id'][.='66.0']" , groups +"/lst[3]//doc[2]/bool[@name='[elevated]'][.='false']" ); assertQ("non-elevated because of sort within group query", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.SORT, "id asc", GroupParams.GROUP_SORT, "id desc", GroupParams.GROUP_FIELD, "str_s", GroupParams.GROUP, "true", GroupParams.GROUP_TOTAL_COUNT, "true", GroupParams.GROUP_LIMIT, "100", CommonParams.FL, "id, score, [elevated]") , "//*[@name='ngroups'][.='3']" , "//*[@name='matches'][.='6']" , groups +"/lst[1]//doc[1]/float[@name='id'][.='22.0']" , groups +"/lst[1]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[1]//doc[2]/float[@name='id'][.='2.0']" , groups +"/lst[1]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[1]/float[@name='id'][.='66.0']" , groups +"/lst[2]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[2]/float[@name='id'][.='6.0']" , groups +"/lst[2]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[1]/float[@name='id'][.='77.0']" , groups +"/lst[3]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[2]/float[@name='id'][.='7.0']" , groups +"/lst[3]//doc[2]/bool[@name='[elevated]'][.='true']" ); assertQ("force elevated sort within sorted group query", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.SORT, "id asc", GroupParams.GROUP_SORT, "id desc", QueryElevationParams.FORCE_ELEVATION, "true", GroupParams.GROUP_FIELD, "str_s", GroupParams.GROUP, "true", GroupParams.GROUP_TOTAL_COUNT, "true", GroupParams.GROUP_LIMIT, "100", CommonParams.FL, "id, score, [elevated]") , "//*[@name='ngroups'][.='3']" , "//*[@name='matches'][.='6']" , groups +"/lst[1]//doc[1]/float[@name='id'][.='7.0']" , groups +"/lst[1]//doc[1]/bool[@name='[elevated]'][.='true']" , groups +"/lst[1]//doc[2]/float[@name='id'][.='77.0']" , groups +"/lst[1]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[1]/float[@name='id'][.='22.0']" , groups +"/lst[2]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[2]//doc[2]/float[@name='id'][.='2.0']" , groups +"/lst[2]//doc[2]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[1]/float[@name='id'][.='66.0']" , groups +"/lst[3]//doc[1]/bool[@name='[elevated]'][.='false']" , groups +"/lst[3]//doc[2]/float[@name='id'][.='6.0']" , groups +"/lst[3]//doc[2]/bool[@name='[elevated]'][.='false']" ); } finally { delete(); } } @Test public void testTrieFieldType() throws Exception { try { init("schema.xml"); clearIndex(); assertU(commit()); assertU(adoc("id", "1", "text", "XXXX XXXX", "str_s", "a" )); assertU(adoc("id", "2", "text", "YYYY", "str_s", "b" )); assertU(adoc("id", "3", "text", "ZZZZ", "str_s", "c" )); assertU(adoc("id", "4", "text", "XXXX XXXX", "str_s", "x" )); assertU(adoc("id", "5", "text", "YYYY YYYY", "str_s", "y" )); assertU(adoc("id", "6", "text", "XXXX XXXX", "str_s", "z" )); assertU(adoc("id", "7", "text", "AAAA", "str_s", "a" )); assertU(adoc("id", "8", "text", "AAAA", "str_s", "a" )); assertU(adoc("id", "9", "text", "AAAA AAAA", "str_s", "a" )); assertU(commit()); assertQ("", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.FL, "id, score, [elevated]") ,"//*[@numFound='3']" ,"//result/doc[1]/int[@name='id'][.='7']" ,"//result/doc[2]/int[@name='id'][.='8']" ,"//result/doc[3]/int[@name='id'][.='9']", "//result/doc[1]/bool[@name='[elevated]'][.='true']", "//result/doc[2]/bool[@name='[elevated]'][.='false']", "//result/doc[3]/bool[@name='[elevated]'][.='false']" ); } finally{ delete(); } } @Test public void testInterface() throws Exception { try { init("schema12.xml"); SolrCore core = h.getCore(); NamedList<String> args = new NamedList<>(); args.add(QueryElevationComponent.FIELD_TYPE, "string"); args.add(QueryElevationComponent.CONFIG_FILE, "elevate.xml"); QueryElevationComponent comp = new QueryElevationComponent(); comp.init(args); comp.inform(core); SolrQueryRequest req = req(); IndexReader reader = req.getSearcher().getIndexReader(); Map<String, ElevationObj> map = comp.getElevationMap(reader, core); req.close(); // Make sure the boosts loaded properly assertEquals(7, map.size()); assertEquals(1, map.get("XXXX").priority.size()); assertEquals(2, map.get("YYYY").priority.size()); assertEquals(3, map.get("ZZZZ").priority.size()); assertEquals(null, map.get("xxxx")); assertEquals(null, map.get("yyyy")); assertEquals(null, map.get("zzzz")); // Now test the same thing with a lowercase filter: 'lowerfilt' args = new NamedList<>(); args.add(QueryElevationComponent.FIELD_TYPE, "lowerfilt"); args.add(QueryElevationComponent.CONFIG_FILE, "elevate.xml"); comp = new QueryElevationComponent(); comp.init(args); comp.inform(core); map = comp.getElevationMap(reader, core); assertEquals(7, map.size()); assertEquals(null, map.get("XXXX")); assertEquals(null, map.get("YYYY")); assertEquals(null, map.get("ZZZZ")); assertEquals(1, map.get("xxxx").priority.size()); assertEquals(2, map.get("yyyy").priority.size()); assertEquals(3, map.get("zzzz").priority.size()); assertEquals("xxxx", comp.getAnalyzedQuery("XXXX")); assertEquals("xxxxyyyy", comp.getAnalyzedQuery("XXXX YYYY")); assertQ("Make sure QEC handles null queries", req("qt", "/elevate", "q.alt", "*:*", "defType", "dismax"), "//*[@numFound='0']"); } finally { delete(); } } @Test public void testMarker() throws Exception { try { init("schema12.xml"); assertU(adoc("id", "1", "title", "XXXX XXXX", "str_s1", "a")); assertU(adoc("id", "2", "title", "YYYY", "str_s1", "b")); assertU(adoc("id", "3", "title", "ZZZZ", "str_s1", "c")); assertU(adoc("id", "4", "title", "XXXX XXXX", "str_s1", "x")); assertU(adoc("id", "5", "title", "YYYY YYYY", "str_s1", "y")); assertU(adoc("id", "6", "title", "XXXX XXXX", "str_s1", "z")); assertU(adoc("id", "7", "title", "AAAA", "str_s1", "a")); assertU(commit()); assertQ("", req(CommonParams.Q, "XXXX", CommonParams.QT, "/elevate", CommonParams.FL, "id, score, [elevated]") , "//*[@numFound='3']" , "//result/doc[1]/str[@name='id'][.='1']" , "//result/doc[2]/str[@name='id'][.='4']" , "//result/doc[3]/str[@name='id'][.='6']", "//result/doc[1]/bool[@name='[elevated]'][.='true']", "//result/doc[2]/bool[@name='[elevated]'][.='false']", "//result/doc[3]/bool[@name='[elevated]'][.='false']" ); assertQ("", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.FL, "id, score, [elevated]") ,"//*[@numFound='1']" ,"//result/doc[1]/str[@name='id'][.='7']", "//result/doc[1]/bool[@name='[elevated]'][.='true']" ); assertQ("", req(CommonParams.Q, "AAAA", CommonParams.QT, "/elevate", CommonParams.FL, "id, score, [elev]") , "//*[@numFound='1']" , "//result/doc[1]/str[@name='id'][.='7']", "not(//result/doc[1]/bool[@name='[elevated]'][.='false'])", "not(//result/doc[1]/bool[@name='[elev]'][.='false'])" // even though we asked for elev, there is no Transformer registered w/ that, so we shouldn't get a result ); } finally { delete(); } } @Test public void testMarkExcludes() throws Exception { try { init("schema12.xml"); assertU(adoc("id", "1", "title", "XXXX XXXX", "str_s1", "a")); assertU(adoc("id", "2", "title", "YYYY", "str_s1", "b")); assertU(adoc("id", "3", "title", "ZZZZ", "str_s1", "c")); assertU(adoc("id", "4", "title", "XXXX XXXX", "str_s1", "x")); assertU(adoc("id", "5", "title", "YYYY YYYY", "str_s1", "y")); assertU(adoc("id", "6", "title", "XXXX XXXX", "str_s1", "z")); assertU(adoc("id", "7", "title", "AAAA", "str_s1", "a")); assertU(adoc("id", "8", "title", "QQQQ", "str_s1", "q")); assertU(adoc("id", "9", "title", "QQQQ QQQQ", "str_s1", "r")); assertU(adoc("id", "10", "title", "QQQQ QQQQ QQQQ", "str_s1", "s")); assertU(commit()); assertQ("", req(CommonParams.Q, "XXXX XXXX", CommonParams.QT, "/elevate", QueryElevationParams.MARK_EXCLUDES, "true", "indent", "true", CommonParams.FL, "id, score, [excluded]") , "//*[@numFound='4']" , "//result/doc[1]/str[@name='id'][.='5']" , "//result/doc[2]/str[@name='id'][.='1']" , "//result/doc[3]/str[@name='id'][.='4']" , "//result/doc[4]/str[@name='id'][.='6']", "//result/doc[1]/bool[@name='[excluded]'][.='false']", "//result/doc[2]/bool[@name='[excluded]'][.='false']", "//result/doc[3]/bool[@name='[excluded]'][.='false']", "//result/doc[4]/bool[@name='[excluded]'][.='true']" ); //ask for excluded as a field, but don't actually request the MARK_EXCLUDES //thus, number 6 should not be returned, b/c it is excluded assertQ("", req(CommonParams.Q, "XXXX XXXX", CommonParams.QT, "/elevate", QueryElevationParams.MARK_EXCLUDES, "false", CommonParams.FL, "id, score, [excluded]") , "//*[@numFound='3']" , "//result/doc[1]/str[@name='id'][.='5']" , "//result/doc[2]/str[@name='id'][.='1']" , "//result/doc[3]/str[@name='id'][.='4']", "//result/doc[1]/bool[@name='[excluded]'][.='false']", "//result/doc[2]/bool[@name='[excluded]'][.='false']", "//result/doc[3]/bool[@name='[excluded]'][.='false']" ); // test that excluded results are on the same positions in the result list // as when elevation component is disabled // (i.e. test that elevation component with MARK_EXCLUDES does not boost // excluded results) assertQ("", req(CommonParams.Q, "QQQQ", CommonParams.QT, "/elevate", QueryElevationParams.ENABLE, "false", "indent", "true", CommonParams.FL, "id, score") , "//*[@numFound='3']" , "//result/doc[1]/str[@name='id'][.='8']" , "//result/doc[2]/str[@name='id'][.='9']" , "//result/doc[3]/str[@name='id'][.='10']" ); assertQ("", req(CommonParams.Q, "QQQQ", CommonParams.QT, "/elevate", QueryElevationParams.MARK_EXCLUDES, "true", "indent", "true", CommonParams.FL, "id, score, [excluded]") , "//*[@numFound='3']" , "//result/doc[1]/str[@name='id'][.='8']" , "//result/doc[2]/str[@name='id'][.='9']" , "//result/doc[3]/str[@name='id'][.='10']", "//result/doc[1]/bool[@name='[excluded]'][.='false']", "//result/doc[2]/bool[@name='[excluded]'][.='false']", "//result/doc[3]/bool[@name='[excluded]'][.='true']" ); } finally { delete(); } } @Test public void testSorting() throws Exception { try { init("schema12.xml"); assertU(adoc("id", "a", "title", "ipod", "str_s1", "a")); assertU(adoc("id", "b", "title", "ipod ipod", "str_s1", "b")); assertU(adoc("id", "c", "title", "ipod ipod ipod", "str_s1", "c")); assertU(adoc("id", "x", "title", "boosted", "str_s1", "x")); assertU(adoc("id", "y", "title", "boosted boosted", "str_s1", "y")); assertU(adoc("id", "z", "title", "boosted boosted boosted", "str_s1", "z")); assertU(commit()); String query = "title:ipod"; Map<String, String> args = new HashMap<>(); args.put(CommonParams.Q, query); args.put(CommonParams.QT, "/elevate"); args.put(CommonParams.FL, "id,score"); args.put("indent", "true"); //args.put( CommonParams.FL, "id,title,score" ); SolrQueryRequest req = new LocalSolrQueryRequest(h.getCore(), new MapSolrParams(args)); IndexReader reader = req.getSearcher().getIndexReader(); QueryElevationComponent booster = (QueryElevationComponent) req.getCore().getSearchComponent("elevate"); assertQ("Make sure standard sort works as expected", req , "//*[@numFound='3']" , "//result/doc[1]/str[@name='id'][.='a']" , "//result/doc[2]/str[@name='id'][.='b']" , "//result/doc[3]/str[@name='id'][.='c']" ); // Explicitly set what gets boosted booster.elevationCache.clear(); booster.setTopQueryResults(reader, query, new String[]{"x", "y", "z"}, null); assertQ("All six should make it", req , "//*[@numFound='6']" , "//result/doc[1]/str[@name='id'][.='x']" , "//result/doc[2]/str[@name='id'][.='y']" , "//result/doc[3]/str[@name='id'][.='z']" , "//result/doc[4]/str[@name='id'][.='a']" , "//result/doc[5]/str[@name='id'][.='b']" , "//result/doc[6]/str[@name='id'][.='c']" ); booster.elevationCache.clear(); // now switch the order: booster.setTopQueryResults(reader, query, new String[]{"a", "x"}, null); assertQ("All four should make it", req , "//*[@numFound='4']" , "//result/doc[1]/str[@name='id'][.='a']" , "//result/doc[2]/str[@name='id'][.='x']" , "//result/doc[3]/str[@name='id'][.='b']" , "//result/doc[4]/str[@name='id'][.='c']" ); // Test reverse sort args.put(CommonParams.SORT, "score asc"); assertQ("All four should make it", req , "//*[@numFound='4']" , "//result/doc[4]/str[@name='id'][.='a']" , "//result/doc[3]/str[@name='id'][.='x']" , "//result/doc[2]/str[@name='id'][.='b']" , "//result/doc[1]/str[@name='id'][.='c']" ); // Try normal sort by 'id' // default 'forceBoost' should be false assertEquals(false, booster.forceElevation); args.put(CommonParams.SORT, "str_s1 asc"); assertQ(null, req , "//*[@numFound='4']" , "//result/doc[1]/str[@name='id'][.='a']" , "//result/doc[2]/str[@name='id'][.='b']" , "//result/doc[3]/str[@name='id'][.='c']" , "//result/doc[4]/str[@name='id'][.='x']" ); args.put(CommonParams.SORT, "id asc"); assertQ(null, req , "//*[@numFound='4']" , "//result/doc[1]/str[@name='id'][.='a']" , "//result/doc[2]/str[@name='id'][.='b']" , "//result/doc[3]/str[@name='id'][.='c']" , "//result/doc[4]/str[@name='id'][.='x']" ); booster.forceElevation = true; args.put(CommonParams.SORT, "id asc"); assertQ(null, req , "//*[@numFound='4']" , "//result/doc[1]/str[@name='id'][.='a']" , "//result/doc[2]/str[@name='id'][.='x']" , "//result/doc[3]/str[@name='id'][.='b']" , "//result/doc[4]/str[@name='id'][.='c']" ); //Test exclusive (not to be confused with exclusion) args.put(QueryElevationParams.EXCLUSIVE, "true"); booster.setTopQueryResults(reader, query, new String[]{"x", "a"}, new String[]{}); assertQ(null, req , "//*[@numFound='2']" , "//result/doc[1]/str[@name='id'][.='x']" , "//result/doc[2]/str[@name='id'][.='a']" ); // Test exclusion booster.elevationCache.clear(); args.remove(CommonParams.SORT); args.remove(QueryElevationParams.EXCLUSIVE); booster.setTopQueryResults(reader, query, new String[]{"x"}, new String[]{"a"}); assertQ(null, req , "//*[@numFound='3']" , "//result/doc[1]/str[@name='id'][.='x']" , "//result/doc[2]/str[@name='id'][.='b']" , "//result/doc[3]/str[@name='id'][.='c']" ); // Test setting ids and excludes from http parameters booster.elevationCache.clear(); args.put(QueryElevationParams.IDS, "x,y,z"); args.put(QueryElevationParams.EXCLUDE, "b"); assertQ("All five should make it", req , "//*[@numFound='5']" , "//result/doc[1]/str[@name='id'][.='x']" , "//result/doc[2]/str[@name='id'][.='y']" , "//result/doc[3]/str[@name='id'][.='z']" , "//result/doc[4]/str[@name='id'][.='a']" , "//result/doc[5]/str[@name='id'][.='c']" ); args.put(QueryElevationParams.IDS, "x,z,y"); args.put(QueryElevationParams.EXCLUDE, "b,c"); assertQ("All four should make it", req , "//*[@numFound='4']" , "//result/doc[1]/str[@name='id'][.='x']" , "//result/doc[2]/str[@name='id'][.='z']" , "//result/doc[3]/str[@name='id'][.='y']" , "//result/doc[4]/str[@name='id'][.='a']" ); req.close(); } finally { delete(); } } // write a test file to boost some docs private void writeFile(File file, String query, String... ids) throws Exception { PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), IOUtils.CHARSET_UTF_8)); out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); out.println("<elevate>"); out.println("<query text=\"" + query + "\">"); for (String id : ids) { out.println(" <doc id=\"" + id + "\"/>"); } out.println("</query>"); out.println("</elevate>"); out.flush(); out.close(); log.info("OUT:" + file.getAbsolutePath()); } @Test public void testElevationReloading() throws Exception { try { init("schema12.xml"); String testfile = "data-elevation.xml"; File f = new File(h.getCore().getDataDir(), testfile); writeFile(f, "aaa", "A"); QueryElevationComponent comp = (QueryElevationComponent) h.getCore().getSearchComponent("elevate"); NamedList<String> args = new NamedList<>(); args.add(QueryElevationComponent.CONFIG_FILE, testfile); comp.init(args); comp.inform(h.getCore()); SolrQueryRequest req = req(); IndexReader reader = req.getSearcher().getIndexReader(); Map<String, ElevationObj> map = comp.getElevationMap(reader, h.getCore()); assertTrue(map.get("aaa").priority.containsKey(new BytesRef("A"))); assertNull(map.get("bbb")); req.close(); // now change the file writeFile(f, "bbb", "B"); assertU(adoc("id", "10000")); // will get same reader if no index change assertU(commit()); req = req(); reader = req.getSearcher().getIndexReader(); map = comp.getElevationMap(reader, h.getCore()); assertNull(map.get("aaa")); assertTrue(map.get("bbb").priority.containsKey(new BytesRef("B"))); req.close(); } finally { delete(); } } }