org.opengeoportal.harvester.api.client.solr.SolrSearchParams.java Source code

Java tutorial

Introduction

Here is the source code for org.opengeoportal.harvester.api.client.solr.SolrSearchParams.java

Source

/**
 * SolrSearchParams.java
 *
 * Copyright (C) 2013
 *
 * This file is part of Open Geoportal Harvester.
 *
 * This software is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * As a special exception, if you link this library with other files to produce
 * an executable, this library does not by itself cause the resulting executable
 * to be covered by the GNU General Public License. This exception does not
 * however invalidate any other reasons why the executable file might be covered
 * by the GNU General Public License.
 *
 * Authors:: Juan Luis Rodrguez (mailto:juanluisrp@geocat.net)
 */
package org.opengeoportal.harvester.api.client.solr;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.SortClause;
import org.opengeoportal.harvester.api.domain.DataType;
import org.opengeoportal.harvester.api.domain.IngestOGP;
import org.opengeoportal.harvester.api.metadata.model.AccessLevel;
import org.springframework.data.solr.core.DefaultQueryParser;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.data.solr.core.query.SimpleStringCriteria;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Multisets;

/**
 * This class contains the fields values used to filter Solr medatada.
 * 
 * @author <a href="mailto:juanluisrp@geocat.net">Juan Luis Rodrguez</a>.
 * 
 */
public class SolrSearchParams {
    /**
     * 
     */
    private static final String PF = "pf";

    /**
     * The default operator for parsing queries.
     */
    public static enum Operator {
        /** OR operator. */
        OR,
        /** AND operator. */
        AND
    }

    /**
     * Number of result records requested per page.
     */
    public static final int NUMBER_OF_RESULTS_PER_PAGE = 40;
    /** Content range date page. */
    private Date dateFrom;
    /** Content range date to. */
    private Date dateTo;
    /** Number of the page to retrieve (zero based). */
    private int page = 0;
    /** Data originator. */
    private String originator;
    /** Number of elements in one page. */
    private int pageSize = NUMBER_OF_RESULTS_PER_PAGE;
    /** Place keyworkds. */
    private String placeKeyword;
    /** Theme keywords. */
    private String themeKeyword;
    /** ISO topic category. */
    private String topicCategory;
    /** Data types (Line, Point, Polygon, ...). */
    private List<DataType> dataTypes;
    /** Remote repositories. */
    private List<String> dataRepositories;
    /** <code>true</code> if restricted data should be excluded. */
    private boolean excludeRestrictedData;
    /** Returned metadata can not ha a solr timestamp before this date. */
    private Date fromSolrTimestamp;
    /** Returned metadata can not ha a solr timestamp after this date. */
    private Date toSolrTimestamp;
    /** Custom Sorl Query. */
    private String customSolrQuery;
    /** Bounding box West coordinte. */
    private Double bboxWest;
    /** Bounding box East coordinate. */
    private Double bboxEast;
    /** Bounding box North coordinate. */
    private Double bboxNorth;
    /** Bounding box South coordinate. */
    private Double bboxSouth;

    /**
     * Construct a new <code>SolrSearchParams</code> with the data contained in
     * ingest configuration and
     * {@link SolrSearchParams#NUMBER_OF_RESULTS_PER_PAGE} page size.
     * 
     * @param ingest
     *            ingest configuration.
     */
    public SolrSearchParams(final IngestOGP ingest) {
        this(ingest, NUMBER_OF_RESULTS_PER_PAGE);
    }

    /**
     * Construct a new <code>SolrSearchParams</code> with the data contained in
     * ingest configuration and
     * {@link SolrSearchParams#NUMBER_OF_RESULTS_PER_PAGE} page size.
     * 
     * @param ingest
     *            ingest configuration.
     * 
     * @param numberOfElementsPerPage
     *            number of elements to be retrieved in one page.
     */
    public SolrSearchParams(final IngestOGP ingest, final int numberOfElementsPerPage) {
        if (ingest == null) {
            throw new IllegalArgumentException(
                    "ingest parameter cannot be null when" + " creating a new SolrSearchParams");
        }
        this.pageSize = numberOfElementsPerPage;
        this.bboxEast = ingest.getBboxEast();
        this.bboxNorth = ingest.getBboxNorth();
        this.bboxSouth = ingest.getBboxSouth();
        this.bboxWest = ingest.getBboxWest();
        this.customSolrQuery = ingest.getCustomSolrQuery();
        this.dataRepositories = Lists.newArrayList(ingest.getDataRepositories());
        this.dataTypes = Lists.newArrayList(ingest.getDataTypes());
        this.dateFrom = ingest.getDateFrom();
        this.dateTo = ingest.getDateTo();
        this.excludeRestrictedData = ingest.isExcludeRestrictedData();
        this.fromSolrTimestamp = ingest.getFromSolrTimestamp();
        this.originator = ingest.getOriginator();
        this.placeKeyword = ingest.getPlaceKeyword();
        this.themeKeyword = ingest.getThemeKeyword();
        this.topicCategory = ingest.getTopicCategory();
        this.toSolrTimestamp = ingest.getToSolrTimestamp();
    }

    /**
     * Transform the record in {@link SolrQuery} executable by an
     * {@link org.apache.solr.client.solrj.impl.HttpSolrServer}.
     * 
     * @return the {@link SolrQuery} built with the data page this.
     */
    public SolrQuery toSolrQuery() {
        SolrQuery solrQuery = new SolrQuery();

        if (StringUtils.isNotBlank(customSolrQuery)) {
            solrQuery.setQuery(customSolrQuery);
        } else {
            solrQuery.setQuery("*:*");
            // data repositories
            if (dataRepositories != null && dataRepositories.size() > 0) {
                Criteria institutionCriteria = null;
                for (String institution : dataRepositories) {
                    if (institutionCriteria == null) {
                        institutionCriteria = new Criteria(SolrRecord.INSTITUTION).is(institution);
                    } else {
                        institutionCriteria = institutionCriteria
                                .or(new Criteria(SolrRecord.INSTITUTION).is(institution));
                    }
                }

                SimpleQuery query = new SimpleQuery(institutionCriteria);
                DefaultQueryParser parser = new DefaultQueryParser();
                String queryString = parser.getQueryString(query);
                solrQuery.addFilterQuery(queryString);
            } else {
                solrQuery.addFilterQuery(SolrRecord.INSTITUTION + ":*");
            }

            // theme keywords
            if (StringUtils.isNotBlank(themeKeyword)) {
                solrQuery.addFilterQuery(SolrRecord.THEME_KEYWORDS + ":" + themeKeyword);
                solrQuery.add(PF, SolrRecord.THEME_KEYWORDS + ":'" + themeKeyword + "'^9.0");
                solrQuery.add(PF, SolrRecord.LAYER_DISPLAY_NAME + ":'" + themeKeyword + "'^9.0");
            }
            if (StringUtils.isNotBlank(placeKeyword)) {
                solrQuery.addFilterQuery(SolrRecord.PLACE_KEYWORDS + ":" + placeKeyword);
                solrQuery.add(PF, SolrRecord.PLACE_KEYWORDS + ":'" + placeKeyword + "'^9.0");
            }
            if (StringUtils.isNotBlank(topicCategory)) {
                solrQuery.addFilterQuery(SolrRecord.ISO_TOPIC_CATEGORY + ":" + this.topicCategory);

            }

            if (dateFrom != null || dateTo != null) {
                Criteria contentDateCriteria = Criteria.where(SolrRecord.CONTENT_DATE).between(dateFrom, dateTo);
                SimpleQuery query = new SimpleQuery(contentDateCriteria);
                DefaultQueryParser parser = new DefaultQueryParser();
                String queryString = parser.getQueryString(query);
                solrQuery.addFilterQuery(queryString);

            }
            if (StringUtils.isNotBlank(originator)) {
                String originatorCriteria = splitAndConcatenateUsingOperator(Operator.AND, SolrRecord.ORIGINATOR,
                        originator);
                solrQuery.addFilterQuery(originatorCriteria);
                solrQuery.add(PF, SolrRecord.ORIGINATOR + ":" + originator);
            }
            if (dataTypes != null && dataTypes.size() > 0) {
                StringBuilder concatenatedType = new StringBuilder();
                for (DataType dType : dataTypes) {
                    concatenatedType.append(dType.toString().replace(" ", "+")).append(" ");
                }
                String dataTypeCriteria = splitAndConcatenateUsingOperator(Operator.OR, SolrRecord.DATA_TYPE,
                        concatenatedType.toString());
                solrQuery.add("fq", dataTypeCriteria);
            }

            if (excludeRestrictedData) {
                solrQuery.addFilterQuery(SolrRecord.ACCESS + ":" + AccessLevel.Public);
            }

            if (fromSolrTimestamp != null || toSolrTimestamp != null) {
                Criteria solrTimestampCriteria = Criteria.where(SolrRecord.TIMESTAMP).between(fromSolrTimestamp,
                        toSolrTimestamp);
                SimpleQuery query = new SimpleQuery(solrTimestampCriteria);
                DefaultQueryParser parser = new DefaultQueryParser();
                String queryString = parser.getQueryString(query);
                solrQuery.addFilterQuery(queryString);
            }
            // Add bbox filter only if user has not specified a custom solr
            // query.
            buildBoundigBoxQuery(solrQuery);

            String synonymsFilter = generateSynonymsQuery();
            if (StringUtils.isNotBlank(synonymsFilter)) {
                solrQuery.addFilterQuery(synonymsFilter);
            }

        }

        solrQuery.setRows(pageSize);
        solrQuery.setStart(page * pageSize);
        solrQuery.addSort(SortClause.desc("score"));

        return solrQuery;
    }

    /**
     * @return
     */
    private String generateSynonymsQuery() {
        ListMultimap<String, String> fieldList = ArrayListMultimap.create();
        if (StringUtils.isNotBlank(themeKeyword)) {
            fieldList.put(SolrRecord.LAYER_DISPLAY_NAME_SYNONYMS, "(" + themeKeyword + ")");
            fieldList.put(SolrRecord.THEME_KEYWORDS_SYNONYMS_LCSH, "(" + themeKeyword + ")");
        }
        if (StringUtils.isNotBlank(placeKeyword)) {
            fieldList.put(SolrRecord.PLACE_KEYWORDS_SYNONYMS, "(" + placeKeyword + ")");
            fieldList.put(SolrRecord.LAYER_DISPLAY_NAME_SYNONYMS, "(" + placeKeyword + ")");
        }
        StringBuilder synonymsQuery = new StringBuilder();
        Iterator<Entry<String, String>> entryIterator = fieldList.entries().iterator();
        while (entryIterator.hasNext()) {
            Entry<String, String> entry = entryIterator.next();
            synonymsQuery.append(entry.getKey()).append(":").append(entry.getValue());

            if (entryIterator.hasNext()) {
                synonymsQuery.append(" OR ");
            }

        }
        return synonymsQuery.toString();
    }

    /**
     * @param operator
     * @param fieldName
     * @param fieldContent
     * @return
     */
    private String splitAndConcatenateUsingOperator(Operator operator, String fieldName, String fieldContent) {
        String[] contentSplitted = StringUtils.split(fieldContent);
        StringBuilder sb = new StringBuilder();
        int length = contentSplitted.length;
        for (int i = 0; i < length; i++) {
            sb.append(fieldName).append(":").append(contentSplitted[i]);
            if (i != length - 1) {
                sb.append(" ").append(operator.toString()).append(" ");
            }
        }
        return sb.toString();
    }

    private void buildBoundigBoxQuery(SolrQuery query) {
        if (isValidBBox()) {
            String fqParam = "{!frange l=0 incl=false cache=false}$intx";
            query.addFilterQuery(fqParam);
            // @formatter:off
            // product(
            //    max(
            //       0,
            //       sub(
            //           min(180,MaxX),
            //           max(-180,MinX)
            //       )
            //    ),
            //    max(
            //       0,
            //       sub(
            //           min(41.902277040963,MaxY),
            //           max(-86.30131338825,MinY)
            //       )
            //    )
            // )
            // @formatter:on
            String intxTemplateString = "product(max(0,sub(min(%f,MaxX)," + "max(%f,MinX))),max(0,sub(min(%f,MaxY),"
                    + "max(%f,MinY))))";
            String intxParam = String.format(Locale.ENGLISH, intxTemplateString, this.bboxEast, this.bboxWest,
                    this.bboxNorth, this.bboxSouth);
            query.setParam("intx", intxParam);
        }
    }

    /**
     * Check if bounding box coordinates are valid. Bounding Box is valid if its
     * four components are not null and west coordinate is lesser than east
     * coordinate and south coord is lesser than north coord.
     * 
     * @return <code>true</code> if bounding box is valid; otherwise return
     *         <code>false</code>.
     */
    private boolean isValidBBox() {
        boolean valid = false;

        if (bboxWest != null && bboxEast != null && bboxNorth != null && bboxSouth != null) {
            valid = this.bboxWest < this.bboxEast && this.bboxSouth < this.bboxNorth;
        }

        return valid;
    }

    /**
     * Split wordlist by blank space and create an OR criteria with each
     * resulting word. Words are added using wildcards.
     * 
     * @param wordList
     *            a string with a list of words
     * @param fieldName
     *            the name of the field where criteria is applied.
     * @return an OR criteria with each word contained in wordlist.
     */
    private Criteria splitAndOrCriteria(String wordList, String fieldName) {
        Criteria orCriteria = null;
        String[] words = StringUtils.split(wordList);
        for (String word : words) {
            if (orCriteria == null) {
                orCriteria = new Criteria(fieldName).contains(word);
            } else {
                orCriteria = orCriteria.or(new Criteria(fieldName).contains(word));
            }

        }

        if (orCriteria != null) {
            SimpleQuery query = new SimpleQuery(orCriteria);
            DefaultQueryParser parser = new DefaultQueryParser();
            String queryString = parser.getQueryString(query);
            return new SimpleStringCriteria("(" + queryString + ")");
        } else {
            return null;
        }
    }

    /**
     * @return the dateFrom
     */
    public Date getDateFrom() {
        return dateFrom;
    }

    /**
     * @return the dateTo
     */
    public Date getDateTo() {
        return dateTo;
    }

    /**
     * @return the page.
     */
    public int getPage() {
        return page;
    }

    /**
     * @return the originator
     */
    public String getOriginator() {
        return originator;
    }

    /**
     * @return the pageSize
     */
    public int getPageSize() {
        return pageSize;
    }

    /**
     * @return the placeKeyword
     */
    public String getPlaceKeyword() {
        return placeKeyword;
    }

    /**
     * @return the themeKeyword
     */
    public String getThemeKeyword() {
        return themeKeyword;
    }

    /**
     * @return the topicCategory
     */
    public String getTopicCategory() {
        return topicCategory;
    }

    /**
     * @param dateFrom
     *            the dateFrom to set
     */
    public void setDateFrom(Date dateFrom) {
        this.dateFrom = dateFrom;
    }

    /**
     * @param dateTo
     *            the dateTo to set
     */
    public void setDateTo(Date dateTo) {
        this.dateTo = dateTo;
    }

    /**
     * @param from
     *            the page to set
     */
    public void setPage(int from) {
        this.page = from;
    }

    /**
     * @param originator
     *            the originator to set
     */
    public void setOriginator(String originator) {
        this.originator = originator;
    }

    /**
     * @param pageSize
     *            the pageSize to set
     */
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    /**
     * @param placeKeyword
     *            the placeKeyword to set
     */
    public void setPlaceKeyword(String placeKeyword) {
        this.placeKeyword = placeKeyword;
    }

    /**
     * @param themeKeyword
     *            the themeKeyword to set
     */
    public void setThemeKeyword(String themeKeyword) {
        this.themeKeyword = themeKeyword;
    }

    /**
     * @param topicCategory
     *            the topicCategory to set
     */
    public void setTopicCategory(String topicCategory) {
        this.topicCategory = topicCategory;
    }

    /**
     * @return the dataTypes
     */
    public List<DataType> getDataTypes() {
        return dataTypes;
    }

    /**
     * @param dataTypes
     *            the dataTypes to set
     */
    public void setDataTypes(List<DataType> dataTypes) {
        this.dataTypes = dataTypes;
    }

    /**
     * @return the dataRepositories
     */
    public List<String> getDataRepositories() {
        return dataRepositories;
    }

    /**
     * @param dataRepositories
     *            the dataRepositories to set
     */
    public void setDataRepositories(List<String> dataRepositories) {
        this.dataRepositories = dataRepositories;
    }

    /**
     * @return the excludeRestrictedData
     */
    public boolean isExcludeRestrictedData() {
        return excludeRestrictedData;
    }

    /**
     * @param excludeRestrictedData
     *            the excludeRestrictedData to set
     */
    public void setExcludeRestrictedData(boolean excludeRestrictedData) {
        this.excludeRestrictedData = excludeRestrictedData;
    }

    /**
     * @return the fromSolrTimestamp
     */
    public Date getFromSolrTimestamp() {
        return fromSolrTimestamp;
    }

    /**
     * @param fromSolrTimestamp
     *            the fromSolrTimestamp to set
     */
    public void setFromSolrTimestamp(Date fromSolrTimestamp) {
        this.fromSolrTimestamp = fromSolrTimestamp;
    }

    /**
     * @return the toSolrTimestamp
     */
    public Date getToSolrTimestamp() {
        return toSolrTimestamp;
    }

    /**
     * @param toSolrTimestamp
     *            the toSolrTimestamp to set
     */
    public void setToSolrTimestamp(Date toSolrTimestamp) {
        this.toSolrTimestamp = toSolrTimestamp;
    }

    /**
     * @return the customSolrQuery
     */
    public String getCustomSolrQuery() {
        return customSolrQuery;
    }

    /**
     * @param customSolrQuery
     *            the customSolrQuery to set
     */
    public void setCustomSolrQuery(String customSolrQuery) {
        this.customSolrQuery = customSolrQuery;
    }

    /**
     * @return the bboxWest
     */
    public Double getBboxWest() {
        return bboxWest;
    }

    /**
     * @param bboxWest
     *            the bboxWest to set
     */
    public void setBboxWest(Double bboxWest) {
        this.bboxWest = bboxWest;
    }

    /**
     * @return the bboxEast
     */
    public Double getBboxEast() {
        return bboxEast;
    }

    /**
     * @param bboxEast
     *            the bboxEast to set
     */
    public void setBboxEast(Double bboxEast) {
        this.bboxEast = bboxEast;
    }

    /**
     * @return the bboxNorth
     */
    public Double getBboxNorth() {
        return bboxNorth;
    }

    /**
     * @param bboxNorth
     *            the bboxNorth to set
     */
    public void setBboxNorth(Double bboxNorth) {
        this.bboxNorth = bboxNorth;
    }

    /**
     * @return the bboxSouth
     */
    public Double getBboxSouth() {
        return bboxSouth;
    }

    /**
     * @param bboxSouth
     *            the bboxSouth to set
     */
    public void setBboxSouth(Double bboxSouth) {
        this.bboxSouth = bboxSouth;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("SolrSearchParams [");
        if (dateFrom != null) {
            builder.append("dateFrom=");
            builder.append(dateFrom);
            builder.append(", ");
        }
        if (dateTo != null) {
            builder.append("dateTo=");
            builder.append(dateTo);
            builder.append(", ");
        }
        builder.append("page=");
        builder.append(page);
        builder.append(", ");
        if (originator != null) {
            builder.append("originator=");
            builder.append(originator);
            builder.append(", ");
        }
        builder.append("pageSize=");
        builder.append(pageSize);
        builder.append(", ");
        if (placeKeyword != null) {
            builder.append("placeKeyword=");
            builder.append(placeKeyword);
            builder.append(", ");
        }
        if (themeKeyword != null) {
            builder.append("themeKeyword=");
            builder.append(themeKeyword);
            builder.append(", ");
        }
        if (topicCategory != null) {
            builder.append("topicCategory=");
            builder.append(topicCategory);
            builder.append(", ");
        }
        if (dataTypes != null) {
            builder.append("dataTypes=");
            builder.append(dataTypes);
            builder.append(", ");
        }
        if (dataRepositories != null) {
            builder.append("dataRepositories=");
            builder.append(dataRepositories);
            builder.append(", ");
        }
        builder.append("excludeRestrictedData=");
        builder.append(excludeRestrictedData);
        builder.append(", ");
        if (fromSolrTimestamp != null) {
            builder.append("fromSolrTimestamp=");
            builder.append(fromSolrTimestamp);
            builder.append(", ");
        }
        if (toSolrTimestamp != null) {
            builder.append("toSolrTimestamp=");
            builder.append(toSolrTimestamp);
            builder.append(", ");
        }
        if (customSolrQuery != null) {
            builder.append("customSolrQuery=");
            builder.append(customSolrQuery);
            builder.append(", ");
        }
        if (bboxWest != null) {
            builder.append("bboxWest=");
            builder.append(bboxWest);
            builder.append(", ");
        }
        if (bboxEast != null) {
            builder.append("bboxEast=");
            builder.append(bboxEast);
            builder.append(", ");
        }
        if (bboxNorth != null) {
            builder.append("bboxNorth=");
            builder.append(bboxNorth);
            builder.append(", ");
        }
        if (bboxSouth != null) {
            builder.append("bboxSouth=");
            builder.append(bboxSouth);
        }
        builder.append("]");
        return builder.toString();
    }

}