/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.repository.internal.resources; import java.util.ArrayList; import java.util.List; import javax.inject.Named; import javax.inject.Singleton; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import org.apache.commons.lang3.StringUtils; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.xwiki.component.annotation.Component; import org.xwiki.extension.rating.RatingExtension; import org.xwiki.extension.repository.xwiki.model.jaxb.COMPARISON; import org.xwiki.extension.repository.xwiki.model.jaxb.ExtensionQuery; import org.xwiki.extension.repository.xwiki.model.jaxb.ExtensionsSearchResult; import org.xwiki.extension.repository.xwiki.model.jaxb.Filter; import org.xwiki.extension.repository.xwiki.model.jaxb.SortClause; import org.xwiki.query.Query; import org.xwiki.query.QueryException; import org.xwiki.query.SecureQuery; import org.xwiki.repository.Resources; import org.xwiki.repository.internal.XWikiRepositoryModel; /** * @version $Id: 27d301df657f1738245f68a136a32e80d3aaa0f6 $ * @since 3.2M3 */ @Component @Named("org.xwiki.repository.internal.resources.SearchRESTResource") @Path(Resources.SEARCH) @Singleton public class SearchRESTResource extends AbstractExtensionRESTResource { /** * @since 3.3M2 */ @GET public ExtensionsSearchResult searchGet(@QueryParam(Resources.QPARAM_SEARCH_QUERY) @DefaultValue("") String pattern, @QueryParam(Resources.QPARAM_LIST_START) @DefaultValue("0") int offset, @QueryParam(Resources.QPARAM_LIST_NUMBER) @DefaultValue("-1") int number, @QueryParam(Resources.QPARAM_LIST_REQUIRETOTALHITS) @DefaultValue("true") boolean requireTotalHits) throws QueryException { ExtensionQuery query = this.extensionObjectFactory.createExtensionQuery(); query.setQuery(pattern); query.setOffset(offset); query.setLimit(number); return searchPost(query); } // TODO: automatically replace Extension fields names with the actual Solr properties names (so that it's possible // to write query like type:jar) private String toSolrStatement(String query) { if (StringUtils.isBlank(query)) { return "*"; } else if (StringUtils.containsNone(query, ' ', ':')) { return "*" + query + "*"; } return query; } @POST public ExtensionsSearchResult searchPost(ExtensionQuery query) throws QueryException { ExtensionsSearchResult result = this.extensionObjectFactory.createExtensionsSearchResult(); Query solrQuery = this.queryManager.createQuery(toSolrStatement(query.getQuery()), "solr"); // ///////////////// // Search only in the current wiki // ///////////////// solrQuery.setWiki(this.xcontextProvider.get().getWikiId()); // ///////////////// // Limit and offset // ///////////////// solrQuery.setLimit(query.getLimit()); solrQuery.setOffset(query.getOffset()); // ///////////////// // Rights // ///////////////// if (query instanceof SecureQuery) { // Show only what the current user has the right to see ((SecureQuery) query).checkCurrentUser(true); } // ///////////////// // Boost // ///////////////// solrQuery.bindValue("qf", DEFAULT_BOOST); // ///////////////// // Fields // ///////////////// solrQuery.bindValue("fl", DEFAULT_FL); // ///////////////// // Ordering // ///////////////// // Convert extension ordering into solr ordering List<String> sortClauses = new ArrayList<>(query.getSortClauses().size() + 1); for (SortClause sortClause : query.getSortClauses()) { String solrField = XWikiRepositoryModel.toSolrField(sortClause.getField()); if (solrField != null) { sortClauses.add(solrField + ' ' + sortClause.getOrder().name().toLowerCase()); } } // Set default ordering if (StringUtils.isEmpty(query.getQuery())) { // Sort by rating by default when search query is empty sortClauses.add(XWikiRepositoryModel.toSolrOrderField(RatingExtension.FIELD_AVERAGE_VOTE) + " desc"); sortClauses.add(XWikiRepositoryModel.toSolrOrderField(RatingExtension.FIELD_TOTAL_VOTES) + " desc"); } else { // Sort by score by default when search query is not empty sortClauses.add("score desc"); } solrQuery.bindValue("sort", sortClauses); // ///////////////// // Filtering // ///////////////// List<String> fq = new ArrayList<>(query.getFilters().size() + 1); // TODO: should be filter only on current wiki ? // We want only valid extensions documents fq.add(XWikiRepositoryModel.SOLRPROP_EXTENSION_VALIDEXTENSION + ":true"); // Request filters for (Filter fiter : query.getFilters()) { String solrField = XWikiRepositoryModel.toSolrField(fiter.getField()); if (solrField != null) { StringBuilder builder = new StringBuilder(); builder.append(solrField); builder.append(':'); if (fiter.getComparison() == COMPARISON.EQUAL) { builder.append(fiter.getValueString()); } else { builder.append('*' + fiter.getValueString() + '*'); } fq.add(builder.toString()); } } solrQuery.bindValue("fq", fq); // ///////////////// // Execute // ///////////////// QueryResponse response = (QueryResponse) solrQuery.execute().get(0); SolrDocumentList documents = response.getResults(); result.setOffset((int) documents.getStart()); result.setTotalHits((int) documents.getNumFound()); // O means unset for solr but we want it to be literally interpreted to be consistent with previous behavior and // other searches behavior if (query.getLimit() != 0) { for (SolrDocument document : documents) { result.getExtensions().add(createExtensionVersionFromSolrDocument(document)); } } return result; } }