001/* 002 * Copyright 2017 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.thesaurus.search; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Map; 021 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.cocoon.environment.Request; 025import org.apache.commons.lang3.StringUtils; 026 027import org.ametys.cms.repository.Content; 028import org.ametys.cms.repository.SubContentExpression; 029import org.ametys.cms.search.cocoon.SearchAction; 030import org.ametys.cms.search.ui.model.SearchUIModel; 031import org.ametys.plugins.repository.AmetysObjectIterable; 032import org.ametys.plugins.repository.AmetysObjectIterator; 033import org.ametys.plugins.repository.AmetysObjectResolver; 034import org.ametys.plugins.repository.query.SortCriteria; 035import org.ametys.plugins.repository.query.expression.AndExpression; 036import org.ametys.plugins.repository.query.expression.Expression; 037import org.ametys.plugins.repository.query.expression.Expression.Operator; 038import org.ametys.plugins.repository.query.expression.NotExpression; 039import org.ametys.plugins.repository.query.expression.StringExpression; 040import org.ametys.plugins.thesaurus.MicroThesaurus; 041import org.ametys.plugins.thesaurus.ThesaurusDAO; 042 043/** 044 * Search terms by JCR query and put a result contents in the request (to be serialized in JSON). 045 */ 046public class SearchTermAction extends SearchAction 047{ 048 private ThesaurusDAO _thesaurusDAO; 049 private AmetysObjectResolver _resolver; 050 051 @Override 052 public void service(ServiceManager smanager) throws ServiceException 053 { 054 super.service(smanager); 055 _thesaurusDAO = (ThesaurusDAO) smanager.lookup(ThesaurusDAO.ROLE); 056 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 057 } 058 059 @SuppressWarnings("unchecked") 060 @Override 061 protected void doSearch(Request request, SearchUIModel model, int offset, int maxResults, Map<String, Object> jsParameters, Map<String, Object> contextualParameters) throws Exception 062 { 063 List<String> contentIds = new ArrayList<>(); 064 065 // If a list of ID is present in the request, use the corresponding contents as the results. 066 if (jsParameters.get("id") != null) 067 { 068 contentIds = (List<String>) jsParameters.get("id"); 069 } 070 else 071 { 072 boolean excludeSubContents = jsParameters.containsKey("excludeSubContents") ? (Boolean) jsParameters.get("excludeSubContents") : false; 073 String xPathQuery = getXPathQuery (jsParameters, excludeSubContents); 074 075 // SAX results between begin and begin + offset 076 try (AmetysObjectIterable<Content> contents = _resolver.query(xPathQuery)) 077 { 078 AmetysObjectIterator<Content> it = contents.iterator(); 079 080 // Index of search 081 it.skip(offset); 082 083 int index = 0; 084 while (it.hasNext() && index < maxResults) 085 { 086 Content content = it.next(); 087 contentIds.add(content.getId()); 088 index++; 089 } 090 } 091 } 092 093 request.setAttribute(SEARCH_CONTENTS, contentIds); 094 } 095 096 /** 097 * Get the query to execute 098 * @param jsParameters The JS parameters 099 * @param excludeSubContents true to exclude sub-contents 100 * @return the query 101 */ 102 protected String getXPathQuery (Map<String, Object> jsParameters, boolean excludeSubContents) 103 { 104 List<Expression> expressions = new ArrayList<>(); 105 106 @SuppressWarnings("unchecked") 107 Map<String, Object> values = (Map<String, Object>) jsParameters.get("values"); 108 if (values != null) 109 { 110 String contentType = (String) values.get("property-contentTypeOrMixin-eq"); 111 if (StringUtils.isNotEmpty(contentType)) 112 { 113 expressions.add(_contentTypeExtensionPoint.createHierarchicalCTExpression(contentType)); 114 } 115 116 if (excludeSubContents) 117 { 118 expressions.add(new NotExpression(new SubContentExpression())); 119 } 120 121 String title = (String) values.get("metadata-title-search"); 122 if (StringUtils.isNotEmpty(title)) 123 { 124 String[] wildcardValues = title.split("\\s"); 125 126 for (String wildcardValue : wildcardValues) 127 { 128 expressions.add(new StringExpression("title", Operator.WD, wildcardValue, false, true)); 129 } 130 } 131 } 132 133 SortCriteria sortCriteria = new SortCriteria(); 134 sortCriteria.addCriterion("title", true, true); 135 136 Expression expr = new AndExpression(expressions.toArray(new Expression[expressions.size()])); 137 String predicats = StringUtils.trimToNull(expr.build()); 138 139 String xpathQuery = ""; 140 MicroThesaurus microthesaurus = getMicrothesaurus(jsParameters); 141 if (microthesaurus != null) 142 { 143 xpathQuery += "/" + microthesaurus.getPath(); 144 } 145 146 xpathQuery += "//element(*, ametys:content)" + (predicats != null ? "[" + predicats + "]" : "") + " " + sortCriteria.build(); 147 148 return xpathQuery; 149 } 150 151 /** 152 * Get the parent microthesaurus 153 * @param jsParameters The JS parameters 154 * @return the parent microthesaurus or null 155 */ 156 protected MicroThesaurus getMicrothesaurus (Map<String, Object> jsParameters) 157 { 158 String microthesaurusId = (String) jsParameters.get("microthesaurusId"); 159 160 if (StringUtils.isNotEmpty(microthesaurusId)) 161 { 162 return _resolver.resolveById(microthesaurusId); 163 } 164 165 String termId = (String) jsParameters.get("termId"); 166 if (StringUtils.isNotEmpty(termId)) 167 { 168 Content term = _resolver.resolveById(termId); 169 return _thesaurusDAO.getParentMicrothesaurus(term); 170 } 171 172 return null; 173 } 174 175}