KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ofbiz > product > product > ProductSearch


1 /*
2  * $Id: ProductSearch.java 5540 2005-08-13 20:48:15Z jonesde $
3  *
4  * Copyright (c) 2003-2005 The Open For Business Project (www.ofbiz.org)
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
20  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */

23 package org.ofbiz.product.product;
24
25 import java.sql.Timestamp JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Set JavaDoc;
34 import java.util.TreeSet JavaDoc;
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 /**
61  * Utilities for product search based on various constraints including categories, features and keywords.
62  *
63  * @author <a HREF="mailto:jonesde@ofbiz.org">David E. Jones</a>
64  * @version $Rev: 5540 $
65  * @since 3.0
66  */

67 public class ProductSearch {
68
69     public static final String JavaDoc module = ProductSearch.class.getName();
70     public static final String JavaDoc resource = "ProductUiLabels";
71
72     public static ArrayList JavaDoc parametricKeywordSearch(Map JavaDoc featureIdByType, String JavaDoc keywordsString, GenericDelegator delegator, String JavaDoc productCategoryId, String JavaDoc visitId, boolean anyPrefix, boolean anySuffix, boolean isAnd) {
73         Set JavaDoc featureIdSet = new HashSet JavaDoc();
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 JavaDoc parametricKeywordSearch(Set JavaDoc featureIdSet, String JavaDoc keywordsString, GenericDelegator delegator, String JavaDoc productCategoryId, boolean includeSubCategories, String JavaDoc visitId, boolean anyPrefix, boolean anySuffix, boolean isAnd) {
82         List JavaDoc productSearchConstraintList = new LinkedList JavaDoc();
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 JavaDoc featureIdIter = featureIdSet.iterator();
94             while (featureIdIter.hasNext()) {
95                 String JavaDoc productFeatureId = (String JavaDoc) 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 JavaDoc searchProducts(List JavaDoc productSearchConstraintList, ResultSortOrder resultSortOrder, GenericDelegator delegator, String JavaDoc visitId) {
104         ProductSearchContext productSearchContext = new ProductSearchContext(delegator, visitId);
105
106         productSearchContext.addProductSearchConstraints(productSearchConstraintList);
107         productSearchContext.setResultSortOrder(resultSortOrder);
108
109         ArrayList JavaDoc productIds = productSearchContext.doSearch();
110         return productIds;
111     }
112
113     public static void getAllSubCategoryIds(String JavaDoc productCategoryId, Set JavaDoc productCategoryIdSet, GenericDelegator delegator, Timestamp JavaDoc nowTimestamp) {
114         if (nowTimestamp == null) {
115             nowTimestamp = UtilDateTime.nowTimestamp();
116         }
117
118         // this will use the GenericDelegator cache as much as possible, but not a dedicated cache because it would get stale to easily and is too much of a pain to maintain in development and production
119

120         // first make sure the current category id is in the Set
121
productCategoryIdSet.add(productCategoryId);
122
123         // now find all sub-categories, filtered by effective dates, and call this routine for them
124
try {
125             List JavaDoc productCategoryRollupList = delegator.findByAndCache("ProductCategoryRollup", UtilMisc.toMap("parentProductCategoryId", productCategoryId));
126             Iterator JavaDoc productCategoryRollupIter = productCategoryRollupList.iterator();
127             while (productCategoryRollupIter.hasNext()) {
128                 GenericValue productCategoryRollup = (GenericValue) productCategoryRollupIter.next();
129
130                 String JavaDoc subProductCategoryId = productCategoryRollup.getString("productCategoryId");
131                 if (productCategoryIdSet.contains(subProductCategoryId)) {
132                     // if this category has already been traversed, no use doing it again; this will also avoid infinite loops
133
continue;
134                 }
135
136                 // do the date filtering in the loop to avoid looping through the list twice
137
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 JavaDoc entityConditionList = new LinkedList JavaDoc();
149         public List JavaDoc orderByList = new LinkedList JavaDoc();
150         public List JavaDoc fieldsToSelect = UtilMisc.toList("productId");
151         public DynamicViewEntity dynamicViewEntity = new DynamicViewEntity();
152         public boolean productIdGroupBy = false;
153         public boolean includedKeywordSearch = false;
154         public Timestamp JavaDoc nowTimestamp = UtilDateTime.nowTimestamp();
155         public List JavaDoc keywordFixedOrSetAndList = new LinkedList JavaDoc();
156         public Set JavaDoc orKeywordFixedSet = new HashSet JavaDoc();
157         public Set JavaDoc andKeywordFixedSet = new HashSet JavaDoc();
158         public List JavaDoc productSearchConstraintList = new LinkedList JavaDoc();
159         public ResultSortOrder resultSortOrder = null;
160         public Integer JavaDoc resultOffset = null;
161         public Integer JavaDoc maxResults = null;
162         protected GenericDelegator delegator = null;
163         protected String JavaDoc visitId = null;
164         protected Integer JavaDoc totalResults = null;
165
166         public ProductSearchContext(GenericDelegator delegator, String JavaDoc 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 JavaDoc productSearchConstraintList) {
179             // Go through the constraints and add them in
180
Iterator JavaDoc 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 JavaDoc resultOffset) {
192             this.resultOffset = resultOffset;
193         }
194
195         public void setMaxResults(Integer JavaDoc maxResults) {
196             this.maxResults = maxResults;
197         }
198
199         public Integer JavaDoc getTotalResults() {
200             return this.totalResults;
201         }
202
203         public ArrayList JavaDoc doSearch() {
204             long startMillis = System.currentTimeMillis();
205
206             // do the query
207
EntityListIterator eli = this.doQuery(delegator);
208             ArrayList JavaDoc 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             // store info about results in the database, attached to the user's visitId, if specified
221
this.saveSearchResultInfo(new Long JavaDoc(productIds.size()), new Double JavaDoc(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             // we know we have a keyword search to do, so keep track of that now...
232
this.includedKeywordSearch = true;
233
234             // if there is anything in the orKeywordFixedSet add it to the keywordFixedOrSetAndList
235
if (orKeywordFixedSet.size() > 0) {
236                 // put in keywordFixedOrSetAndList to process with other or lists where at least one is required
237
keywordFixedOrSetAndList.add(orKeywordFixedSet);
238             }
239
240             // remove all or sets from the or set and list where the or set is size 1 and put them in the and list
241
Iterator JavaDoc keywordFixedOrSetAndTestIter = keywordFixedOrSetAndList.iterator();
242             while (keywordFixedOrSetAndTestIter.hasNext()) {
243                 Set JavaDoc keywordFixedOrSet = (Set JavaDoc) keywordFixedOrSetAndTestIter.next();
244                 if (keywordFixedOrSet.size() == 0) {
245                     keywordFixedOrSetAndTestIter.remove();
246                 } else if (keywordFixedOrSet.size() == 1) {
247                     // treat it as just another and
248
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                 // add up the relevancyWeight fields from all keyword member entities for a total to sort by
260

261                 Iterator JavaDoc keywordIter = andKeywordFixedSet.iterator();
262                 while (keywordIter.hasNext()) {
263                     String JavaDoc keyword = (String JavaDoc) keywordIter.next();
264
265                     // make index based values and increment
266
String JavaDoc entityAlias = "PK" + index;
267                     String JavaDoc 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                     //don't add an alias for this, will be part of a complex alias: dynamicViewEntity.addAlias(entityAlias, prefix + "RelevancyWeight", "relevancyWeight", null, null, null, null);
276
relevancyComplexAlias.addComplexAliasMember(new ComplexAliasField(entityAlias, "relevancyWeight", null, null));
277                 }
278
279                 //TODO: find out why Oracle and other dbs don't like the query resulting from this and fix: productIdGroupBy = true;
280

281                 if (!doingBothAndOr) {
282                     dynamicViewEntity.addAlias(null, "totalRelevancy", null, null, null, null, null, relevancyComplexAlias);
283                 }
284             }
285             if (keywordFixedOrSetAndList.size() > 0) {
286                 Iterator JavaDoc keywordFixedOrSetAndIter = keywordFixedOrSetAndList.iterator();
287                 while (keywordFixedOrSetAndIter.hasNext()) {
288                     Set JavaDoc keywordFixedOrSet = (Set JavaDoc) keywordFixedOrSetAndIter.next();
289                     // make index based values and increment
290
String JavaDoc entityAlias = "PK" + index;
291                     String JavaDoc 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 JavaDoc keywordOrList = new LinkedList JavaDoc();
298                     Iterator JavaDoc keywordIter = keywordFixedOrSet.iterator();
299                     while (keywordIter.hasNext()) {
300                         String JavaDoc keyword = (String JavaDoc) 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             // handle the now assembled or and and keyword fixed lists
322
this.finishKeywordConstraints();
323
324             if (resultSortOrder != null) {
325                 resultSortOrder.setSortOrder(this);
326             }
327
328             dynamicViewEntity.addAlias("PROD", "productId", null, null, null, new Boolean JavaDoc(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 JavaDoc makeProductIdList(EntityListIterator eli) {
346             ArrayList JavaDoc productIds = new ArrayList JavaDoc(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 JavaDoc initialResult = null;
355                 
356                 /* this method has been replaced by the following to address issue with SAP DB and possibly other DBs
357                 if (resultOffset != null) {
358                     Debug.logInfo("Before relative, current index=" + eli.currentIndex(), module);
359                     hasResults = eli.relative(resultOffset.intValue());
360                 } else {
361                     initialResult = eli.next();
362                     if (initialResult != null) {
363                         hasResults = true;
364                     }
365                 }
366                  */

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                 // get the first as the current one
379
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                     // nothing to get...
390
int failTotal = 0;
391                     if (this.resultOffset != null) {
392                         failTotal = this.resultOffset.intValue() - 1;
393                     }
394                     this.totalResults = new Integer JavaDoc(failTotal);
395                     return productIds;
396                 }
397
398                 
399                 // init numRetreived to one since we have already grabbed the initial one
400
int numRetreived = 1;
401                 int duplicatesFound = 0;
402
403                 Set JavaDoc productIdSet = new HashSet JavaDoc();
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 JavaDoc 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                     /*
419                     StringBuffer lineMsg = new StringBuffer("Got search result line: ");
420                     Iterator fieldsToSelectIter = fieldsToSelect.iterator();
421                     while (fieldsToSelectIter.hasNext()) {
422                         String fieldName = (String) fieldsToSelectIter.next();
423                         lineMsg.append(fieldName);
424                         lineMsg.append("=");
425                         lineMsg.append(searchResult.get(fieldName));
426                         if (fieldsToSelectIter.hasNext()) {
427                             lineMsg.append(", ");
428                         }
429                     }
430                     Debug.logInfo(lineMsg.toString(), module);
431                     */

432                 }
433
434                 if (searchResult != null) {
435                     // we weren't at the end, so go to the end and get the index
436
//Debug.logInfo("Getting totalResults from ending index - before last() currentIndex=" + eli.currentIndex(), module);
437
if (eli.last()) {
438                         this.totalResults = new Integer JavaDoc(eli.currentIndex());
439                         //Debug.logInfo("Getting totalResults from ending index - after last() currentIndex=" + eli.currentIndex(), module);
440
}
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 JavaDoc(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 JavaDoc numResults, Double JavaDoc secondsTotal) {
459             // uses entities: ProductSearchResult and ProductSearchConstraint
460

461             try {
462                 // make sure this is in a transaction
463
boolean beganTransaction = TransactionUtil.begin();
464
465                 try {
466
467                     GenericValue productSearchResult = delegator.makeValue("ProductSearchResult", null);
468                     String JavaDoc 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 JavaDoc 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 JavaDoc 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     // ======================================================================
504
// Search Constraint Classes
505
// ======================================================================
506

507     public static abstract class ProductSearchConstraint implements java.io.Serializable JavaDoc {
508         public ProductSearchConstraint() { }
509
510         public abstract void addConstraint(ProductSearchContext productSearchContext);
511         /** pretty print for log messages and even UI stuff */
512         public abstract String JavaDoc prettyPrintConstraint(GenericDelegator delegator, boolean detailed);
513     }
514
515     public static class CategoryConstraint extends ProductSearchConstraint {
516         public static final String JavaDoc constraintName = "Category";
517         protected String JavaDoc productCategoryId;
518         protected boolean includeSubCategories;
519
520         public CategoryConstraint(String JavaDoc productCategoryId, boolean includeSubCategories) {
521             this.productCategoryId = productCategoryId;
522             this.includeSubCategories = includeSubCategories;
523         }
524
525         public void addConstraint(ProductSearchContext productSearchContext) {
526             List JavaDoc productCategoryIdList = null;
527             if (includeSubCategories) {
528                 // find all sub-categories recursively, make a Set of productCategoryId
529
Set JavaDoc productCategoryIdSet = new HashSet JavaDoc();
530                 ProductSearch.getAllSubCategoryIds(productCategoryId, productCategoryIdSet, productSearchContext.getDelegator(), productSearchContext.nowTimestamp);
531                 productCategoryIdList = new ArrayList JavaDoc(productCategoryIdSet);
532             } else {
533                 productCategoryIdList = UtilMisc.toList(productCategoryId);
534             }
535
536             // make index based values and increment
537
String JavaDoc entityAlias = "PCM" + productSearchContext.index;
538             String JavaDoc 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             // add in productSearchConstraint, don't worry about the productSearchResultId or constraintSeqId, those will be fill in later
551
productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", this.productCategoryId, "includeSubCategories", this.includeSubCategories ? "Y" : "N")));
552         }
553
554         /** pretty print for log messages and even UI stuff */
555         public String JavaDoc 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 JavaDoc ppBuf = new StringBuffer JavaDoc();
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 JavaDoc 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 JavaDoc constraintName = "Feature";
603         protected String JavaDoc productFeatureId;
604
605         public FeatureConstraint(String JavaDoc productFeatureId) {
606             this.productFeatureId = productFeatureId;
607         }
608
609         public void addConstraint(ProductSearchContext productSearchContext) {
610             // make index based values and increment
611
String JavaDoc entityAlias = "PFA" + productSearchContext.index;
612             String JavaDoc 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             // add in productSearchConstraint, don't worry about the productSearchResultId or constraintSeqId, those will be fill in later
625
productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", this.productFeatureId)));
626         }
627
628         public String JavaDoc 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 JavaDoc 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 JavaDoc constraintName = "Feature Set";
662         protected Set JavaDoc productFeatureIdSet;
663
664         public FeatureSetConstraint(Collection JavaDoc productFeatureIdSet) {
665             this.productFeatureIdSet = new HashSet JavaDoc(productFeatureIdSet);
666         }
667
668         public void addConstraint(ProductSearchContext productSearchContext) {
669             // make index based values and increment
670
String JavaDoc entityAlias = "PFA" + productSearchContext.index;
671             String JavaDoc 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             // add in productSearchConstraint, don't worry about the productSearchResultId or constraintSeqId, those will be fill in later
684
StringBuffer JavaDoc featureIdInfo = new StringBuffer JavaDoc();
685             Iterator JavaDoc featureIdIter = this.productFeatureIdSet.iterator();
686             while (featureIdIter.hasNext()) {
687                 String JavaDoc featureId = (String JavaDoc) 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 JavaDoc prettyPrintConstraint(GenericDelegator delegator, boolean detailed) {
698             StringBuffer JavaDoc infoOut = new StringBuffer JavaDoc();
699             try {
700                 Iterator JavaDoc featureIdIter = this.productFeatureIdSet.iterator();
701                 while (featureIdIter.hasNext()) {
702                     String JavaDoc featureId = (String JavaDoc) 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 JavaDoc 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 JavaDoc constraintName = "Keyword";
752         protected String JavaDoc keywordsString;
753         protected boolean anyPrefix;
754         protected boolean anySuffix;
755         protected boolean isAnd;
756         protected boolean removeStems;
757
758         public KeywordConstraint(String JavaDoc keywordsString, boolean anyPrefix, boolean anySuffix, Boolean JavaDoc 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 JavaDoc makeFullKeywordSet(GenericDelegator delegator) {
771             Set JavaDoc keywordSet = KeywordSearch.makeKeywordSet(this.keywordsString, null, true);
772             Set JavaDoc fullKeywordSet = new TreeSet JavaDoc();
773
774             // expand the keyword list according to the thesaurus and create a new set of keywords
775
Iterator JavaDoc keywordIter = keywordSet.iterator();
776             while (keywordIter.hasNext()) {
777                 String JavaDoc keyword = (String JavaDoc) keywordIter.next();
778                 Set JavaDoc expandedSet = new TreeSet JavaDoc();
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             // just make the fixed keyword lists and put them in the context
791
if (isAnd) {
792                 // when isAnd is true we need to make a list of keyword sets where each set corresponds to one
793
//incoming/entered keyword and contains all of the expanded keywords plus the entered keyword if none of
794
//the expanded keywords are flagged as replacements; now the tricky part: each set should be or'ed together,
795
//but then the sets should be and'ed to produce the overall expression; create the SQL for this
796
//needs some work as the current method only support a list of and'ed words and a list of or'ed words, not
797
//a list of or'ed sets to be and'ed together
798
Set JavaDoc keywordSet = KeywordSearch.makeKeywordSet(this.keywordsString, null, true);
799
800                 // expand the keyword list according to the thesaurus and create a new set of keywords
801
Iterator JavaDoc keywordIter = keywordSet.iterator();
802                 while (keywordIter.hasNext()) {
803                     String JavaDoc keyword = (String JavaDoc) keywordIter.next();
804                     Set JavaDoc expandedSet = new TreeSet JavaDoc();
805                     boolean replaceEntered = KeywordSearch.expandKeywordForSearch(keyword, expandedSet, productSearchContext.getDelegator());
806                     if (!replaceEntered) {
807                         expandedSet.add(keyword);
808                     }
809                     Set JavaDoc fixedSet = KeywordSearch.fixKeywordsForSearch(expandedSet, anyPrefix, anySuffix, removeStems, isAnd);
810                     Set JavaDoc fixedKeywordSet = new HashSet JavaDoc();
811                     fixedKeywordSet.addAll(fixedSet);
812                     productSearchContext.keywordFixedOrSetAndList.add(fixedKeywordSet);
813                 }
814             } else {
815                 // when isAnd is false, just add all of the new entries to the big list
816
Set JavaDoc keywordFirstPass = makeFullKeywordSet(productSearchContext.getDelegator()); // includes keyword expansion, etc
817
Set JavaDoc keywordSet = KeywordSearch.fixKeywordsForSearch(keywordFirstPass, anyPrefix, anySuffix, removeStems, isAnd);
818                 productSearchContext.orKeywordFixedSet.addAll(keywordSet);
819             }
820
821             // add in productSearchConstraint, don't worry about the productSearchResultId or constraintSeqId, those will be fill in later
822
Map JavaDoc 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         /** pretty print for log messages and even UI stuff */
831         public String JavaDoc 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 JavaDoc 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 JavaDoc constraintName = "LastUpdatedRange";
869         protected Timestamp JavaDoc fromDate;
870         protected Timestamp JavaDoc thruDate;
871
872         public LastUpdatedRangeConstraint(Timestamp JavaDoc fromDate, Timestamp JavaDoc thruDate) {
873             this.fromDate = fromDate;
874             this.thruDate = thruDate;
875         }
876
877         public void addConstraint(ProductSearchContext productSearchContext) {
878             // TODO: implement LastUpdatedRangeConstraint makeEntityCondition
879
}
880
881         /** pretty print for log messages and even UI stuff */
882         public String JavaDoc prettyPrintConstraint(GenericDelegator delegator, boolean detailed) {
883             // TODO: implement the pretty print for log messages and even UI stuff
884
return null;
885         }
886
887         public boolean equals(Object JavaDoc 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 JavaDoc constraintName = "ListPriceRange";
918         protected Double JavaDoc lowPrice;
919         protected Double JavaDoc highPrice;
920
921         public ListPriceRangeConstraint(Double JavaDoc lowPrice, Double JavaDoc highPrice) {
922             this.lowPrice = lowPrice;
923             this.highPrice = highPrice;
924         }
925
926         public void addConstraint(ProductSearchContext productSearchContext) {
927             // TODO: implement ListPriceRangeConstraint makeEntityCondition
928
}
929
930         /** pretty print for log messages and even UI stuff */
931         public String JavaDoc prettyPrintConstraint(GenericDelegator delegator, boolean detailed) {
932             // TODO: implement the pretty print for log messages and even UI stuff
933
return null;
934         }
935
936         public boolean equals(Object JavaDoc 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 JavaDoc constraintName = "Supplier";
967         protected String JavaDoc supplierPartyId;
968
969         public SupplierConstraint(String JavaDoc supplierPartyId) {
970             this.supplierPartyId = supplierPartyId;
971         }
972
973         public void addConstraint(ProductSearchContext productSearchContext) {
974             // make index based values and increment
975
String JavaDoc entityAlias = "SP" + productSearchContext.index;
976             String JavaDoc 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             // add in productSearchConstraint, don't worry about the productSearchResultId or constraintSeqId, those will be fill in later
985
productSearchContext.productSearchConstraintList.add(productSearchContext.getDelegator().makeValue("ProductSearchConstraint", UtilMisc.toMap("constraintName", constraintName, "infoString", this.supplierPartyId)));
986         }
987
988         public String JavaDoc prettyPrintConstraint(GenericDelegator delegator, boolean detailed) {
989             return "Supplier: " + PartyHelper.getPartyName(delegator, supplierPartyId, false);
990         }
991
992         public boolean equals(Object JavaDoc 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    // ======================================================================
1013
// Result Sort Classes
1014
// ======================================================================
1015

1016    public static abstract class ResultSortOrder implements java.io.Serializable JavaDoc {
1017        public ResultSortOrder() {
1018        }
1019
1020        public abstract void setSortOrder(ProductSearchContext productSearchContext);
1021        public abstract String JavaDoc getOrderName();
1022        public abstract String JavaDoc 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                // we have to check this in order to be sure that there is a totalRelevancy to sort by...
1033
productSearchContext.orderByList.add("-totalRelevancy");
1034                productSearchContext.fieldsToSelect.add("totalRelevancy");
1035            }
1036        }
1037
1038        public String JavaDoc getOrderName() {
1039            return "KeywordRelevancy";
1040        }
1041
1042        public String JavaDoc 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 JavaDoc fieldName;
1053        protected boolean ascending;
1054
1055        /** Some good field names to try might include:
1056         * [productName]
1057         * [totalQuantityOrdered] for most popular or most purchased
1058         * [lastModifiedDate]
1059         *
1060         * You can also include any other field on the Product entity.
1061         */

1062        public SortProductField(String JavaDoc 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 JavaDoc getOrderName() {
1082            return "ProductField:" + this.fieldName;
1083        }
1084
1085        public String JavaDoc 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 JavaDoc productPriceTypeId;
1105        protected String JavaDoc currencyUomId;
1106        protected String JavaDoc productStoreGroupId;
1107        protected boolean ascending;
1108
1109        public SortProductPrice(String JavaDoc productPriceTypeId, boolean ascending) {
1110            this.productPriceTypeId = productPriceTypeId;
1111            this.ascending = ascending;
1112        }
1113
1114        public SortProductPrice(String JavaDoc productPriceTypeId, String JavaDoc currencyUomId, String JavaDoc 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            // SortProductPrice, this will be a bit more complex, need to add a ProductPrice member entity
1130
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 JavaDoc getOrderName() {
1156            return "ProductPrice:" + productPriceTypeId;
1157        }
1158
1159        public String JavaDoc prettyPrintSortOrder(boolean detailed) {
1160            String JavaDoc 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    /** A rather large and verbose method that doesn't use the cool constraint and sort order objects */
1177    /*
1178    public static ArrayList parametricKeywordSearchStandAlone(Set featureIdSet, String keywordsString, GenericDelegator delegator, String productCategoryId, boolean includeSubCategories, String visitId, boolean anyPrefix, boolean anySuffix, boolean isAnd) {
1179        // TODO: implement this for the new features
1180        boolean removeStems = UtilProperties.propertyValueEquals("prodsearch", "remove.stems", "true");
1181
1182        Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
1183
1184        // make view-entity & EntityCondition
1185        int index = 1;
1186        List entityConditionList = new LinkedList();
1187        List orderByList = new LinkedList();
1188        List fieldsToSelect = UtilMisc.toList("productId");
1189        DynamicViewEntity dynamicViewEntity = new DynamicViewEntity();
1190        dynamicViewEntity.addMemberEntity("PROD", "Product");
1191        dynamicViewEntity.addAlias("PROD", "productName");
1192        boolean productIdGroupBy = false;
1193
1194        // Category
1195        if (productCategoryId != null && productCategoryId.length() > 0) {
1196            List productCategoryIdList = null;
1197            if (includeSubCategories) {
1198                // find all sub-categories recursively, make a Set of productCategoryId
1199                Set productCategoryIdSet = new HashSet();
1200                getAllSubCategoryIds(productCategoryId, productCategoryIdSet, delegator, nowTimestamp);
1201                productCategoryIdList = new ArrayList(productCategoryIdSet);
1202            } else {
1203                productCategoryIdList = UtilMisc.toList(productCategoryId);
1204            }
1205
1206            // make index based values and increment
1207            String entityAlias = "PCM" + index;
1208            String prefix = "pcm" + index;
1209            index++;
1210
1211            dynamicViewEntity.addMemberEntity(entityAlias, "ProductCategoryMember");
1212            dynamicViewEntity.addAlias(entityAlias, prefix + "ProductCategoryId", "productCategoryId", null, null, null, null);
1213            dynamicViewEntity.addAlias(entityAlias, prefix + "FromDate", "fromDate", null, null, null, null);
1214            dynamicViewEntity.addAlias(entityAlias, prefix + "ThruDate", "thruDate", null, null, null, null);
1215            dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
1216            entityConditionList.add(new EntityExpr(prefix + "ProductCategoryId", EntityOperator.IN, productCategoryIdList));
1217            entityConditionList.add(new EntityExpr(new EntityExpr(prefix + "ThruDate", EntityOperator.EQUALS, null), EntityOperator.OR, new EntityExpr(prefix + "ThruDate", EntityOperator.GREATER_THAN, nowTimestamp)));
1218            entityConditionList.add(new EntityExpr(prefix + "FromDate", EntityOperator.LESS_THAN, nowTimestamp));
1219        }
1220
1221        // Keyword
1222        List keywordFirstPass = KeywordSearch.makeKeywordList(keywordsString);
1223        List keywordList = KeywordSearch.fixKeywords(keywordFirstPass, anyPrefix, anySuffix, removeStems, isAnd);
1224
1225        if (keywordList.size() > 0) {
1226            if (isAnd) {
1227                // add up the relevancyWeight fields from all keyword member entities for a total to sort by
1228                ComplexAlias complexAlias = new ComplexAlias("+");
1229
1230                Iterator keywordIter = keywordList.iterator();
1231                while (keywordIter.hasNext()) {
1232                    String keyword = (String) keywordIter.next();
1233
1234                    // make index based values and increment
1235                    String entityAlias = "PK" + index;
1236                    String prefix = "pk" + index;
1237                    index++;
1238
1239                    dynamicViewEntity.addMemberEntity(entityAlias, "ProductKeyword");
1240                    dynamicViewEntity.addAlias(entityAlias, prefix + "Keyword", "keyword", null, null, null, null);
1241                    dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
1242                    entityConditionList.add(new EntityExpr(prefix + "Keyword", EntityOperator.LIKE, keyword));
1243
1244                    //don't add an alias for this, will be part of a complex alias: dynamicViewEntity.addAlias(entityAlias, prefix + "RelevancyWeight", "relevancyWeight", null, null, null, null);
1245                    complexAlias.addComplexAliasMember(new ComplexAliasField(entityAlias, "relevancyWeight"));
1246                }
1247                dynamicViewEntity.addAlias(null, "totalRelevancy", null, null, null, null, null, complexAlias);
1248                orderByList.add("-totalRelevancy");
1249                fieldsToSelect.add("totalRelevancy");
1250            } else {
1251                // make index based values and increment
1252                String entityAlias = "PK" + index;
1253                String prefix = "pk" + index;
1254                index++;
1255
1256                dynamicViewEntity.addMemberEntity(entityAlias, "ProductKeyword");
1257                dynamicViewEntity.addAlias(entityAlias, "totalRelevancy", "relevancyWeight", null, null, null, "sum");
1258                dynamicViewEntity.addAlias(entityAlias, prefix + "Keyword", "keyword", null, null, null, null);
1259                dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
1260                orderByList.add("-totalRelevancy");
1261                fieldsToSelect.add("totalRelevancy");
1262                List keywordOrList = new LinkedList();
1263                Iterator keywordIter = keywordList.iterator();
1264                while (keywordIter.hasNext()) {
1265                    String keyword = (String) keywordIter.next();
1266                    keywordOrList.add(new EntityExpr(prefix + "Keyword", EntityOperator.LIKE, keyword));
1267                }
1268                entityConditionList.add(new EntityConditionList(keywordOrList, EntityOperator.OR));
1269
1270                productIdGroupBy = true;
1271            }
1272        }
1273
1274        // Features
1275        if (featureIdSet != null && featureIdSet.size() > 0) {
1276            Iterator featureIdIter = featureIdSet.iterator();
1277            while (featureIdIter.hasNext()) {
1278                String productFeatureId = (String) featureIdIter.next();
1279
1280                // make index based values and increment
1281                String entityAlias = "PFA" + index;
1282                String prefix = "pfa" + index;
1283                index++;
1284
1285                dynamicViewEntity.addMemberEntity(entityAlias, "ProductFeatureAppl");
1286                dynamicViewEntity.addAlias(entityAlias, prefix + "ProductFeatureId", "productFeatureId", null, null, null, null);
1287                dynamicViewEntity.addAlias(entityAlias, prefix + "FromDate", "fromDate", null, null, null, null);
1288                dynamicViewEntity.addAlias(entityAlias, prefix + "ThruDate", "thruDate", null, null, null, null);
1289                dynamicViewEntity.addViewLink("PROD", entityAlias, Boolean.FALSE, ModelKeyMap.makeKeyMapList("productId"));
1290                entityConditionList.add(new EntityExpr(prefix + "ProductFeatureId", EntityOperator.EQUALS, productFeatureId));
1291                entityConditionList.add(new EntityExpr(new EntityExpr(prefix + "ThruDate", EntityOperator.EQUALS, null), EntityOperator.OR, new EntityExpr(prefix + "ThruDate", EntityOperator.GREATER_THAN, nowTimestamp)));
1292                entityConditionList.add(new EntityExpr(prefix + "FromDate", EntityOperator.LESS_THAN, nowTimestamp));
1293            }
1294        }
1295
1296        dynamicViewEntity.addAlias("PROD", "productId", null, null, null, new Boolean(productIdGroupBy), null);
1297        EntityCondition whereCondition = new EntityConditionList(entityConditionList, EntityOperator.AND);
1298        EntityFindOptions efo = new EntityFindOptions();
1299        efo.setDistinct(true);
1300
1301        EntityListIterator eli = null;
1302        try {
1303            eli = delegator.findListIteratorByCondition(dynamicViewEntity, whereCondition, null, fieldsToSelect, orderByList, efo);
1304        } catch (GenericEntityException e) {
1305            Debug.logError(e, "Error in product search", module);
1306            return null;
1307        }
1308
1309        ArrayList productIds = new ArrayList(100);
1310        Set productIdSet = new HashSet();
1311        GenericValue searchResult = null;
1312        while ((searchResult = (GenericValue) eli.next()) != null) {
1313            String productId = searchResult.getString("productId");
1314            if (!productIdSet.contains(productId)) {
1315                productIds.add(productId);
1316                productIdSet.add(productId);
1317            }
1318        }
1319
1320        return productIds;
1321    }
1322     */

1323}
1324
Popular Tags