Source Code Cross Referenced for SolrPluginUtils.java in  » Search-Engine » apache-solr-1.2.0 » org » apache » solr » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Search Engine » apache solr 1.2.0 » org.apache.solr.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.lucene.analysis.Analyzer;
019:        import org.apache.lucene.document.Document;
020:        import org.apache.lucene.queryParser.ParseException;
021:        import org.apache.lucene.queryParser.QueryParser;
022:        import org.apache.lucene.search.*;
023:        import org.apache.lucene.search.BooleanClause.Occur;
024:        import org.apache.solr.core.SolrCore;
025:        import org.apache.solr.core.SolrException;
026:        import org.apache.solr.request.SolrParams;
027:        import org.apache.solr.request.SolrQueryRequest;
028:        import org.apache.solr.request.SolrQueryResponse;
029:        import org.apache.solr.request.DefaultSolrParams;
030:        import org.apache.solr.request.AppendedSolrParams;
031:        import org.apache.solr.schema.IndexSchema;
032:        import org.apache.solr.schema.SchemaField;
033:        import org.apache.solr.search.*;
034:
035:        import java.io.IOException;
036:        import java.io.StringReader;
037:        import java.util.*;
038:        import java.util.logging.Level;
039:        import java.util.regex.Pattern;
040:
041:        /**
042:         * <p>Utilities that may be of use to RequestHandlers.</p>
043:         *
044:         * <p>
045:         * Many of these functions have code that was stolen/mutated from
046:         * StandardRequestHandler.
047:         * </p>
048:         *
049:         * <p>:TODO: refactor StandardRequestHandler to use these utilities</p>
050:         *
051:         * <p>:TODO: Many "standard" functionality methods are not cognisant of
052:         * default parameter settings.  
053:         */
054:        public class SolrPluginUtils {
055:
056:            /**
057:             * Set defaults on a SolrQueryRequest.
058:             *
059:             * RequestHandlers can use this method to ensure their defaults are
060:             * visible to other components such as the response writer
061:             */
062:            public static void setDefaults(SolrQueryRequest req,
063:                    SolrParams defaults) {
064:                setDefaults(req, defaults, null, null);
065:            }
066:
067:            /**
068:             * Set default-ish params on a SolrQueryRequest.
069:             *
070:             * RequestHandlers can use this method to ensure their defaults and
071:             * overrides are visible to other components such as the response writer
072:             *
073:             * @param req The request whose params we are interested i
074:             * @param defaults values to be used if no values are specified in the request params
075:             * @param appends values to be appended to those from the request (or defaults) when dealing with multi-val params, or treated as another layer of defaults for singl-val params.
076:             * @param invariants values which will be used instead of any request, or default values, regardless of context.
077:             */
078:            public static void setDefaults(SolrQueryRequest req,
079:                    SolrParams defaults, SolrParams appends,
080:                    SolrParams invariants) {
081:
082:                SolrParams p = req.getParams();
083:                if (defaults != null) {
084:                    p = new DefaultSolrParams(p, defaults);
085:                }
086:                if (appends != null) {
087:                    p = new AppendedSolrParams(p, appends);
088:                }
089:                if (invariants != null) {
090:                    p = new DefaultSolrParams(invariants, p);
091:                }
092:                req.setParams(p);
093:            }
094:
095:            /** standard param for field list */
096:            @Deprecated
097:            public static String FL = SolrParams.FL;
098:
099:            /**
100:             * SolrIndexSearch.numDocs(Query,Query) freaks out if the filtering
101:             * query is null, so we use this workarround.
102:             */
103:            public static int numDocs(SolrIndexSearcher s, Query q, Query f)
104:                    throws IOException {
105:
106:                return (null == f) ? s.getDocSet(q).size() : s.numDocs(q, f);
107:
108:            }
109:
110:            /**
111:             * Returns the param, or the default if it's empty or not specified.
112:             * @deprecated use SolrParam.get(String,String)
113:             */
114:            public static String getParam(SolrQueryRequest req, String param,
115:                    String def) {
116:
117:                String v = req.getParam(param);
118:                // Note: parameters passed but given only white-space value are
119:                // considered equvalent to passing nothing for that parameter.
120:                if (null == v || "".equals(v.trim())) {
121:                    return def;
122:                }
123:                return v;
124:            }
125:
126:            /**
127:             * Treats the param value as a Number, returns the default if nothing is
128:             * there or if it's not a number.
129:             * @deprecated use SolrParam.getFloat(String,float)
130:             */
131:            public static Number getNumberParam(SolrQueryRequest req,
132:                    String param, Number def) {
133:
134:                Number r = def;
135:                String v = req.getParam(param);
136:                if (null == v || "".equals(v.trim())) {
137:                    return r;
138:                }
139:                try {
140:                    r = new Float(v);
141:                } catch (NumberFormatException e) {
142:                    /* :NOOP" */
143:                }
144:                return r;
145:            }
146:
147:            /**
148:             * Treats parameter value as a boolean.  The string 'false' is false; 
149:             * any other non-empty string is true.
150:             * @deprecated use SolrParam.getBool(String,boolean)
151:             */
152:            public static boolean getBooleanParam(SolrQueryRequest req,
153:                    String param, boolean def) {
154:                String v = req.getParam(param);
155:                if (null == v || "".equals(v.trim())) {
156:                    return def;
157:                }
158:                return !"false".equals(v.trim());
159:            }
160:
161:            private final static Pattern splitList = Pattern.compile(",| ");
162:
163:            /** Split a value that may contain a comma, space of bar separated list. */
164:            public static String[] split(String value) {
165:                return splitList.split(value.trim(), 0);
166:            }
167:
168:            /**
169:             * Assumes the standard query param of "fl" to specify the return fields
170:             * @see #setReturnFields(String,SolrQueryResponse)
171:             */
172:            public static int setReturnFields(SolrQueryRequest req,
173:                    SolrQueryResponse res) {
174:
175:                return setReturnFields(req.getParam(FL), res);
176:            }
177:
178:            /**
179:             * Given a space seperated list of field names, sets the field list on the
180:             * SolrQueryResponse.
181:             *
182:             * @return bitfield of SolrIndexSearcher flags that need to be set
183:             */
184:            public static int setReturnFields(String fl, SolrQueryResponse res) {
185:                int flags = 0;
186:                if (fl != null) {
187:                    // TODO - this could become more efficient if widely used.
188:                    // TODO - should field order be maintained?
189:                    String[] flst = split(fl);
190:                    if (flst.length > 0
191:                            && !(flst.length == 1 && flst[0].length() == 0)) {
192:                        Set<String> set = new HashSet<String>();
193:                        for (String fname : flst) {
194:                            if ("score".equalsIgnoreCase(fname))
195:                                flags |= SolrIndexSearcher.GET_SCORES;
196:                            set.add(fname);
197:                        }
198:                        res.setReturnFields(set);
199:                    }
200:                }
201:                return flags;
202:            }
203:
204:            /**
205:             * Pre-fetch documents into the index searcher's document cache.
206:             *
207:             * This is an entirely optional step which you might want to perform for
208:             * the following reasons:
209:             *
210:             * <ul>
211:             *     <li>Locates the document-retrieval costs in one spot, which helps
212:             *     detailed performance measurement</li>
213:             *   
214:             *     <li>Determines a priori what fields will be needed to be fetched by
215:             *     various subtasks, like response writing and highlighting.  This
216:             *     minimizes the chance that many needed fields will be loaded lazily.
217:             *     (it is more efficient to load all the field we require normally).</li>
218:             * </ul>
219:             *
220:             * If lazy field loading is disabled, this method does nothing.
221:             */
222:            public static void optimizePreFetchDocs(DocList docs, Query query,
223:                    SolrQueryRequest req, SolrQueryResponse res)
224:                    throws IOException {
225:                SolrIndexSearcher searcher = req.getSearcher();
226:                if (!searcher.enableLazyFieldLoading) {
227:                    // nothing to do
228:                    return;
229:                }
230:
231:                Set<String> fieldFilter = null;
232:                Set<String> returnFields = res.getReturnFields();
233:                if (returnFields != null) {
234:                    // copy return fields list
235:                    fieldFilter = new HashSet<String>(returnFields);
236:                    // add highlight fields
237:                    if (HighlightingUtils.isHighlightingEnabled(req)) {
238:                        for (String field : HighlightingUtils
239:                                .getHighlightFields(query, req, null))
240:                            fieldFilter.add(field);
241:                    }
242:                    // fetch unique key if one exists.
243:                    SchemaField keyField = req.getSearcher().getSchema()
244:                            .getUniqueKeyField();
245:                    if (null != keyField)
246:                        fieldFilter.add(keyField.getName());
247:                }
248:
249:                // get documents
250:                DocIterator iter = docs.iterator();
251:                for (int i = 0; i < docs.size(); i++) {
252:                    searcher.doc(iter.nextDoc(), fieldFilter);
253:                }
254:            }
255:
256:            /**
257:             * <p>
258:             * Returns a NamedList containing many "standard" pieces of debugging
259:             * information.
260:             * </p>
261:             *
262:             * <ul>
263:             * <li>rawquerystring - the 'q' param exactly as specified by the client
264:             * </li>
265:             * <li>querystring - the 'q' param after any preprocessing done by the plugin
266:             * </li>
267:             * <li>parsedquery - the main query executed formated by the Solr
268:             *     QueryParsing utils class (which knows about field types)
269:             * </li>
270:             * <li>parsedquery_toString - the main query executed formated by it's
271:             *     own toString method (in case it has internal state Solr
272:             *     doesn't know about)
273:             * </li>
274:             * <li>expain - the list of score explanations for each document in
275:             *     results against query.
276:             * </li>
277:             * <li>otherQuery - the query string specified in 'explainOther' query param.
278:             * </li>
279:             * <li>explainOther - the list of score explanations for each document in
280:             *     results against 'otherQuery'
281:             * </li>
282:             * </ul>
283:             *
284:             * @param req the request we are dealing with
285:             * @param userQuery the users query as a string, after any basic
286:             *                  preprocessing has been done
287:             * @param query the query built from the userQuery
288:             *              (and perhaps other clauses) that identifies the main
289:             *              result set of the response.
290:             * @param results the main result set of the response
291:             * @deprecated Use doStandardDebug(SolrQueryRequest,String,Query,DocList) with setDefaults
292:             */
293:            public static NamedList doStandardDebug(SolrQueryRequest req,
294:                    String userQuery, Query query, DocList results,
295:                    CommonParams params) throws IOException {
296:
297:                String debug = getParam(req, SolrParams.DEBUG_QUERY,
298:                        params.debugQuery);
299:
300:                NamedList dbg = null;
301:                if (debug != null) {
302:                    dbg = new SimpleOrderedMap();
303:
304:                    /* userQuery may have been pre-processes .. expose that */
305:                    dbg.add("rawquerystring", req.getQueryString());
306:                    dbg.add("querystring", userQuery);
307:
308:                    /* QueryParsing.toString isn't perfect, use it to see converted
309:                     * values, use regular toString to see any attributes of the
310:                     * underlying Query it may have missed.
311:                     */
312:                    dbg.add("parsedquery", QueryParsing.toString(query, req
313:                            .getSchema()));
314:                    dbg.add("parsedquery_toString", query.toString());
315:
316:                    dbg.add("explain", getExplainList(query, results, req
317:                            .getSearcher(), req.getSchema()));
318:                    String otherQueryS = req.getParam("explainOther");
319:                    if (otherQueryS != null && otherQueryS.length() > 0) {
320:                        DocList otherResults = doSimpleQuery(otherQueryS, req
321:                                .getSearcher(), req.getSchema(), 0, 10);
322:                        dbg.add("otherQuery", otherQueryS);
323:                        dbg.add("explainOther", getExplainList(query,
324:                                otherResults, req.getSearcher(), req
325:                                        .getSchema()));
326:                    }
327:                }
328:
329:                return dbg;
330:            }
331:
332:            /**
333:             * <p>
334:             * Returns a NamedList containing many "standard" pieces of debugging
335:             * information.
336:             * </p>
337:             *
338:             * <ul>
339:             * <li>rawquerystring - the 'q' param exactly as specified by the client
340:             * </li>
341:             * <li>querystring - the 'q' param after any preprocessing done by the plugin
342:             * </li>
343:             * <li>parsedquery - the main query executed formated by the Solr
344:             *     QueryParsing utils class (which knows about field types)
345:             * </li>
346:             * <li>parsedquery_toString - the main query executed formated by it's
347:             *     own toString method (in case it has internal state Solr
348:             *     doesn't know about)
349:             * </li>
350:             * <li>expain - the list of score explanations for each document in
351:             *     results against query.
352:             * </li>
353:             * <li>otherQuery - the query string specified in 'explainOther' query param.
354:             * </li>
355:             * <li>explainOther - the list of score explanations for each document in
356:             *     results against 'otherQuery'
357:             * </li>
358:             * </ul>
359:             *
360:             * @param req the request we are dealing with
361:             * @param userQuery the users query as a string, after any basic
362:             *                  preprocessing has been done
363:             * @param query the query built from the userQuery
364:             *              (and perhaps other clauses) that identifies the main
365:             *              result set of the response.
366:             * @param results the main result set of the response
367:             */
368:            public static NamedList doStandardDebug(SolrQueryRequest req,
369:                    String userQuery, Query query, DocList results)
370:                    throws IOException {
371:
372:                String debug = req.getParam(SolrParams.DEBUG_QUERY);
373:
374:                NamedList dbg = null;
375:                if (debug != null) {
376:                    dbg = new SimpleOrderedMap();
377:
378:                    /* userQuery may have been pre-processes .. expose that */
379:                    dbg.add("rawquerystring", req.getQueryString());
380:                    dbg.add("querystring", userQuery);
381:
382:                    /* QueryParsing.toString isn't perfect, use it to see converted
383:                     * values, use regular toString to see any attributes of the
384:                     * underlying Query it may have missed.
385:                     */
386:                    dbg.add("parsedquery", QueryParsing.toString(query, req
387:                            .getSchema()));
388:                    dbg.add("parsedquery_toString", query.toString());
389:
390:                    dbg.add("explain", getExplainList(query, results, req
391:                            .getSearcher(), req.getSchema()));
392:                    String otherQueryS = req.getParam("explainOther");
393:                    if (otherQueryS != null && otherQueryS.length() > 0) {
394:                        DocList otherResults = doSimpleQuery(otherQueryS, req
395:                                .getSearcher(), req.getSchema(), 0, 10);
396:                        dbg.add("otherQuery", otherQueryS);
397:                        dbg.add("explainOther", getExplainList(query,
398:                                otherResults, req.getSearcher(), req
399:                                        .getSchema()));
400:                    }
401:                }
402:
403:                return dbg;
404:            }
405:
406:            /**
407:             * Generates an list of Explanations for each item in a list of docs.
408:             *
409:             * @param query The Query you want explanations in the context of
410:             * @param docs The Documents you want explained relative that query
411:             */
412:            public static NamedList getExplainList(Query query, DocList docs,
413:                    SolrIndexSearcher searcher, IndexSchema schema)
414:                    throws IOException {
415:
416:                NamedList explainList = new SimpleOrderedMap();
417:                DocIterator iterator = docs.iterator();
418:                for (int i = 0; i < docs.size(); i++) {
419:                    int id = iterator.nextDoc();
420:
421:                    Explanation explain = searcher.explain(query, id);
422:
423:                    Document doc = searcher.doc(id);
424:                    String strid = schema.printableUniqueKey(doc);
425:                    String docname = "";
426:                    if (strid != null)
427:                        docname = "id=" + strid + ",";
428:                    docname = docname + "internal_docid=" + id;
429:
430:                    explainList.add(docname, "\n" + explain.toString());
431:                }
432:                return explainList;
433:            }
434:
435:            /**
436:             * Executes a basic query in lucene syntax
437:             */
438:            public static DocList doSimpleQuery(String sreq,
439:                    SolrIndexSearcher searcher, IndexSchema schema, int start,
440:                    int limit) throws IOException {
441:                List<String> commands = StrUtils.splitSmart(sreq, ';');
442:
443:                String qs = commands.size() >= 1 ? commands.get(0) : "";
444:                Query query = QueryParsing.parseQuery(qs, schema);
445:
446:                // If the first non-query, non-filter command is a simple sort on an indexed field, then
447:                // we can use the Lucene sort ability.
448:                Sort sort = null;
449:                if (commands.size() >= 2) {
450:                    QueryParsing.SortSpec sortSpec = QueryParsing.parseSort(
451:                            commands.get(1), schema);
452:                    if (sortSpec != null) {
453:                        sort = sortSpec.getSort();
454:                        if (sortSpec.getCount() >= 0) {
455:                            limit = sortSpec.getCount();
456:                        }
457:                    }
458:                }
459:
460:                DocList results = searcher.getDocList(query, (DocSet) null,
461:                        sort, start, limit);
462:                return results;
463:            }
464:
465:            /**
466:             * Given a string containing fieldNames and boost info,
467:             * converts it to a Map from field name to boost info.
468:             *
469:             * <p>
470:             * Doesn't care if boost info is negative, you're on your own.
471:             * </p>
472:             * <p>
473:             * Doesn't care if boost info is missing, again: you're on your own.
474:             * </p>
475:             *
476:             * @param in a String like "fieldOne^2.3 fieldTwo fieldThree^-0.4"
477:             * @return Map of fieldOne =&gt; 2.3, fieldTwo =&gt; null, fieldThree =&gt; -0.4
478:             */
479:            public static Map<String, Float> parseFieldBoosts(String in) {
480:                return parseFieldBoosts(new String[] { in });
481:            }
482:
483:            /**
484:             * Like <code>parseFieldBoosts(String)</code>, but parses all the strings
485:             * in the provided array (which may be null).
486:             *
487:             * @param fieldLists an array of Strings eg. <code>{"fieldOne^2.3", "fieldTwo"}</code>
488:             * @return Map of fieldOne =&gt; 2.3, fieldThree =&gt; -0.4
489:             */
490:            public static Map<String, Float> parseFieldBoosts(
491:                    String[] fieldLists) {
492:                if (null == fieldLists || 0 == fieldLists.length) {
493:                    return new HashMap<String, Float>();
494:                }
495:                Map<String, Float> out = new HashMap<String, Float>(7);
496:                for (String in : fieldLists) {
497:                    if (null == in || "".equals(in.trim()))
498:                        continue;
499:                    String[] bb = in.trim().split("\\s+");
500:                    for (String s : bb) {
501:                        String[] bbb = s.split("\\^");
502:                        out.put(bbb[0], 1 == bbb.length ? null : Float
503:                                .valueOf(bbb[1]));
504:                    }
505:                }
506:                return out;
507:            }
508:
509:            /**
510:             * Given a string containing functions with optional boosts, returns
511:             * an array of Queries representing those functions with the specified
512:             * boosts.
513:             * <p>
514:             * NOTE: intra-function whitespace is not allowed.
515:             * </p>
516:             * @see #parseFieldBoosts
517:             */
518:            public static List<Query> parseFuncs(IndexSchema s, String in)
519:                    throws ParseException {
520:
521:                Map<String, Float> ff = parseFieldBoosts(in);
522:                List<Query> funcs = new ArrayList<Query>(ff.keySet().size());
523:                for (String f : ff.keySet()) {
524:                    Query fq = QueryParsing.parseFunction(f, s);
525:                    Float b = ff.get(f);
526:                    if (null != b) {
527:                        fq.setBoost(b);
528:                    }
529:                    funcs.add(fq);
530:                }
531:                return funcs;
532:            }
533:
534:            /**
535:             * Checks the number of optional clauses in the query, and compares it
536:             * with the specification string to determine the proper value to use.
537:             *
538:             * <p>
539:             * Details about the specification format can be found
540:             * <a href="doc-files/min-should-match.html">here</a>
541:             * </p>
542:             *
543:             * <p>A few important notes...</p>
544:             * <ul>
545:             * <li>
546:             * If the calculations based on the specification determine that no
547:             * optional clauses are needed, BooleanQuerysetMinMumberShouldMatch
548:             * will never be called, but the usual rules about BooleanQueries
549:             * still apply at search time (a BooleanQuery containing no required
550:             * clauses must still match at least one optional clause)
551:             * <li>
552:             * <li>
553:             * No matter what number the calculation arrives at,
554:             * BooleanQuery.setMinShouldMatch() will never be called with a
555:             * value greater then the number of optional clauses (or less then 1)
556:             * </li>
557:             * </ul>
558:             *
559:             * <p>:TODO: should optimize the case where number is same
560:             * as clauses to just make them all "required"
561:             * </p>
562:             */
563:            public static void setMinShouldMatch(BooleanQuery q, String spec) {
564:
565:                int optionalClauses = 0;
566:                for (BooleanClause c : (List<BooleanClause>) q.clauses()) {
567:                    if (c.getOccur() == Occur.SHOULD) {
568:                        optionalClauses++;
569:                    }
570:                }
571:
572:                int msm = calculateMinShouldMatch(optionalClauses, spec);
573:                if (0 < msm) {
574:                    q.setMinimumNumberShouldMatch(msm);
575:                }
576:            }
577:
578:            /**
579:             * helper exposed for UnitTests
580:             * @see #setMinShouldMatch
581:             */
582:            static int calculateMinShouldMatch(int optionalClauseCount,
583:                    String spec) {
584:
585:                int result = optionalClauseCount;
586:
587:                if (-1 < spec.indexOf("<")) {
588:                    /* we have conditional spec(s) */
589:
590:                    for (String s : spec.trim().split(" ")) {
591:                        String[] parts = s.split("<");
592:                        int upperBound = (new Integer(parts[0])).intValue();
593:                        if (optionalClauseCount <= upperBound) {
594:                            return result;
595:                        } else {
596:                            result = calculateMinShouldMatch(
597:                                    optionalClauseCount, parts[1]);
598:                        }
599:                    }
600:                    return result;
601:                }
602:
603:                /* otherwise, simple expresion */
604:
605:                if (-1 < spec.indexOf("%")) {
606:                    /* percentage */
607:                    int percent = new Integer(spec.replace("%", "")).intValue();
608:                    float calc = (result * percent) / 100f;
609:                    result = calc < 0 ? result + (int) calc : (int) calc;
610:                } else {
611:                    int calc = (new Integer(spec)).intValue();
612:                    result = calc < 0 ? result + calc : calc;
613:                }
614:
615:                return (optionalClauseCount < result ? optionalClauseCount
616:                        : (result < 0 ? 0 : result));
617:
618:            }
619:
620:            /**
621:             * Recursively walks the "from" query pulling out sub-queries and
622:             * adding them to the "to" query.
623:             *
624:             * <p>
625:             * Boosts are multiplied as needed.  Sub-BooleanQueryies which are not
626:             * optional will not be flattened.  From will be mangled durring the walk,
627:             * so do not attempt to reuse it.
628:             * </p>
629:             */
630:            public static void flattenBooleanQuery(BooleanQuery to,
631:                    BooleanQuery from) {
632:
633:                for (BooleanClause clause : (List<BooleanClause>) from
634:                        .clauses()) {
635:
636:                    Query cq = clause.getQuery();
637:                    cq.setBoost(cq.getBoost() * from.getBoost());
638:
639:                    if (cq instanceof  BooleanQuery && !clause.isRequired()
640:                            && !clause.isProhibited()) {
641:
642:                        /* we can recurse */
643:                        flattenBooleanQuery(to, (BooleanQuery) cq);
644:
645:                    } else {
646:                        to.add(clause);
647:                    }
648:                }
649:            }
650:
651:            /**
652:             * Escapes all special characters except '"', '-', and '+'
653:             *
654:             * @see QueryParser#escape
655:             */
656:            public static CharSequence partialEscape(CharSequence s) {
657:                StringBuffer sb = new StringBuffer();
658:                for (int i = 0; i < s.length(); i++) {
659:                    char c = s.charAt(i);
660:                    if (c == '\\' || c == '!' || c == '(' || c == ')'
661:                            || c == ':' || c == '^' || c == '[' || c == ']'
662:                            || c == '{' || c == '}' || c == '~' || c == '*'
663:                            || c == '?') {
664:                        sb.append('\\');
665:                    }
666:                    sb.append(c);
667:                }
668:                return sb;
669:            }
670:
671:            /**
672:             * Returns it's input if there is an even (ie: balanced) number of
673:             * '"' characters -- otherwise returns a String in which all '"'
674:             * characters are striped out.
675:             */
676:            public static CharSequence stripUnbalancedQuotes(CharSequence s) {
677:                int count = 0;
678:                for (int i = 0; i < s.length(); i++) {
679:                    if (s.charAt(i) == '\"') {
680:                        count++;
681:                    }
682:                }
683:                if (0 == (count & 1)) {
684:                    return s;
685:                }
686:                return s.toString().replace("\"", "");
687:            }
688:
689:            /**
690:             * A subclass of SolrQueryParser that supports aliasing fields for
691:             * constructing DisjunctionMaxQueries.
692:             */
693:            public static class DisjunctionMaxQueryParser extends
694:                    SolrQueryParser {
695:
696:                /** A simple container for storing alias info
697:                 * @see #aliases
698:                 */
699:                protected static class Alias {
700:                    public float tie;
701:                    public Map<String, Float> fields;
702:                }
703:
704:                /**
705:                 * Where we store a map from field name we expect to see in our query
706:                 * string, to Alias object containing the fields to use in our
707:                 * DisjunctionMaxQuery and the tiebreaker to use.
708:                 */
709:                protected Map<String, Alias> aliases = new HashMap<String, Alias>(
710:                        3);
711:
712:                public DisjunctionMaxQueryParser(IndexSchema s,
713:                        String defaultField) {
714:                    super (s, defaultField);
715:                    // don't trust that our parent class won't ever change it's default
716:                    setDefaultOperator(QueryParser.Operator.OR);
717:                }
718:
719:                public DisjunctionMaxQueryParser(IndexSchema s) {
720:                    this (s, null);
721:                }
722:
723:                /**
724:                 * Add an alias to this query parser.
725:                 *
726:                 * @param field the field name that should trigger alias mapping
727:                 * @param fieldBoosts the mapping from fieldname to boost value that
728:                 *                    should be used to build up the clauses of the
729:                 *                    DisjunctionMaxQuery.
730:                 * @param tiebreaker to the tiebreaker to be used in the
731:                 *                   DisjunctionMaxQuery
732:                 * @see SolrPluginUtils#parseFieldBoosts
733:                 */
734:                public void addAlias(String field, float tiebreaker,
735:                        Map<String, Float> fieldBoosts) {
736:
737:                    Alias a = new Alias();
738:                    a.tie = tiebreaker;
739:                    a.fields = fieldBoosts;
740:                    aliases.put(field, a);
741:                }
742:
743:                /**
744:                 * Delegates to the super class unless the field has been specified
745:                 * as an alias -- in which case we recurse on each of
746:                 * the aliased fields, and the results are composed into a
747:                 * DisjunctionMaxQuery.  (so yes: aliases which point at other
748:                 * aliases should work)
749:                 */
750:                protected Query getFieldQuery(String field, String queryText)
751:                        throws ParseException {
752:
753:                    if (aliases.containsKey(field)) {
754:
755:                        Alias a = aliases.get(field);
756:                        DisjunctionMaxQuery q = new DisjunctionMaxQuery(a.tie);
757:
758:                        /* we might not get any valid queries from delegation,
759:                         * in which we should return null
760:                         */
761:                        boolean ok = false;
762:
763:                        for (String f : a.fields.keySet()) {
764:
765:                            Query sub = getFieldQuery(f, queryText);
766:                            if (null != sub) {
767:                                if (null != a.fields.get(f)) {
768:                                    sub.setBoost(a.fields.get(f));
769:                                }
770:                                q.add(sub);
771:                                ok = true;
772:                            }
773:                        }
774:                        return ok ? q : null;
775:
776:                    } else {
777:                        return super .getFieldQuery(field, queryText);
778:                    }
779:                }
780:
781:            }
782:
783:            /**
784:             * Determines the correct Sort based on the request parameter "sort"
785:             *
786:             * @return null if no sort is specified.
787:             */
788:            public static Sort getSort(SolrQueryRequest req) {
789:
790:                String sort = req.getParam(SolrParams.SORT);
791:                if (null == sort || sort.equals("")) {
792:                    return null;
793:                }
794:
795:                SolrException sortE = null;
796:                QueryParsing.SortSpec ss = null;
797:                try {
798:                    ss = QueryParsing.parseSort(sort, req.getSchema());
799:                } catch (SolrException e) {
800:                    sortE = e;
801:                }
802:
803:                if ((null == ss) || (null != sortE)) {
804:                    /* we definitely had some sort of sort string from the user,
805:                     * but no SortSpec came out of it
806:                     */
807:                    SolrCore.log.log(Level.WARNING, "Invalid sort \"" + sort
808:                            + "\" was specified, ignoring", sortE);
809:                    return null;
810:                }
811:
812:                return ss.getSort();
813:            }
814:
815:            /**
816:             * Builds a list of Query objects that should be used to filter results
817:             * @see SolrParams#FQ
818:             * @return null if no filter queries
819:             */
820:            public static List<Query> parseFilterQueries(SolrQueryRequest req)
821:                    throws ParseException {
822:                return parseQueryStrings(req, req.getParams().getParams(
823:                        SolrParams.FQ));
824:            }
825:
826:            /** Turns an array of query strings into a List of Query objects.
827:             *
828:             * @return null if no queries are generated
829:             */
830:            public static List<Query> parseQueryStrings(SolrQueryRequest req,
831:                    String[] queries) throws ParseException {
832:                if (null == queries || 0 == queries.length)
833:                    return null;
834:                List<Query> out = new LinkedList<Query>();
835:                SolrIndexSearcher s = req.getSearcher();
836:                /* Ignore SolrParams.DF - could have init param FQs assuming the
837:                 * schema default with query param DF intented to only affect Q.
838:                 * If user doesn't want schema default, they should be explicit in the FQ.
839:                 */
840:                SolrQueryParser qp = new SolrQueryParser(s.getSchema(), null);
841:                for (String q : queries) {
842:                    if (null != q && 0 != q.trim().length()) {
843:                        out.add(qp.parse(q));
844:                    }
845:                }
846:                return out;
847:            }
848:
849:            /**
850:             * A CacheRegenerator that can be used whenever the items in the cache
851:             * are not dependant on the current searcher.
852:             *
853:             * <p>
854:             * Flat out copies the oldKey=&gt;oldVal pair into the newCache
855:             * </p>
856:             */
857:            public static class IdentityRegenerator implements  CacheRegenerator {
858:                public boolean regenerateItem(SolrIndexSearcher newSearcher,
859:                        SolrCache newCache, SolrCache oldCache, Object oldKey,
860:                        Object oldVal) throws IOException {
861:
862:                    newCache.put(oldKey, oldVal);
863:                    return true;
864:                }
865:
866:            }
867:
868:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.