Package org.eclipse.orion.internal.server.search

Source Code of org.eclipse.orion.internal.server.search.SearchServlet

/*******************************************************************************
* Copyright (c) 2011, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.orion.internal.server.search;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.QueryResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
import org.eclipse.orion.server.core.LogHelper;
import org.eclipse.orion.server.core.ProtocolConstants;
import org.eclipse.orion.server.servlets.OrionServlet;

/**
* Servlet for performing searches against files in the workspace.
*/
public class SearchServlet extends OrionServlet {
  /**
   * Separator between query terms
   */
  private static final String AND = " AND "; //$NON-NLS-1$
  private static final String OR = " OR "; //$NON-NLS-1$
  private static final long serialVersionUID = 1L;
  private static final String FIELD_NAMES = "Name,NameLower,Length,Directory,LastModified,Location,Path"; //$NON-NLS-1$
  private static final List<String> FIELD_LIST = Arrays.asList(FIELD_NAMES.split(",")); //$NON-NLS-1$

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    traceRequest(req);
    SolrQuery query = buildSolrQuery(req);
    if (query == null) {
      handleException(resp, "Invalid search request", null, HttpServletResponse.SC_BAD_REQUEST);
      return;
    }
    try {
      QueryResponse solrResponse = SearchActivator.getInstance().getSolrServer().query(query);
      writeResponse(query, req, resp, solrResponse);
    } catch (SolrServerException e) {
      LogHelper.log(e);
      resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
  }

  private SolrQuery buildSolrQuery(HttpServletRequest req) {
    SolrQuery query = new SolrQuery();
    query.setParam(CommonParams.WT, "json"); //$NON-NLS-1$
    query.setParam(CommonParams.FL, FIELD_NAMES);
    String queryString = getEncodedParameter(req, CommonParams.Q);
    if (queryString == null)
      return null;
    if (queryString.length() > 0) {
      String processedQuery = ""; //$NON-NLS-1$
      //divide into search terms delimited by space or plus ('+') character
      List<String> terms = new ArrayList<String>(Arrays.asList(queryString.split("[\\s\\+]+"))); //$NON-NLS-1$
      while (!terms.isEmpty()) {
        String term = terms.remove(0);
        if (term.length() == 0)
          continue;
        if (isSearchField(term)) {
          if (term.startsWith("NameLower:")) { //$NON-NLS-1$
            //decode the search term, we do not want to decode the location
            try {
              term = URLDecoder.decode(term, "UTF-8"); //$NON-NLS-1$
            } catch (UnsupportedEncodingException e) {
              //try with encoded term
            }
            //solr does not lowercase queries containing wildcards
            //https://issues.apache.org/jira/browse/SOLR-219
            processedQuery += "NameLower:" + term.substring(10).toLowerCase(); //$NON-NLS-1$
          } else if (term.startsWith("Location:")) { //$NON-NLS-1${
            //Use Location as a filter query to improve query performence
            //See https://bugs.eclipse.org/bugs/show_bug.cgi?id=415874
            //processedQuery += "Location:" + term.substring(9 + req.getContextPath().length()); //$NON-NLS-1$
            query.addFilterQuery("Location:" + term.substring(9 + req.getContextPath().length()));
            continue;
          } else if (term.startsWith("Name:")) { //$NON-NLS-1$
            try {
              term = URLDecoder.decode(term, "UTF-8"); //$NON-NLS-1$
            } catch (UnsupportedEncodingException e) {
              //try with encoded term
            }
            processedQuery += "Name:(" + term.substring(5).replaceAll("/", OR) + ")";
          } else {
            //all other field searches are case sensitive
            processedQuery += term;
          }
        } else {
          //decode the term string now
          try {
            term = URLDecoder.decode(term, "UTF-8"); //$NON-NLS-1$
          } catch (UnsupportedEncodingException e) {
            //try with encoded term
          }
          boolean isPhrase = term.charAt(0) == '"';
          boolean leadingWildCard = term.charAt(0) == '*';
          //see https://bugs.eclipse.org/bugs/show_bug.cgi?id=415874#c12
          //For a term starting with "*" we are using the leading wild card.
          //Otherwise we only use tailing wild card to improve performance.
          if (leadingWildCard) {
            term = term.substring(1);
          }
          //solr does not lowercase queries containing wildcards
          //see https://bugs.eclipse.org/bugs/show_bug.cgi?id=359766
          String processedTerm = ClientUtils.escapeQueryChars(term.toLowerCase());
          //add leading and trailing wildcards to match word segments
          if (!isPhrase) {
            if (leadingWildCard)
              processedTerm = '*' + processedTerm;
            if (processedTerm.charAt(processedTerm.length() - 1) != '*')
              processedTerm += '*';
          }
          processedQuery += processedTerm;
        }
        processedQuery += AND;
      }
      queryString = processedQuery;
    }
    //remove trailing AND
    if (queryString.endsWith(AND))
      queryString = queryString.substring(0, queryString.length() - AND.length());
    query.setQuery(queryString);

    //filter to search only documents belonging to current user
    query.addFilterQuery(ProtocolConstants.KEY_USER_NAME + ':' + ClientUtils.escapeQueryChars(req.getRemoteUser()));

    //other common fields
    setField(req, query, CommonParams.ROWS);
    setField(req, query, CommonParams.START);
    setField(req, query, CommonParams.SORT);
    return query;
  }

  /**
   * Returns a request parameter in encoded form. Returns <code>null</code>
   * if no such parameter is defined or has an empty value.
   */
  private String getEncodedParameter(HttpServletRequest req, String key) {
    //TODO need to get query string unencoded - maybe use req.getQueryString() and parse manually
    String query = req.getQueryString();
    for (String param : query.split("&")) { //$NON-NLS-1$
      String[] pair = param.split("=", 2); //$NON-NLS-1$
      if (pair.length == 2 && key.equals(pair[0]))
        return pair[1];
    }
    return null;
  }

  /**
   * Returns whether the search term is against a particular field rather than the default field
   * (search on name, location, etc).
   */
  private boolean isSearchField(String term) {
    for (String field : FIELD_LIST) {
      if (term.startsWith(field + ":")) //$NON-NLS-1$
        return true;
    }
    return false;
  }

  private void setField(HttpServletRequest req, SolrQuery query, String parameter) {
    String value = req.getParameter(parameter);
    if (value != null)
      query.set(parameter, value);
  }

  /**
   * Writes the response to the search query to the HTTP response's output stream.
   */
  private void writeResponse(SolrQuery query, HttpServletRequest httpRequest, HttpServletResponse httpResponse, QueryResponse queryResponse) throws IOException {
    SolrCore core = SearchActivator.getInstance().getSolrCore();
    //this seems to be the only way to obtain the JSON response representation
    SolrQueryRequest solrRequest = null;
    try {
      solrRequest = new LocalSolrQueryRequest(core, query.toNamedList());
      SolrQueryResponse solrResponse = new SolrQueryResponse();
      // Added encoding check as per Bugzilla 406757
      if (httpRequest.getCharacterEncoding() == null) {
        httpRequest.setCharacterEncoding("UTF-8"); //$NON-NLS-1$
        httpResponse.setCharacterEncoding("UTF-8"); //$NON-NLS-1$
      }
      //bash the query in the response to remove user info
      @SuppressWarnings("unchecked")
      NamedList<Object> params = (NamedList<Object>) queryResponse.getHeader().get("params"); //$NON-NLS-1$
      params.remove(CommonParams.Q);
      params.add(CommonParams.Q, httpRequest.getParameter(CommonParams.Q));
      NamedList<Object> values = queryResponse.getResponse();
      String contextPath = httpRequest.getContextPath();
      if (contextPath.length() > 0)
        setSearchResultContext(values, contextPath);
      solrResponse.setAllValues(values);
      QueryResponseWriter writer = core.getQueryResponseWriter("json"); //$NON-NLS-1$
      writer.write(httpResponse.getWriter(), solrRequest, solrResponse);
    } finally {
      if (solrRequest != null) {
        solrRequest.close();
      }
    }
  }

  /**
   * Prepend the server context path to the location of search result documents.
   */
  private void setSearchResultContext(NamedList<Object> values, String contextPath) {
    //find the search result documents in the search response
    SolrDocumentList documents = (SolrDocumentList) values.get("response"); //$NON-NLS-1$
    if (documents == null)
      return;
    for (SolrDocument doc : documents) {
      String location = (String) doc.getFieldValue(ProtocolConstants.KEY_LOCATION);
      if (location != null) {
        //prepend the context path and update the document
        location = contextPath + location;
        doc.setField(ProtocolConstants.KEY_LOCATION, location);
      }
    }
  }
}
TOP

Related Classes of org.eclipse.orion.internal.server.search.SearchServlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.