001/* 002 * Copyright 2019 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.plugins.odfweb.service.search; 017 018import java.util.Arrays; 019import java.util.Collection; 020import java.util.Map; 021import java.util.Optional; 022import java.util.Set; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026 027import org.ametys.cms.search.advanced.AbstractTreeNode; 028import org.ametys.cms.search.query.AndQuery; 029import org.ametys.cms.search.query.ConstantNilScoreQuery; 030import org.ametys.cms.search.query.ContentTypeQuery; 031import org.ametys.cms.search.query.IsolateQuery; 032import org.ametys.cms.search.query.JoinQuery; 033import org.ametys.cms.search.query.MaxScoreOrQuery; 034import org.ametys.cms.search.query.Query; 035import org.ametys.odf.program.Program; 036import org.ametys.odf.program.ProgramFactory; 037import org.ametys.odf.program.SubProgramFactory; 038import org.ametys.odf.program.TraversableProgramPart; 039import org.ametys.plugins.odfweb.service.search.helper.DegreeUniversityHelper; 040import org.ametys.web.frontoffice.search.instance.model.SearchServiceCriterion; 041import org.ametys.web.frontoffice.search.metamodel.AdditionalParameterValueMap; 042import org.ametys.web.frontoffice.search.metamodel.Returnable; 043import org.ametys.web.frontoffice.search.metamodel.SearchServiceCriterionDefinition; 044import org.ametys.web.frontoffice.search.metamodel.Searchable; 045import org.ametys.web.frontoffice.search.metamodel.impl.AbstractContentBasedSearchable; 046 047/** 048 * {@link Searchable} for {@link Program}s 049 */ 050public class ProgramSearchable extends AbstractContentBasedSearchable 051{ 052 /** Avalon Role */ 053 public static final String ROLE = ProgramSearchable.class.getName(); 054 055 /** The additional parameter for indicating if search has to be made also on subprograms */ 056 public static final String PARAMETER_SEARCH_ON_SUBPROGRAMS = "searchSubprogram"; 057 058 /** The prefix for program searchable */ 059 public static final String CRITERION_DEFINITIONS_PREFIX_ID = "ProgramSearchable$"; 060 061 /** The prefix for criterion definition in program searcheable */ 062 public static final String PROGRAM_SEARCHEABLE_CRITERION_DEFINITION_PREFIX = CRITERION_DEFINITIONS_PREFIX_ID + ProgramFactory.PROGRAM_CONTENT_TYPE + "$"; 063 064 /** The degree university helper */ 065 protected DegreeUniversityHelper _degreeUniversityHelper; 066 067 @Override 068 public void service(ServiceManager manager) throws ServiceException 069 { 070 super.service(manager); 071 _degreeUniversityHelper = (DegreeUniversityHelper) manager.lookup(DegreeUniversityHelper.ROLE); 072 } 073 074 @Override 075 protected String associatedContentReturnableRole() 076 { 077 return ProgramReturnable.ROLE; 078 } 079 080 @Override 081 protected String getCriterionDefinitionPrefix() 082 { 083 return CRITERION_DEFINITIONS_PREFIX_ID; 084 } 085 086 @Override 087 public Collection<Returnable> relationsWith() 088 { 089 return Arrays.asList(_associatedContentReturnable); 090 } 091 092 @Override 093 protected Set<String> getContentTypeIds(AdditionalParameterValueMap additionalParameterValues) 094 { 095 return Set.of(ProgramFactory.PROGRAM_CONTENT_TYPE); 096 } 097 098 @Override 099 public Optional<Query> joinQuery(Query queryOnCriterion, SearchServiceCriterionDefinition criterion, Collection<Returnable> returnables, AdditionalParameterValueMap additionalParameters) 100 { 101 if (returnables.contains(_associatedContentReturnable)) 102 { 103 return Optional.of(queryOnCriterion); 104 } 105 return Optional.empty(); 106 } 107 108 @Override 109 public Query buildQuery( 110 AbstractTreeNode<SearchServiceCriterion<?>> criterionTree, 111 Map<String, Object> userCriteria, 112 Collection<Returnable> returnables, 113 Collection<Searchable> searchables, 114 AdditionalParameterValueMap additionalParameters, 115 String currentLang, 116 Map<String, Object> contextualParameters) 117 { 118 Query query = super.buildQuery(criterionTree, userCriteria, returnables, searchables, additionalParameters, currentLang, contextualParameters); 119 if (_searchOnSubprograms(additionalParameters)) 120 { 121 // This query search on 122 // - programs matching user criteria 123 // OR 124 // - subprograms matching user criteria 125 // This is the same query because it is assumed that programs and subprograms have the same attributes (properties are added for subprogram to match with missing program attributes) 126 return new MaxScoreOrQuery( 127 new IsolateQuery(query), 128 getProgramThroughSubprogramsQuery(query) 129 ); 130 } 131 else 132 { 133 return query; 134 } 135 } 136 137 private boolean _searchOnSubprograms(AdditionalParameterValueMap additionalParameters) 138 { 139 return additionalParameters.getValue(PARAMETER_SEARCH_ON_SUBPROGRAMS); 140 } 141 142 /** 143 * Gets the {@link Query} that returns Programs by querying their SubPrograms 144 * @param queryOnCriterion The query on SubPrograms 145 * @return The {@link Query} that returns Programs 146 */ 147 protected Query getProgramThroughSubprogramsQuery(Query queryOnCriterion) 148 { 149 Query queryOnSubprograms = new AndQuery( 150 new ConstantNilScoreQuery(new ContentTypeQuery(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE)), 151 queryOnCriterion 152 ); 153 return new ProgramThroughProgramPartsQuery(queryOnSubprograms); 154 } 155 156 @Override 157 public Collection<SearchServiceCriterionDefinition> getCriteria(AdditionalParameterValueMap additionalParameterValues) 158 { 159 Collection<SearchServiceCriterionDefinition> criteria = super.getCriteria(additionalParameterValues); 160 161 SearchServiceCriterionDefinition degreeUniversityCriterionDefinition = _degreeUniversityHelper.getDegreeUniversityCriterionDefinition(this); 162 if (degreeUniversityCriterionDefinition != null) 163 { 164 criteria.add(degreeUniversityCriterionDefinition); 165 } 166 167 return criteria; 168 } 169 170 /** 171 * A {@link Query} that returns Programs, by querying their child ProgramParts 172 */ 173 protected static class ProgramThroughProgramPartsQuery extends JoinQuery 174 { 175 /** 176 * Builds a ProgramThroughProgramPartsQuery 177 * @param subQuery The query on ProgramParts 178 */ 179 protected ProgramThroughProgramPartsQuery(Query subQuery) 180 { 181 super(subQuery, TraversableProgramPart.CHILD_PROGRAM_PARTS); 182 } 183 } 184}