001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.search.config.parser;
029
030import org.opencms.file.CmsObject;
031import org.opencms.jsp.search.config.CmsSearchConfigurationCommon;
032import org.opencms.jsp.search.config.I_CmsSearchConfiguration;
033import org.opencms.jsp.search.config.I_CmsSearchConfigurationCommon;
034import org.opencms.jsp.search.config.I_CmsSearchConfigurationDidYouMean;
035import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetField;
036import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetQuery;
037import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetRange;
038import org.opencms.jsp.search.config.I_CmsSearchConfigurationGeoFilter;
039import org.opencms.jsp.search.config.I_CmsSearchConfigurationHighlighting;
040import org.opencms.jsp.search.config.I_CmsSearchConfigurationPagination;
041import org.opencms.jsp.search.config.I_CmsSearchConfigurationSorting;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.search.solr.CmsSolrIndex;
045import org.opencms.util.CmsPair;
046
047import java.util.Collections;
048import java.util.Map;
049
050import org.apache.commons.logging.Log;
051
052/** Search configuration parser reading a configuration containing a plain Solr query.
053 * Only fl might be added additionally. */
054public class CmsPlainQuerySearchConfigurationParser implements I_CmsSearchConfigurationParser {
055
056    /** Logger for the class. */
057    protected static final Log LOG = CmsLog.getLog(CmsPlainQuerySearchConfigurationParser.class);
058
059    /** The default return fields. */
060    private static final String DEFAULT_FL = "id,path";
061
062    /** The whole query string. */
063    protected String m_queryString;
064
065    /** The optional base configuration that should be changed by the JSON configuration. */
066    private I_CmsSearchConfiguration m_baseConfig;
067
068    /** Constructor taking the JSON as String.
069     * @param query The query that is passed to Solr.
070     */
071    public CmsPlainQuerySearchConfigurationParser(String query) {
072
073        this(query, null);
074    }
075
076    /** Constructor taking the JSON as String.
077     * @param query The query that is passed to Solr (additional Solr params).
078     * @param baseConfig A base configuration that is adjusted by the JSON configuration string.
079     */
080    public CmsPlainQuerySearchConfigurationParser(String query, I_CmsSearchConfiguration baseConfig) {
081
082        if ((null != query) && !(query.startsWith("fl=") || query.contains("&fl="))) {
083            query = query + "&fl=" + DEFAULT_FL;
084        }
085        m_queryString = query;
086        m_baseConfig = baseConfig;
087
088    }
089
090    /**
091     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseCommon(CmsObject)
092     */
093    public I_CmsSearchConfigurationCommon parseCommon(CmsObject cms) {
094
095        String queryString = m_queryString;
096        CmsPair<String, String> idxExtract = extractParam(queryString, "index");
097        CmsPair<String, String> coreExtract = extractParam(idxExtract.getFirst(), "core");
098        CmsPair<String, String> maxResultsExtract = extractParam(coreExtract.getFirst(), "maxresults");
099        String resString = maxResultsExtract.getSecond();
100        String indexName = idxExtract.getSecond();
101        if (null != indexName) {
102            indexName = indexName.trim();
103        }
104        if (null == indexName) {
105            indexName = cms.getRequestContext().getCurrentProject().isOnlineProject()
106            ? CmsSolrIndex.DEFAULT_INDEX_NAME_ONLINE
107            : CmsSolrIndex.DEFAULT_INDEX_NAME_OFFLINE;
108        }
109        Integer maxResNum = null;
110        if (null != resString) {
111            try {
112                maxResNum = Integer.valueOf(resString);
113            } catch (NumberFormatException e) {
114                if (LOG.isErrorEnabled()) {
115                    LOG.error("Ignoring param \"maxresults=" + resString + "\" since its not a valid integer.", e);
116                }
117            }
118        }
119        if (null == maxResNum) {
120            try {
121                CmsSolrIndex idx = OpenCms.getSearchManager().getIndexSolr(indexName);
122                if (null != idx) {
123                    maxResNum = Integer.valueOf(idx.getMaxProcessedResults());
124                } else {
125                    maxResNum = Integer.valueOf(CmsSolrIndex.MAX_RESULTS_UNLIMITED);
126                }
127            } catch (Throwable t) {
128                // This is ok, it's allowed to have an external other index here.
129                LOG.debug(
130                    "Parsing plain search configuration for none-CmsSolrIndex "
131                        + indexName
132                        + ". Setting max processed results to unlimited.");
133                maxResNum = Integer.valueOf(CmsSolrIndex.MAX_RESULTS_UNLIMITED);
134            }
135        }
136
137        return new CmsSearchConfigurationCommon(
138            null,
139            null,
140            null,
141            null,
142            Boolean.TRUE,
143            Boolean.TRUE,
144            null,
145            indexName,
146            coreExtract.getSecond(),
147            maxResultsExtract.getFirst(),
148            null,
149            null,
150            null,
151            maxResNum.intValue());
152    }
153
154    /**
155    * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseDidYouMean()
156    */
157    public I_CmsSearchConfigurationDidYouMean parseDidYouMean() {
158
159        return null != m_baseConfig ? m_baseConfig.getDidYouMeanConfig() : null;
160    }
161
162    /**
163     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseFieldFacets()
164     */
165    public Map<String, I_CmsSearchConfigurationFacetField> parseFieldFacets() {
166
167        return null != m_baseConfig ? m_baseConfig.getFieldFacetConfigs() : Collections.emptyMap();
168    }
169
170    /**
171     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseGeoFilter()
172     */
173    @Override
174    public I_CmsSearchConfigurationGeoFilter parseGeoFilter() {
175
176        return null;
177    }
178
179    /**
180     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseHighlighter()
181     */
182    public I_CmsSearchConfigurationHighlighting parseHighlighter() {
183
184        return null != m_baseConfig ? m_baseConfig.getHighlighterConfig() : null;
185    }
186
187    /**
188     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parsePagination()
189     */
190    public I_CmsSearchConfigurationPagination parsePagination() {
191
192        return null != m_baseConfig ? m_baseConfig.getPaginationConfig() : null;
193    }
194
195    /**
196     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseQueryFacet()
197     */
198    public I_CmsSearchConfigurationFacetQuery parseQueryFacet() {
199
200        return null != m_baseConfig ? m_baseConfig.getQueryFacetConfig() : null;
201    }
202
203    /**
204     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseRangeFacets()
205     */
206    public Map<String, I_CmsSearchConfigurationFacetRange> parseRangeFacets() {
207
208        return null != m_baseConfig ? m_baseConfig.getRangeFacetConfigs() : Collections.emptyMap();
209    }
210
211    /**
212     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseSorting()
213     */
214    public I_CmsSearchConfigurationSorting parseSorting() {
215
216        return null != m_baseConfig ? m_baseConfig.getSortConfig() : null;
217    }
218
219    /**
220     * Extracts the value of a parameter from a query string.
221     *
222     * For example, extractParam("a=foo&b=bar", "a") will return CmsPair("b=bar", "foo").
223     * @param params the parameter string.
224     * @param paramKey the key of the parameter to extract the value for.
225     * @return a pair of "params without the extracted parameter" and the value of the extracted parameter.
226     */
227    CmsPair<String, String> extractParam(String params, String paramKey) {
228
229        String extract = null;
230        int beginIdx = params.indexOf(paramKey + "=");
231        if (beginIdx >= 0) {
232            String sub = params.substring(beginIdx + paramKey.length() + 1);
233            int endIdx = sub.indexOf("&");
234            if (endIdx >= 0) {
235                extract = sub.substring(0, endIdx);
236                params = params.substring(0, beginIdx) + sub.substring(endIdx + 1);
237            } else {
238                extract = sub;
239                params = beginIdx > 0 ? params.substring(0, beginIdx - 1) : ""; // cut trailing '&'
240            }
241        }
242        return new CmsPair<>(params, extract);
243
244    }
245}