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