001// -------------------------------------------------------------------------------- 002// Copyright 2002-2026 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.model.control.item.server.search; 018 019import static com.echothree.model.jooq.server.keys.item.ItemForeignKeys.ITEMS_ACTIVE_DETAIL_FK; 020import static com.echothree.model.jooq.server.keys.workflow.WorkflowForeignKeys.WORKFLOW_ENTITY_STATUSES_ENTITY_INSTANCE_FK; 021import static com.echothree.model.jooq.server.tables.core.EntityInstances.EntityInstances; 022import static com.echothree.model.jooq.server.tables.item.ItemDetails.ItemDetails; 023import static com.echothree.model.jooq.server.tables.item.Items.Items; 024import static com.echothree.model.jooq.server.tables.workflow.WorkflowEntityStatuses.WorkflowEntityStatuses; 025import com.echothree.model.control.core.common.ComponentVendors; 026import com.echothree.model.control.core.common.EntityTypes; 027import com.echothree.model.control.core.server.control.EntityInstanceControl; 028import com.echothree.model.control.index.common.IndexConstants; 029import com.echothree.model.control.index.common.IndexFieldVariations; 030import com.echothree.model.control.index.common.IndexFields; 031import com.echothree.model.control.index.common.IndexTypes; 032import com.echothree.model.control.index.server.analyzer.BasicAnalyzer; 033import com.echothree.model.control.item.common.ItemDescriptionTypes; 034import com.echothree.model.control.item.server.analyzer.ItemAnalyzer; 035import com.echothree.model.control.item.server.control.ItemControl; 036import com.echothree.model.control.item.server.logic.ItemDescriptionLogic; 037import com.echothree.model.control.search.common.SearchSortDirections; 038import com.echothree.model.control.search.common.SearchSortOrders; 039import com.echothree.model.control.search.server.search.BaseSearchEvaluator; 040import com.echothree.model.control.search.server.search.EntityInstancePKHolder; 041import com.echothree.model.data.item.server.entity.ItemType; 042import com.echothree.model.data.item.server.entity.ItemUseType; 043import com.echothree.model.data.party.server.entity.Language; 044import com.echothree.model.data.search.server.entity.SearchDefaultOperator; 045import com.echothree.model.data.search.server.entity.SearchSortDirection; 046import com.echothree.model.data.search.server.entity.SearchSortOrder; 047import com.echothree.model.data.search.server.entity.SearchType; 048import com.echothree.model.data.search.server.entity.SearchUseType; 049import com.echothree.model.data.user.server.entity.UserVisit; 050import com.echothree.model.data.workflow.server.entity.WorkflowStep; 051import com.echothree.util.server.message.ExecutionErrorAccumulator; 052import com.echothree.util.server.persistence.Session; 053import java.util.List; 054import org.apache.lucene.search.SortField; 055 056public class ItemSearchEvaluator 057 extends BaseSearchEvaluator { 058 059 private String itemNameOrAlias; 060 private ItemType itemType; 061 private ItemUseType itemUseType; 062 private WorkflowStep itemStatusWorkflowStep; 063 List<WorkflowStep> itemStatusWorkflowSteps; 064 065 /** Creates a new instance of ItemSearchEvaluator */ 066 public ItemSearchEvaluator(UserVisit userVisit, Language language, SearchType searchType, SearchDefaultOperator searchDefaultOperator, SearchSortOrder searchSortOrder, 067 SearchSortDirection searchSortDirection, SearchUseType searchUseType) { 068 super(userVisit, searchDefaultOperator, searchType, searchSortOrder, searchSortDirection, searchUseType, ComponentVendors.ECHO_THREE.name(), 069 EntityTypes.Item.name(), IndexTypes.ITEM.name(), language, null); 070 071 setField(ItemDescriptionLogic.getInstance().getIndexDefaultItemDescriptionTypeName()); 072 } 073 074 /** Counts the number of search parameters. */ 075 @Override 076 protected int countParameters() { 077 return super.countParameters() + (itemNameOrAlias == null ? 0 : 1) + (itemType == null ? 0 : 1) 078 + (itemUseType == null ? 0 : 1) + (itemStatusWorkflowStep == null ? 0 : 1) + (itemStatusWorkflowSteps == null ? 0 : 1); 079 } 080 081 /** Determines if the result of the search may be cached. For Items, the only field that may be used is the description, "q." If any 082 * others are utilized, the result may not be cached. 083 */ 084 @Override 085 protected boolean isResultCachable() { 086 return countParameters() == 1 && q != null; 087 } 088 089 public EntityInstancePKHolder getEntityInstancePKHolderByItemType(ItemType itemType) { 090 var query = session.getDslContext() 091 .select(EntityInstances.ENTITY_INSTANCE) 092 .from(EntityInstances) 093 .join(Items).on(Items.ITEM.cast(Long.class).eq(EntityInstances.ENTITY_UNIQUE)) 094 .join(ItemDetails).onKey(ITEMS_ACTIVE_DETAIL_FK) 095 .where(ItemDetails.ITEM_TYPE.eq(itemType.getPrimaryKey()), 096 EntityInstances.ENTITY_TYPE.eq(entityType.getPrimaryKey())); 097 098 return getEntityInstancePKHolderFromQuery(query); 099 } 100 101 public EntityInstancePKHolder getEntityInstancePKHolderByItemUseType(ItemUseType itemUseType) { 102 var query = session.getDslContext() 103 .select(EntityInstances.ENTITY_INSTANCE) 104 .from(EntityInstances) 105 .join(Items).on(Items.ITEM.cast(Long.class).eq(EntityInstances.ENTITY_UNIQUE)) 106 .join(ItemDetails).onKey(ITEMS_ACTIVE_DETAIL_FK) 107 .where(ItemDetails.ITEM_USE_TYPE.eq(itemUseType.getPrimaryKey()), 108 EntityInstances.ENTITY_TYPE.eq(entityType.getPrimaryKey())); 109 110 return getEntityInstancePKHolderFromQuery(query); 111 } 112 113 public EntityInstancePKHolder getEntityInstancePKHolderByItemStatusWorkflowStep(WorkflowStep itemStatusWorkflowStep) { 114 var query = session.getDslContext() 115 .select(EntityInstances.ENTITY_INSTANCE) 116 .from(EntityInstances) 117 .join(Items).on(Items.ITEM.cast(Long.class).eq(EntityInstances.ENTITY_UNIQUE)) 118 .join(ItemDetails).onKey(ITEMS_ACTIVE_DETAIL_FK) 119 .join(WorkflowEntityStatuses).onKey(WORKFLOW_ENTITY_STATUSES_ENTITY_INSTANCE_FK) 120 .where(EntityInstances.ENTITY_TYPE.eq(entityType.getPrimaryKey()), 121 WorkflowEntityStatuses.WORKFLOW_STEP.eq(itemStatusWorkflowStep.getPrimaryKey()), 122 WorkflowEntityStatuses.THRU_TIME.eq(Session.MAX_TIME)); 123 124 return getEntityInstancePKHolderFromQuery(query); 125 } 126 127 /** 128 * Returns the itemNameOrAlias. 129 * @return the itemNameOrAlias 130 */ 131 public String getItemNameOrAlias() { 132 return itemNameOrAlias; 133 } 134 135 /** 136 * Sets the itemNameOrAlias. 137 * @param itemNameOrAlias the itemNameOrAlias to set 138 */ 139 public void setItemNameOrAlias(String itemNameOrAlias) { 140 this.itemNameOrAlias = itemNameOrAlias; 141 } 142 143 /** 144 * Returns the itemType. 145 * @return the itemType 146 */ 147 public ItemType getItemType() { 148 return itemType; 149 } 150 151 /** 152 * Sets the itemType. 153 * @param itemType the itemType to set 154 */ 155 public void setItemType(ItemType itemType) { 156 this.itemType = itemType; 157 } 158 159 /** 160 * Returns the itemUseType. 161 * @return the itemUseType 162 */ 163 public ItemUseType getItemUseType() { 164 return itemUseType; 165 } 166 167 /** 168 * Sets the itemUseType. 169 * @param itemUseType the itemUseType to set 170 */ 171 public void setItemUseType(ItemUseType itemUseType) { 172 this.itemUseType = itemUseType; 173 } 174 175 /** 176 * Returns the itemStatusWorkflowStep. 177 * @return the itemStatusWorkflowStep 178 */ 179 public WorkflowStep getItemStatusWorkflowStep() { 180 return itemStatusWorkflowStep; 181 } 182 183 /** 184 * Sets the itemStatusWorkflowStep. 185 * @param itemStatusWorkflowStep the itemStatusWorkflowStep to set 186 */ 187 public void setItemStatusWorkflowStep(WorkflowStep itemStatusWorkflowStep) { 188 this.itemStatusWorkflowStep = itemStatusWorkflowStep; 189 } 190 191 /** 192 * Sets the itemStatusWorkflowSteps. 193 * @param itemStatusWorkflowSteps the itemStatusWorkflowSteps to set 194 */ 195 public void setItemStatusWorkflowSteps(List<WorkflowStep> itemStatusWorkflowSteps) { 196 this.itemStatusWorkflowSteps = itemStatusWorkflowSteps; 197 } 198 199 @Override 200 public SortField[] getSortFields(String searchSortOrderName) { 201 SortField[] sortFields = null; 202 var reverse = searchSortDirection.getLastDetail().getSearchSortDirectionName().equals(SearchSortDirections.DESCENDING.name()); 203 204 if(searchSortOrderName.equals(SearchSortOrders.SCORE.name())) { 205 sortFields = new SortField[]{ 206 new SortField(null, SortField.Type.SCORE, reverse), 207 new SortField(ItemDescriptionTypes.DEFAULT_DESCRIPTION.name() + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(), SortField.Type.STRING, reverse) 208 }; 209 } else if(searchSortOrderName.equals(SearchSortOrders.DEFAULT_DESCRIPTION.name())) { 210 sortFields = new SortField[]{new SortField(ItemDescriptionTypes.DEFAULT_DESCRIPTION.name() + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(), SortField.Type.STRING, reverse)}; 211 } else if(searchSortOrderName.equals(SearchSortOrders.ITEM_NAME.name())) { 212 sortFields = new SortField[]{new SortField(IndexFields.itemName.name() + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(), SortField.Type.STRING, reverse)}; 213 } else if(searchSortOrderName.equals(SearchSortOrders.CREATED_TIME.name())) { 214 sortFields = new SortField[]{new SortField(IndexFields.createdTime.name(), SortField.Type.LONG, reverse)}; 215 } else if(searchSortOrderName.equals(SearchSortOrders.MODIFIED_TIME.name())) { 216 sortFields = new SortField[]{new SortField(IndexFields.modifiedTime.name(), SortField.Type.LONG, reverse)}; 217 } 218 219 return sortFields; 220 } 221 222 @Override 223 public BasicAnalyzer getAnalyzer(final ExecutionErrorAccumulator eea, final Language language) { 224 return new ItemAnalyzer(eea, language, entityType); 225 } 226 227 @Override 228 protected EntityInstancePKHolder executeSearch(final ExecutionErrorAccumulator eea) { 229 EntityInstancePKHolder resultSet = null; 230 231 if(itemNameOrAlias == null) { 232 resultSet = super.executeSearch(eea); 233 234 if(resultSet == null || resultSet.size() > 0) { 235 if(q != null) { 236 if(resultSet == null || resultSet.size() > 0) { 237 var entityInstancePKHolder = executeQuery(eea); 238 239 if(resultSet == null) { 240 resultSet = entityInstancePKHolder; 241 } else { 242 resultSet.retainAll(entityInstancePKHolder); 243 } 244 } 245 } 246 } 247 248 if(resultSet == null || resultSet.size() > 0) { 249 if(itemType != null) { 250 var entityInstancePKHolder = getEntityInstancePKHolderByItemType(itemType); 251 252 if(resultSet == null) { 253 resultSet = entityInstancePKHolder; 254 } else { 255 resultSet.retainAll(entityInstancePKHolder); 256 } 257 } 258 } 259 260 if(resultSet == null || resultSet.size() > 0) { 261 if(itemUseType != null) { 262 var entityInstancePKHolder = getEntityInstancePKHolderByItemUseType(itemUseType); 263 264 if(resultSet == null) { 265 resultSet = entityInstancePKHolder; 266 } else { 267 resultSet.retainAll(entityInstancePKHolder); 268 } 269 } 270 } 271 272 if(resultSet == null || resultSet.size() > 0) { 273 if(itemStatusWorkflowStep != null) { 274 var entityInstancePKHolder = getEntityInstancePKHolderByItemStatusWorkflowStep(itemStatusWorkflowStep); 275 276 if(resultSet == null) { 277 resultSet = entityInstancePKHolder; 278 } else { 279 resultSet.retainAll(entityInstancePKHolder); 280 } 281 } 282 } 283 284 if(resultSet == null || resultSet.size() > 0) { 285 if(itemStatusWorkflowSteps != null) { 286 EntityInstancePKHolder entityInstancePKHolder = null; 287 288 for(var workflowStep : itemStatusWorkflowSteps) { 289 var individualPKHolder = getEntityInstancePKHolderByItemStatusWorkflowStep(workflowStep); 290 291 if(entityInstancePKHolder == null) { 292 entityInstancePKHolder = individualPKHolder; 293 } else { 294 entityInstancePKHolder.addAll(individualPKHolder); 295 } 296 } 297 298 if(resultSet == null) { 299 resultSet = entityInstancePKHolder; 300 } else { 301 resultSet.retainAll(entityInstancePKHolder); 302 } 303 } 304 } 305 } else { 306 var itemControl = Session.getModelController(ItemControl.class); 307 var item = itemControl.getItemByNameThenAlias(itemNameOrAlias); 308 309 if(item != null) { 310 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 311 312 resultSet = new EntityInstancePKHolder(1); 313 resultSet.add(entityInstanceControl.getEntityInstanceByBasePK(item.getPrimaryKey()).getPrimaryKey(), null); 314 } 315 } 316 317 return resultSet; 318 } 319 320}