001/* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.ole.docstore.discovery.service; 017 018import org.kuali.ole.docstore.discovery.model.SearchCondition; 019import org.kuali.ole.docstore.discovery.model.SearchParams; 020import org.kuali.rice.core.api.config.property.ConfigContext; 021import org.slf4j.Logger; 022import org.slf4j.LoggerFactory; 023import org.springframework.util.StringUtils; 024 025import java.io.BufferedReader; 026import java.io.InputStreamReader; 027import java.io.UnsupportedEncodingException; 028import java.net.URL; 029import java.net.URLConnection; 030import java.net.URLDecoder; 031import java.net.URLEncoder; 032import java.text.CharacterIterator; 033import java.text.StringCharacterIterator; 034import java.util.*; 035 036public class DiscoveryServiceImpl 037 implements DiscoveryService { 038 private static final Logger LOG = LoggerFactory.getLogger(DiscoveryServiceImpl.class); 039 private static String docSearchUrl = null; 040 private static DiscoveryService discoveryService = null; 041 042 private DiscoveryServiceImpl() { 043 LOG.debug("DiscoveryServiceImpl "); 044 init(); 045 } 046 047 public static DiscoveryService getInstance() { 048 if (null == discoveryService) { 049 discoveryService = new DiscoveryServiceImpl(); 050 } 051 return discoveryService; 052 } 053 054 protected void init() { 055 LOG.debug("DiscoveryServiceImpl init "); 056 if(ConfigContext.getCurrentContextConfig()!=null){ 057 setDocSearchUrl(ConfigContext.getCurrentContextConfig().getProperty("docSearchURL")); 058 } 059 } 060 061 public String search(SearchParams searchParams) { 062 String response = ""; 063 LOG.debug("in search1"); 064 String searchType = searchParams.getSearchType(); 065 LOG.debug("searchType " + searchType); 066 StringBuffer query = new StringBuffer(); 067 query.append(SolrServerManager.getInstance().getSolrCoreURL() + "/select"); 068 if (searchType.equals(SEARCH_TYPE_MORE_FACET)) { 069 String facetPrefix = searchParams.getFacetPrefix(); 070 String facetPageSize = searchParams.getFacetPageSize(); 071 String facetSort = searchParams.getFacetSort(); 072 String facetOffset = searchParams.getFacetOffset(); 073 buildInitialQuery(query, searchParams); 074 query.append("&facet=true"); 075 076 query.append("&facet.mincount=1"); 077 078 query.append("&facet.prefix=" + facetPrefix); 079 080 query.append("&facet.offset=" + facetOffset); 081 082 query.append("&facet.limit=" + facetPageSize); 083 084 query.append("&facet.sort=" + facetSort); 085 086 query.append("&facet.field=" + searchParams.getFacetField()); 087 088 Map<String, String> facetTermsMap = searchParams.getFacetTermsMap(); 089 query.append(buildFilterQuery(facetTermsMap)); 090 } else { 091 if (searchParams.getSearchFieldsList() != null && searchParams.getSearchFieldsList().size() <= 0 && ( 092 searchType != null && !searchType.equalsIgnoreCase(SEARCH_TYPE_LINK))) { 093 buildInitialQuery(query, searchParams); 094 searchParams.setSearchTerms(""); 095 } else if (searchType != null && (searchType.equalsIgnoreCase(SEARCH_TYPE_ADVANCED) 096 || searchType.equalsIgnoreCase(SEARCH_TYPE_FACET) || searchType 097 .equalsIgnoreCase(SEARCH_TYPE_FACET_DELETE))) { 098 buildInitialQuery(query, searchParams); 099 if (searchParams.getSearchFieldsList().size() > 0) { 100 query.append("AND("); 101 } 102 query.append(buildQueryWithSearchParameters(searchParams.getSearchFieldsList())); 103 LOG.debug("query for search terms............." + query.toString()); 104 String searchTerms = buildQueryWithSearchParameters(searchParams.getSearchFieldsList()) 105 .replaceAll("_search", ""); 106 try { 107 searchTerms = URLDecoder.decode(searchTerms, "UTF-8"); 108 } catch (UnsupportedEncodingException e) { 109 e.printStackTrace(); 110 } 111 searchParams.setSearchTerms(searchTerms.substring(0, searchTerms.lastIndexOf(")"))); 112 } 113 if (SEARCH_TYPE_LINK.equals(searchType)) { 114 query.append("?q=id:"); 115 query.append(searchParams.getLinkValue()); 116 } 117 query.append("&rows=" + searchParams.getResultPageSize()); 118 String docType = searchParams.getDocType(); 119 LOG.info("docType-->" + docType); 120 121 query.append("&start=" + searchParams.getResultFromIndex()); 122 List<String> facetList = new ArrayList<String>(); 123 facetList.add(AUTHOR_FACET); 124 facetList.add(SUBJECT_FACET); 125 facetList.add(FORMAT_FACET); 126 facetList.add(LANGUAGE_FACET); 127 facetList.add(PUBLICATION_DATE_FACET); 128 facetList.add(GENRE_FACET); 129 query.append(buildQueryWithFacetParameters(facetList, 1, 10)); 130 LOG.debug("sort terms buildQueryWithSortFields" + searchParams.getSortByTerms()); 131 LOG.debug("sort Field buildQueryWithSortFields" + searchParams.getSortField()); 132 LOG.debug("sort Order buildQueryWithSortFields" + searchParams.getSortOrder()); 133 query.append(buildQueryWithSortFields(searchParams.getSortField(), searchParams.getSortOrder())); 134 query.append(buildQueryWithFieldListParameters(searchParams.getFieldList())); 135 Map<String, String> facetTermsMap = searchParams.getFacetTermsMap(); 136 query.append(buildFilterQuery(facetTermsMap)); 137 String holdingFields 138 = "LocalId_display,Uri_display,HoldingsNote_display,ReceiptStatus_display,CallNumber_display,CallNumberPrefix_display,CallNumberType_display,ClassificationPart_display,Location_display,ShelvingSchemeCode_display"; 139 String itemFields 140 = "LocalId_display,ItemBarcode_display,ItemTypeFullValue_display,VendorLineItemIdentifier_display,ShelvingOrderValue_display,ShelvingSchemeValue_display,PurchaseOrderLineItemIdentifier_display,CopyNumber_display,Enumeration_display,Chronology_display,VolumeNumber_display,ItemStatus_display"; 141 String instanceFields = "LocalId_display,Source_display"; 142 String patronFields 143 = "RecordNumber_display,BeginDate_display,Name_display,BorrowerType_display,BarCodeNumber_display,BarCodeStatus_display,"; 144 String onixplFields 145 = "ContractNumber_display,Title_display,Method_display,Status_display,Type_display,Licensor_display,Licensee_display"; 146 String licenseBinaryFields 147 = "Name_display,FileName_display,DateUploaded_display,Owner_display,Notes_display"; 148 String eInstanceFields = "AccessStatus_display,Imprint_display,Platform_display,StatisticalSearchingCodeValue_display,EResource_name_display"; 149 //query.append("&fl=" + fieldList); 150 query.append( 151 "&fl=LocalId_display,Title_display,Author_display,Publisher_display,Description_display,Subject_display,Location_display,PublicationDate_display,Format_display,DocType,DocFormat,id,ItemLinks,BibliographicLinks,Barcode_display,instanceIdentifier,holdingsIdentifier,itemIdentifier,bibIdentifier,staffOnlyFlag" 152 /* 153 + "," + "245a,245b" 154 + "," + "100a,110a,111a,700a,710a,711a,800a,810a,811a,400a,410a,411a" 155 + "," + "600a,610a,611a,630a,650a,651a,653a" 156 + "," + "505a" 157 + "," + "856u" 158 + "," + "260b" 159 */ + "," + holdingFields + "," + itemFields + "," + instanceFields + "," + patronFields 160 + "," + onixplFields + "," + licenseBinaryFields + "," + eInstanceFields); 161 } 162 LOG.debug("query---> " + query); 163 try { 164 String queryStr = query.toString().replaceAll(" ", "+"); 165 166 searchParams.setSearchQuery(queryStr); 167 URL url = new URL(queryStr); 168 URLConnection urlc = null; 169 urlc = url.openConnection(); 170 urlc.setDoOutput(true); 171 urlc.setAllowUserInteraction(false); 172 BufferedReader br = new BufferedReader(new InputStreamReader(urlc.getInputStream())); 173 StringBuilder sb = new StringBuilder(); 174 String line; 175 while ((line = br.readLine()) != null) { 176 sb.append(line); 177 sb.append("\n"); 178 } 179 br.close(); 180 response = sb.toString(); 181 } catch (Exception e) { 182 LOG.error("Exception:" + e.getMessage(), e); 183 } 184 return response; 185 } 186 187 public String getFieldList(String docType) { 188 String fieldList = ""; 189 String holdingFields = HOLDINGS_FIELDS; 190 String itemFields = ITEM_FIELDS; 191 String instanceFields = INSTANCE_FIELDS; 192 String bibFields = BIB_FIELDS; 193 194 if (docType.equalsIgnoreCase(BIBLIOGRAPHIC)) { 195 fieldList = bibFields; 196 197 } else if (docType.equalsIgnoreCase(INSTANCE)) { 198 fieldList = instanceFields; 199 200 } else if (docType.equalsIgnoreCase(HOLDINGS)) { 201 fieldList = holdingFields; 202 203 } else if (docType.equalsIgnoreCase(ITEM)) { 204 fieldList = itemFields; 205 } 206 return fieldList; 207 } 208 209 210 public static void setDocSearchUrl(String docSearchUrl) { 211 } 212 213 public static String getDocSearchUrl() { 214 return docSearchUrl; 215 } 216 217 public String buildQuery(SearchParams searchParams) { 218 StringBuffer query = new StringBuffer(); 219 String searchType = searchParams.getSearchType(); 220 query.append(SolrServerManager.getInstance().getSolrCoreURL() + "/select"); 221 buildInitialQuery(query, searchParams); 222 if (searchParams.getSearchFieldsList().size() > 0) { 223 query.append("AND("); 224 } 225 query.append(buildQueryWithSearchParameters(searchParams.getSearchFieldsList())); 226 if (searchParams.getResultPageSize() != null) { 227 query.append("&rows=" + searchParams.getResultPageSize()); 228 } 229 if (searchParams.getResultFromIndex() != null) { 230 query.append("&start=" + searchParams.getResultFromIndex()); 231 } 232 List<String> facetFieldList = searchParams.getFacetFieldList(); 233 query.append(buildQueryWithFacetParameters(facetFieldList, 1, 10)); 234 query.append(buildQueryWithSortFields(searchParams.getSortField(), searchParams.getSortOrder())); 235 query.append(buildQueryWithFieldListParameters(searchParams.getFieldList())); 236 Map<String, String> facetTermsMap = searchParams.getFacetTermsMap(); 237 query.append(buildFilterQuery(facetTermsMap)); 238 239 return query.toString(); 240 } 241 242 243 public String buildQueryWithSearchParameters(List<SearchCondition> searchFieldsList) { 244 SearchCondition docSearchFieldsDTO = null; 245 StringBuffer queryStringbuffer = new StringBuffer(); 246 StringBuffer highlightBuffer = new StringBuffer("&hl.fl="); 247 if (searchFieldsList != null && searchFieldsList.size() > 0) { 248 249 for (int i = 0; i < searchFieldsList.size(); i++) { 250 int searchScopeAddLimit = i; 251 docSearchFieldsDTO = searchFieldsList.get(i); 252 if (docSearchFieldsDTO.getOperator() != null) { 253 //queryStringbuffer.append(docSearchFieldsDTO.getOperator()); 254 } 255 queryStringbuffer.append("("); 256 if (docSearchFieldsDTO.getDocField().equalsIgnoreCase("all")) { 257 queryStringbuffer.append("all_text"); 258 highlightBuffer.append("*"); 259 260 } else { 261 queryStringbuffer.append(docSearchFieldsDTO.getDocField()); 262 highlightBuffer.append(docSearchFieldsDTO.getDocField()); 263 264 265 if (i != searchFieldsList.size() - 1) { 266 highlightBuffer.append(","); 267 } 268 } 269 queryStringbuffer.append(":"); 270 String searchScope = docSearchFieldsDTO.getSearchScope(); 271 String searchText = docSearchFieldsDTO.getSearchText(); 272 String searchOperator = docSearchFieldsDTO.getOperator(); 273// searchText = searchText.toLowerCase(); 274 LOG.debug("searchText-->" + searchText); 275// searchText = searchText.replaceAll("[~!(){}\\[\\]':-]+"," "); 276// String modifiedSearchText = searchText.replaceAll("[~!(){}<>\\[\\]':\\-\\\\^]+", " ").toLowerCase(); 277 String modifiedSearchText = getModifiedText(searchText); 278 String searchTextVal = null; 279 if (modifiedSearchText.length() > 0) { 280 queryStringbuffer.append("("); 281 if (searchScope.equalsIgnoreCase("AND")) { 282 modifiedSearchText = modifiedSearchText.replaceAll("\\s+", " "); 283 searchTextVal = modifiedSearchText.trim().replace(" ", " AND "); 284 } else if (searchScope.equalsIgnoreCase("OR")) { 285 modifiedSearchText = modifiedSearchText.replaceAll("\\s+", " "); 286 searchTextVal = modifiedSearchText.trim().replace(" ", " OR "); 287 } else if (searchScope.equalsIgnoreCase("phrase")) { 288 searchTextVal = "\"" + searchText + "\""; 289 } 290 try { 291 searchTextVal = URLEncoder.encode(searchTextVal, "UTF-8"); 292 } catch (UnsupportedEncodingException e) { 293 e.printStackTrace(); 294 } 295 queryStringbuffer.append(searchTextVal); 296 LOG.debug("searchTextVal............" + searchTextVal + "........" + queryStringbuffer.toString()); 297 queryStringbuffer.append(")"); 298 } 299 queryStringbuffer.append(")"); 300 ++searchScopeAddLimit; 301 if (searchScopeAddLimit != searchFieldsList.size()) { 302 queryStringbuffer.append(searchOperator); 303 } 304 } 305 queryStringbuffer.append(")"); 306 String highLight = highlightBuffer.toString().replace("LocalId_search", "LocalId_display"); 307 queryStringbuffer.append(highLight); 308 309 queryStringbuffer.append("&hl=true"); 310 } 311 312 313 return queryStringbuffer.toString(); 314 } 315 316 public String buildQueryWithFacetParameters(List<String> facetsParametersList, int facetMinCount, int facetLimit) { 317 String facetFieldName = ""; 318 String queryWithFacetParameters = ""; 319 if (facetsParametersList != null) { 320 StringBuffer facetsQueryStringbuffer = new StringBuffer(); 321 322 facetsQueryStringbuffer.append("&facet=true"); 323 324 facetsQueryStringbuffer.append("&facet.mincount=" + facetMinCount); 325 326 facetsQueryStringbuffer.append("&"); 327 328 for (int i = 0; i < facetsParametersList.size(); i++) { 329 facetFieldName = facetsParametersList.get(i); 330 facetsQueryStringbuffer.append("facet.field=" + facetFieldName); 331 facetsQueryStringbuffer.append("&"); 332 333 } 334 queryWithFacetParameters = facetsQueryStringbuffer.substring(0, facetsQueryStringbuffer.length() - 1); 335 } 336 return queryWithFacetParameters; 337 } 338 339 340 public String buildQueryWithSortFields(String sortField, String sortOrder) { 341 StringBuffer sortFieldsQuery = new StringBuffer(); 342 if (null != sortField) { 343 sortFieldsQuery.append("&"); 344 sortFieldsQuery.append("sort="); 345 sortFieldsQuery.append(sortField); 346 if (null != sortOrder) { 347 sortFieldsQuery.append(" "); 348 sortFieldsQuery.append(sortOrder); 349 } 350 } 351 return sortFieldsQuery.toString(); 352 } 353 354 public String buildQueryWithFieldListParameters(List<String> fieldsList) { 355 String queryWithFieldListParameters = ""; 356 if (fieldsList != null) { 357 StringBuffer fieldsListQueryStringbuffer = new StringBuffer(); 358 fieldsListQueryStringbuffer.append("&"); 359 fieldsListQueryStringbuffer.append("fl="); 360 for (int i = 0; i < fieldsList.size(); i++) { 361 fieldsListQueryStringbuffer.append(fieldsList.get(i)); 362 fieldsListQueryStringbuffer.append(","); 363 } 364 queryWithFieldListParameters = fieldsListQueryStringbuffer 365 .substring(0, fieldsListQueryStringbuffer.length() - 1); 366 } 367 return queryWithFieldListParameters; 368 } 369 370 public static String convertListToStringFieldValues(Map<String, String> map) { 371 StringBuffer sb = new StringBuffer(); 372 Set set = map.keySet(); 373 Iterator<String> ite = set.iterator(); 374 while (ite.hasNext()) { 375 sb.append(ite.next()); 376 sb.append("|"); 377 } 378 String str = sb.toString(); 379 if (str != null && str.length() > 0) { 380 str = str.substring(0, str.length() - 1); 381 } 382 return str; 383 } 384 385 public String buildFilterQuery(Map<String, String> facetTermsMap) { 386 String filterQuery = ""; 387 int temp = 0; 388 int tokenInt = 0; 389 if ((null != facetTermsMap) && (facetTermsMap.size() > 0)) { 390 391 String facetTerms = convertListToStringFieldValues(facetTermsMap); 392 StringBuffer facetQueryTemp = new StringBuffer(); 393 facetQueryTemp.append("&terms=" + facetTerms); 394 facetQueryTemp.append("&fq="); 395 StringTokenizer sttoken = new StringTokenizer(facetTerms, "|"); 396 String token; 397 while (sttoken.hasMoreElements()) { 398 token = sttoken.nextToken(); 399 facetQueryTemp.append("("); 400 facetQueryTemp.append(facetTermsMap.get(token)); 401 facetQueryTemp.append(":\""); 402 facetQueryTemp.append(token); 403 facetQueryTemp.append("\")"); 404 facetQueryTemp.append("AND"); 405 } 406 if (facetQueryTemp.length() > 0) { 407 filterQuery = (facetQueryTemp.toString().substring(0, facetQueryTemp.toString().length() - 3)); 408 } 409 } 410 return filterQuery; 411 } 412 413 414 /** 415 * @param query 416 * @param searchParams Usage: This method builds initial SOLR query with DocType and DocFormat as SolrParams 417 */ 418 private void buildInitialQuery(StringBuffer query, SearchParams searchParams) { 419 query.append("?q="); 420 //query.append("(DocType:" + searchParams.getDocType() + ")"); 421 if (searchParams.getDocFormat().equalsIgnoreCase("marc")) { 422 query.append("((DocType:" + searchParams.getDocType() + ")" + "OR(DocType:item))"); 423 } else { 424 query.append("(DocType:" + searchParams.getDocType() + ")"); 425 } 426 427 if (searchParams.getDocFormat() != null && !searchParams.getDocFormat().equalsIgnoreCase("all")) { 428 if ("dublin".equals(searchParams.getDocFormat())) { 429 searchParams.setDocFormat("dublin"); 430 } 431 query.append("AND(DocFormat:" + searchParams.getDocFormat() + ")"); 432 } 433 } 434 435 private String getModifiedText(String searchText) { 436 StringBuffer modifiedText = new StringBuffer(); 437 StringCharacterIterator stringCharacterIterator = new StringCharacterIterator(searchText); 438 char character = stringCharacterIterator.current(); 439 while (character != CharacterIterator.DONE) { 440 441 if (character == '\\') { 442 modifiedText.append("\\\\"); 443 } else if (character == '?') { 444 modifiedText.append("\\?"); 445 } else if (character == '*' && StringUtils.isEmpty(modifiedText.toString())) { 446 modifiedText.append("\\*"); 447 } else if (character == '+') { 448 modifiedText.append("\\+"); 449 } else if (character == ':') { 450 modifiedText.append("\\:"); 451 } else if (character == '{') { 452 modifiedText.append("\\{"); 453 } else if (character == '}') { 454 modifiedText.append("\\}"); 455 } else if (character == '[') { 456 modifiedText.append("\\["); 457 } else if (character == ']') { 458 modifiedText.append("\\]"); 459 } else if (character == '(') { 460 modifiedText.append("\\("); 461 } else if (character == ')') { 462 modifiedText.append("\\)"); 463 } else if (character == '^') { 464 modifiedText.append("\\^"); 465 } else if (character == '~') { 466 modifiedText.append("\\~"); 467 } else if (character == '-') { 468 modifiedText.append("\\-"); 469 } else if (character == '!') { 470 modifiedText.append("\\!"); 471 } else if (character == '\'') { 472 modifiedText.append("\\'"); 473 } else if (character == '@') { 474 modifiedText.append("\\@"); 475 } else if (character == '#') { 476 modifiedText.append("\\#"); 477 } else if (character == '$') { 478 modifiedText.append("\\$"); 479 } else if (character == '%') { 480 modifiedText.append("\\%"); 481 } else { 482 //the char is not a special one 483 //add it to the result as is 484 modifiedText.append(character); 485 } 486 character = stringCharacterIterator.next(); 487 } 488 489 return modifiedText.toString().toLowerCase(); 490 } 491 492}