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