001/* 002 * Copyright 2018 Anyware Services 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 org.ametys.web.frontoffice.search.requesttime.impl; 017 018import java.util.Collection; 019import java.util.List; 020import java.util.Locale; 021import java.util.Map; 022import java.util.Set; 023import java.util.function.Function; 024import java.util.stream.Collectors; 025 026import org.apache.cocoon.environment.Request; 027import org.apache.commons.collections4.CollectionUtils; 028import org.apache.commons.lang3.ArrayUtils; 029import org.apache.commons.lang3.tuple.Pair; 030 031import org.ametys.cms.search.Sort; 032import org.ametys.cms.search.Sort.Order; 033import org.ametys.cms.search.cocoon.SearchAction; 034import org.ametys.cms.search.solr.SearcherFactory.Searcher; 035import org.ametys.web.frontoffice.search.instance.SearchServiceInstance; 036import org.ametys.web.frontoffice.search.metamodel.SortDefinition; 037import org.ametys.web.frontoffice.search.requesttime.SearchComponent; 038import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments; 039 040/** 041 * {@link SearchComponent} for specifying the sort in the search. 042 */ 043public class SortSearchComponent implements SearchComponent 044{ 045 @Override 046 public int priority() 047 { 048 return SEARCH_PRIORITY - 6000; 049 } 050 051 @Override 052 public boolean supports(SearchComponentArguments args) 053 { 054 return args.launchSearch(); 055 } 056 057 @Override 058 public void execute(SearchComponentArguments args) throws Exception 059 { 060 Searcher searcher = args.searcher(); 061 SearchServiceInstance serviceInstance = args.serviceInstance(); 062 List<Pair<SortDefinition, Order>> serviceInitialSorts = serviceInstance.getInitialSorts(); 063 Collection<SortDefinition> serviceProposedSorts = serviceInstance.getProposedSorts(); 064 List<Pair<String, Sort.Order>> userSorts = args.userInputs().sorts(); 065 066 Map<String, SortDefinition> serviceProposedSortsById = serviceProposedSorts.stream() 067 .collect(Collectors.toMap(SortDefinition::getId, Function.identity())); 068 069 checkValidInputs(serviceProposedSortsById, userSorts); 070 071 setSearchLanguageAttribute(args); 072 073 // Apply user sorts (keeping user order) 074 for (Pair<String, Sort.Order> userSort : userSorts) 075 { 076 SortDefinition sortDefinition = serviceProposedSortsById.get(userSort.getLeft()); 077 Sort.Order order = userSort.getRight(); 078 searcher.addSort(new Sort(sortDefinition.getField(), order)); 079 } 080 081 // Apply initial sorts if no user sort 082 if (userSorts.isEmpty()) 083 { 084 for (Pair<SortDefinition, Sort.Order> initialSort : serviceInitialSorts) 085 { 086 searcher.addSort(new Sort(initialSort.getLeft().getField(), initialSort.getRight())); 087 } 088 } 089 } 090 091 /** 092 * Checks the user inputs are valid 093 * @param serviceProposedSortsById The proposed sorts of the service instance 094 * @param userSorts The user input sorts 095 * @throws InvalidUserInputException if at least user one input is invalid 096 */ 097 protected void checkValidInputs(Map<String, SortDefinition> serviceProposedSortsById, List<Pair<String, Sort.Order>> userSorts) throws InvalidUserInputException 098 { 099 Collection<String> proposedSortIds = serviceProposedSortsById.keySet(); 100 Set<String> userSortIds = userSorts 101 .stream() 102 .map(Pair::getLeft) 103 .collect(Collectors.toSet()); 104 105 if (!CollectionUtils.containsAll(proposedSortIds, userSortIds)) 106 { 107 throw new InvalidUserInputException("At least one of the user input sorts is invalid because it was not declared by the service instance."); 108 } 109 110 for (Pair<String, Sort.Order> userSort : userSorts) 111 { 112 String sortDefId = userSort.getLeft(); 113 SortDefinition sortDefinition = serviceProposedSortsById.get(sortDefId); 114 Sort.Order order = userSort.getRight(); 115 if (!ArrayUtils.contains(sortDefinition.orders(), order)) 116 { 117 throw new InvalidUserInputException("The user input sort order '" + order + "' for sort '" + sortDefId + "' is not a valid sort order."); 118 } 119 } 120 } 121 122 /** 123 * Sets the SEARCH_LOCALE attribute in request 124 * @param args the arguments 125 */ 126 protected void setSearchLanguageAttribute(SearchComponentArguments args) 127 { 128 String currentLang = args.currentLang(); 129 Request request = args.request(); 130 // Need to pass this attribute for the computing of sort field to use for MultilingualStringSearchField 131 request.setAttribute(SearchAction.SEARCH_LOCALE, new Locale(currentLang)); 132 } 133}