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}