001/* 002 * Copyright 2020 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.workspaces.search.module; 017 018import java.util.Collection; 019import java.util.Collections; 020import java.util.Date; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.environment.Request; 031import org.apache.cocoon.xml.AttributesImpl; 032import org.apache.cocoon.xml.XMLUtils; 033import org.apache.commons.collections4.IterableUtils; 034import org.apache.commons.lang3.StringUtils; 035import org.apache.solr.client.solrj.util.ClientUtils; 036 037import org.ametys.cms.content.indexing.solr.SolrFieldNames; 038import org.ametys.cms.contenttype.ContentTypesHelper; 039import org.ametys.cms.search.SearchResult; 040import org.ametys.cms.search.SearchResults; 041import org.ametys.cms.search.SearchResultsIterable; 042import org.ametys.cms.search.SearchResultsIterator; 043import org.ametys.cms.search.query.AndQuery; 044import org.ametys.cms.search.query.DocumentTypeQuery; 045import org.ametys.cms.search.query.FullTextQuery; 046import org.ametys.cms.search.query.OrQuery; 047import org.ametys.cms.search.query.Query; 048import org.ametys.cms.search.query.Query.Operator; 049import org.ametys.cms.search.solr.SearcherFactory; 050import org.ametys.cms.search.solr.SearcherFactory.Searcher; 051import org.ametys.cms.transformation.xslt.ResolveURIComponent; 052import org.ametys.core.util.DateUtils; 053import org.ametys.plugins.explorer.resources.Resource; 054import org.ametys.plugins.repository.AmetysObject; 055import org.ametys.plugins.repository.AmetysObjectIterable; 056import org.ametys.plugins.repository.EmptyIterable; 057import org.ametys.plugins.workspaces.WorkspacesHelper; 058import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule; 059import org.ametys.plugins.workspaces.project.objects.Project; 060import org.ametys.plugins.workspaces.search.query.ProjectQuery; 061 062/** 063 * Generator for posts search module 064 */ 065public class DocumentSearchModuleGenerator extends AbstractSolrSearchModuleGenerator 066{ 067 /** Searcher Factory */ 068 protected SearcherFactory _searcherFactory; 069 /** The content types helper */ 070 protected ContentTypesHelper _cTypesHelper; 071 /** Workspace Helper */ 072 protected WorkspacesHelper _workspaceHelper; 073 074 @Override 075 public void service(ServiceManager smanager) throws ServiceException 076 { 077 super.service(smanager); 078 _searcherFactory = (SearcherFactory) manager.lookup(SearcherFactory.ROLE); 079 _cTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 080 _workspaceHelper = (WorkspacesHelper) manager.lookup(WorkspacesHelper.ROLE); 081 } 082 083 @SuppressWarnings("unchecked") 084 @Override 085 protected SearchResults< ? extends AmetysObject> getSearchResults(String siteName, String lang, String textfield, Request request, int offset, int limit) throws Exception 086 { 087 Searcher searcher = _searcherFactory.create(); 088 089 List<Project> projects = getProjects(request, true); 090 091 projects = filterProjectsForModule(projects, DocumentWorkspaceModule.DODUMENT_MODULE_ID); 092 if (projects.isEmpty()) 093 { 094 // No projet availables for this module 095 return new EmptySearchResults(); 096 } 097 098 Collection<Query> queries = new HashSet<>(); 099 100 DocumentTypeQuery documentTypeQuery = new DocumentTypeQuery("projectResource"); 101 queries.add(documentTypeQuery); 102 103 if (StringUtils.isNotBlank(textfield)) 104 { 105 Query fullTextQuery = new FullTextQuery("*" + textfield + "*", lang, Operator.SEARCH, false); 106 107 Query fileNameQuery = () -> SolrFieldNames.FILENAME + ":(*" + ClientUtils.escapeQueryChars(textfield) + "*)"; 108 Query searchQuery = new OrQuery(fullTextQuery, fileNameQuery); 109 queries.add(searchQuery); 110 } 111 112 Set<String> projectIds = projects.stream() 113 .map(Project::getId) 114 .collect(Collectors.toSet()); 115 queries.add(new ProjectQuery(projectIds)); 116 117 Query fullQuery = new AndQuery(queries); 118 119 return searcher 120 .withLimits(offset, limit) 121 .withQuery(fullQuery) 122 .searchWithFacets(); 123 } 124 125 @Override 126 protected void saxHit(AmetysObject object, String lang) throws Exception 127 { 128 if (object instanceof Resource) 129 { 130 Resource file = (Resource) object; 131 AttributesImpl attrs = new AttributesImpl(); 132 attrs.addCDATAAttribute("id", file.getId()); 133 XMLUtils.startElement(contentHandler, "hit", attrs); 134 135 XMLUtils.createElement(contentHandler, "title", file.getName()); 136 XMLUtils.createElement(contentHandler, "uri", ResolveURIComponent.resolve("project-resource", file.getId(), false, true)); 137 XMLUtils.createElement(contentHandler, "length", String.valueOf(file.getLength())); 138 XMLUtils.createElement(contentHandler, "mimeType", file.getMimeType()); 139 XMLUtils.createElement(contentHandler, "fileType", _workspaceHelper.getFileType(file).name().toLowerCase()); 140 141 Date creationDate = file.getCreationDate(); 142 XMLUtils.createElement(contentHandler, "creationDate", DateUtils.dateToString(creationDate)); 143 144 Date lastModified = file.getLastModified(); 145 XMLUtils.createElement(contentHandler, "lastModified", DateUtils.dateToString(lastModified)); 146 147 Project project = getProject(object); 148 saxProject(project); 149 XMLUtils.endElement(contentHandler, "hit"); 150 } 151 } 152 153 class EmptySearchResults<A extends AmetysObject> implements SearchResults<A> 154 { 155 public SearchResultsIterable<SearchResult<A>> getResults() 156 { 157 return new EmptySearchResultsIterable<>(); 158 } 159 160 public AmetysObjectIterable<A> getObjects() 161 { 162 return new EmptyIterable<>(); 163 } 164 165 public Iterable<String> getObjectIds() 166 { 167 return IterableUtils.emptyIterable(); 168 } 169 170 public Map<String, Map<String, Integer>> getFacetResults() 171 { 172 return Collections.EMPTY_MAP; 173 } 174 175 public long getTotalCount() 176 { 177 return 0; 178 } 179 180 public float getMaxScore() 181 { 182 return 0; 183 } 184 185 public Optional<Map<String, Object>> getDebugMap() 186 { 187 return Optional.empty(); 188 } 189 190 } 191 192 class EmptySearchResultsIterable<A extends AmetysObject> implements SearchResultsIterable<SearchResult<A>> 193 { 194 195 public long getSize() 196 { 197 return 0; 198 } 199 200 @SuppressWarnings("unchecked") 201 public SearchResultsIterator<SearchResult<A>> iterator() 202 { 203 return new EmptySearchResultsIterator(); 204 } 205 } 206 207 class EmptySearchResultsIterator<A extends AmetysObject> implements SearchResultsIterator<SearchResult<A>> 208 { 209 EmptySearchResultsIterator() 210 { 211 // Empty 212 } 213 214 @Override 215 public boolean hasNext() 216 { 217 return false; 218 } 219 220 @Override 221 public SearchResult<A> next() 222 { 223 throw new UnsupportedOperationException(); 224 } 225 226 @Override 227 public void skip(long skipNum) 228 { 229 throw new UnsupportedOperationException(); 230 } 231 } 232}