001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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    @Override
094    public I_CmsSearchConfigurationCommon parseCommon(CmsObject cms) {
095
096        String queryString = m_queryString;
097        CmsPair<String, String> idxExtract = extractParam(queryString, "index");
098        CmsPair<String, String> coreExtract = extractParam(idxExtract.getFirst(), "core");
099        CmsPair<String, String> maxResultsExtract = extractParam(coreExtract.getFirst(), "maxresults");
100        String resString = maxResultsExtract.getSecond();
101        String indexName = idxExtract.getSecond();
102        if (null != indexName) {
103            indexName = indexName.trim();
104        }
105        if (null == indexName) {
106            indexName = cms.getRequestContext().getCurrentProject().isOnlineProject()
107            ? CmsSolrIndex.DEFAULT_INDEX_NAME_ONLINE
108            : CmsSolrIndex.DEFAULT_INDEX_NAME_OFFLINE;
109        }
110        Integer maxResNum = null;
111        if (null != resString) {
112            try {
113                maxResNum = Integer.valueOf(resString);
114            } catch (NumberFormatException e) {
115                if (LOG.isErrorEnabled()) {
116                    LOG.error("Ignoring param \"maxresults=" + resString + "\" since its not a valid integer.", e);
117                }
118            }
119        }
120        if (null == maxResNum) {
121            try {
122                CmsSolrIndex idx = OpenCms.getSearchManager().getIndexSolr(indexName);
123                if (null != idx) {
124                    maxResNum = Integer.valueOf(idx.getMaxProcessedResults());
125                } else {
126                    maxResNum = Integer.valueOf(CmsSolrIndex.MAX_RESULTS_UNLIMITED);
127                }
128            } catch (Throwable t) {
129                // This is ok, it's allowed to have an external other index here.
130                LOG.debug(
131                    "Parsing plain search configuration for none-CmsSolrIndex "
132                        + indexName
133                        + ". Setting max processed results to unlimited.");
134                maxResNum = Integer.valueOf(CmsSolrIndex.MAX_RESULTS_UNLIMITED);
135            }
136        }
137
138        return new CmsSearchConfigurationCommon(
139            null,
140            null,
141            null,
142            null,
143            Boolean.TRUE,
144            Boolean.TRUE,
145            null,
146            indexName,
147            coreExtract.getSecond(),
148            maxResultsExtract.getFirst(),
149            null,
150            null,
151            null,
152            maxResNum.intValue());
153    }
154
155    /**
156    * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseDidYouMean()
157    */
158    @Override
159    public I_CmsSearchConfigurationDidYouMean parseDidYouMean() {
160
161        return null != m_baseConfig ? m_baseConfig.getDidYouMeanConfig() : null;
162    }
163
164    /**
165     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseFieldFacets()
166     */
167    @Override
168    public Map<String, I_CmsSearchConfigurationFacetField> parseFieldFacets() {
169
170        return null != m_baseConfig ? m_baseConfig.getFieldFacetConfigs() : Collections.emptyMap();
171    }
172
173    /**
174     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseGeoFilter()
175     */
176    @Override
177    public I_CmsSearchConfigurationGeoFilter parseGeoFilter() {
178
179        return null;
180    }
181
182    /**
183     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseHighlighter()
184     */
185    @Override
186    public I_CmsSearchConfigurationHighlighting parseHighlighter() {
187
188        return null != m_baseConfig ? m_baseConfig.getHighlighterConfig() : null;
189    }
190
191    /**
192     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parsePagination()
193     */
194    @Override
195    public I_CmsSearchConfigurationPagination parsePagination() {
196
197        return null != m_baseConfig ? m_baseConfig.getPaginationConfig() : null;
198    }
199
200    /**
201     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseQueryFacet()
202     */
203    @Override
204    public I_CmsSearchConfigurationFacetQuery parseQueryFacet() {
205
206        return null != m_baseConfig ? m_baseConfig.getQueryFacetConfig() : null;
207    }
208
209    /**
210     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseRangeFacets()
211     */
212    @Override
213    public Map<String, I_CmsSearchConfigurationFacetRange> parseRangeFacets() {
214
215        return null != m_baseConfig ? m_baseConfig.getRangeFacetConfigs() : Collections.emptyMap();
216    }
217
218    /**
219     * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseSorting()
220     */
221    @Override
222    public I_CmsSearchConfigurationSorting parseSorting() {
223
224        return null != m_baseConfig ? m_baseConfig.getSortConfig() : null;
225    }
226
227    /**
228     * Extracts the value of a parameter from a query string.
229     *
230     * For example, extractParam("a=foo&b=bar", "a") will return CmsPair("b=bar", "foo").
231     * @param params the parameter string.
232     * @param paramKey the key of the parameter to extract the value for.
233     * @return a pair of "params without the extracted parameter" and the value of the extracted parameter.
234     */
235    CmsPair<String, String> extractParam(String params, String paramKey) {
236
237        String extract = null;
238        int beginIdx = params.indexOf(paramKey + "=");
239        if (beginIdx >= 0) {
240            String sub = params.substring(beginIdx + paramKey.length() + 1);
241            int endIdx = sub.indexOf("&");
242            if (endIdx >= 0) {
243                extract = sub.substring(0, endIdx);
244                params = params.substring(0, beginIdx) + sub.substring(endIdx + 1);
245            } else {
246                extract = sub;
247                params = beginIdx > 0 ? params.substring(0, beginIdx - 1) : ""; // cut trailing '&'
248            }
249        }
250        return new CmsPair<>(params, extract);
251
252    }
253}