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.common.EntityAttributeTypes;
020import com.echothree.model.control.core.common.exception.InvalidEntityAttributeTypeException;
021import com.echothree.model.control.core.server.control.CoreControl;
022import com.echothree.model.control.search.common.transfer.UserVisitSearchFacetIntegerTransfer;
023import com.echothree.model.control.search.common.transfer.UserVisitSearchFacetListItemTransfer;
024import com.echothree.model.control.search.common.transfer.UserVisitSearchFacetLongTransfer;
025import com.echothree.model.control.search.common.transfer.UserVisitSearchFacetTransfer;
026import com.echothree.model.control.search.server.database.EntityIntegerAttributeFacetQuery;
027import com.echothree.model.control.search.server.database.EntityListItemAttributeFacetQuery;
028import com.echothree.model.control.search.server.database.EntityLongAttributeFacetQuery;
029import com.echothree.model.control.search.server.database.EntityMultipleListItemAttributeFacetQuery;
030import com.echothree.model.control.search.server.graphql.UserVisitSearchFacetIntegerObject;
031import com.echothree.model.control.search.server.graphql.UserVisitSearchFacetListItemObject;
032import com.echothree.model.control.search.server.graphql.UserVisitSearchFacetLongObject;
033import com.echothree.model.control.search.server.graphql.UserVisitSearchFacetObject;
034import com.echothree.model.data.core.server.entity.EntityAttribute;
035import com.echothree.model.data.core.server.entity.EntityIntegerRangeDetail;
036import com.echothree.model.data.core.server.entity.EntityLongRangeDetail;
037import com.echothree.model.data.search.server.entity.UserVisitSearch;
038import com.echothree.util.common.message.ExecutionErrors;
039import com.echothree.util.common.transfer.ListWrapper;
040import com.echothree.util.server.control.BaseLogic;
041import com.echothree.util.server.message.ExecutionErrorAccumulator;
042import com.echothree.util.server.persistence.Session;
043import java.util.ArrayList;
044import java.util.List;
045import javax.enterprise.context.ApplicationScoped;
046import javax.enterprise.inject.spi.CDI;
047
048@ApplicationScoped
049public class UserVisitSearchFacetLogic
050        extends BaseLogic {
051
052    protected UserVisitSearchFacetLogic() {
053        super();
054    }
055
056    public static UserVisitSearchFacetLogic getInstance() {
057        return CDI.current().select(UserVisitSearchFacetLogic.class).get();
058    }
059    
060    private List<EntityIntegerRangeDetail> getEntityIntegerRangeDetails(final EntityAttribute entityAttribute) {
061        final var coreControl = Session.getModelController(CoreControl.class);
062        final var entityIntegerRanges = coreControl.getEntityIntegerRanges(entityAttribute);
063        final var entityIntegerRangeDetails = new ArrayList<EntityIntegerRangeDetail>(entityIntegerRanges.size());
064        
065        entityIntegerRanges.forEach((entityIntegerRange) -> {
066            entityIntegerRangeDetails.add(entityIntegerRange.getLastDetail());
067        });
068        
069        return entityIntegerRangeDetails;
070    }
071    
072    private List<EntityLongRangeDetail> getEntityLongRangeDetails(final EntityAttribute entityAttribute) {
073        final var coreControl = Session.getModelController(CoreControl.class);
074        final var entityLongRanges = coreControl.getEntityLongRanges(entityAttribute);
075        final var entityLongRangeDetails = new ArrayList<EntityLongRangeDetail>(entityLongRanges.size());
076        
077        entityLongRanges.forEach((entityLongRange) -> {
078            entityLongRangeDetails.add(entityLongRange.getLastDetail());
079        });
080        
081        return entityLongRangeDetails;
082    }
083
084    // Substantial portions of this are duplicated in getUserVisitSearchFacetObject(...).
085    public UserVisitSearchFacetTransfer getUserVisitSearchFacetTransfer(final ExecutionErrorAccumulator eea, final UserVisitSearch userVisitSearch,
086            final EntityAttribute entityAttribute) {
087        final var coreControl = Session.getModelController(CoreControl.class);
088        List<UserVisitSearchFacetListItemTransfer> userVisitSearchFacetListItemTransfers = null;
089        List<UserVisitSearchFacetIntegerTransfer> userVisitSearchFacetIntegerTransfers = null;
090        List<UserVisitSearchFacetLongTransfer> userVisitSearchFacetLongTransfers = null;
091        final var userVisit = userVisitSearch.getUserVisit();
092        final var entityAttributeTypeName = entityAttribute.getLastDetail().getEntityAttributeType().getEntityAttributeTypeName();
093
094        if(entityAttributeTypeName.equals(EntityAttributeTypes.LISTITEM.name())) {
095            final var entityListItemAttributeFacetResults = new EntityListItemAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
096
097            userVisitSearchFacetListItemTransfers = new ArrayList<>(entityListItemAttributeFacetResults.size());
098
099            for(var entityListItemAttributeFacetResult : entityListItemAttributeFacetResults) {
100                userVisitSearchFacetListItemTransfers.add(
101                        new UserVisitSearchFacetListItemTransfer(coreControl.getEntityListItemTransfer(userVisit, entityListItemAttributeFacetResult.getEntityListItem(), null),
102                                entityListItemAttributeFacetResult.getCount()));
103            }
104        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.MULTIPLELISTITEM.name())) {
105            final var entityMultipleListItemAttributeFacetResults = new EntityMultipleListItemAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
106
107            userVisitSearchFacetListItemTransfers = new ArrayList<>(entityMultipleListItemAttributeFacetResults.size());
108
109            for(var entityListItemAttributeFacetResult : entityMultipleListItemAttributeFacetResults) {
110                userVisitSearchFacetListItemTransfers.add(
111                        new UserVisitSearchFacetListItemTransfer(coreControl.getEntityListItemTransfer(userVisit, entityListItemAttributeFacetResult.getEntityListItem(), null),
112                                entityListItemAttributeFacetResult.getCount()));
113            }
114        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.INTEGER.name())) {
115            final var entityIntegerRangeDetails = getEntityIntegerRangeDetails(entityAttribute);
116            final var currentSize = entityIntegerRangeDetails.size();
117
118            userVisitSearchFacetIntegerTransfers = new ArrayList<>(currentSize); // maximum size, may be smaller.
119
120            if(currentSize > 0) {
121                final var entityIntegerAttributeFacetResults = new EntityIntegerAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
122
123                for(var entityIntegerAttributeFacetResult : entityIntegerAttributeFacetResults) {
124                    final var addedUserVisitSearchFacetIntegerTransfers = new ArrayList<UserVisitSearchFacetIntegerTransfer>();
125                    final var integerAttribute = entityIntegerAttributeFacetResult.getIntegerAttribute();
126
127                    for(var iter = entityIntegerRangeDetails.iterator(); iter.hasNext(); ) {
128                        final var entityIntegerRangeDetail = iter.next();
129                        final var minimumIntegerValue = entityIntegerRangeDetail.getMinimumIntegerValue();
130                        final var maximumIntegerValue = entityIntegerRangeDetail.getMaximumIntegerValue();
131
132                        if((minimumIntegerValue == null && maximumIntegerValue != null && integerAttribute <= maximumIntegerValue)
133                                || (minimumIntegerValue != null && maximumIntegerValue == null && integerAttribute >= minimumIntegerValue)
134                                || (minimumIntegerValue != null && maximumIntegerValue != null && integerAttribute >= minimumIntegerValue && integerAttribute <= maximumIntegerValue)) {
135                            addedUserVisitSearchFacetIntegerTransfers.add(
136                                    new UserVisitSearchFacetIntegerTransfer(coreControl.getEntityIntegerRangeTransfer(userVisit, entityIntegerRangeDetail.getEntityIntegerRange(), null),
137                                            entityIntegerAttributeFacetResult.getCount()));
138                            iter.remove();
139                        }
140                    }
141
142                    for(var userVisitSearchFacetIntegerTransfer : userVisitSearchFacetIntegerTransfers) {
143                        final var entityIntegerRange = userVisitSearchFacetIntegerTransfer.getEntityIntegerRange();
144                        final var minimumIntegerValue = entityIntegerRange.getMinimumIntegerValue();
145                        final var maximumIntegerValue = entityIntegerRange.getMaximumIntegerValue();
146
147                        if((minimumIntegerValue == null && maximumIntegerValue != null && integerAttribute <= maximumIntegerValue)
148                                || (minimumIntegerValue != null && maximumIntegerValue == null && integerAttribute >= minimumIntegerValue)
149                                || (minimumIntegerValue != null && maximumIntegerValue != null && integerAttribute >= minimumIntegerValue && integerAttribute <= maximumIntegerValue)) {
150                            userVisitSearchFacetIntegerTransfer.setCount(userVisitSearchFacetIntegerTransfer.getCount() + entityIntegerAttributeFacetResult.getCount());
151                        }
152                    }
153
154                    userVisitSearchFacetIntegerTransfers.addAll(addedUserVisitSearchFacetIntegerTransfers);
155                }
156            }
157
158            userVisitSearchFacetIntegerTransfers.sort(
159                    (UserVisitSearchFacetIntegerTransfer o1, UserVisitSearchFacetIntegerTransfer o2) -> Long.compare(o2.getCount(), o1.getCount())
160            );
161        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.LONG.name())) {
162            final var entityLongRangeDetails = getEntityLongRangeDetails(entityAttribute);
163            final var currentSize = entityLongRangeDetails.size();
164
165            userVisitSearchFacetLongTransfers = new ArrayList<>(currentSize); // maximum size, may be smaller.
166
167            if(currentSize > 0) {
168                final var entityLongAttributeFacetResults = new EntityLongAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
169
170                for(var entityLongAttributeFacetResult : entityLongAttributeFacetResults) {
171                    final var addedUserVisitSearchFacetLongTransfers = new ArrayList<UserVisitSearchFacetLongTransfer>();
172                    final var longAttribute = entityLongAttributeFacetResult.getLongAttribute();
173
174                    for(var iter = entityLongRangeDetails.iterator(); iter.hasNext(); ) {
175                        final var entityLongRangeDetail = iter.next();
176                        final var minimumLongValue = entityLongRangeDetail.getMinimumLongValue();
177                        final var maximumLongValue = entityLongRangeDetail.getMaximumLongValue();
178
179                        if((minimumLongValue == null && maximumLongValue != null && longAttribute <= maximumLongValue)
180                                || (minimumLongValue != null && maximumLongValue == null && longAttribute >= minimumLongValue)
181                                || (minimumLongValue != null && maximumLongValue != null && longAttribute >= minimumLongValue && longAttribute <= maximumLongValue)) {
182                            addedUserVisitSearchFacetLongTransfers.add(
183                                    new UserVisitSearchFacetLongTransfer(coreControl.getEntityLongRangeTransfer(userVisit, entityLongRangeDetail.getEntityLongRange(), null),
184                                            entityLongAttributeFacetResult.getCount()));
185                            iter.remove();
186                        }
187                    }
188
189                    for(var userVisitSearchFacetLongTransfer : userVisitSearchFacetLongTransfers) {
190                        final var entityLongRange = userVisitSearchFacetLongTransfer.getEntityLongRange();
191                        final var minimumLongValue = entityLongRange.getMinimumLongValue();
192                        final var maximumLongValue = entityLongRange.getMaximumLongValue();
193
194                        if((minimumLongValue == null && maximumLongValue != null && longAttribute <= maximumLongValue)
195                                || (minimumLongValue != null && maximumLongValue == null && longAttribute >= minimumLongValue)
196                                || (minimumLongValue != null && maximumLongValue != null && longAttribute >= minimumLongValue && longAttribute <= maximumLongValue)) {
197                            userVisitSearchFacetLongTransfer.setCount(userVisitSearchFacetLongTransfer.getCount() + entityLongAttributeFacetResult.getCount());
198                        }
199                    }
200
201                    userVisitSearchFacetLongTransfers.addAll(addedUserVisitSearchFacetLongTransfers);
202                }
203            }
204
205            userVisitSearchFacetLongTransfers.sort(
206                    (UserVisitSearchFacetLongTransfer o1, UserVisitSearchFacetLongTransfer o2) -> Long.compare(o2.getCount(), o1.getCount())
207            );
208        } else {
209            handleExecutionError(InvalidEntityAttributeTypeException.class, eea, ExecutionErrors.InvalidEntityAttributeType.name(), entityAttributeTypeName);
210        }
211
212        return eea.hasExecutionErrors() ? null : new UserVisitSearchFacetTransfer(coreControl.getEntityAttributeTransfer(userVisit, entityAttribute, null),
213                userVisitSearchFacetListItemTransfers == null ? null : new ListWrapper<>(userVisitSearchFacetListItemTransfers),
214                userVisitSearchFacetIntegerTransfers == null ? null : new ListWrapper<>(userVisitSearchFacetIntegerTransfers),
215                userVisitSearchFacetLongTransfers == null ? null : new ListWrapper<>(userVisitSearchFacetLongTransfers));
216    }
217
218    // Substantial portions of this are duplicated in getUserVisitSearchFacetTransfer(...).
219    public UserVisitSearchFacetObject getUserVisitSearchFacetObject(final ExecutionErrorAccumulator eea, final UserVisitSearch userVisitSearch,
220            final EntityAttribute entityAttribute) {
221        List<UserVisitSearchFacetListItemObject> userVisitSearchFacetListItemObjects = null;
222        List<UserVisitSearchFacetIntegerObject> userVisitSearchFacetIntegerObjects = null;
223        List<UserVisitSearchFacetLongObject> userVisitSearchFacetLongObjects = null;
224        final var entityAttributeTypeName = entityAttribute.getLastDetail().getEntityAttributeType().getEntityAttributeTypeName();
225
226        if(entityAttributeTypeName.equals(EntityAttributeTypes.LISTITEM.name())) {
227            final var entityListItemAttributeFacetResults = new EntityListItemAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
228
229            userVisitSearchFacetListItemObjects = new ArrayList<>(entityListItemAttributeFacetResults.size());
230
231            for(var entityListItemAttributeFacetResult : entityListItemAttributeFacetResults) {
232                userVisitSearchFacetListItemObjects.add(
233                        new UserVisitSearchFacetListItemObject(entityListItemAttributeFacetResult.getEntityListItem(),
234                                entityListItemAttributeFacetResult.getCount()));
235            }
236        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.MULTIPLELISTITEM.name())) {
237            final var entityMultipleListItemAttributeFacetResults = new EntityMultipleListItemAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
238
239            userVisitSearchFacetListItemObjects = new ArrayList<>(entityMultipleListItemAttributeFacetResults.size());
240
241            for(var entityListItemAttributeFacetResult : entityMultipleListItemAttributeFacetResults) {
242                userVisitSearchFacetListItemObjects.add(
243                        new UserVisitSearchFacetListItemObject(entityListItemAttributeFacetResult.getEntityListItem(),
244                                entityListItemAttributeFacetResult.getCount()));
245            }
246        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.INTEGER.name())) {
247            final var entityIntegerRangeDetails = getEntityIntegerRangeDetails(entityAttribute);
248            final var currentSize = entityIntegerRangeDetails.size();
249
250            userVisitSearchFacetIntegerObjects = new ArrayList<>(currentSize); // maximum size, may be smaller.
251
252            if(currentSize > 0) {
253                final var entityIntegerAttributeFacetResults = new EntityIntegerAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
254
255                for(var entityIntegerAttributeFacetResult : entityIntegerAttributeFacetResults) {
256                    final var addedUserVisitSearchFacetIntegerObjects = new ArrayList<UserVisitSearchFacetIntegerObject>();
257                    final var integerAttribute = entityIntegerAttributeFacetResult.getIntegerAttribute();
258
259                    for(var iter = entityIntegerRangeDetails.iterator(); iter.hasNext(); ) {
260                        final var entityIntegerRangeDetail = iter.next();
261                        final var minimumIntegerValue = entityIntegerRangeDetail.getMinimumIntegerValue();
262                        final var maximumIntegerValue = entityIntegerRangeDetail.getMaximumIntegerValue();
263
264                        if((minimumIntegerValue == null && maximumIntegerValue != null && integerAttribute <= maximumIntegerValue)
265                                || (minimumIntegerValue != null && maximumIntegerValue == null && integerAttribute >= minimumIntegerValue)
266                                || (minimumIntegerValue != null && maximumIntegerValue != null && integerAttribute >= minimumIntegerValue && integerAttribute <= maximumIntegerValue)) {
267                            addedUserVisitSearchFacetIntegerObjects.add(
268                                    new UserVisitSearchFacetIntegerObject(entityIntegerRangeDetail.getEntityIntegerRange(),
269                                            entityIntegerAttributeFacetResult.getCount()));
270                            iter.remove();
271                        }
272                    }
273
274                    for(var userVisitSearchFacetIntegerObject : userVisitSearchFacetIntegerObjects) {
275                        final var entityIntegerRange = userVisitSearchFacetIntegerObject.getEntityIntegerRange();
276                        final var minimumIntegerValue = entityIntegerRange.getLastDetail().getMinimumIntegerValue();
277                        final var maximumIntegerValue = entityIntegerRange.getLastDetail().getMaximumIntegerValue();
278
279                        if((minimumIntegerValue == null && maximumIntegerValue != null && integerAttribute <= maximumIntegerValue)
280                                || (minimumIntegerValue != null && maximumIntegerValue == null && integerAttribute >= minimumIntegerValue)
281                                || (minimumIntegerValue != null && maximumIntegerValue != null && integerAttribute >= minimumIntegerValue && integerAttribute <= maximumIntegerValue)) {
282                            userVisitSearchFacetIntegerObject.setCount(userVisitSearchFacetIntegerObject.getCount() + entityIntegerAttributeFacetResult.getCount());
283                        }
284                    }
285
286                    userVisitSearchFacetIntegerObjects.addAll(addedUserVisitSearchFacetIntegerObjects);
287                }
288            }
289
290            userVisitSearchFacetIntegerObjects.sort(
291                    (UserVisitSearchFacetIntegerObject o1, UserVisitSearchFacetIntegerObject o2) -> Long.compare(o2.getCount(), o1.getCount())
292            );
293        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.LONG.name())) {
294            final var entityLongRangeDetails = getEntityLongRangeDetails(entityAttribute);
295            final var currentSize = entityLongRangeDetails.size();
296
297            userVisitSearchFacetLongObjects = new ArrayList<>(currentSize); // maximum size, may be smaller.
298
299            if(currentSize > 0) {
300                final var entityLongAttributeFacetResults = new EntityLongAttributeFacetQuery(userVisitSearch).execute(entityAttribute);
301
302                for(var entityLongAttributeFacetResult : entityLongAttributeFacetResults) {
303                    final var addedUserVisitSearchFacetLongObjects = new ArrayList<UserVisitSearchFacetLongObject>();
304                    final var longAttribute = entityLongAttributeFacetResult.getLongAttribute();
305
306                    for(var iter = entityLongRangeDetails.iterator(); iter.hasNext(); ) {
307                        final var entityLongRangeDetail = iter.next();
308                        final var minimumLongValue = entityLongRangeDetail.getMinimumLongValue();
309                        final var maximumLongValue = entityLongRangeDetail.getMaximumLongValue();
310
311                        if((minimumLongValue == null && maximumLongValue != null && longAttribute <= maximumLongValue)
312                                || (minimumLongValue != null && maximumLongValue == null && longAttribute >= minimumLongValue)
313                                || (minimumLongValue != null && maximumLongValue != null && longAttribute >= minimumLongValue && longAttribute <= maximumLongValue)) {
314                            addedUserVisitSearchFacetLongObjects.add(
315                                    new UserVisitSearchFacetLongObject(entityLongRangeDetail.getEntityLongRange(),
316                                            entityLongAttributeFacetResult.getCount()));
317                            iter.remove();
318                        }
319                    }
320
321                    for(var userVisitSearchFacetLongObject : userVisitSearchFacetLongObjects) {
322                        final var entityLongRange = userVisitSearchFacetLongObject.getEntityLongRange();
323                        final var minimumLongValue = entityLongRange.getLastDetail().getMinimumLongValue();
324                        final var maximumLongValue = entityLongRange.getLastDetail().getMaximumLongValue();
325
326                        if((minimumLongValue == null && maximumLongValue != null && longAttribute <= maximumLongValue)
327                                || (minimumLongValue != null && maximumLongValue == null && longAttribute >= minimumLongValue)
328                                || (minimumLongValue != null && maximumLongValue != null && longAttribute >= minimumLongValue && longAttribute <= maximumLongValue)) {
329                            userVisitSearchFacetLongObject.setCount(userVisitSearchFacetLongObject.getCount() + entityLongAttributeFacetResult.getCount());
330                        }
331                    }
332
333                    userVisitSearchFacetLongObjects.addAll(addedUserVisitSearchFacetLongObjects);
334                }
335            }
336
337            userVisitSearchFacetLongObjects.sort(
338                    (UserVisitSearchFacetLongObject o1, UserVisitSearchFacetLongObject o2) -> Long.compare(o2.getCount(), o1.getCount())
339            );
340        } else {
341            handleExecutionError(InvalidEntityAttributeTypeException.class, eea, ExecutionErrors.InvalidEntityAttributeType.name(), entityAttributeTypeName);
342        }
343
344        return (eea != null && eea.hasExecutionErrors()) ? null : new UserVisitSearchFacetObject(entityAttribute,
345                userVisitSearchFacetListItemObjects, userVisitSearchFacetIntegerObjects, userVisitSearchFacetLongObjects);
346    }
347
348}