001/** 002 * Copyright 2014 Tampere University of Technology, Pori Department 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 */ 016package service.tut.pori.contentanalysis; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Date; 021import java.util.EnumSet; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Set; 029import java.util.UUID; 030 031import org.apache.commons.lang3.ArrayUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.log4j.Logger; 034import org.apache.solr.client.solrj.response.QueryResponse; 035import org.apache.solr.client.solrj.response.UpdateResponse; 036import org.apache.solr.common.SolrException; 037import org.springframework.beans.factory.annotation.Autowired; 038 039import service.tut.pori.contentanalysis.CAContentCore.ServiceType; 040import service.tut.pori.contentanalysis.CAContentCore.Visibility; 041import service.tut.pori.contentanalysis.MediaObject.ConfirmationStatus; 042import service.tut.pori.contentanalysis.MediaObject.MediaObjectType; 043import core.tut.pori.dao.SimpleSolrTemplate; 044import core.tut.pori.dao.SolrDAO; 045import core.tut.pori.dao.SolrQueryBuilder; 046import core.tut.pori.dao.SQLSelectBuilder.OrderDirection; 047import core.tut.pori.dao.filter.AbstractQueryFilter; 048import core.tut.pori.dao.filter.AndQueryFilter; 049import core.tut.pori.dao.filter.AndSubQueryFilter; 050import core.tut.pori.dao.filter.OrQueryFilter; 051import core.tut.pori.dao.filter.OrSubQueryFilter; 052import core.tut.pori.dao.filter.RangeQueryFilter; 053import core.tut.pori.http.parameters.DataGroups; 054import core.tut.pori.http.parameters.Limits; 055import core.tut.pori.http.parameters.QueryParameter; 056import core.tut.pori.http.parameters.SortOptions; 057import core.tut.pori.http.parameters.SortOptions.Option; 058import core.tut.pori.users.UserIdentity; 059import core.tut.pori.utils.MediaUrlValidator.MediaType; 060 061 062/** 063 * The DAO for storing and retrieving media objects. 064 * 065 * This class can also be used to retrieve media object suggestions. Note that if you wish to associate media objects with photos you must use PhotoDAO, not this class. 066 * 067 */ 068public class MediaObjectDAO extends SolrDAO{ 069 private static final String BEAN_ID_SOLR_SERVER = "solrServerMediaObjects"; 070 private static final int[] DEFAULT_CONFIRMATION_STATUS_LIST = ConfirmationStatus.toIntArray(EnumSet.of(ConfirmationStatus.CANDIDATE, ConfirmationStatus.USER_CONFIRMED)); 071 private static final SortOptions DEFAULT_SORT_OPTIONS; 072 static{ 073 DEFAULT_SORT_OPTIONS = new SortOptions(); 074 DEFAULT_SORT_OPTIONS.addSortOption(new SortOptions.Option(SOLR_FIELD_ID, OrderDirection.ASCENDING, Definitions.ELEMENT_MEDIA_OBJECTLIST)); 075 } 076 private static final Visibility DEFAULT_VISIBILITY = Visibility.PRIVATE; 077 private static final String[] FIELDS_DATA_GROUP_DEFAULTS = new String[]{ 078 Definitions.SOLR_FIELD_BACKEND_ID, Definitions.SOLR_FIELD_CONFIDENCE, SOLR_FIELD_ID, Definitions.SOLR_FIELD_NAME, Definitions.SOLR_FIELD_CREATOR_OBJECT_ID, Definitions.SOLR_FIELD_RANK, Definitions.SOLR_FIELD_UPDATED, Definitions.SOLR_FIELD_VALUE, // members 079 Definitions.SOLR_FIELD_USER_ID, // user identity 080 Definitions.SOLR_FIELD_SERVICE_ID, // service type 081 Definitions.SOLR_FIELD_MEDIA_OBJECT_TYPE, // object type 082 Definitions.SOLR_FIELD_MEDIA_TYPE, // media type 083 Definitions.SOLR_FIELD_VISUAL_SHAPE_VALUE, // for visual shape 084 Definitions.SOLR_FIELD_VISUAL_SHAPE_TYPE, // for visual shape 085 Definitions.SOLR_FIELD_STATUS // confirmation status 086 }; 087 private static final String[] FIELDS_DATA_GROUP_TIMECODES = new String[]{Definitions.SOLR_FIELD_TIMECODES}; 088 private static final String[] FIELDS_RESOLVE_OBJECT_IDS = new String[]{SOLR_FIELD_ID, Definitions.SOLR_FIELD_BACKEND_ID, Definitions.SOLR_FIELD_USER_ID, Definitions.SOLR_FIELD_CREATOR_OBJECT_ID}; 089 private static final String[] FIELDS_UPDATE = new String[]{Definitions.SOLR_FIELD_USER_ID, SOLR_FIELD_ID, Definitions.SOLR_FIELD_BACKEND_ID, Definitions.SOLR_FIELD_CREATOR_OBJECT_ID}; 090 private static final Logger LOGGER = Logger.getLogger(MediaObjectDAO.class); 091 @Autowired 092 private KeywordsDAO _keywordsDAO = null; 093 @Autowired 094 private PhotoDAO _photoDAO = null; 095 096 /** 097 * @param objects 098 * @return true on success 099 */ 100 public boolean insert(MediaObjectList objects){ 101 if(MediaObjectList.isEmpty(objects)){ 102 LOGGER.debug("Ignored empty object list."); 103 return true; 104 } 105 Date updated = new Date(); 106 List<MediaObject> v = objects.getMediaObjects(); 107 for(Iterator<MediaObject> vIter = v.iterator(); vIter.hasNext();){ // make sure all objects has updated timestamps 108 MediaObject o = vIter.next(); 109 if(o.getUpdated() == null){ 110 o.setUpdated(updated); 111 } 112 if(o.getMediaObjectId() != null){ 113 LOGGER.warn("Replacing Media object Id exist for media object with existing id, id: "+o.getMediaObjectId()); 114 } 115 String mediaObjectId = UUID.randomUUID().toString(); 116 o.setMediaObjectId(mediaObjectId); 117 if(o.getObjectId() == null){ 118 LOGGER.debug("No objectId given, using media object id."); 119 o.setObjectId(mediaObjectId); 120 } 121 if(o.getOwnerUserId() == null){ 122 LOGGER.debug("Adding media object without owner."); 123 } 124 if(o.getVisibility() == null){ 125 LOGGER.debug("No visibility value given, using default: "+DEFAULT_VISIBILITY.name()); 126 o.setVisibility(DEFAULT_VISIBILITY); 127 } 128 } 129 if(!MediaObjectList.isValid(objects)){ // check validity after ids have been generated 130 LOGGER.warn("Tried to add invalid object list."); 131 return false; 132 } 133 SimpleSolrTemplate template = getSolrTemplate(BEAN_ID_SOLR_SERVER); 134 UpdateResponse response = template.addBeans(v); 135 if(response.getStatus() == SolrException.ErrorCode.UNKNOWN.code){ 136 return true; 137 }else{ 138 LOGGER.warn("Failed to add media objects."); 139 return false; 140 } 141 } 142 143 /** 144 * helper methods for resolving object ids (backendId, mediaObjectId, objectId, userId) 145 * 146 * @param backendIdObjectIdMap 147 * @param mediaObjectIds 148 * @param objects 149 */ 150 private void resolveObjectIds(Map<Integer, HashSet<String>> backendIdObjectIdMap, Set<String> mediaObjectIds, List<MediaObject> objects){ 151 boolean noMediaObjectIds = mediaObjectIds.isEmpty(); 152 boolean noObjectIds = backendIdObjectIdMap.isEmpty(); 153 154 if(noMediaObjectIds && noObjectIds){ 155 LOGGER.debug("No media object ids or object ids."); 156 return; 157 } 158 159 SolrQueryBuilder query = new SolrQueryBuilder(); 160 query.addFields(FIELDS_RESOLVE_OBJECT_IDS); 161 if(!noMediaObjectIds){ 162 query.addCustomFilter(new OrQueryFilter(SOLR_FIELD_ID, mediaObjectIds)); 163 } 164 165 if(!noObjectIds){ 166 for(Entry<Integer, HashSet<String>> e : backendIdObjectIdMap.entrySet()){ 167 query.addCustomFilter(new OrSubQueryFilter(new AbstractQueryFilter[]{new AndQueryFilter(Definitions.SOLR_FIELD_BACKEND_ID, e.getKey()), new AndQueryFilter(Definitions.SOLR_FIELD_CREATOR_OBJECT_ID, e.getValue())})); 168 } 169 } 170 171 List<MediaObject> results = getSolrTemplate(BEAN_ID_SOLR_SERVER).queryForList(query.toSolrQuery(Definitions.ELEMENT_MEDIA_OBJECTLIST), MediaObject.class); 172 if(results == null){ 173 LOGGER.debug("No results."); 174 return; 175 } 176 177 for(MediaObject object : objects){ 178 for(MediaObject result : results){ 179 if(result.getMediaObjectId().equals(object.getMediaObjectId())){ 180 UserIdentity oUserId = object.getOwnerUserId(); 181 UserIdentity rUserId = result.getOwnerUserId(); 182 if(oUserId != null && !UserIdentity.equals(oUserId, rUserId)){ 183 LOGGER.warn("Replacing conflicting userId."); 184 } 185 object.setOwnerUserId(rUserId); 186 187 String oObjectId = object.getObjectId(); 188 String rObjectId = result.getObjectId(); 189 if(oObjectId != null && !oObjectId.equals(rObjectId )){ 190 LOGGER.warn("Replacing conflicting objectId."); 191 } 192 object.setObjectId(rObjectId); 193 194 Integer oBackendId = object.getBackendId(); 195 Integer rBackendId = result.getBackendId(); 196 if(oBackendId != null && !oBackendId.equals(rBackendId)){ 197 LOGGER.warn("Replacing conflicting backendId."); 198 } 199 object.setBackendId(rBackendId); 200 }else if(result.getObjectId().equals(object.getObjectId())){ 201 Integer rBackendId = result.getBackendId(); 202 Integer oBackendId = object.getBackendId(); 203 if((rBackendId == null && oBackendId == null) || (rBackendId != null && rBackendId.equals(oBackendId))){ 204 UserIdentity oUserId = object.getOwnerUserId(); 205 UserIdentity rUserId = result.getOwnerUserId(); 206 if(oUserId != null && !UserIdentity.equals(oUserId, rUserId)){ 207 LOGGER.warn("Replacing conflicting userId."); 208 } 209 object.setOwnerUserId(rUserId); 210 object.setMediaObjectId(result.getMediaObjectId()); 211 } // if it is a match 212 } // else ignore non-matching media object, this can be later matched or new object without a match 213 } // for results 214 } // for objects 215 } 216 217 /** 218 * Sets all missing ids for the given media objects if ids are found. 219 * Objects without valid mediaObjectId or backendId+objectId pair are ignored. 220 * 221 * @param mediaObjects 222 */ 223 public void resolveObjectIds(MediaObjectList mediaObjects){ 224 if(MediaObjectList.isEmpty(mediaObjects)){ 225 LOGGER.debug("Empty object list."); 226 return; 227 } 228 229 HashSet<String> mediaObjectIds = new HashSet<>(); 230 HashMap<Integer, HashSet<String>> backendIdObjectIdMap = new HashMap<>(); 231 List<MediaObject> objects = mediaObjects.getMediaObjects(); 232 for(Iterator<MediaObject> vIter = objects.iterator(); vIter.hasNext();){ // get media object ids, backend ids and object ids 233 MediaObject vo = vIter.next(); 234 String mediaObjectId = vo.getMediaObjectId(); 235 if(!StringUtils.isBlank(mediaObjectId)){ 236 mediaObjectIds.add(mediaObjectId); 237 }else{ 238 String objectId = vo.getObjectId(); 239 if(!StringUtils.isBlank(objectId)){ 240 Integer backendId = vo.getBackendId(); 241 HashSet<String> objectIds = backendIdObjectIdMap.get(backendId); 242 if(objectIds == null){ 243 objectIds = new HashSet<>(); 244 backendIdObjectIdMap.put(backendId, objectIds); 245 } 246 objectIds.add(objectId); 247 }else{ 248 LOGGER.debug("Ignored media object without objectId and mediaObjectId."); 249 } 250 } 251 } 252 resolveObjectIds(backendIdObjectIdMap, mediaObjectIds, objects); 253 } 254 255 /** 256 * 257 * @param objects 258 * @return true on success. Note that <i>nothing updated</i> equals to success if no other errors are present. 259 */ 260 public boolean updateIfNewer(MediaObjectList objects){ 261 return update(objects, true); 262 } 263 264 /** 265 * 266 * @param objects 267 * @return true on success 268 */ 269 public boolean update(MediaObjectList objects){ 270 return update(objects, false); 271 } 272 273 /** 274 * The objects must have a valid id, either objectId (provided by back-end or client) + backendId (Note: null is a valid backendId if object created by the user) or mediaObjectId (provided by system) 275 * 276 * @param objects 277 * @param onlyNewer if true, only the objects which are newer will be added to the database. If the passed object does not have updated set, it will be ignored if onlyNewer is true. 278 * @return true on success 279 */ 280 private boolean update(MediaObjectList objects, boolean onlyNewer){ 281 if(MediaObjectList.isEmpty(objects)){ 282 LOGGER.debug("Ignored empty object list."); 283 return true; 284 } 285 if(!MediaObjectList.isValid(objects)){ 286 LOGGER.warn("Tried update with invalid object list."); 287 return false; 288 } 289 290 List<String> voids = objects.getMediaObjectIds(); 291 if(voids == null){ 292 LOGGER.warn("Could not get media object ids."); 293 return false; 294 } 295 296 SolrQueryBuilder solr = new SolrQueryBuilder(); 297 solr.addFields(FIELDS_UPDATE); 298 if(onlyNewer){ 299 solr.addField(Definitions.SOLR_FIELD_UPDATED); 300 } 301 SimpleSolrTemplate t = getSolrTemplate(BEAN_ID_SOLR_SERVER); 302 List<MediaObject> refList = new ArrayList<>(); 303 304 solr.clearCustomFilters(); 305 solr.addCustomFilter(new AndQueryFilter(SOLR_FIELD_ID, voids)); 306 refList = t.queryForList(solr.toSolrQuery(Definitions.ELEMENT_MEDIA_OBJECTLIST), MediaObject.class); 307 if(refList.isEmpty()){ 308 LOGGER.warn("Tried to update non-existent objects."); 309 return false; 310 } 311 312 MediaObjectList references = MediaObjectList.getMediaObjectList(refList, null); 313 314 List<MediaObject> mediaObjects = objects.getMediaObjects(); 315 List<MediaObject> update = new ArrayList<>(mediaObjects.size()); 316 Date updated = new Date(); 317 for(MediaObject object : mediaObjects){ // check for that userId, backendId or objectId is not being changed 318 String mediaObjectId = object.getMediaObjectId(); 319 MediaObject reference = references.getMediaObject(mediaObjectId); 320 if(reference == null){ 321 LOGGER.warn("Tried to update non-existent object, id: "+mediaObjectId); 322 return false; 323 }else if(!reference.getObjectId().equals(object.getObjectId())){ 324 LOGGER.warn("Object creator id mismatch for object, id: "+mediaObjectId); 325 return false; 326 }else if(!UserIdentity.equals(reference.getOwnerUserId(), object.getOwnerUserId())){ 327 LOGGER.warn("Object user identity mismatch for object, id: "+mediaObjectId); 328 return false; 329 }else{ 330 Integer rBackendId = reference.getBackendId(); 331 Integer oBackendId = object.getBackendId(); 332 if(rBackendId != null){ 333 if(!rBackendId.equals(oBackendId)){ 334 LOGGER.warn("Object backend id mismatch for object, id: "+mediaObjectId); 335 return false; 336 } 337 }else if(oBackendId != null){ // both are not null 338 LOGGER.warn("Object backend id mismatch for object, id: "+mediaObjectId); 339 return false; 340 } 341 } 342 Date oUpdated = object.getUpdated(); 343 if(onlyNewer){ 344 if(oUpdated == null){ 345 LOGGER.warn("Ignored object without updated timestamp, id: "+mediaObjectId); 346 continue; 347 }else if(oUpdated.before(reference.getUpdated())){ 348 LOGGER.debug("Ignored object with older timestamp, id: "+mediaObjectId); 349 continue; 350 } 351 }else if(oUpdated == null){ 352 object.setUpdated(updated); // make sure there is a timestamp 353 } 354 if(object.getVisibility() == null){ 355 LOGGER.debug("Object is missing visibility, using default: "+DEFAULT_VISIBILITY.name()); 356 object.setVisibility(DEFAULT_VISIBILITY); 357 } 358 update.add(object); 359 } 360 361 if(t.addBeans(update).getStatus() == SolrException.ErrorCode.UNKNOWN.code){ 362 return true; 363 }else{ 364 LOGGER.warn("Failed to update media objects."); 365 return false; 366 } 367 } 368 369 370 371 /** 372 * This will also automatically remove any associations between the given media objects and their photos. 373 * 374 * @param mediaobjectIds 375 * @return true on success 376 */ 377 public boolean remove(Collection<String> mediaobjectIds){ 378 if(mediaobjectIds == null || mediaobjectIds.isEmpty()){ 379 LOGGER.debug("Ignored empty media object id list."); 380 return true; 381 } 382 383 for(String mediaObjectId : mediaobjectIds){ // remove associations 384 _photoDAO.deassociate(null, mediaObjectId); 385 } 386 SimpleSolrTemplate template = getSolrTemplate(BEAN_ID_SOLR_SERVER); 387 if(template.deleteById(mediaobjectIds).getStatus() == SolrException.ErrorCode.UNKNOWN.code){ 388 return true; 389 }else{ 390 LOGGER.warn("Failed to remove media objects."); 391 return false; 392 } 393 } 394 395 /** 396 * 397 * @param dataGroups 398 * @param limits 399 * @param mediaTypes target media types for the retrieval 400 * @param serviceTypes 401 * @param mediaObjectIds 402 * @param userIdFilter 403 * @return list of media objects or null if none was found 404 * @throws IllegalArgumentException on bad query terms 405 */ 406 public MediaObjectList getMediaObjects(DataGroups dataGroups, Limits limits, EnumSet<MediaType> mediaTypes, EnumSet<ServiceType> serviceTypes, Collection<String> mediaObjectIds, long[] userIdFilter) throws IllegalArgumentException { 407 if(mediaTypes == null || mediaTypes.isEmpty()){ 408 throw new IllegalArgumentException("Invalid MediaType "+MediaType.class.toString()+" given."); 409 } 410 return getMediaObjectList(dataGroups, limits, mediaTypes, serviceTypes, mediaObjectIds, userIdFilter); 411 } 412 413 /** 414 * helper method for retrieving the media object list 415 * 416 * @param dataGroups 417 * @param limits 418 * @param mediaTypes target media types, not null, nor empty 419 * @param serviceTypes 420 * @param mediaObjectIds 421 * @param userIdFilter 422 * @return list of media objects or null if none was found 423 */ 424 private MediaObjectList getMediaObjectList(DataGroups dataGroups, Limits limits, EnumSet<MediaType> mediaTypes, EnumSet<ServiceType> serviceTypes, Collection<String> mediaObjectIds, long[] userIdFilter) { 425 SolrQueryBuilder solr = new SolrQueryBuilder(); 426 if(!processDataGroups(dataGroups, solr)){ 427 LOGGER.debug("Process data groups did not result viable combination for search, returning null..."); 428 return null; 429 } 430 431 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_MEDIA_TYPE, MediaType.toInt(mediaTypes))); 432 433 if(!ServiceType.isEmpty(serviceTypes)){ 434 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_SERVICE_ID, ServiceType.toIdArray(serviceTypes))); 435 } 436 437 if(mediaObjectIds != null && !mediaObjectIds.isEmpty()){ 438 solr.addCustomFilter(new AndQueryFilter(SOLR_FIELD_ID, mediaObjectIds)); 439 } 440 441 if(userIdFilter != null){ 442 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_USER_ID, userIdFilter)); 443 } 444 445 solr.setSortOptions(DEFAULT_SORT_OPTIONS); 446 solr.setLimits(limits); 447 448 QueryResponse response = getSolrTemplate(BEAN_ID_SOLR_SERVER).query(solr.toSolrQuery(Definitions.ELEMENT_MEDIA_OBJECTLIST)); 449 List<MediaObject> mediaObjects = SimpleSolrTemplate.getList(response, MediaObject.class); 450 if(mediaObjects == null){ 451 LOGGER.debug("No results."); 452 return null; 453 } 454 455 ResultInfo info = null; 456 if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_RESULT_INFO, dataGroups)){ 457 LOGGER.debug("Resolving result info for the requested objects."); 458 info = new ResultInfo(limits.getStartItem(Definitions.ELEMENT_MEDIA_OBJECTLIST), limits.getEndItem(Definitions.ELEMENT_MEDIA_OBJECTLIST), response.getResults().getNumFound()); 459 } 460 461 MediaObjectList voList = MediaObjectList.getMediaObjectList(mediaObjects, info); 462 _keywordsDAO.assignFriendlyKeywords(voList); 463 return voList; 464 } 465 466 /** 467 * 468 * @param authenticatedUser 469 * @param dataGroups 470 * @param limits 471 * @param serviceTypes 472 * @param userIdFilter 473 * @param mediaObjectTerms list of terms to use for search. Note: if the objects have mediaObjectIds set, these will be directly used as filter 474 * @return list of ids or null if none 475 */ 476 public List<String> getMediaObjectIds(UserIdentity authenticatedUser, DataGroups dataGroups, Limits limits, EnumSet<ServiceType> serviceTypes, long[] userIdFilter, MediaObjectList mediaObjectTerms) { 477 SolrQueryBuilder solr = new SolrQueryBuilder(); 478 if(!processDataGroups(dataGroups, solr)){ 479 LOGGER.debug("Process data groups did not result viable combination for search, returning null..."); 480 return null; 481 } 482 483 if(!UserIdentity.isValid(authenticatedUser)){ 484 LOGGER.debug("Invalid authenticated user, limiting search to public content."); 485 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, Visibility.PUBLIC.toInt())); 486 }else{ 487 solr.addCustomFilter(new AndSubQueryFilter(new AbstractQueryFilter[]{new OrQueryFilter(Definitions.SOLR_FIELD_USER_ID, authenticatedUser.getUserId()), new OrQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, Visibility.PUBLIC.toInt())})); 488 } 489 490 if(!ServiceType.isEmpty(serviceTypes)){ 491 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_SERVICE_ID, ServiceType.toIdArray(serviceTypes))); 492 } 493 494 if(!ArrayUtils.isEmpty(userIdFilter)){ 495 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_USER_ID, userIdFilter)); 496 } 497 498 processMediaObjects(solr, mediaObjectTerms); 499 500 solr.setLimits(limits); 501 solr.setSortOptions(DEFAULT_SORT_OPTIONS); 502 solr.addField(SOLR_FIELD_ID); 503 504 return SimpleSolrTemplate.getObjects(getSolrTemplate(BEAN_ID_SOLR_SERVER).query(solr.toSolrQuery(Definitions.ELEMENT_MEDIA_OBJECTLIST)), SOLR_FIELD_ID, String.class); 505 } 506 507 /** 508 * 509 * @param solr 510 * @param mediaObjectTerms 511 * @throws IllegalArgumentException on too high term count 512 */ 513 private void processMediaObjects(SolrQueryBuilder solr, MediaObjectList mediaObjectTerms) throws IllegalArgumentException { 514 if(MediaObjectList.isEmpty(mediaObjectTerms)){ 515 LOGGER.debug("No media objects."); 516 return; 517 } 518 LOGGER.debug("Creating media object filters..."); 519 520 List<MediaObject> terms = mediaObjectTerms.getMediaObjects(); 521 List<String> mediaObjectIds = new ArrayList<>(); 522 List<AbstractQueryFilter> filters = new ArrayList<>(); 523 for(MediaObject term : terms){ 524 String mediaObjectId = term.getMediaObjectId(); 525 if(!StringUtils.isBlank(mediaObjectId)){ 526 LOGGER.debug("media object id was given, using as filter, id: "+mediaObjectId); 527 mediaObjectIds.add(mediaObjectId); 528 }else{ 529 String value = term.getValue(); 530 if(StringUtils.isBlank(value)){ 531 LOGGER.debug("No value for visual object, attempting to use name."); 532 value = term.getName(); 533 if(StringUtils.isBlank(value)){ 534 LOGGER.warn("Ignording media object search term without value or name."); 535 continue; 536 } 537 } 538 539 OrSubQueryFilter subFilter = new OrSubQueryFilter(); 540 subFilter.addFilter(new AndQueryFilter(Definitions.SOLR_FIELD_VALUE_SEARCH, value)); 541 542 MediaObjectType type = term.getMediaObjectType(); 543 if(type != null){ 544 subFilter.addFilter(new AndQueryFilter(Definitions.SOLR_FIELD_MEDIA_OBJECT_TYPE, type.toInt())); 545 } 546 547 Visibility visibility = term.getVisibility(); 548 if(visibility != null){ 549 subFilter.addFilter(new AndQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, visibility.toInt())); 550 } 551 552 ServiceType serviceType = term.getServiceType(); 553 if(serviceType != null){ 554 subFilter.addFilter(new AndQueryFilter(Definitions.SOLR_FIELD_SERVICE_ID, serviceType.getServiceId())); 555 } 556 557 ConfirmationStatus status = term.getConfirmationStatus(); 558 if(status != null){ 559 subFilter.addFilter(new AndQueryFilter(Definitions.SOLR_FIELD_STATUS, status.toInt())); 560 } 561 562 Integer temp = term.getRank(); 563 if(temp != null){ 564 subFilter.addFilter(new RangeQueryFilter(Definitions.SOLR_FIELD_RANK, temp, null)); 565 } 566 567 temp = term.getBackendId(); 568 if(temp != null){ 569 subFilter.addFilter(new RangeQueryFilter(Definitions.SOLR_FIELD_BACKEND_ID, temp, null)); 570 } 571 572 Double confidence = term.getConfidence(); 573 if(confidence != null){ 574 subFilter.addFilter(new RangeQueryFilter(Definitions.SOLR_FIELD_CONFIDENCE, confidence, null)); 575 } 576 577 filters.add(subFilter); 578 } 579 } // for 580 581 if(!filters.isEmpty()){ 582 solr.addCustomFilter(new AndSubQueryFilter(filters.toArray(new AbstractQueryFilter[filters.size()]))); 583 } 584 585 if(!mediaObjectIds.isEmpty()){ 586 solr.addCustomFilter(new AndQueryFilter(SOLR_FIELD_ID, mediaObjectIds)); 587 } 588 } 589 590 /** 591 * 592 * @param solr 593 * @param sortOptions 594 */ 595 private void setOrderBy(SolrQueryBuilder solr, SortOptions sortOptions){ 596 if(sortOptions == null || !sortOptions.hasValues()){ 597 solr.setSortOptions(DEFAULT_SORT_OPTIONS); 598 return; 599 } 600 601 Set<Option> so = sortOptions.getSortOptions(Definitions.ELEMENT_MEDIA_OBJECTLIST); 602 if(so == null){ 603 return; 604 } 605 606 for(Iterator<Option> iter = so.iterator();iter.hasNext();){ 607 Option o = iter.next(); 608 String elementName = o.getElementName(); 609 if(Definitions.ELEMENT_CONFIDENCE.equals(elementName)){ 610 solr.addSortOption(new Option(Definitions.SOLR_FIELD_CONFIDENCE, o.getOrderDirection(), Definitions.ELEMENT_MEDIA_OBJECTLIST)); 611 }else if(Definitions.ELEMENT_RANK.equals(elementName)){ 612 solr.addSortOption(new Option(Definitions.SOLR_FIELD_RANK, o.getOrderDirection(), Definitions.ELEMENT_MEDIA_OBJECTLIST)); 613 }else if(Definitions.ELEMENT_VALUE.equals(elementName)){ 614 solr.addSortOption(new Option(Definitions.SOLR_FIELD_VALUE, o.getOrderDirection(), Definitions.ELEMENT_MEDIA_OBJECTLIST)); 615 }else{ 616 LOGGER.debug("Ignored unknown sort element: "+elementName); 617 } 618 } 619 } 620 621 /** 622 * Note that because of solr limitations, the media object count cannot exceed MAX_FILTER_COUNT. 623 * 624 * @param authenticatedUser 625 * @param dataGroups 626 * @param limits 627 * @param mediaTypes list of target media types for the search 628 * @param serviceTypes 629 * @param sortOptions 630 * @param userIdFilter 631 * @param mediaObjectTerms list of terms to use for search. Note: if the objects have mediaObjectIds set, these will be directly used as filter 632 * @return list of media objects or null if none was found 633 * @throws IllegalArgumentException on bad values 634 */ 635 public MediaObjectList search(UserIdentity authenticatedUser, DataGroups dataGroups, Limits limits, EnumSet<MediaType> mediaTypes, EnumSet<ServiceType> serviceTypes, SortOptions sortOptions, long[] userIdFilter, MediaObjectList mediaObjectTerms) throws IllegalArgumentException { 636 if(mediaTypes == null || mediaTypes.isEmpty()){ 637 throw new IllegalArgumentException("Invalid "+MediaType.class.toString()+" given."); 638 } 639 640 SolrQueryBuilder solr = new SolrQueryBuilder(); 641 642 if(!processDataGroups(dataGroups, solr)){ 643 LOGGER.debug("Process data groups did not result viable combination for search, returning null..."); 644 return null; 645 } 646 647 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_MEDIA_TYPE, MediaType.toInt(mediaTypes))); 648 649 processMediaObjects(solr, mediaObjectTerms); // throws IllegalArgumentException if term count exceeds maximum 650 651 if(!UserIdentity.isValid(authenticatedUser)){ 652 LOGGER.debug("Invalid authenticated user, limiting search to public content."); 653 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, Visibility.PUBLIC.toInt())); 654 }else{ 655 solr.addCustomFilter(new AndSubQueryFilter(new AbstractQueryFilter[]{new OrQueryFilter(Definitions.SOLR_FIELD_USER_ID, authenticatedUser.getUserId()), new OrQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, Visibility.PUBLIC.toInt())})); 656 } 657 658 if(!ServiceType.isEmpty(serviceTypes)){ 659 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_SERVICE_ID, ServiceType.toIdArray(serviceTypes))); 660 } 661 662 if(!ArrayUtils.isEmpty(userIdFilter)){ 663 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_USER_ID, userIdFilter)); 664 } 665 666 solr.setLimits(limits); 667 setOrderBy(solr, sortOptions); 668 669 QueryResponse response = getSolrTemplate(BEAN_ID_SOLR_SERVER).query(solr.toSolrQuery(Definitions.ELEMENT_MEDIA_OBJECTLIST)); 670 List<? extends MediaObject> mediaObjects = SimpleSolrTemplate.getList(response, MediaObject.class); 671 if(mediaObjects == null){ 672 LOGGER.debug("No results."); 673 return null; 674 } 675 676 ResultInfo info = null; 677 if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_RESULT_INFO, dataGroups)){ 678 LOGGER.debug("Resolving result info for the requested objects."); 679 info = new ResultInfo(limits.getStartItem(Definitions.ELEMENT_MEDIA_OBJECTLIST), limits.getEndItem(Definitions.ELEMENT_MEDIA_OBJECTLIST), response.getResults().getNumFound()); 680 } 681 682 MediaObjectList voList = MediaObjectList.getMediaObjectList(mediaObjects, info); 683 _keywordsDAO.assignFriendlyKeywords(voList); 684 return voList; 685 } 686 687 /** 688 * helper method for processing the data groups and setting filters for the query builder. 689 * 690 * @param dataGroups For applicable values see {@link service.tut.pori.contentanalysis.MediaObject.MediaObjectType#fromDataGroups(DataGroups)}. 691 * @param solr 692 * @return true if the data groups can returned results. 693 */ 694 private boolean processDataGroups(DataGroups dataGroups, SolrQueryBuilder solr){ 695 if(!DataGroups.hasDataGroup(DataGroups.DATA_GROUP_ALL, dataGroups, Definitions.ELEMENT_MEDIA_OBJECTLIST)){ // do not care about filters if ALL is present 696 boolean onlyBasic = DataGroups.hasDataGroup(DataGroups.DATA_GROUP_BASIC, dataGroups, Definitions.ELEMENT_MEDIA_OBJECTLIST); 697 698 EnumSet<ConfirmationStatus> statuses = ConfirmationStatus.fromDataGroups(dataGroups); 699 if(statuses == null){ 700 LOGGER.debug("No "+ConfirmationStatus.class.toString()+" filter, using defaults."); 701 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_STATUS, DEFAULT_CONFIRMATION_STATUS_LIST)); 702 }else{ 703 onlyBasic = false; 704 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_STATUS, ConfirmationStatus.toIntArray(statuses))); 705 } 706 707 EnumSet<MediaObjectType> types = MediaObjectType.fromDataGroups(dataGroups); 708 if(types != null){ 709 onlyBasic = false; 710 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_MEDIA_OBJECT_TYPE, MediaObjectType.toIntArray(types))); 711 } 712 713 if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_TIMECODES, dataGroups, Definitions.ELEMENT_MEDIA_OBJECTLIST)){ 714 onlyBasic = false; 715 solr.addFields(FIELDS_DATA_GROUP_TIMECODES); 716 } 717 718 if(onlyBasic){ 719 LOGGER.debug("Data group "+DataGroups.DATA_GROUP_BASIC+" given without other media object related data groups, no results can be found."); 720 return false; 721 } 722 723 solr.addFields(FIELDS_DATA_GROUP_DEFAULTS); 724 } 725 return true; 726 } 727 728 /** 729 * Suggestion/Autocomplete from Solr 730 * @param authenticatedUser 731 * @param dataGroups filters based on MediaObjectType. For applicable values see {@link service.tut.pori.contentanalysis.MediaObject.MediaObjectType#fromDataGroups(DataGroups)}. 732 * @param limits 733 * @param query the term to be searched for. 734 * @return response 735 */ 736 public QueryResponse getSuggestions(UserIdentity authenticatedUser, DataGroups dataGroups, Limits limits, String query) { 737 SolrQueryBuilder solr = new SolrQueryBuilder(); 738 739 //set visibility restrictions 740 if(!UserIdentity.isValid(authenticatedUser)){ 741 LOGGER.debug("Invalid authenticated user, limiting search to public content."); 742 solr.addCustomFilter(new AndQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, Visibility.PUBLIC.toInt())); 743 }else{ 744 solr.addCustomFilter(new AndSubQueryFilter(new AbstractQueryFilter[]{new OrQueryFilter(Definitions.SOLR_FIELD_USER_ID, authenticatedUser.getUserId()), new OrQueryFilter(Definitions.SOLR_FIELD_VISIBILITY, Visibility.PUBLIC.toInt())})); 745 } 746 747 processDataGroups(dataGroups, solr); //sets the query filter for MediaObjectType 748 solr.setQueryParameter(new QueryParameter(query)); 749 solr.setLimits(limits); 750 751 return getSolrTemplate(BEAN_ID_SOLR_SERVER).query(SolrQueryBuilder.setRequestHandler(solr.toSolrQuery(), SolrQueryBuilder.RequestHandlerType.SUGGEST)); 752 } 753}