001/*
002 *  Copyright 2014 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.search.query;
017
018import org.ametys.cms.content.indexing.solr.SolrFieldNames;
019import org.ametys.cms.search.QueryBuilder;
020
021/**
022 * A {@link Query} realizing a full-text search on contents (search on all properties).
023 * Available operators:<ul>
024 * <li>SEARCH (default)</li>
025 * <li>SEARCH_STEMMED</li>
026 * <li>LIKE</li>
027 * <li>EQ</li>
028 * </ul>
029 */
030public class FullTextQuery implements Query
031{
032    
033    /** The value to test. */
034    protected String _value;
035    /** The language. */
036    protected String _language;
037    /** The operator. */
038    protected Operator _operator;
039    /** <code>true</code> if the value is already escaped and there is no need to escape again the value during {@link #build() the build of the query}. */
040    protected boolean _valueAlreadyEscaped;
041    
042    /**
043     * Build a FullTextQuery.
044     * @param value the value.
045     */
046    public FullTextQuery(String value)
047    {
048        this(value, Operator.SEARCH);
049    }
050    
051    /**
052     * Build a FullTextQuery.
053     * @param value the value.
054     * @param operator The operator to use.
055     */
056    public FullTextQuery(String value, Operator operator)
057    {
058        this(value, null, operator);
059    }
060    
061    /**
062     * Build a FullTextQuery.
063     * @param value the value.
064     * @param language the language.
065     */
066    public FullTextQuery(String value, String language)
067    {
068        this(value, language, Operator.SEARCH);
069    }
070    
071    /**
072     * Build a FullTextQuery.
073     * @param value the value.
074     * @param language the language.
075     * @param operator The operator to use.
076     */
077    public FullTextQuery(String value, String language, Operator operator)
078    {
079        this(value, language, operator, false);
080    }
081    
082    /**
083     * Build a FullTextQuery.
084     * @param value the value.
085     * @param language the language.
086     * @param operator The operator to use.
087     * @param alreadyEscaped true if the value is already escaped and there is no need to escape again the value during {@link #build() the build of the query}.
088     */
089    public FullTextQuery(String value, String language, Operator operator, boolean alreadyEscaped)
090    {
091        _value = value;
092        _language = language;
093        _operator = operator;
094        _valueAlreadyEscaped = alreadyEscaped;
095    }
096    
097    /**
098     * Get the value.
099     * @return the value.
100     */
101    public String getValue()
102    {
103        return _value;
104    }
105    
106    /**
107     * Get the language.
108     * @return the language.
109     */
110    public String getLanguage()
111    {
112        return _language;
113    }
114    
115    /**
116     * Get the operator to use.
117     * @return the operator.
118     */
119    public Operator getOperator()
120    {
121        return _operator;
122    }
123    
124    @Override
125    public String build() throws QuerySyntaxException
126    {
127        // TODO params?
128        String language = _language != null ? _language : QueryBuilder.DEFAULT_LANGUAGE;
129        
130        AbstractTextQuery.checkStringValue(_value);
131        
132        String escapedValue = _valueAlreadyEscaped ? _value : AbstractTextQuery.escapeStringValue(_value, _operator);
133        
134        StringBuilder solrQuery = new StringBuilder();
135        
136        switch (_operator)
137        {
138            case EQ:
139                solrQuery.append(SolrFieldNames.FULL_EXACT_WS)
140                         .append(":\"").append(escapedValue).append('"');
141                break;
142            case LIKE:
143                solrQuery.append(SolrFieldNames.FULL_GENERAL)
144                         .append(":(").append(escapedValue).append(')');
145                break;
146            case SEARCH_STEMMED:
147                solrQuery.append(SolrFieldNames.FULL_STEMMED_PREFIX).append(language)
148                         .append(":(").append(escapedValue).append(')');
149                break;
150            case SEARCH:
151            default:
152                solrQuery.append(SolrFieldNames.FULL_PREFIX).append(language)
153                         .append(":(").append(escapedValue).append(')');
154                break;
155        }
156        
157        return solrQuery.toString();
158    }
159
160    @Override
161    public int hashCode()
162    {
163        final int prime = 31;
164        int result = 1;
165        result = prime * result + ((_language == null) ? 0 : _language.hashCode());
166        result = prime * result + ((_operator == null) ? 0 : _operator.hashCode());
167        result = prime * result + ((_value == null) ? 0 : _value.hashCode());
168        return result;
169    }
170
171    @Override
172    public boolean equals(Object obj)
173    {
174        if (this == obj)
175        {
176            return true;
177        }
178        if (obj == null)
179        {
180            return false;
181        }
182        if (getClass() != obj.getClass())
183        {
184            return false;
185        }
186        FullTextQuery other = (FullTextQuery) obj;
187        if (_language == null)
188        {
189            if (other._language != null)
190            {
191                return false;
192            }
193        }
194        else if (!_language.equals(other._language))
195        {
196            return false;
197        }
198        if (_operator != other._operator)
199        {
200            return false;
201        }
202        if (_value == null)
203        {
204            if (other._value != null)
205            {
206                return false;
207            }
208        }
209        else if (!_value.equals(other._value))
210        {
211            return false;
212        }
213        return true;
214    }
215}