001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.util;
017:
018: import org.apache.solr.search.SolrQueryParser;
019: import org.apache.solr.util.NamedList;
020: import org.apache.solr.util.SolrPluginUtils;
021: import org.apache.solr.util.SolrPluginUtils.DisjunctionMaxQueryParser;
022:
023: import org.apache.lucene.index.Term;
024: import org.apache.lucene.search.Query;
025: import org.apache.lucene.search.TermQuery;
026: import org.apache.lucene.search.PhraseQuery;
027: import org.apache.lucene.search.DisjunctionMaxQuery;
028: import org.apache.lucene.search.BooleanQuery;
029: import org.apache.lucene.search.BooleanClause;
030: import org.apache.lucene.search.BooleanClause.Occur;
031:
032: import org.xmlpull.v1.XmlPullParserFactory;
033:
034: import junit.framework.Test;
035: import junit.framework.TestCase;
036: import junit.framework.TestSuite;
037:
038: import java.io.File;
039: import java.math.BigDecimal;
040: import java.util.Random;
041: import java.util.Date;
042: import java.util.List;
043: import java.util.Arrays;
044: import java.util.Map;
045: import java.util.HashMap;
046: import java.util.Iterator;
047:
048: /**
049: * Tests that the functions in SolrPluginUtils work as advertised.
050: */
051: public class SolrPluginUtilsTest extends AbstractSolrTestCase {
052:
053: public String getSchemaFile() {
054: return "schema.xml";
055: }
056:
057: public String getSolrConfigFile() {
058: return "solrconfig.xml";
059: }
060:
061: public void testPartialEscape() {
062:
063: assertEquals("", pe(""));
064: assertEquals("foo", pe("foo"));
065: assertEquals("foo\\:bar", pe("foo:bar"));
066: assertEquals("+foo\\:bar", pe("+foo:bar"));
067: assertEquals("foo \\! bar", pe("foo ! bar"));
068: assertEquals("foo\\?", pe("foo?"));
069: assertEquals("foo \"bar\"", pe("foo \"bar\""));
070: assertEquals("foo\\! \"bar\"", pe("foo! \"bar\""));
071:
072: }
073:
074: public void testStripUnbalancedQuotes() {
075:
076: assertEquals("", strip(""));
077: assertEquals("foo", strip("foo"));
078: assertEquals("foo \"bar\"", strip("foo \"bar\""));
079: assertEquals("42", strip("42\""));
080: assertEquals("\"how now brown cow?\"",
081: strip("\"how now brown cow?\""));
082: assertEquals("\"you go\" \"now!\"",
083: strip("\"you go\" \"now!\""));
084:
085: }
086:
087: public void testParseFieldBoosts() throws Exception {
088:
089: Map<String, Float> e1 = new HashMap<String, Float>();
090: e1.put("fieldOne", 2.3f);
091: e1.put("fieldTwo", null);
092: e1.put("fieldThree", -0.4f);
093:
094: assertEquals(
095: "basic e1",
096: e1,
097: SolrPluginUtils
098: .parseFieldBoosts("fieldOne^2.3 fieldTwo fieldThree^-0.4"));
099: assertEquals(
100: "spacey e1",
101: e1,
102: SolrPluginUtils
103: .parseFieldBoosts(" fieldOne^2.3 fieldTwo fieldThree^-0.4 "));
104: assertEquals(
105: "really spacey e1",
106: e1,
107: SolrPluginUtils
108: .parseFieldBoosts(" \t fieldOne^2.3 \n fieldTwo fieldThree^-0.4 "));
109: assertEquals("really spacey e1", e1, SolrPluginUtils
110: .parseFieldBoosts(new String[] { " \t fieldOne^2.3 \n",
111: " fieldTwo fieldThree^-0.4 ", " " }));
112:
113: Map<String, Float> e2 = new HashMap<String, Float>();
114: assertEquals("empty e2", e2, SolrPluginUtils
115: .parseFieldBoosts(""));
116: assertEquals("spacey e2", e2, SolrPluginUtils
117: .parseFieldBoosts(" \t "));
118: }
119:
120: public void testDisjunctionMaxQueryParser() throws Exception {
121:
122: Query out;
123: String t;
124:
125: DisjunctionMaxQueryParser qp = new SolrPluginUtils.DisjunctionMaxQueryParser(
126: h.getCore().getSchema());
127:
128: qp
129: .addAlias(
130: "hoss",
131: 0.01f,
132: SolrPluginUtils
133: .parseFieldBoosts("title^2.0 title_stemmed name^1.2 subject^0.5"));
134: qp.addAlias("test", 0.01f, SolrPluginUtils
135: .parseFieldBoosts("text^2.0"));
136: qp.addAlias("unused", 1.0f, SolrPluginUtils
137: .parseFieldBoosts("subject^0.5 sind^1.5"));
138:
139: /* first some sanity tests that don't use aliasing at all */
140:
141: t = "XXXXXXXX";
142: out = qp.parse(t);
143: assertNotNull(t + " sanity test gave back null", out);
144: assertTrue(t + " sanity test isn't TermQuery: "
145: + out.getClass(), out instanceof TermQuery);
146: assertEquals(t + " sanity test is wrong field", h.getCore()
147: .getSchema().getDefaultSearchFieldName(),
148: ((TermQuery) out).getTerm().field());
149:
150: t = "subject:XXXXXXXX";
151: out = qp.parse(t);
152: assertNotNull(t + " sanity test gave back null", out);
153: assertTrue(t + " sanity test isn't TermQuery: "
154: + out.getClass(), out instanceof TermQuery);
155: assertEquals(t + " sanity test is wrong field", "subject",
156: ((TermQuery) out).getTerm().field());
157:
158: /* field has untokenzied type, so this should be a term anyway */
159: t = "sind:\"simple phrase\"";
160: out = qp.parse(t);
161: assertNotNull(t + " sanity test gave back null", out);
162: assertTrue(t + " sanity test isn't TermQuery: "
163: + out.getClass(), out instanceof TermQuery);
164: assertEquals(t + " sanity test is wrong field", "sind",
165: ((TermQuery) out).getTerm().field());
166:
167: t = "subject:\"simple phrase\"";
168: out = qp.parse(t);
169: assertNotNull(t + " sanity test gave back null", out);
170: assertTrue(t + " sanity test isn't PhraseQuery: "
171: + out.getClass(), out instanceof PhraseQuery);
172: assertEquals(t + " sanity test is wrong field", "subject",
173: ((PhraseQuery) out).getTerms()[0].field());
174:
175: /* now some tests that use aliasing */
176:
177: /* basic usage of single "term" */
178: t = "hoss:XXXXXXXX";
179: out = qp.parse(t);
180: assertNotNull(t + " was null", out);
181: assertTrue(t + " wasn't a DMQ:" + out.getClass(),
182: out instanceof DisjunctionMaxQuery);
183: assertEquals(t + " wrong number of clauses", 4,
184: countItems(((DisjunctionMaxQuery) out).iterator()));
185:
186: /* odd case, but should still work, DMQ of one clause */
187: t = "test:YYYYY";
188: out = qp.parse(t);
189: assertNotNull(t + " was null", out);
190: assertTrue(t + " wasn't a DMQ:" + out.getClass(),
191: out instanceof DisjunctionMaxQuery);
192: assertEquals(t + " wrong number of clauses", 1,
193: countItems(((DisjunctionMaxQuery) out).iterator()));
194:
195: /* basic usage of multiple "terms" */
196: t = "hoss:XXXXXXXX test:YYYYY";
197: out = qp.parse(t);
198: assertNotNull(t + " was null", out);
199: assertTrue(t + " wasn't a boolean:" + out.getClass(),
200: out instanceof BooleanQuery);
201: {
202: BooleanQuery bq = (BooleanQuery) out;
203: List<BooleanClause> clauses = bq.clauses();
204: assertEquals(t + " wrong number of clauses", 2, clauses
205: .size());
206: Query sub = clauses.get(0).getQuery();
207: assertTrue(t + " first wasn't a DMQ:" + sub.getClass(),
208: sub instanceof DisjunctionMaxQuery);
209: assertEquals(t + " first had wrong number of clauses", 4,
210: countItems(((DisjunctionMaxQuery) sub).iterator()));
211: sub = clauses.get(1).getQuery();
212: assertTrue(t + " second wasn't a DMQ:" + sub.getClass(),
213: sub instanceof DisjunctionMaxQuery);
214: assertEquals(t + " second had wrong number of clauses", 1,
215: countItems(((DisjunctionMaxQuery) sub).iterator()));
216: }
217:
218: /* a phrase, and a term that is a stop word for some fields */
219: t = "hoss:\"XXXXXX YYYYY\" hoss:the";
220: out = qp.parse(t);
221: assertNotNull(t + " was null", out);
222: assertTrue(t + " wasn't a boolean:" + out.getClass(),
223: out instanceof BooleanQuery);
224: {
225: BooleanQuery bq = (BooleanQuery) out;
226: List<BooleanClause> clauses = bq.clauses();
227: assertEquals(t + " wrong number of clauses", 2, clauses
228: .size());
229: Query sub = clauses.get(0).getQuery();
230: assertTrue(t + " first wasn't a DMQ:" + sub.getClass(),
231: sub instanceof DisjunctionMaxQuery);
232: assertEquals(t + " first had wrong number of clauses", 4,
233: countItems(((DisjunctionMaxQuery) sub).iterator()));
234: sub = clauses.get(1).getQuery();
235: assertTrue(t + " second wasn't a DMQ:" + sub.getClass(),
236: sub instanceof DisjunctionMaxQuery);
237: assertEquals(
238: t
239: + " second had wrong number of clauses (stop words)",
240: 2, countItems(((DisjunctionMaxQuery) sub)
241: .iterator()));
242: }
243:
244: }
245:
246: private static int countItems(Iterator i) {
247: int count = 0;
248: while (i.hasNext()) {
249: count++;
250: i.next();
251: }
252: return count;
253: }
254:
255: public void testMinShouldMatchCalculator() {
256:
257: /* zero is zero is zero */
258: assertEquals(0, calcMSM(5, "0"));
259: assertEquals(0, calcMSM(5, "0%"));
260: assertEquals(0, calcMSM(5, "-5"));
261: assertEquals(0, calcMSM(5, "-100%"));
262:
263: /* basic integers */
264: assertEquals(3, calcMSM(5, "3"));
265: assertEquals(2, calcMSM(5, "-3"));
266: assertEquals(3, calcMSM(3, "3"));
267: assertEquals(0, calcMSM(3, "-3"));
268: assertEquals(3, calcMSM(3, "5"));
269: assertEquals(0, calcMSM(3, "-5"));
270:
271: /* positive percentages with rounding */
272: assertEquals(0, calcMSM(3, "25%"));
273: assertEquals(1, calcMSM(4, "25%"));
274: assertEquals(1, calcMSM(5, "25%"));
275: assertEquals(2, calcMSM(10, "25%"));
276:
277: /* negative percentages with rounding */
278: assertEquals(3, calcMSM(3, "-25%"));
279: assertEquals(3, calcMSM(4, "-25%"));
280: assertEquals(4, calcMSM(5, "-25%"));
281: assertEquals(8, calcMSM(10, "-25%"));
282:
283: /* conditional */
284: assertEquals(1, calcMSM(1, "3<0"));
285: assertEquals(2, calcMSM(2, "3<0"));
286: assertEquals(3, calcMSM(3, "3<0"));
287: assertEquals(0, calcMSM(4, "3<0"));
288: assertEquals(0, calcMSM(5, "3<0"));
289: assertEquals(1, calcMSM(1, "3<25%"));
290: assertEquals(2, calcMSM(2, "3<25%"));
291: assertEquals(3, calcMSM(3, "3<25%"));
292: assertEquals(1, calcMSM(4, "3<25%"));
293: assertEquals(1, calcMSM(5, "3<25%"));
294:
295: /* multiple conditionals */
296: assertEquals(1, calcMSM(1, "3<-25% 10<-3"));
297: assertEquals(2, calcMSM(2, "3<-25% 10<-3"));
298: assertEquals(3, calcMSM(3, "3<-25% 10<-3"));
299: assertEquals(3, calcMSM(4, "3<-25% 10<-3"));
300: assertEquals(4, calcMSM(5, "3<-25% 10<-3"));
301: assertEquals(5, calcMSM(6, "3<-25% 10<-3"));
302: assertEquals(6, calcMSM(7, "3<-25% 10<-3"));
303: assertEquals(6, calcMSM(8, "3<-25% 10<-3"));
304: assertEquals(7, calcMSM(9, "3<-25% 10<-3"));
305: assertEquals(8, calcMSM(10, "3<-25% 10<-3"));
306: assertEquals(8, calcMSM(11, "3<-25% 10<-3"));
307: assertEquals(9, calcMSM(12, "3<-25% 10<-3"));
308: assertEquals(97, calcMSM(100, "3<-25% 10<-3"));
309:
310: BooleanQuery q = new BooleanQuery();
311: q.add(new TermQuery(new Term("a", "b")), Occur.SHOULD);
312: q.add(new TermQuery(new Term("a", "c")), Occur.SHOULD);
313: q.add(new TermQuery(new Term("a", "d")), Occur.SHOULD);
314: q.add(new TermQuery(new Term("a", "d")), Occur.SHOULD);
315:
316: SolrPluginUtils.setMinShouldMatch(q, "0");
317: assertEquals(0, q.getMinimumNumberShouldMatch());
318:
319: SolrPluginUtils.setMinShouldMatch(q, "1");
320: assertEquals(1, q.getMinimumNumberShouldMatch());
321:
322: SolrPluginUtils.setMinShouldMatch(q, "50%");
323: assertEquals(2, q.getMinimumNumberShouldMatch());
324:
325: SolrPluginUtils.setMinShouldMatch(q, "99");
326: assertEquals(4, q.getMinimumNumberShouldMatch());
327:
328: q.add(new TermQuery(new Term("a", "e")), Occur.MUST);
329: q.add(new TermQuery(new Term("a", "f")), Occur.MUST);
330:
331: SolrPluginUtils.setMinShouldMatch(q, "50%");
332: assertEquals(2, q.getMinimumNumberShouldMatch());
333:
334: }
335:
336: /** macro */
337: public String pe(CharSequence s) {
338: return SolrPluginUtils.partialEscape(s).toString();
339: }
340:
341: /** macro */
342: public String strip(CharSequence s) {
343: return SolrPluginUtils.stripUnbalancedQuotes(s).toString();
344: }
345:
346: /** macro */
347: public int calcMSM(int clauses, String spec) {
348: return SolrPluginUtils.calculateMinShouldMatch(clauses, spec);
349: }
350: }
|