1 23 package org.ofbiz.product.product; 24 25 import java.sql.Timestamp ; 26 import java.util.ArrayList ; 27 import java.util.Collection ; 28 import java.util.HashSet ; 29 import java.util.Iterator ; 30 import java.util.LinkedList ; 31 import java.util.List ; 32 import java.util.Map ; 33 import java.util.Set ; 34 import java.util.TreeSet ; 35 36 import org.ofbiz.base.util.Debug; 37 import org.ofbiz.base.util.UtilDateTime; 38 import org.ofbiz.base.util.UtilMisc; 39 import org.ofbiz.base.util.UtilProperties; 40 import org.ofbiz.base.util.UtilValidate; 41 import org.ofbiz.entity.GenericDelegator; 42 import org.ofbiz.entity.GenericEntityException; 43 import org.ofbiz.entity.GenericValue; 44 import org.ofbiz.entity.condition.EntityCondition; 45 import org.ofbiz.entity.condition.EntityConditionList; 46 import org.ofbiz.entity.condition.EntityExpr; 47 import org.ofbiz.entity.condition.EntityOperator; 48 import org.ofbiz.entity.model.DynamicViewEntity; 49 import org.ofbiz.entity.model.ModelKeyMap; 50 import org.ofbiz.entity.model.ModelViewEntity.ComplexAlias; 51 import org.ofbiz.entity.model.ModelViewEntity.ComplexAliasField; 52 import org.ofbiz.entity.transaction.GenericTransactionException; 53 import org.ofbiz.entity.transaction.TransactionUtil; 54 import org.ofbiz.entity.util.EntityFindOptions; 55 import org.ofbiz.entity.util.EntityListIterator; 56 import org.ofbiz.entity.util.EntityUtil; 57 import org.ofbiz.party.party.PartyHelper; 58 59 60 67 public class ProductSearch { 68 69 public static final String module = ProductSearch.class.getName(); 70 public static final String resource = "ProductUiLabels"; 71 72 public static ArrayList parametricKeywordSearch(Map featureIdByType, String keywordsString, GenericDelegator delegator, String productCategoryId, String visitId, boolean anyPrefix, boolean anySuffix, boolean isAnd) { 73 Set featureIdSet = new HashSet (); 74 if (featureIdByType != null) { 75 featureIdSet.addAll(featureIdByType.values()); 76 } 77 78 return parametricKeywordSearch(featureIdSet, keywordsString, delegator, productCategoryId, true, visitId, anyPrefix, anySuffix, isAnd); 79 } 80 81 public static ArrayList parametricKeywordSearch(Set featureIdSet, String keywordsString, GenericDelegator delegator, String productCategoryId, boolean includeSubCategories, String visitId, boolean anyPrefix, boolean anySuffix, boolean isAnd) { 82 List productSearchConstraintList = new LinkedList (); 83 84 if (UtilValidate.isNotEmpty(productCategoryId)) { 85 productSearchConstraintList.add(new CategoryConstraint(productCategoryId, includeSubCategories)); 86 } 87 88 if (UtilValidate.isNotEmpty(keywordsString)) { 89 productSearchConstraintList.add(new KeywordConstraint(keywordsString, anyPrefix, anySuffix, null, isAnd)); 90 } 91 92 if (featureIdSet != null && featureIdSet.size() > 0) { 93 Iterator featureIdIter = featureIdSet.iterator(); 94 while (featureIdIter.hasNext()) { 95 String productFeatureId = (String ) featureIdIter.next(); 96 productSearchConstraintList.add(new FeatureConstraint(productFeatureId)); 97 } 98 } 99 100 return searchProducts(productSearchConstraintList, new SortKeywordRelevancy(), delegator, visitId); 101 } 102 103 public static ArrayList searchProducts(List productSearchConstraintList, ResultSortOrder resultSortOrder, GenericDelegator delegator, String visitId) { 104 ProductSearchContext productSearchContext = new ProductSearchContext(delegator, visitId); 105 106 productSearchContext.addProductSearchConstraints(productSearchConstraintList); 107 productSearchContext.setResultSortOrder(resultSortOrder); 108 109 ArrayList productIds = productSearchContext.doSearch(); 110 return productIds; 111 } 112 113 public static void getAllSubCategoryIds(String productCategoryId, Set productCategoryIdSet, GenericDelegator delegator, Timestamp nowTimestamp) { 114 if (nowTimestamp == null) { 115 nowTimestamp = UtilDateTime.nowTimestamp(); 116 } 117 118 120 productCategoryIdSet.add(productCategoryId); 122 123 try { 125 List productCategoryRollupList = delegator.findByAndCache("ProductCategoryRollup", UtilMisc.toMap("parentProductCategoryId", productCategoryId)); 126 Iterator productCategoryRollupIter = productCategoryRollupList.iterator(); 127 while (productCategoryRollupIter.hasNext()) { 128 GenericValue productCategoryRollup = (GenericValue) productCategoryRollupIter.next(); 129 130 String subProductCategoryId = productCategoryRollup.getString("productCategoryId"); 131 if (productCategoryIdSet.contains(subProductCategoryId)) { 132 continue; 134 } 135 136 if (EntityUtil.isValueActive(productCategoryRollup, nowTimestamp)) { 138 getAllSubCategoryIds(subProductCategoryId, productCategoryIdSet, delegator, nowTimestamp); 139 } 140 } 141 } catch (GenericEntityException e) { 142 Debug.logError(e, "Error finding sub-categories for product search", module); 143 } 144 } 145 146 public static class ProductSearchContext { 147 public int index = 1; 148 public List entityConditionList = new LinkedList (); 149 public List orderByList = new LinkedList (); 150 public List fieldsToSelect = UtilMisc.toList("productId"); 151 public DynamicViewEntity dynamicViewEntity = new DynamicViewEntity(); 152 public boolean productIdGroupBy = false; 153 public boolean includedKeywordSearch = false; 154 public Timestamp nowTimestamp = UtilDateTime.nowTimestamp(); 155 public List keywordFixedOrSetAndList = new LinkedList (); 156 public Set orKeywordFixedSet = new HashSet (); 157 public Set andKeywordFixedSet = new HashSet (); 158 public List productSearchConstraintList = new LinkedList (); 159 public ResultSortOrder resultSortOrder = null; 160 public Integer resultOffset = null; 161 public Integer maxResults = null; 162 protected GenericDelegator delegator = null; 163 protected String visitId = null; 164 protected Integer totalResults = null; 165 166 public ProductSearchContext(GenericDelegator delegator, String visitId) { 167 this.delegator = delegator; 168 this.visitId = visitId; 169 dynamicViewEntity.addMemberEntity("PROD", "Product"); 170 dynamicViewEntity.addMemberEntity("PRODCI", "ProductCalculatedInfo"); 171 dynamicViewEntity.addViewLink("PROD", "PRODCI", Boolean.TRUE, ModelKeyMap.makeKeyMapList("productId")); 172 } 173 174 public GenericDelegator getDelegator() { 175 return this.delegator; 176 } 177 178 public void addProductSearchConstraints(List productSearchConstraintList) { 179 Iterator productSearchConstraintIter = productSearchConstraintList.iterator(); 181 while (productSearchConstraintIter.hasNext()) { 182 ProductSearchConstraint constraint = (ProductSearchConstraint) productSearchConstraintIter.next(); 183 constraint.addConstraint(this); 184 } 185 } 186 187 public void setResultSortOrder(ResultSortOrder resultSortOrder) { 188 this.resultSortOrder = resultSortOrder; 189 } 190 191 public void setResultOffset(Integer resultOffset) { 192 this.resultOffset = resultOffset; 193 } 194 195 public void setMaxResults(Integer maxResults) { 196 this.maxResults = maxResults; 197 } 198 199 public Integer getTotalResults() { 200 return this.totalResults; 201 } 202 203 public ArrayList doSearch() { 204 long startMillis = System.currentTimeMillis(); 205 206 EntityListIterator eli = this.doQuery(delegator); 208 ArrayList productIds = this.makeProductIdList(eli); 209 if (eli != null) { 210 try { 211 eli.close(); 212 } catch (GenericEntityException e) { 213 Debug.logError(e, "Error closing ProductSearch EntityListIterator"); 214 } 215 } 216 217 long endMillis = System.currentTimeMillis(); 218 double totalSeconds = ((double)endMillis - (double)startMillis)/1000.0; 219 220 this.saveSearchResultInfo(new Long (productIds.size()), new Double (totalSeconds)); 222 223 return productIds; 224 } 225 226 public void finishKeywordConstraints() { 227 if (orKeywordFixedSet.size() == 0 && andKeywordFixedSet.size() == 0 && keywordFixedOrSetAndList.size() == 0) { 228 return; 229 } 230 231 this.includedKeywordSearch = true; 233 234 if (orKeywordFixedSet.size() > 0) { 236 keywordFixedOrSetAndList.add(orKeywordFixedSet); 238 } 239 240 Iterator keywordFixedOrSetAndTestIter = keywordFixedOrSetAndList.iterator(); 242 while (keywordFixedOrSetAndTestIter.hasNext()) { 243 Set keywordFixedOrSet = (Set ) keywordFixedOrSetAndTestIter.next(); 244 if (keywordFixedOrSet.size() == 0) { 245 keywordFixedOrSetAndTestIter.remove(); 246 } else if (keywordFixedOrSet.size() == 1) { 247 andKeywordFixedSet.add(keywordFixedOrSet.iterator().next()); 249 keywordFixedOrSetAndTestIter.remove(); 250 } 251 } 252 253 boolean doingBothAndOr = (keywordFixedOrSetAndList.size() > 1) || (keywordFixedOrSetAndList.size() > 0 && andKeywordFixedSet.size() > 0); 254 255 Debug.logInfo("Finished initial setup of keywords, doingBothAndOr=" + doingBothAndOr + ", andKeywordFixedSet=" + andKeywordFixedSet + "\n keywordFixedOrSetAndList=" + keywordFixedOrSetAndList, module); 256 257 ComplexAlias relevancyComplexAlias = new ComplexAlias("+"); 258 if (andKeywordFixedSet.size() > 0) { 259 261 Iterator keywordIter = andKeywordFixedSet.iterator(); 262 while (keywordIter.hasNext()) { 263 String keyword = (String ) keywordIter.next(); 264 265 String entityAlias = "PK" + index; 267 String prefix = "pk" + index; 268 index++; 269 270 dynamicViewEntity.addMemberEntity(entityAlias, "ProductKeyword"); 271 dynamicViewEntity.addAlias(entityAlias, prefix + "Keyword", "keyword", null, null, null, null); 272 dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId")); 273 entityConditionList.add(new EntityExpr(prefix + "Keyword", EntityOperator.LIKE, keyword)); 274 275 relevancyComplexAlias.addComplexAliasMember(new ComplexAliasField(entityAlias, "relevancyWeight", null, null)); 277 } 278 279 281 if (!doingBothAndOr) { 282 dynamicViewEntity.addAlias(null, "totalRelevancy", null, null, null, null, null, relevancyComplexAlias); 283 } 284 } 285 if (keywordFixedOrSetAndList.size() > 0) { 286 Iterator keywordFixedOrSetAndIter = keywordFixedOrSetAndList.iterator(); 287 while (keywordFixedOrSetAndIter.hasNext()) { 288 Set keywordFixedOrSet = (Set ) keywordFixedOrSetAndIter.next(); 289 String entityAlias = "PK" + index; 291 String prefix = "pk" + index; 292 index++; 293 294 dynamicViewEntity.addMemberEntity(entityAlias, "ProductKeyword"); 295 dynamicViewEntity.addAlias(entityAlias, prefix + "Keyword", "keyword", null, null, null, null); 296 dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId")); 297 List keywordOrList = new LinkedList (); 298 Iterator keywordIter = keywordFixedOrSet.iterator(); 299 while (keywordIter.hasNext()) { 300 String keyword = (String ) keywordIter.next(); 301 keywordOrList.add(new EntityExpr(prefix + "Keyword", EntityOperator.LIKE, keyword)); 302 } 303 entityConditionList.add(new EntityConditionList(keywordOrList, EntityOperator.OR)); 304 305 productIdGroupBy = true; 306 307 if (doingBothAndOr) { 308 relevancyComplexAlias.addComplexAliasMember(new ComplexAliasField(entityAlias, "relevancyWeight", null, "sum")); 309 } else { 310 dynamicViewEntity.addAlias(entityAlias, "totalRelevancy", "relevancyWeight", null, null, null, "sum"); 311 } 312 } 313 } 314 315 if (doingBothAndOr) { 316 dynamicViewEntity.addAlias(null, "totalRelevancy", null, null, null, null, null, relevancyComplexAlias); 317 } 318 } 319 320 public EntityListIterator doQuery(GenericDelegator delegator) { 321 this.finishKeywordConstraints(); 323 324 if (resultSortOrder != null) { 325 resultSortOrder.setSortOrder(this); 326 } 327 328 dynamicViewEntity.addAlias("PROD", "productId", null, null, null, new Boolean (productIdGroupBy), null); 329 EntityCondition whereCondition = new EntityConditionList(entityConditionList, EntityOperator.AND); 330 EntityFindOptions efo = new EntityFindOptions(); 331 efo.setDistinct(true); 332 efo.setResultSetType(EntityFindOptions.TYPE_SCROLL_INSENSITIVE); 333 334 EntityListIterator eli = null; 335 try { 336 eli = delegator.findListIteratorByCondition(dynamicViewEntity, whereCondition, null, fieldsToSelect, orderByList, efo); 337 } catch (GenericEntityException e) { 338 Debug.logError(e, "Error in product search", module); 339 return null; 340 } 341 342 return eli; 343 } 344 345 public ArrayList makeProductIdList(EntityListIterator eli) { 346 ArrayList productIds = new ArrayList (maxResults == null ? 100 : maxResults.intValue()); 347 if (eli == null) { 348 Debug.logWarning("The eli is null, returning zero results", module); 349 return productIds; 350 } 351 352 try { 353 boolean hasResults = false; 354 Object initialResult = null; 355 356 367 368 initialResult = eli.next(); 369 if (initialResult != null) { 370 hasResults = true; 371 } 372 if (resultOffset != null && resultOffset.intValue() > 1) { 373 if (Debug.infoOn()) Debug.logInfo("Before relative, current index=" + eli.currentIndex(), module); 374 hasResults = eli.relative(resultOffset.intValue() - 1); 375 initialResult = null; 376 } 377 378 GenericValue searchResult = null; 380 if (hasResults) { 381 if (initialResult != null) { 382 searchResult = (GenericValue) initialResult; 383 } else { 384 searchResult = eli.currentGenericValue(); 385 } 386 } 387 388 if (searchResult == null) { 389 int failTotal = 0; 391 if (this.resultOffset != null) { 392 failTotal = this.resultOffset.intValue() - 1; 393 } 394 this.totalResults = new Integer (failTotal); 395 return productIds; 396 } 397 398 399 int numRetreived = 1; 401 int duplicatesFound = 0; 402 403 Set productIdSet = new HashSet (); 404 405 productIds.add(searchResult.getString("productId")); 406 productIdSet.add(searchResult.getString("productId")); 407 408 while (((searchResult = (GenericValue) eli.next()) != null) && (maxResults == null || numRetreived < maxResults.intValue())) { 409 String productId = searchResult.getString("productId"); 410 if (!productIdSet.contains(productId)) { 411 productIds.add(productId); 412 productIdSet.add(productId); 413 numRetreived++; 414 } else { 415 duplicatesFound++; 416 } 417 418 432 } 433 434 if (searchResult != null) { 435 if (eli.last()) { 438 this.totalResults = new Integer (eli.currentIndex()); 439 } 441 } 442 if (this.totalResults == null || this.totalResults.intValue() == 0) { 443 int total = numRetreived; 444 if (this.resultOffset != null) { 445 total += (this.resultOffset.intValue() - 1); 446 } 447 this.totalResults = new Integer (total); 448 } 449 450 Debug.logInfo("Got search values, numRetreived=" + numRetreived + ", totalResults=" + totalResults + ", maxResults=" + maxResults + ", resultOffset=" + resultOffset + ", duplicatesFound(in the current results)=" + duplicatesFound, module); 451 452 } catch (GenericEntityException e) { 453 Debug.logError(e, "Error getting results from the product search query", module); 454 } 455 return productIds; 456 } 457 458 public void saveSearchResultInfo(Long numResults, Double secondsTotal) { 459 461 try { 462 boolean beganTransaction = TransactionUtil.begin(); 464 465 try { 466 467 GenericValue productSearchResult = delegator.makeValue("ProductSearchResult", null); 468 String productSearchResultId = delegator.getNextSeqId("ProductSearchResult"); 469 470 productSearchResult.set("productSearchResultId", productSearchResultId); 471 productSearchResult.set("visitId", this.visitId); 472 if (this.resultSortOrder != null) { 473 productSearchResult.set("orderByName", this.resultSortOrder.getOrderName()); 474 productSearchResult.set("isAscending", this.resultSortOrder.isAscending() ? "Y" : "N"); 475 } 476 productSearchResult.set("numResults", numResults); 477 productSearchResult.set("secondsTotal", secondsTotal); 478 productSearchResult.set("searchDate", nowTimestamp); 479 productSearchResult.create(); 480 481 Iterator productSearchConstraintIter = productSearchConstraintList.iterator(); 482 int seqId = 1; 483 while (productSearchConstraintIter.hasNext()) { 484 GenericValue productSearchConstraint = (GenericValue) productSearchConstraintIter.next(); 485 productSearchConstraint.set("productSearchResultId", productSearchResultId); 486 productSearchConstraint.set("constraintSeqId", Integer.toString(seqId)); 487 productSearchConstraint.create(); 488 seqId++; 489 } 490 491 TransactionUtil.commit(beganTransaction); 492 } catch (GenericEntityException e1) { 493 String errMsg = "Error saving product search result info/stats"; 494 Debug.logError(e1, errMsg, module); 495 TransactionUtil.rollback(beganTransaction, errMsg, e1); 496 } 497 } catch (GenericTransactionException e) { 498 Debug.logError(e, "Error saving product search result info/stats", module); 499 } 500 } 501 } 502 503 507 public static abstract class ProductSearchConstraint implements java.io.Serializable { 508 public ProductSearchConstraint() { } 509 510 public abstract void addConstraint(ProductSearchContext productSearchContext); 511 512 public abstract String prettyPrintConstraint(GenericDelegator delegator, boolean detailed); 513 } 514 515 public static class CategoryConstraint extends ProductSearchConstraint { 516 public static final String constraintName = "Category"; 517 protected String productCategoryId; 518 protected boolean includeSubCategories; 519 520 public CategoryConstraint(String productCategoryId, boolean includeSubCategories) { 521 this.productCategoryId = productCategoryId; 522 this.includeSubCategories = includeSubCategories; 523 } 524 525 public void addConstraint(ProductSearchContext productSearchContext) { 526 List productCategoryIdList = null; 527 if (includeSubCategories) { 528 Set productCategoryIdSet = new HashSet (); 530 ProductSearch.getAllSubCategoryIds(productCategoryId, productCategoryIdSet, productSearchContext.getDelegator(), productSearchContext.nowTimestamp); 531 productCategoryIdList = new ArrayList (productCategoryIdSet); 532 } else { 533 productCategoryIdList = UtilMisc.toList(productCategoryId); 534 } 535 536 String entityAlias = "PCM" + productSearchContext.index; 538 String prefix = "pcm" + productSearchContext.index; 539 productSearchContext.index++; 540 541 productSearchContext.dynamicViewEntity.addMemberEntity(entityAlias, "ProductCategoryMember"); 542 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "ProductCategoryId", "productCategoryId", null, null, null, null); 543 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "FromDate", "fromDate", null, null, null, null); 544 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "ThruDate", "thruDate", null, null, null, null); 545 productSearchContext.dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId")); 546 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "ProductCategoryId", EntityOperator.IN, productCategoryIdList)); 547 productSearchContext.entityConditionList.add(new EntityExpr(new EntityExpr(prefix + "ThruDate", EntityOperator.EQUALS, null), EntityOperator.OR, new EntityExpr(prefix + "ThruDate", EntityOperator.GREATER_THAN, productSearchContext.nowTimestamp))); 548 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "FromDate", EntityOperator.LESS_THAN, productSearchContext.nowTimestamp)); 549 550 productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", this.productCategoryId, "includeSubCategories", this.includeSubCategories ? "Y" : "N"))); 552 } 553 554 555 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 556 GenericValue productCategory = null; 557 try { 558 productCategory = delegator.findByPrimaryKeyCache("ProductCategory", UtilMisc.toMap("productCategoryId", productCategoryId)); 559 } catch (GenericEntityException e) { 560 Debug.logError(e, "Error finding ProductCategory information for constraint pretty print", module); 561 } 562 StringBuffer ppBuf = new StringBuffer (); 563 ppBuf.append("Category: "); 564 if (productCategory != null) { 565 ppBuf.append(productCategory.getString("description")); 566 } 567 if (productCategory == null || detailed) { 568 ppBuf.append(" ["); 569 ppBuf.append(productCategoryId); 570 ppBuf.append("]"); 571 } 572 if (includeSubCategories) { 573 ppBuf.append(" (and all sub-categories)"); 574 } 575 return ppBuf.toString(); 576 } 577 578 public boolean equals(Object obj) { 579 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 580 if (psc instanceof CategoryConstraint) { 581 CategoryConstraint that = (CategoryConstraint) psc; 582 if (this.includeSubCategories != that.includeSubCategories) { 583 return false; 584 } 585 if (this.productCategoryId == null) { 586 if (that.productCategoryId != null) { 587 return false; 588 } 589 } else { 590 if (!this.productCategoryId.equals(that.productCategoryId)) { 591 return false; 592 } 593 } 594 return true; 595 } else { 596 return false; 597 } 598 } 599 } 600 601 public static class FeatureConstraint extends ProductSearchConstraint { 602 public static final String constraintName = "Feature"; 603 protected String productFeatureId; 604 605 public FeatureConstraint(String productFeatureId) { 606 this.productFeatureId = productFeatureId; 607 } 608 609 public void addConstraint(ProductSearchContext productSearchContext) { 610 String entityAlias = "PFA" + productSearchContext.index; 612 String prefix = "pfa" + productSearchContext.index; 613 productSearchContext.index++; 614 615 productSearchContext.dynamicViewEntity.addMemberEntity(entityAlias, "ProductFeatureAppl"); 616 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "ProductFeatureId", "productFeatureId", null, null, null, null); 617 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "FromDate", "fromDate", null, null, null, null); 618 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "ThruDate", "thruDate", null, null, null, null); 619 productSearchContext.dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId")); 620 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "ProductFeatureId", EntityOperator.EQUALS, productFeatureId)); 621 productSearchContext.entityConditionList.add(new EntityExpr(new EntityExpr(prefix + "ThruDate", EntityOperator.EQUALS, null), EntityOperator.OR, new EntityExpr(prefix + "ThruDate", EntityOperator.GREATER_THAN, productSearchContext.nowTimestamp))); 622 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "FromDate", EntityOperator.LESS_THAN, productSearchContext.nowTimestamp)); 623 624 productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", this.productFeatureId))); 626 } 627 628 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 629 GenericValue productFeature = null; 630 GenericValue productFeatureType = null; 631 try { 632 productFeature = delegator.findByPrimaryKeyCache("ProductFeature", UtilMisc.toMap("productFeatureId", productFeatureId)); 633 productFeatureType = productFeature == null ? null : productFeature.getRelatedOne("ProductFeatureType"); 634 } catch (GenericEntityException e) { 635 Debug.logError(e, "Error finding ProductFeature and Type information for constraint pretty print", module); 636 } 637 return (productFeatureType == null ? "Feature: " : productFeatureType.getString("description") + ": ") + (productFeature == null ? "[" + this.productFeatureId + "]" : productFeature.getString("description")); 638 } 639 640 public boolean equals(Object obj) { 641 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 642 if (psc instanceof FeatureConstraint) { 643 FeatureConstraint that = (FeatureConstraint) psc; 644 if (this.productFeatureId == null) { 645 if (that.productFeatureId != null) { 646 return false; 647 } 648 } else { 649 if (!this.productFeatureId.equals(that.productFeatureId)) { 650 return false; 651 } 652 } 653 return true; 654 } else { 655 return false; 656 } 657 } 658 } 659 660 public static class FeatureSetConstraint extends ProductSearchConstraint { 661 public static final String constraintName = "Feature Set"; 662 protected Set productFeatureIdSet; 663 664 public FeatureSetConstraint(Collection productFeatureIdSet) { 665 this.productFeatureIdSet = new HashSet (productFeatureIdSet); 666 } 667 668 public void addConstraint(ProductSearchContext productSearchContext) { 669 String entityAlias = "PFA" + productSearchContext.index; 671 String prefix = "pfa" + productSearchContext.index; 672 productSearchContext.index++; 673 674 productSearchContext.dynamicViewEntity.addMemberEntity(entityAlias, "ProductFeatureAppl"); 675 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "ProductFeatureId", "productFeatureId", null, null, null, null); 676 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "FromDate", "fromDate", null, null, null, null); 677 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "ThruDate", "thruDate", null, null, null, null); 678 productSearchContext.dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId")); 679 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "ProductFeatureId", EntityOperator.IN, productFeatureIdSet)); 680 productSearchContext.entityConditionList.add(new EntityExpr(new EntityExpr(prefix + "ThruDate", EntityOperator.EQUALS, null), EntityOperator.OR, new EntityExpr(prefix + "ThruDate", EntityOperator.GREATER_THAN, productSearchContext.nowTimestamp))); 681 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "FromDate", EntityOperator.LESS_THAN, productSearchContext.nowTimestamp)); 682 683 StringBuffer featureIdInfo = new StringBuffer (); 685 Iterator featureIdIter = this.productFeatureIdSet.iterator(); 686 while (featureIdIter.hasNext()) { 687 String featureId = (String ) featureIdIter.next(); 688 featureIdInfo.append(featureId); 689 if (featureIdIter.hasNext()) { 690 featureIdInfo.append(","); 691 } 692 } 693 694 productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", featureIdInfo.toString()))); 695 } 696 697 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 698 StringBuffer infoOut = new StringBuffer (); 699 try { 700 Iterator featureIdIter = this.productFeatureIdSet.iterator(); 701 while (featureIdIter.hasNext()) { 702 String featureId = (String ) featureIdIter.next(); 703 GenericValue productFeature = delegator.findByPrimaryKeyCache("ProductFeature", UtilMisc.toMap("productFeatureId", featureId)); 704 GenericValue productFeatureType = productFeature == null ? null : productFeature.getRelatedOneCache("ProductFeatureType"); 705 if (productFeatureType == null) { 706 infoOut.append("Feature: "); 707 } else { 708 infoOut.append(productFeatureType.getString("description")); 709 infoOut.append(": "); 710 } 711 if (productFeature == null) { 712 infoOut.append("["); 713 infoOut.append(featureId); 714 infoOut.append("]"); 715 } else { 716 infoOut.append(productFeature.getString("description")); 717 } 718 719 if (featureIdIter.hasNext()) { 720 infoOut.append(", "); 721 } 722 } 723 } catch (GenericEntityException e) { 724 Debug.logError(e, "Error finding ProductFeature and Type information for constraint pretty print", module); 725 } 726 727 return infoOut.toString(); 728 } 729 730 public boolean equals(Object obj) { 731 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 732 if (psc instanceof FeatureConstraint) { 733 FeatureSetConstraint that = (FeatureSetConstraint) psc; 734 if (this.productFeatureIdSet == null) { 735 if (that.productFeatureIdSet != null) { 736 return false; 737 } 738 } else { 739 if (!this.productFeatureIdSet.equals(that.productFeatureIdSet)) { 740 return false; 741 } 742 } 743 return true; 744 } else { 745 return false; 746 } 747 } 748 } 749 750 public static class KeywordConstraint extends ProductSearchConstraint { 751 public static final String constraintName = "Keyword"; 752 protected String keywordsString; 753 protected boolean anyPrefix; 754 protected boolean anySuffix; 755 protected boolean isAnd; 756 protected boolean removeStems; 757 758 public KeywordConstraint(String keywordsString, boolean anyPrefix, boolean anySuffix, Boolean removeStems, boolean isAnd) { 759 this.keywordsString = keywordsString; 760 this.anyPrefix = anyPrefix; 761 this.anySuffix = anySuffix; 762 this.isAnd = isAnd; 763 if (removeStems != null) { 764 this.removeStems = removeStems.booleanValue(); 765 } else { 766 this.removeStems = UtilProperties.propertyValueEquals("prodsearch", "remove.stems", "true"); 767 } 768 } 769 770 public Set makeFullKeywordSet(GenericDelegator delegator) { 771 Set keywordSet = KeywordSearch.makeKeywordSet(this.keywordsString, null, true); 772 Set fullKeywordSet = new TreeSet (); 773 774 Iterator keywordIter = keywordSet.iterator(); 776 while (keywordIter.hasNext()) { 777 String keyword = (String ) keywordIter.next(); 778 Set expandedSet = new TreeSet (); 779 boolean replaceEntered = KeywordSearch.expandKeywordForSearch(keyword, expandedSet, delegator); 780 fullKeywordSet.addAll(expandedSet); 781 if (!replaceEntered) { 782 fullKeywordSet.add(keyword); 783 } 784 } 785 786 return fullKeywordSet; 787 } 788 789 public void addConstraint(ProductSearchContext productSearchContext) { 790 if (isAnd) { 792 Set keywordSet = KeywordSearch.makeKeywordSet(this.keywordsString, null, true); 799 800 Iterator keywordIter = keywordSet.iterator(); 802 while (keywordIter.hasNext()) { 803 String keyword = (String ) keywordIter.next(); 804 Set expandedSet = new TreeSet (); 805 boolean replaceEntered = KeywordSearch.expandKeywordForSearch(keyword, expandedSet, productSearchContext.getDelegator()); 806 if (!replaceEntered) { 807 expandedSet.add(keyword); 808 } 809 Set fixedSet = KeywordSearch.fixKeywordsForSearch(expandedSet, anyPrefix, anySuffix, removeStems, isAnd); 810 Set fixedKeywordSet = new HashSet (); 811 fixedKeywordSet.addAll(fixedSet); 812 productSearchContext.keywordFixedOrSetAndList.add(fixedKeywordSet); 813 } 814 } else { 815 Set keywordFirstPass = makeFullKeywordSet(productSearchContext.getDelegator()); Set keywordSet = KeywordSearch.fixKeywordsForSearch(keywordFirstPass, anyPrefix, anySuffix, removeStems, isAnd); 818 productSearchContext.orKeywordFixedSet.addAll(keywordSet); 819 } 820 821 Map valueMap = UtilMisc.toMap("constraintName", constraintName, "infoString", this.keywordsString); 823 valueMap.put("anyPrefix", this.anyPrefix ? "Y" : "N"); 824 valueMap.put("anySuffix", this.anySuffix ? "Y" : "N"); 825 valueMap.put("isAnd", this.isAnd ? "Y" : "N"); 826 valueMap.put("removeStems", this.removeStems ? "Y" : "N"); 827 productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", valueMap)); 828 } 829 830 831 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 832 return "Keyword(s): \"" + this.keywordsString + "\", where " + (isAnd ? "all words match" : "any word matches"); 833 } 834 835 public boolean equals(Object obj) { 836 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 837 if (psc instanceof KeywordConstraint) { 838 KeywordConstraint that = (KeywordConstraint) psc; 839 if (this.anyPrefix != that.anyPrefix) { 840 return false; 841 } 842 if (this.anySuffix != that.anySuffix) { 843 return false; 844 } 845 if (this.isAnd != that.isAnd) { 846 return false; 847 } 848 if (this.removeStems != that.removeStems) { 849 return false; 850 } 851 if (this.keywordsString == null) { 852 if (that.keywordsString != null) { 853 return false; 854 } 855 } else { 856 if (!this.keywordsString.equals(that.keywordsString)) { 857 return false; 858 } 859 } 860 return true; 861 } else { 862 return false; 863 } 864 } 865 } 866 867 public static class LastUpdatedRangeConstraint extends ProductSearchConstraint { 868 public static final String constraintName = "LastUpdatedRange"; 869 protected Timestamp fromDate; 870 protected Timestamp thruDate; 871 872 public LastUpdatedRangeConstraint(Timestamp fromDate, Timestamp thruDate) { 873 this.fromDate = fromDate; 874 this.thruDate = thruDate; 875 } 876 877 public void addConstraint(ProductSearchContext productSearchContext) { 878 } 880 881 882 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 883 return null; 885 } 886 887 public boolean equals(Object obj) { 888 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 889 if (psc instanceof LastUpdatedRangeConstraint) { 890 LastUpdatedRangeConstraint that = (LastUpdatedRangeConstraint) psc; 891 if (this.fromDate == null) { 892 if (that.fromDate != null) { 893 return false; 894 } 895 } else { 896 if (!this.fromDate.equals(that.fromDate)) { 897 return false; 898 } 899 } 900 if (this.thruDate == null) { 901 if (that.thruDate != null) { 902 return false; 903 } 904 } else { 905 if (!this.thruDate.equals(that.thruDate)) { 906 return false; 907 } 908 } 909 return true; 910 } else { 911 return false; 912 } 913 } 914 } 915 916 public static class ListPriceRangeConstraint extends ProductSearchConstraint { 917 public static final String constraintName = "ListPriceRange"; 918 protected Double lowPrice; 919 protected Double highPrice; 920 921 public ListPriceRangeConstraint(Double lowPrice, Double highPrice) { 922 this.lowPrice = lowPrice; 923 this.highPrice = highPrice; 924 } 925 926 public void addConstraint(ProductSearchContext productSearchContext) { 927 } 929 930 931 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 932 return null; 934 } 935 936 public boolean equals(Object obj) { 937 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 938 if (psc instanceof ListPriceRangeConstraint) { 939 ListPriceRangeConstraint that = (ListPriceRangeConstraint) psc; 940 if (this.lowPrice == null) { 941 if (that.lowPrice != null) { 942 return false; 943 } 944 } else { 945 if (!this.lowPrice.equals(that.lowPrice)) { 946 return false; 947 } 948 } 949 if (this.highPrice == null) { 950 if (that.highPrice != null) { 951 return false; 952 } 953 } else { 954 if (!this.highPrice.equals(that.highPrice)) { 955 return false; 956 } 957 } 958 return true; 959 } else { 960 return false; 961 } 962 } 963 } 964 965 public static class SupplierConstraint extends ProductSearchConstraint { 966 public static final String constraintName = "Supplier"; 967 protected String supplierPartyId; 968 969 public SupplierConstraint(String supplierPartyId) { 970 this.supplierPartyId = supplierPartyId; 971 } 972 973 public void addConstraint(ProductSearchContext productSearchContext) { 974 String entityAlias = "SP" + productSearchContext.index; 976 String prefix = "sp" + productSearchContext.index; 977 productSearchContext.index++; 978 979 productSearchContext.dynamicViewEntity.addMemberEntity(entityAlias, "SupplierProduct"); 980 productSearchContext.dynamicViewEntity.addAlias(entityAlias, prefix + "SupplierPartyId", "partyId", null, null, null, null); 981 productSearchContext.dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId")); 982 productSearchContext.entityConditionList.add(new EntityExpr(prefix + "SupplierPartyId", EntityOperator.EQUALS, supplierPartyId)); 983 984 productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", this.supplierPartyId))); 986 } 987 988 public String prettyPrintConstraint(GenericDelegator delegator, boolean detailed) { 989 return "Supplier: " + PartyHelper.getPartyName(delegator, supplierPartyId, false); 990 } 991 992 public boolean equals(Object obj) { 993 ProductSearchConstraint psc = (ProductSearchConstraint) obj; 994 if (psc instanceof SupplierConstraint) { 995 SupplierConstraint that = (SupplierConstraint) psc; 996 if (this.supplierPartyId == null) { 997 if (that.supplierPartyId != null) { 998 return false; 999 } 1000 } else { 1001 if (!this.supplierPartyId.equals(that.supplierPartyId)) { 1002 return false; 1003 } 1004 } 1005 return true; 1006 } else { 1007 return false; 1008 } 1009 } 1010 } 1011 1012 1016 public static abstract class ResultSortOrder implements java.io.Serializable { 1017 public ResultSortOrder() { 1018 } 1019 1020 public abstract void setSortOrder(ProductSearchContext productSearchContext); 1021 public abstract String getOrderName(); 1022 public abstract String prettyPrintSortOrder(boolean detailed); 1023 public abstract boolean isAscending(); 1024 } 1025 1026 public static class SortKeywordRelevancy extends ResultSortOrder { 1027 public SortKeywordRelevancy() { 1028 } 1029 1030 public void setSortOrder(ProductSearchContext productSearchContext) { 1031 if (productSearchContext.includedKeywordSearch) { 1032 productSearchContext.orderByList.add("-totalRelevancy"); 1034 productSearchContext.fieldsToSelect.add("totalRelevancy"); 1035 } 1036 } 1037 1038 public String getOrderName() { 1039 return "KeywordRelevancy"; 1040 } 1041 1042 public String prettyPrintSortOrder(boolean detailed) { 1043 return "Keyword Relevancy"; 1044 } 1045 1046 public boolean isAscending() { 1047 return false; 1048 } 1049 } 1050 1051 public static class SortProductField extends ResultSortOrder { 1052 protected String fieldName; 1053 protected boolean ascending; 1054 1055 1062 public SortProductField(String fieldName, boolean ascending) { 1063 this.fieldName = fieldName; 1064 this.ascending = ascending; 1065 } 1066 1067 public void setSortOrder(ProductSearchContext productSearchContext) { 1068 if (productSearchContext.getDelegator().getModelEntity("Product").isField(fieldName)) { 1069 productSearchContext.dynamicViewEntity.addAlias("PROD", fieldName); 1070 } else if (productSearchContext.getDelegator().getModelEntity("ProductCalculatedInfo").isField(fieldName)) { 1071 productSearchContext.dynamicViewEntity.addAlias("PRODCI", fieldName); 1072 } 1073 if (ascending) { 1074 productSearchContext.orderByList.add("+" + fieldName); 1075 } else { 1076 productSearchContext.orderByList.add("-" + fieldName); 1077 } 1078 productSearchContext.fieldsToSelect.add(fieldName); 1079 } 1080 1081 public String getOrderName() { 1082 return "ProductField:" + this.fieldName; 1083 } 1084 1085 public String prettyPrintSortOrder(boolean detailed) { 1086 if ("productName".equals(this.fieldName)) { 1087 return "Product Name"; 1088 } else if ("totalQuantityOrdered".equals(this.fieldName)) { 1089 return "Popularity by Orders"; 1090 } else if ("totalTimesViewed".equals(this.fieldName)) { 1091 return "Popularity by Views"; 1092 } else if ("averageCustomerRating".equals(this.fieldName)) { 1093 return "Customer Rating"; 1094 } 1095 return this.fieldName; 1096 } 1097 1098 public boolean isAscending() { 1099 return this.ascending; 1100 } 1101 } 1102 1103 public static class SortProductPrice extends ResultSortOrder { 1104 protected String productPriceTypeId; 1105 protected String currencyUomId; 1106 protected String productStoreGroupId; 1107 protected boolean ascending; 1108 1109 public SortProductPrice(String productPriceTypeId, boolean ascending) { 1110 this.productPriceTypeId = productPriceTypeId; 1111 this.ascending = ascending; 1112 } 1113 1114 public SortProductPrice(String productPriceTypeId, String currencyUomId, String productStoreGroupId, boolean ascending) { 1115 this.productPriceTypeId = productPriceTypeId; 1116 this.currencyUomId = currencyUomId; 1117 this.productStoreGroupId = productStoreGroupId; 1118 this.ascending = ascending; 1119 } 1120 1121 public void setSortOrder(ProductSearchContext productSearchContext) { 1122 if (this.currencyUomId == null) { 1123 this.currencyUomId = UtilProperties.getPropertyValue("general", "currency.uom.id.default", "USD"); 1124 } 1125 if (this.productStoreGroupId == null) { 1126 this.productStoreGroupId = "_NA_"; 1127 } 1128 1129 productSearchContext.dynamicViewEntity.addMemberEntity("SPPRC", "ProductPrice"); 1131 productSearchContext.dynamicViewEntity.addViewLink("PROD", "SPPRC", Boolean.TRUE, UtilMisc.toList(new ModelKeyMap("productId", "productId"))); 1132 productSearchContext.dynamicViewEntity.addAlias("SPPRC", "sortProductPriceTypeId", "productPriceTypeId", null, null, null, null); 1133 productSearchContext.dynamicViewEntity.addAlias("SPPRC", "sortCurrencyUomId", "currencyUomId", null, null, null, null); 1134 productSearchContext.dynamicViewEntity.addAlias("SPPRC", "sortProductStoreGroupId", "productStoreGroupId", null, null, null, null); 1135 productSearchContext.dynamicViewEntity.addAlias("SPPRC", "sortFromDate", "fromDate", null, null, null, null); 1136 productSearchContext.dynamicViewEntity.addAlias("SPPRC", "sortThruDate", "thruDate", null, null, null, null); 1137 productSearchContext.dynamicViewEntity.addAlias("SPPRC", "sortPrice", "price", null, null, null, null); 1138 1139 productSearchContext.entityConditionList.add(new EntityExpr("sortProductPriceTypeId", EntityOperator.EQUALS, this.productPriceTypeId)); 1140 productSearchContext.entityConditionList.add(new EntityExpr("sortCurrencyUomId", EntityOperator.EQUALS, this.currencyUomId)); 1141 productSearchContext.entityConditionList.add(new EntityExpr("sortProductStoreGroupId", EntityOperator.EQUALS, this.productStoreGroupId)); 1142 productSearchContext.entityConditionList.add(new EntityExpr("sortFromDate", EntityOperator.LESS_THAN_EQUAL_TO, productSearchContext.nowTimestamp)); 1143 productSearchContext.entityConditionList.add(new EntityExpr( 1144 new EntityExpr("sortThruDate", EntityOperator.EQUALS, null), EntityOperator.OR, 1145 new EntityExpr("sortThruDate", EntityOperator.GREATER_THAN_EQUAL_TO, productSearchContext.nowTimestamp))); 1146 1147 if (ascending) { 1148 productSearchContext.orderByList.add("+sortPrice"); 1149 } else { 1150 productSearchContext.orderByList.add("-sortPrice"); 1151 } 1152 productSearchContext.fieldsToSelect.add("sortPrice"); 1153 } 1154 1155 public String getOrderName() { 1156 return "ProductPrice:" + productPriceTypeId; 1157 } 1158 1159 public String prettyPrintSortOrder(boolean detailed) { 1160 String priceTypeName = null; 1161 if ("LIST_PRICE".equals(this.productPriceTypeId)) { 1162 priceTypeName = "List Price"; 1163 } else if ("DEFAULT_PRICE".equals(this.productPriceTypeId)) { 1164 priceTypeName = "Default Price"; 1165 } else if ("AVERAGE_COST".equals(this.productPriceTypeId)) { 1166 priceTypeName = "Average Cost"; 1167 } 1168 return (priceTypeName == null ? "Price" : priceTypeName) + " (" + (this.ascending ? "Low to High)" : "High to Low)"); 1169 } 1170 1171 public boolean isAscending() { 1172 return this.ascending; 1173 } 1174 } 1175 1176 1177 1323} 1324 | Popular Tags |