/** * 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.util; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.SolrPluginUtils.DisjunctionMaxQueryParser; import org.apache.solr.core.SolrCore; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.DocList; import org.apache.solr.search.DocSlice; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.common.util.*; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.lucene.index.Term; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.BooleanClause.Occur; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.HashSet; /** * Tests that the functions in SolrPluginUtils work as advertised. */ public class SolrPluginUtilsTest extends SolrTestCaseJ4 { @BeforeClass public static void beforeClass() throws Exception { initCore("solrconfig.xml","schema.xml"); } @Test public void testDocListConversion() throws Exception { assertU("", adoc("id", "3234", "val_t", "quick red fox")); assertU("", adoc("id", "3235", "val_t", "quick green fox")); assertU("", adoc("id", "3236", "val_t", "quick brown fox")); commit(); SolrIndexSearcher srchr = h.getCore().getSearcher().get(); SolrIndexSearcher.QueryResult qr = new SolrIndexSearcher.QueryResult(); SolrIndexSearcher.QueryCommand cmd = new SolrIndexSearcher.QueryCommand(); cmd.setQuery(new MatchAllDocsQuery()); qr = srchr.search(qr, cmd); DocList docs = qr.getDocList(); Set<String> fields = new HashSet<String>(); fields.add("val_t"); SolrDocumentList list = SolrPluginUtils.docListToSolrDocumentList(docs, srchr, fields, null); assertTrue("list Size: " + list.size() + " is not: " + docs.size(), list.size() == docs.size()); for (SolrDocument document : list) { assertNotNull(document.get("val_t")); } } @Test public void testPartialEscape() { assertEquals("",pe("")); assertEquals("foo",pe("foo")); assertEquals("foo\\:bar",pe("foo:bar")); assertEquals("+foo\\:bar",pe("+foo:bar")); assertEquals("foo \\! bar",pe("foo ! bar")); assertEquals("foo\\?",pe("foo?")); assertEquals("foo \"bar\"",pe("foo \"bar\"")); assertEquals("foo\\! \"bar\"",pe("foo! \"bar\"")); } @Test public void testStripUnbalancedQuotes() { assertEquals("",strip("")); assertEquals("foo",strip("foo")); assertEquals("foo \"bar\"",strip("foo \"bar\"")); assertEquals("42",strip("42\"")); assertEquals("\"how now brown cow?\"",strip("\"how now brown cow?\"")); assertEquals("\"you go\" \"now!\"",strip("\"you go\" \"now!\"")); } @Test public void testStripIllegalOperators() { assertEquals("",stripOp("")); assertEquals("foo",stripOp("foo")); assertEquals("foo -bar",stripOp("foo -bar")); assertEquals("foo +bar",stripOp("foo +bar")); assertEquals("foo + bar",stripOp("foo + bar")); assertEquals("foo+ bar",stripOp("foo+ bar")); assertEquals("foo+ bar",stripOp("foo+ bar")); assertEquals("foo+",stripOp("foo+")); assertEquals("foo bar",stripOp("foo bar -")); assertEquals("foo bar ",stripOp("foo bar - + ++")); assertEquals("foo bar",stripOp("foo --bar")); assertEquals("foo bar ",stripOp("foo -------------------------------------------------------------------------------------------------------------------------bar --")); assertEquals("foo bar ",stripOp("foo --bar -----------------------------------------------------------------------------------------------------------------------")); } @Test public void testParseFieldBoosts() throws Exception { Map<String,Float> e1 = new HashMap<String,Float>(); e1.put("fieldOne",2.3f); e1.put("fieldTwo",null); e1.put("fieldThree",-0.4f); assertEquals("basic e1", e1, SolrPluginUtils.parseFieldBoosts ("fieldOne^2.3 fieldTwo fieldThree^-0.4")); assertEquals("spacey e1", e1, SolrPluginUtils.parseFieldBoosts (" fieldOne^2.3 fieldTwo fieldThree^-0.4 ")); assertEquals("really spacey e1", e1, SolrPluginUtils.parseFieldBoosts (" \t fieldOne^2.3 \n fieldTwo fieldThree^-0.4 ")); assertEquals("really spacey e1", e1, SolrPluginUtils.parseFieldBoosts (new String[]{" \t fieldOne^2.3 \n", " fieldTwo fieldThree^-0.4 ", " "})); Map<String,Float> e2 = new HashMap<String,Float>(); assertEquals("empty e2", e2, SolrPluginUtils.parseFieldBoosts ("")); assertEquals("spacey e2", e2, SolrPluginUtils.parseFieldBoosts (" \t ")); } @Test public void testDisjunctionMaxQueryParser() throws Exception { Query out; String t; DisjunctionMaxQueryParser qp = new SolrPluginUtils.DisjunctionMaxQueryParser(h.getCore().getSchema()); qp.addAlias("hoss", 0.01f, SolrPluginUtils.parseFieldBoosts ("title^2.0 title_stemmed name^1.2 subject^0.5")); qp.addAlias("test", 0.01f, SolrPluginUtils.parseFieldBoosts("text^2.0")); qp.addAlias("unused", 1.0f, SolrPluginUtils.parseFieldBoosts ("subject^0.5 sind^1.5")); /* first some sanity tests that don't use aliasing at all */ t = "XXXXXXXX"; out = qp.parse(t); assertNotNull(t+" sanity test gave back null", out); assertTrue(t+" sanity test isn't TermQuery: " + out.getClass(), out instanceof TermQuery); assertEquals(t+" sanity test is wrong field", h.getCore().getSchema().getDefaultSearchFieldName(), ((TermQuery)out).getTerm().field()); t = "subject:XXXXXXXX"; out = qp.parse(t); assertNotNull(t+" sanity test gave back null", out); assertTrue(t+" sanity test isn't TermQuery: " + out.getClass(), out instanceof TermQuery); assertEquals(t+" sanity test is wrong field", "subject", ((TermQuery)out).getTerm().field()); /* field has untokenzied type, so this should be a term anyway */ t = "sind:\"simple phrase\""; out = qp.parse(t); assertNotNull(t+" sanity test gave back null", out); assertTrue(t+" sanity test isn't TermQuery: " + out.getClass(), out instanceof TermQuery); assertEquals(t+" sanity test is wrong field", "sind", ((TermQuery)out).getTerm().field()); t = "subject:\"simple phrase\""; out = qp.parse(t); assertNotNull(t+" sanity test gave back null", out); assertTrue(t+" sanity test isn't PhraseQuery: " + out.getClass(), out instanceof PhraseQuery); assertEquals(t+" sanity test is wrong field", "subject", ((PhraseQuery)out).getTerms()[0].field()); /* now some tests that use aliasing */ /* basic usage of single "term" */ t = "hoss:XXXXXXXX"; out = qp.parse(t); assertNotNull(t+" was null", out); assertTrue(t+" wasn't a DMQ:" + out.getClass(), out instanceof DisjunctionMaxQuery); assertEquals(t+" wrong number of clauses", 4, countItems(((DisjunctionMaxQuery)out).iterator())); /* odd case, but should still work, DMQ of one clause */ t = "test:YYYYY"; out = qp.parse(t); assertNotNull(t+" was null", out); assertTrue(t+" wasn't a DMQ:" + out.getClass(), out instanceof DisjunctionMaxQuery); assertEquals(t+" wrong number of clauses", 1, countItems(((DisjunctionMaxQuery)out).iterator())); /* basic usage of multiple "terms" */ t = "hoss:XXXXXXXX test:YYYYY"; out = qp.parse(t); assertNotNull(t+" was null", out); assertTrue(t+" wasn't a boolean:" + out.getClass(), out instanceof BooleanQuery); { BooleanQuery bq = (BooleanQuery)out; List<BooleanClause> clauses = bq.clauses(); assertEquals(t+" wrong number of clauses", 2, clauses.size()); Query sub = clauses.get(0).getQuery(); assertTrue(t+" first wasn't a DMQ:" + sub.getClass(), sub instanceof DisjunctionMaxQuery); assertEquals(t+" first had wrong number of clauses", 4, countItems(((DisjunctionMaxQuery)sub).iterator())); sub = clauses.get(1).getQuery(); assertTrue(t+" second wasn't a DMQ:" + sub.getClass(), sub instanceof DisjunctionMaxQuery); assertEquals(t+" second had wrong number of clauses", 1, countItems(((DisjunctionMaxQuery)sub).iterator())); } /* a phrase, and a term that is a stop word for some fields */ t = "hoss:\"XXXXXX YYYYY\" hoss:the"; out = qp.parse(t); assertNotNull(t+" was null", out); assertTrue(t+" wasn't a boolean:" + out.getClass(), out instanceof BooleanQuery); { BooleanQuery bq = (BooleanQuery)out; List<BooleanClause> clauses = bq.clauses(); assertEquals(t+" wrong number of clauses", 2, clauses.size()); Query sub = clauses.get(0).getQuery(); assertTrue(t+" first wasn't a DMQ:" + sub.getClass(), sub instanceof DisjunctionMaxQuery); assertEquals(t+" first had wrong number of clauses", 4, countItems(((DisjunctionMaxQuery)sub).iterator())); sub = clauses.get(1).getQuery(); assertTrue(t+" second wasn't a DMQ:" + sub.getClass(), sub instanceof DisjunctionMaxQuery); assertEquals(t+" second had wrong number of clauses (stop words)", 2, countItems(((DisjunctionMaxQuery)sub).iterator())); } } private static int countItems(Iterator i) { int count = 0; while (i.hasNext()) { count++; i.next(); } return count; } @Test public void testMinShouldMatchCalculator() { /* zero is zero is zero */ assertEquals(0, calcMSM(5, "0")); assertEquals(0, calcMSM(5, "0%")); assertEquals(0, calcMSM(5, "-5")); assertEquals(0, calcMSM(5, "-100%")); /* basic integers */ assertEquals(3, calcMSM(5, "3")); assertEquals(2, calcMSM(5, "-3")); assertEquals(3, calcMSM(3, "3")); assertEquals(0, calcMSM(3, "-3")); assertEquals(3, calcMSM(3, "5")); assertEquals(0, calcMSM(3, "-5")); /* positive percentages with rounding */ assertEquals(0, calcMSM(3, "25%")); assertEquals(1, calcMSM(4, "25%")); assertEquals(1, calcMSM(5, "25%")); assertEquals(2, calcMSM(10, "25%")); /* negative percentages with rounding */ assertEquals(3, calcMSM(3, "-25%")); assertEquals(3, calcMSM(4, "-25%")); assertEquals(4, calcMSM(5, "-25%")); assertEquals(8, calcMSM(10, "-25%")); /* conditional */ assertEquals(1, calcMSM(1, "3<0")); assertEquals(2, calcMSM(2, "3<0")); assertEquals(3, calcMSM(3, "3<0")); assertEquals(0, calcMSM(4, "3<0")); assertEquals(0, calcMSM(5, "3<0")); assertEquals(1, calcMSM(1, "3<25%")); assertEquals(2, calcMSM(2, "3<25%")); assertEquals(3, calcMSM(3, "3<25%")); assertEquals(1, calcMSM(4, "3<25%")); assertEquals(1, calcMSM(5, "3<25%")); /* multiple conditionals */ assertEquals(1, calcMSM(1, "3<-25% 10<-3")); assertEquals(2, calcMSM(2, "3<-25% 10<-3")); assertEquals(3, calcMSM(3, "3<-25% 10<-3")); assertEquals(3, calcMSM(4, "3<-25% 10<-3")); assertEquals(4, calcMSM(5, "3<-25% 10<-3")); assertEquals(5, calcMSM(6, "3<-25% 10<-3")); assertEquals(6, calcMSM(7, "3<-25% 10<-3")); assertEquals(6, calcMSM(8, "3<-25% 10<-3")); assertEquals(7, calcMSM(9, "3<-25% 10<-3")); assertEquals(8, calcMSM(10, "3<-25% 10<-3")); assertEquals(8, calcMSM(11, "3<-25% 10<-3")); assertEquals(9, calcMSM(12, "3<-25% 10<-3")); assertEquals(97, calcMSM(100, "3<-25% 10<-3")); BooleanQuery q = new BooleanQuery(); q.add(new TermQuery(new Term("a","b")), Occur.SHOULD); q.add(new TermQuery(new Term("a","c")), Occur.SHOULD); q.add(new TermQuery(new Term("a","d")), Occur.SHOULD); q.add(new TermQuery(new Term("a","d")), Occur.SHOULD); SolrPluginUtils.setMinShouldMatch(q, "0"); assertEquals(0, q.getMinimumNumberShouldMatch()); SolrPluginUtils.setMinShouldMatch(q, "1"); assertEquals(1, q.getMinimumNumberShouldMatch()); SolrPluginUtils.setMinShouldMatch(q, "50%"); assertEquals(2, q.getMinimumNumberShouldMatch()); SolrPluginUtils.setMinShouldMatch(q, "99"); assertEquals(4, q.getMinimumNumberShouldMatch()); q.add(new TermQuery(new Term("a","e")), Occur.MUST); q.add(new TermQuery(new Term("a","f")), Occur.MUST); SolrPluginUtils.setMinShouldMatch(q, "50%"); assertEquals(2, q.getMinimumNumberShouldMatch()); } /** macro */ public String pe(CharSequence s) { return SolrPluginUtils.partialEscape(s).toString(); } /** macro */ public String strip(CharSequence s) { return SolrPluginUtils.stripUnbalancedQuotes(s).toString(); } /** macro */ public String stripOp(CharSequence s) { return SolrPluginUtils.stripIllegalOperators(s).toString(); } /** macro */ public int calcMSM(int clauses, String spec) { return SolrPluginUtils.calculateMinShouldMatch(clauses, spec); } }