001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 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.search.server.logic;
018
019import com.echothree.model.control.core.server.control.EntityInstanceControl;
020import com.echothree.model.control.search.common.exception.DuplicateSearchResultActionException;
021import com.echothree.model.control.search.common.exception.UnknownCachedExecutedSearchResultException;
022import com.echothree.model.control.search.common.exception.UnknownDefaultSearchDefaultOperatorException;
023import com.echothree.model.control.search.common.exception.UnknownDefaultSearchSortDirectionException;
024import com.echothree.model.control.search.common.exception.UnknownDefaultSearchSortOrderException;
025import com.echothree.model.control.search.common.exception.UnknownSearchDefaultOperatorNameException;
026import com.echothree.model.control.search.common.exception.UnknownSearchKindNameException;
027import com.echothree.model.control.search.common.exception.UnknownSearchResultActionTypeNameException;
028import com.echothree.model.control.search.common.exception.UnknownSearchResultException;
029import com.echothree.model.control.search.common.exception.UnknownSearchSortDirectionNameException;
030import com.echothree.model.control.search.common.exception.UnknownSearchSortOrderNameException;
031import com.echothree.model.control.search.common.exception.UnknownSearchTypeNameException;
032import com.echothree.model.control.search.common.exception.UnknownSearchUseTypeNameException;
033import com.echothree.model.control.search.common.exception.UnknownUserVisitSearchException;
034import com.echothree.model.control.search.server.control.SearchControl;
035import com.echothree.model.control.search.server.database.CachedSearchToInvalidateQuery;
036import com.echothree.model.data.index.server.entity.Index;
037import com.echothree.model.data.search.server.entity.Search;
038import com.echothree.model.data.search.server.entity.SearchDefaultOperator;
039import com.echothree.model.data.search.server.entity.SearchKind;
040import com.echothree.model.data.search.server.entity.SearchResultAction;
041import com.echothree.model.data.search.server.entity.SearchResultActionType;
042import com.echothree.model.data.search.server.entity.SearchSortDirection;
043import com.echothree.model.data.search.server.entity.SearchSortOrder;
044import com.echothree.model.data.search.server.entity.SearchType;
045import com.echothree.model.data.search.server.entity.SearchUseType;
046import com.echothree.model.data.search.server.entity.UserVisitSearch;
047import com.echothree.model.data.user.server.entity.UserVisit;
048import com.echothree.util.common.message.ExecutionErrors;
049import com.echothree.util.common.persistence.BasePK;
050import com.echothree.util.server.control.BaseLogic;
051import com.echothree.util.server.message.ExecutionErrorAccumulator;
052import com.echothree.util.server.persistence.BaseEntity;
053import com.echothree.util.server.persistence.Session;
054import javax.enterprise.context.ApplicationScoped;
055import javax.enterprise.inject.spi.CDI;
056
057@ApplicationScoped
058public class SearchLogic
059        extends BaseLogic {
060
061    protected SearchLogic() {
062        super();
063    }
064
065    public static SearchLogic getInstance() {
066        return CDI.current().select(SearchLogic.class).get();
067    }
068    
069    public SearchDefaultOperator getSearchDefaultOperatorByName(final ExecutionErrorAccumulator eea, final String searchDefaultOperatorName) {
070        var searchControl = Session.getModelController(SearchControl.class);
071        var searchDefaultOperator = searchControl.getSearchDefaultOperatorByName(searchDefaultOperatorName);
072
073        if(searchDefaultOperator == null) {
074            handleExecutionError(UnknownSearchDefaultOperatorNameException.class, eea, ExecutionErrors.UnknownSearchDefaultOperatorName.name(), searchDefaultOperatorName);
075        }
076
077        return searchDefaultOperator;
078    }
079    
080    public SearchDefaultOperator getDefaultSearchDefaultOperator(final ExecutionErrorAccumulator eea) {
081        var searchControl = Session.getModelController(SearchControl.class);
082        var searchDefaultOperator = searchControl.getDefaultSearchDefaultOperator();
083
084        if(searchDefaultOperator == null) {
085            handleExecutionError(UnknownDefaultSearchDefaultOperatorException.class, eea, ExecutionErrors.UnknownDefaultSearchDefaultOperator.name());
086        }
087
088        return searchDefaultOperator;
089    }
090
091    public SearchSortDirection getSearchSortDirectionByName(final ExecutionErrorAccumulator eea, final String searchSortDirectionName) {
092        var searchControl = Session.getModelController(SearchControl.class);
093        var searchSortDirection = searchControl.getSearchSortDirectionByName(searchSortDirectionName);
094
095        if(searchSortDirection == null) {
096            handleExecutionError(UnknownSearchSortDirectionNameException.class, eea, ExecutionErrors.UnknownSearchSortDirectionName.name(), searchSortDirectionName);
097        }
098
099        return searchSortDirection;
100    }
101    
102    public SearchSortDirection getDefaultSearchSortDirection(final ExecutionErrorAccumulator eea) {
103        var searchControl = Session.getModelController(SearchControl.class);
104        var searchSortDirection = searchControl.getDefaultSearchSortDirection();
105
106        if(searchSortDirection == null) {
107            handleExecutionError(UnknownDefaultSearchSortDirectionException.class, eea, ExecutionErrors.UnknownDefaultSearchSortDirection.name());
108        }
109
110        return searchSortDirection;
111    }
112
113    public SearchUseType getSearchUseTypeByName(final ExecutionErrorAccumulator eea, final String searchUseTypeName) {
114        var searchControl = Session.getModelController(SearchControl.class);
115        var searchUseType = searchControl.getSearchUseTypeByName(searchUseTypeName);
116
117        if(searchUseType == null) {
118            handleExecutionError(UnknownSearchUseTypeNameException.class, eea, ExecutionErrors.UnknownSearchUseTypeName.name(), searchUseTypeName);
119        }
120
121        return searchUseType;
122    }
123    
124    public SearchResultActionType getSearchResultActionTypeByName(final ExecutionErrorAccumulator eea, final String searchResultActionTypeName) {
125        var searchControl = Session.getModelController(SearchControl.class);
126        var searchResultActionType = searchControl.getSearchResultActionTypeByName(searchResultActionTypeName);
127
128        if(searchResultActionType == null) {
129            handleExecutionError(UnknownSearchResultActionTypeNameException.class, eea, ExecutionErrors.UnknownSearchResultActionTypeName.name(), searchResultActionTypeName);
130        }
131
132        return searchResultActionType;
133    }
134    
135    public SearchKind getSearchKindByName(final ExecutionErrorAccumulator eea, final String searchKindName) {
136        var searchControl = Session.getModelController(SearchControl.class);
137        var searchKind = searchControl.getSearchKindByName(searchKindName);
138
139        if(searchKind == null) {
140            handleExecutionError(UnknownSearchKindNameException.class, eea, ExecutionErrors.UnknownSearchKindName.name(), searchKindName);
141        }
142
143        return searchKind;
144    }
145
146    public SearchType getSearchTypeByName(final ExecutionErrorAccumulator eea, final SearchKind searchKind, final String searchTypeName) {
147        var searchControl = Session.getModelController(SearchControl.class);
148        var searchType = searchControl.getSearchTypeByName(searchKind, searchTypeName);
149
150        if(searchType == null) {
151            handleExecutionError(UnknownSearchTypeNameException.class, eea, ExecutionErrors.UnknownSearchTypeName.name(),
152                    searchKind.getLastDetail().getSearchKindName(), searchTypeName);
153        }
154
155        return searchType;
156    }
157
158    public SearchType getSearchTypeByName(final ExecutionErrorAccumulator eea, final String searchKindName, final String searchTypeName) {
159        var searchKind = getSearchKindByName(eea, searchKindName);
160        
161        return hasExecutionErrors(eea) ? null : getSearchTypeByName(eea, searchKind, searchTypeName);
162    }
163    
164    public SearchSortOrder getSearchSortOrderByName(final ExecutionErrorAccumulator eea, final SearchKind searchKind, final String searchSortOrderName) {
165        var searchControl = Session.getModelController(SearchControl.class);
166        var searchSortOrder = searchControl.getSearchSortOrderByName(searchKind, searchSortOrderName);
167
168        if(searchSortOrder == null) {
169            handleExecutionError(UnknownSearchSortOrderNameException.class, eea, ExecutionErrors.UnknownSearchSortOrderName.name(),
170                    searchKind.getLastDetail().getSearchKindName(), searchSortOrderName);
171        }
172
173        return searchSortOrder;
174    }
175
176    public SearchSortOrder getSearchSortOrderByName(final ExecutionErrorAccumulator eea, final String searchKindName, final String searchSortOrderName) {
177        var searchKind = getSearchKindByName(eea, searchKindName);
178        
179        return hasExecutionErrors(eea) ? null : getSearchSortOrderByName(eea, searchKind, searchSortOrderName);
180    }
181    
182    public SearchSortOrder getDefaultSearchSortOrder(final ExecutionErrorAccumulator eea, final SearchKind searchKind) {
183        var searchControl = Session.getModelController(SearchControl.class);
184        var searchSortOrder = searchControl.getDefaultSearchSortOrder(searchKind);
185
186        if(searchSortOrder == null) {
187            handleExecutionError(UnknownDefaultSearchSortOrderException.class, eea, ExecutionErrors.UnknownDefaultSearchSortOrder.name(),
188                    searchKind.getLastDetail().getSearchKindName());
189        }
190
191        return searchSortOrder;
192    }
193
194    public SearchSortOrder getDefaultSearchSortOrder(final ExecutionErrorAccumulator eea, final String searchKindName) {
195        var searchKind = getSearchKindByName(eea, searchKindName);
196        
197        return hasExecutionErrors(eea) ? null : getDefaultSearchSortOrder(eea, searchKind);
198    }
199    
200    public UserVisitSearch getUserVisitSearch(final ExecutionErrorAccumulator eea, final UserVisit userVisit, final SearchType searchType) {
201        var searchControl = Session.getModelController(SearchControl.class);
202        var userVisitSearch = searchControl.getUserVisitSearch(userVisit, searchType);
203        
204        if(userVisitSearch == null) {
205            handleExecutionError(UnknownUserVisitSearchException.class, eea, ExecutionErrors.UnknownUserVisitSearch.name());
206        }
207        
208        return userVisitSearch;
209    }
210    
211    public UserVisitSearch getUserVisitSearchByName(final ExecutionErrorAccumulator eea, final UserVisit userVisit, final String searchKindName,
212            final String searchTypeName) {
213        var searchType = getSearchTypeByName(eea, searchKindName, searchTypeName);
214        var userVisitSearch = hasExecutionErrors(eea) ? null : getUserVisitSearch(eea, userVisit, searchType);
215        
216        return userVisitSearch;
217    }
218    
219    public void removeUserVisitSearch(final ExecutionErrorAccumulator eea, final UserVisit userVisit, final String searchKindName,
220            final String searchTypeName) {
221        var searchControl = Session.getModelController(SearchControl.class);
222        var userVisitSearch = getUserVisitSearchByName(eea, userVisit, searchKindName, searchTypeName);
223
224        if(!hasExecutionErrors(eea)) {
225            searchControl.removeUserVisitSearch(userVisitSearch);
226        }
227    }
228    
229    public Long countSearchResults(final Search search) {
230        var searchControl = Session.getModelController(SearchControl.class);
231        var cachedSearch = search.getCachedSearch();
232        long count;
233        
234        if(cachedSearch == null) {
235            count = searchControl.countSearchResults(search);
236        } else {
237            var cachedExecutedSearch = searchControl.getCachedExecutedSearch(cachedSearch);
238            
239            count = searchControl.countCachedExecutedSearchResults(cachedExecutedSearch);
240        }
241        
242        return count;
243    }
244    
245    public Long countUserVisitSearchResults(final ExecutionErrorAccumulator eea, final UserVisit userVisit, final String searchKindName,
246            final String searchTypeName) {
247        var userVisitSearch = getUserVisitSearchByName(eea, userVisit, searchKindName, searchTypeName);
248        var count = hasExecutionErrors(eea) ? null : countSearchResults(userVisitSearch.getSearch());
249
250        return count;
251    }
252    
253    public SearchResultAction createSearchResultAction(final ExecutionErrorAccumulator eea, final UserVisit userVisit, final String searchKindName,
254            final String searchTypeName, final String searchResultActionTypeName, final BaseEntity baseEntity, final BasePK createdBy) {
255        SearchResultAction itemSearchResultAction = null;
256        var userVisitSearch = getUserVisitSearchByName(eea, userVisit, searchKindName, searchTypeName);
257
258        if(eea == null || !eea.hasExecutionErrors()) {
259            var searchResultActionType = getSearchResultActionTypeByName(eea, searchResultActionTypeName);
260            
261            if(eea == null || !eea.hasExecutionErrors()) {
262                var entityInstanceControl = Session.getModelController(EntityInstanceControl.class);
263                var searchControl = Session.getModelController(SearchControl.class);
264                var search = userVisitSearch.getSearch();
265                var cachedSearch = search.getCachedSearch();
266                var basePK = baseEntity.getPrimaryKey();
267                var entityInstance = entityInstanceControl.getEntityInstanceByBasePK(basePK);
268
269                if(cachedSearch == null) {
270                    if(!searchControl.searchResultExists(search, entityInstance)) {
271                        handleExecutionError(UnknownSearchResultException.class, eea, ExecutionErrors.UnknownSearchResult.name(),
272                                searchKindName, searchTypeName, basePK.getEntityRef());
273                    }
274                } else {
275                    var cachedExecutedSearch = searchControl.getCachedExecutedSearch(cachedSearch);
276
277                    if(!searchControl.cachedExecutedSearchResultExists(cachedExecutedSearch, entityInstance)) {
278                        handleExecutionError(UnknownCachedExecutedSearchResultException.class, eea, ExecutionErrors.UnknownCachedExecutedSearchResult.name(),
279                                searchKindName, searchTypeName, basePK.getEntityRef());
280                    }
281                }
282
283                if(eea == null || !eea.hasExecutionErrors()) {
284                    if(searchControl.searchResultActionExists(search, searchResultActionType, entityInstance)) {
285                        handleExecutionError(DuplicateSearchResultActionException.class, eea, ExecutionErrors.DuplicateSearchResultAction.name(),
286                                searchKindName, searchTypeName, searchResultActionTypeName, basePK.getEntityRef());
287                    } else {
288                        searchControl.createSearchResultAction(search, searchResultActionType, getSession().START_TIME_LONG, entityInstance, createdBy);
289                    }
290                }
291            }
292        }
293        
294        return itemSearchResultAction;
295    }
296    
297    public void invalidateCachedSearchesByIndex(Index index) {
298        for(var cachedSearchToInvalidateResult : new CachedSearchToInvalidateQuery().execute(index)) {
299            cachedSearchToInvalidateResult.getCachedSearchStatus().setIsConsistent(false);
300        }
301    }
302    
303}