1 17 package org.alfresco.repo.search.impl.lucene; 18 19 import java.io.IOException ; 20 import java.io.Serializable ; 21 import java.util.ArrayList ; 22 import java.util.HashMap ; 23 import java.util.HashSet ; 24 import java.util.List ; 25 import java.util.ListIterator ; 26 import java.util.Map ; 27 import java.util.Set ; 28 29 import org.alfresco.repo.search.CannedQueryDef; 30 import org.alfresco.repo.search.EmptyResultSet; 31 import org.alfresco.repo.search.Indexer; 32 import org.alfresco.repo.search.QueryRegisterComponent; 33 import org.alfresco.repo.search.SearcherException; 34 import org.alfresco.repo.search.impl.NodeSearcher; 35 import org.alfresco.service.cmr.dictionary.DictionaryService; 36 import org.alfresco.service.cmr.repository.InvalidNodeRefException; 37 import org.alfresco.service.cmr.repository.NodeRef; 38 import org.alfresco.service.cmr.repository.NodeService; 39 import org.alfresco.service.cmr.repository.Path; 40 import org.alfresco.service.cmr.repository.StoreRef; 41 import org.alfresco.service.cmr.repository.XPathException; 42 import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; 43 import org.alfresco.service.cmr.search.QueryParameter; 44 import org.alfresco.service.cmr.search.QueryParameterDefinition; 45 import org.alfresco.service.cmr.search.ResultSet; 46 import org.alfresco.service.cmr.search.SearchParameters; 47 import org.alfresco.service.cmr.search.SearchService; 48 import org.alfresco.service.namespace.NamespacePrefixResolver; 49 import org.alfresco.service.namespace.QName; 50 import org.alfresco.util.ISO9075; 51 import org.alfresco.util.SearchLanguageConversion; 52 import org.apache.lucene.search.Hits; 53 import org.apache.lucene.search.Query; 54 import org.apache.lucene.search.Searcher; 55 import org.apache.lucene.search.Sort; 56 import org.apache.lucene.search.SortField; 57 import org.saxpath.SAXPathException; 58 59 import com.werken.saxpath.XPathReader; 60 61 70 public class LuceneSearcherImpl extends LuceneBase implements LuceneSearcher 71 { 72 75 private static final String DEFAULT_FIELD = "TEXT"; 76 77 private NamespacePrefixResolver namespacePrefixResolver; 78 79 private NodeService nodeService; 80 81 private DictionaryService dictionaryService; 82 83 private QueryRegisterComponent queryRegister; 84 85 private LuceneIndexer indexer; 86 87 90 91 100 public static LuceneSearcherImpl getSearcher(StoreRef storeRef, LuceneIndexer indexer, LuceneConfig config) 101 { 102 LuceneSearcherImpl searcher = new LuceneSearcherImpl(); 103 searcher.setLuceneConfig(config); 104 try 105 { 106 searcher.initialise(storeRef, indexer == null ? null : indexer.getDeltaId(), false, false); 107 searcher.indexer = indexer; 108 } 109 catch (LuceneIndexException e) 110 { 111 throw new SearcherException(e); 112 } 113 return searcher; 114 } 115 116 124 public static LuceneSearcherImpl getSearcher(StoreRef storeRef, LuceneConfig config) 125 { 126 return getSearcher(storeRef, null, config); 127 } 128 129 public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) 130 { 131 this.namespacePrefixResolver = namespacePrefixResolver; 132 } 133 134 public boolean indexExists() 135 { 136 return mainIndexExists(); 137 } 138 139 public void setNodeService(NodeService nodeService) 140 { 141 this.nodeService = nodeService; 142 } 143 144 public void setDictionaryService(DictionaryService dictionaryService) 145 { 146 this.dictionaryService = dictionaryService; 147 } 148 149 public void setQueryRegister(QueryRegisterComponent queryRegister) 150 { 151 this.queryRegister = queryRegister; 152 } 153 154 public ResultSet query(StoreRef store, String language, String queryString, Path[] queryOptions, 155 QueryParameterDefinition[] queryParameterDefinitions) throws SearcherException 156 { 157 SearchParameters sp = new SearchParameters(); 158 sp.addStore(store); 159 sp.setLanguage(language); 160 sp.setQuery(queryString); 161 if (queryOptions != null) 162 { 163 for (Path path : queryOptions) 164 { 165 sp.addAttrbutePath(path); 166 } 167 } 168 if (queryParameterDefinitions != null) 169 { 170 for (QueryParameterDefinition qpd : queryParameterDefinitions) 171 { 172 sp.addQueryParameterDefinition(qpd); 173 } 174 } 175 sp.excludeDataInTheCurrentTransaction(true); 176 177 return query(sp); 178 } 179 180 public ResultSet query(SearchParameters searchParameters) 181 { 182 if (searchParameters.getStores().size() != 1) 183 { 184 throw new IllegalStateException ("Only one store can be searched at present"); 185 } 186 187 String parameterisedQueryString; 188 if (searchParameters.getQueryParameterDefinitions().size() > 0) 189 { 190 Map <QName, QueryParameterDefinition> map = new HashMap <QName, QueryParameterDefinition>(); 191 192 for (QueryParameterDefinition qpd : searchParameters.getQueryParameterDefinitions()) 193 { 194 map.put(qpd.getQName(), qpd); 195 } 196 197 parameterisedQueryString = parameterise(searchParameters.getQuery(), map, null, namespacePrefixResolver); 198 } 199 else 200 { 201 parameterisedQueryString = searchParameters.getQuery(); 202 } 203 204 if (searchParameters.getLanguage().equalsIgnoreCase(SearchService.LANGUAGE_LUCENE)) 205 { 206 try 207 { 208 209 int defaultOperator; 210 if (searchParameters.getDefaultOperator() == SearchParameters.AND) 211 { 212 defaultOperator = LuceneQueryParser.DEFAULT_OPERATOR_AND; 213 } 214 else 215 { 216 defaultOperator = LuceneQueryParser.DEFAULT_OPERATOR_OR; 217 } 218 219 Query query = LuceneQueryParser.parse(parameterisedQueryString, DEFAULT_FIELD, new LuceneAnalyser( 220 dictionaryService), namespacePrefixResolver, dictionaryService, defaultOperator); 221 Searcher searcher = getSearcher(indexer); 222 if (searcher == null) 223 { 224 return new EmptyResultSet(); 226 } 227 228 Hits hits; 229 230 if (searchParameters.getSortDefinitions().size() > 0) 231 { 232 int index = 0; 233 SortField[] fields = new SortField[searchParameters.getSortDefinitions().size()]; 234 for (SearchParameters.SortDefinition sd : searchParameters.getSortDefinitions()) 235 { 236 switch (sd.getSortType()) 237 { 238 case FIELD: 239 fields[index++] = new SortField(sd.getField(), !sd.isAscending()); 240 break; 241 case DOCUMENT: 242 fields[index++] = new SortField(null, SortField.DOC, !sd.isAscending()); 243 break; 244 case SCORE: 245 fields[index++] = new SortField(null, SortField.SCORE, !sd.isAscending()); 246 break; 247 } 248 249 } 250 hits = searcher.search(query, new Sort(fields)); 251 } 252 else 253 { 254 hits = searcher.search(query); 255 } 256 257 return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray( 258 new Path[0]), searchParameters); 259 260 } 261 catch (ParseException e) 262 { 263 throw new SearcherException("Failed to parse query: " + parameterisedQueryString, e); 264 } 265 catch (IOException e) 266 { 267 throw new SearcherException("IO exception during search", e); 268 } 269 } 270 else if (searchParameters.getLanguage().equalsIgnoreCase(SearchService.LANGUAGE_XPATH)) 271 { 272 try 273 { 274 XPathReader reader = new XPathReader(); 275 LuceneXPathHandler handler = new LuceneXPathHandler(); 276 handler.setNamespacePrefixResolver(namespacePrefixResolver); 277 handler.setDictionaryService(dictionaryService); 278 reader.setXPathHandler(handler); 283 reader.parse(parameterisedQueryString); 284 Query query = handler.getQuery(); 285 Searcher searcher = getSearcher(null); 286 if (searcher == null) 287 { 288 return new EmptyResultSet(); 290 } 291 Hits hits = searcher.search(query); 292 return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray( 293 new Path[0]), searchParameters); 294 } 295 catch (SAXPathException e) 296 { 297 throw new SearcherException("Failed to parse query: " + searchParameters.getQuery(), e); 298 } 299 catch (IOException e) 300 { 301 throw new SearcherException("IO exception during search", e); 302 } 303 } 304 else 305 { 306 throw new SearcherException("Unknown query language: " + searchParameters.getLanguage()); 307 } 308 } 309 310 public ResultSet query(StoreRef store, String language, String query) 311 { 312 return query(store, language, query, null, null); 313 } 314 315 public ResultSet query(StoreRef store, String language, String query, 316 QueryParameterDefinition[] queryParameterDefintions) 317 { 318 return query(store, language, query, null, queryParameterDefintions); 319 } 320 321 public ResultSet query(StoreRef store, String language, String query, Path[] attributePaths) 322 { 323 return query(store, language, query, attributePaths, null); 324 } 325 326 public ResultSet query(StoreRef store, QName queryId, QueryParameter[] queryParameters) 327 { 328 CannedQueryDef definition = queryRegister.getQueryDefinition(queryId); 329 330 340 checkParameters(definition, queryParameters); 341 342 String queryString = parameterise(definition.getQuery(), definition.getQueryParameterMap(), queryParameters, 343 definition.getNamespacePrefixResolver()); 344 345 return query(store, definition.getLanguage(), queryString, null, null); 346 } 347 348 356 private void checkParameters(CannedQueryDef definition, QueryParameter[] queryParameters) 357 throws QueryParameterisationException 358 { 359 List <QName> missing = new ArrayList <QName>(); 360 361 Set <QName> parameterQNameSet = new HashSet <QName>(); 362 if (queryParameters != null) 363 { 364 for (QueryParameter parameter : queryParameters) 365 { 366 parameterQNameSet.add(parameter.getQName()); 367 } 368 } 369 370 for (QueryParameterDefinition parameterDefinition : definition.getQueryParameterDefs()) 371 { 372 if (!parameterDefinition.hasDefaultValue()) 373 { 374 if (!parameterQNameSet.contains(parameterDefinition.getQName())) 375 { 376 missing.add(parameterDefinition.getQName()); 377 } 378 } 379 } 380 381 if (missing.size() > 0) 382 { 383 StringBuilder buffer = new StringBuilder (128); 384 buffer.append("The query is missing values for the following parameters: "); 385 for (QName qName : missing) 386 { 387 buffer.append(qName); 388 buffer.append(", "); 389 } 390 buffer.delete(buffer.length() - 1, buffer.length() - 1); 391 buffer.delete(buffer.length() - 1, buffer.length() - 1); 392 throw new QueryParameterisationException(buffer.toString()); 393 } 394 } 395 396 404 private String parameterise(String unparameterised, Map <QName, QueryParameterDefinition> map, 405 QueryParameter[] queryParameters, NamespacePrefixResolver nspr) throws QueryParameterisationException 406 { 407 408 Map <QName, List <Serializable >> valueMap = new HashMap <QName, List <Serializable >>(); 409 410 if (queryParameters != null) 411 { 412 for (QueryParameter parameter : queryParameters) 413 { 414 List <Serializable > list = valueMap.get(parameter.getQName()); 415 if (list == null) 416 { 417 list = new ArrayList <Serializable >(); 418 valueMap.put(parameter.getQName(), list); 419 } 420 list.add(parameter.getValue()); 421 } 422 } 423 424 Map <QName, ListIterator <Serializable >> iteratorMap = new HashMap <QName, ListIterator <Serializable >>(); 425 426 List <QName> missing = new ArrayList <QName>(1); 427 StringBuilder buffer = new StringBuilder (unparameterised); 428 int index = 0; 429 while ((index = buffer.indexOf("${", index)) != -1) 430 { 431 int endIndex = buffer.indexOf("}", index); 432 String qNameString = buffer.substring(index + 2, endIndex); 433 QName key = QName.createQName(qNameString, nspr); 434 QueryParameterDefinition parameterDefinition = map.get(key); 435 if (parameterDefinition == null) 436 { 437 missing.add(key); 438 buffer.replace(index, endIndex + 1, ""); 439 } 440 else 441 { 442 ListIterator <Serializable > it = iteratorMap.get(key); 443 if ((it == null) || (!it.hasNext())) 444 { 445 List <Serializable > list = valueMap.get(key); 446 if ((list != null) && (list.size() > 0)) 447 { 448 it = list.listIterator(); 449 } 450 if (it != null) 451 { 452 iteratorMap.put(key, it); 453 } 454 } 455 String value; 456 if (it == null) 457 { 458 value = parameterDefinition.getDefault(); 459 } 460 else 461 { 462 value = DefaultTypeConverter.INSTANCE.convert(String .class, it.next()); 463 } 464 buffer.replace(index, endIndex + 1, value); 465 } 466 } 467 if (missing.size() > 0) 468 { 469 StringBuilder error = new StringBuilder (); 470 error.append("The query uses the following parameters which are not defined: "); 471 for (QName qName : missing) 472 { 473 error.append(qName); 474 error.append(", "); 475 } 476 error.delete(error.length() - 1, error.length() - 1); 477 error.delete(error.length() - 1, error.length() - 1); 478 throw new QueryParameterisationException(error.toString()); 479 } 480 return buffer.toString(); 481 } 482 483 486 public List <NodeRef> selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, 487 NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks, String language) 488 throws InvalidNodeRefException, XPathException 489 { 490 NodeSearcher nodeSearcher = new NodeSearcher(nodeService, dictionaryService, this); 491 return nodeSearcher.selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver, 492 followAllParentLinks, language); 493 } 494 495 498 public List <Serializable > selectProperties(NodeRef contextNodeRef, String xpath, 499 QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, 500 boolean followAllParentLinks, String language) throws InvalidNodeRefException, XPathException 501 { 502 NodeSearcher nodeSearcher = new NodeSearcher(nodeService, dictionaryService, this); 503 return nodeSearcher.selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver, 504 followAllParentLinks, language); 505 } 506 507 510 public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern) 511 { 512 return contains(nodeRef, propertyQName, googleLikePattern, SearchParameters.Operator.OR); 513 } 514 515 518 public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern, 519 SearchParameters.Operator defaultOperator) 520 { 521 ResultSet resultSet = null; 522 try 523 { 524 StringBuilder sb = new StringBuilder (); 526 sb.append("+ID:\"").append(nodeRef.toString()).append("\" +(TEXT:(") 527 .append(googleLikePattern.toLowerCase()).append(") "); 528 if (propertyQName != null) 529 { 530 sb.append(" OR @").append( 531 LuceneQueryParser.escape(QName.createQName(propertyQName.getNamespaceURI(), 532 ISO9075.encode(propertyQName.getLocalName())).toString())); 533 sb.append(":(").append(googleLikePattern.toLowerCase()).append(")"); 534 } 535 else 536 { 537 for (QName key : nodeService.getProperties(nodeRef).keySet()) 538 { 539 sb.append(" OR @").append( 540 LuceneQueryParser.escape(QName.createQName(key.getNamespaceURI(), 541 ISO9075.encode(key.getLocalName())).toString())); 542 sb.append(":(").append(googleLikePattern.toLowerCase()).append(")"); 543 } 544 } 545 sb.append(")"); 546 547 SearchParameters sp = new SearchParameters(); 548 sp.setLanguage(SearchService.LANGUAGE_LUCENE); 549 sp.setQuery(sb.toString()); 550 sp.setDefaultOperator(defaultOperator); 551 sp.addStore(nodeRef.getStoreRef()); 552 553 resultSet = this.query(sp); 554 boolean answer = resultSet.length() > 0; 555 return answer; 556 } 557 finally 558 { 559 if (resultSet != null) 560 { 561 resultSet.close(); 562 } 563 } 564 } 565 566 572 public boolean like(NodeRef nodeRef, QName propertyQName, String sqlLikePattern, boolean includeFTS) 573 { 574 if (propertyQName == null) 575 { 576 throw new IllegalArgumentException ("Property QName is mandatory for the like expression"); 577 } 578 579 StringBuilder sb = new StringBuilder (sqlLikePattern.length() * 3); 580 581 if (includeFTS) 582 { 583 String pattern = SearchLanguageConversion.convertXPathLikeToLucene(sqlLikePattern.toLowerCase()); 585 586 sb = new StringBuilder (); 588 sb.append("+ID:\"").append(nodeRef.toString()).append("\" +("); 589 if (includeFTS) 591 { 592 sb.append("TEXT:(").append(pattern).append(") "); 593 } 594 if (propertyQName != null) 595 { 596 sb.append(" @").append( 597 LuceneQueryParser.escape(QName.createQName(propertyQName.getNamespaceURI(), 598 ISO9075.encode(propertyQName.getLocalName())).toString())).append(":(").append(pattern) 599 .append(")"); 600 } 601 sb.append(")"); 602 603 ResultSet resultSet = null; 604 try 605 { 606 resultSet = this.query(nodeRef.getStoreRef(), "lucene", sb.toString()); 607 boolean answer = resultSet.length() > 0; 608 return answer; 609 } 610 finally 611 { 612 if (resultSet != null) 613 { 614 resultSet.close(); 615 } 616 } 617 } 618 else 619 { 620 String pattern = SearchLanguageConversion.convertXPathLikeToRegex(sqlLikePattern.toLowerCase()); 622 623 Serializable property = nodeService.getProperty(nodeRef, propertyQName); 624 if (property == null) 625 { 626 return false; 627 } 628 else 629 { 630 String propertyString = DefaultTypeConverter.INSTANCE.convert(String .class, nodeService.getProperty( 631 nodeRef, propertyQName)); 632 return propertyString.toLowerCase().matches(pattern); 633 } 634 } 635 } 636 637 public List <NodeRef> selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, 638 NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks) 639 throws InvalidNodeRefException, XPathException 640 { 641 return selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, 642 SearchService.LANGUAGE_XPATH); 643 } 644 645 public List <Serializable > selectProperties(NodeRef contextNodeRef, String xpath, 646 QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, 647 boolean followAllParentLinks) throws InvalidNodeRefException, XPathException 648 { 649 return selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, 650 SearchService.LANGUAGE_XPATH); 651 } 652 } 653 | Popular Tags |