001/*
002 *  Copyright 2015 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 java.util.HashMap;
019import java.util.Map;
020import java.util.Optional;
021
022/**
023 * Represents a search predicate.
024 */
025@FunctionalInterface
026public interface Query
027{
028    /** Constant for mandatory boolean clauses */
029    public static final String BOOL_MUST = "must";
030    
031    /** Constant for prohibited boolean clauses */
032    public static final String BOOL_MUST_NOT = "must_not";
033    
034    /** Constant for optional boolean clauses */
035    public static final String BOOL_SHOULD = "should";
036    
037    /** Constant for mandatory, but not affecting score, boolean clauses */
038    public static final String BOOL_FILTER = "filter";
039    
040    /** Enumeration of available operators in {@link Query} */
041    public enum Operator
042    {
043        /** Operator testing the existence of a property. */
044        EXISTS("exists"),
045        /** Constant of test's operator for textual search, on unstemmed terms. Works only for type String */
046        SEARCH("search"),
047        /** Constant of test's operator for textual search, on stemmed terms. Works only for type String */
048        SEARCH_STEMMED("searchStemmed"),
049        /** Constant of test's operator for 'like' comparison. Works only for type String */
050        LIKE("like"), 
051        /** Constant of test's operator for fuzzy search. Works only for type String */
052        FUZZY("fuzzy"),
053        /** Constant of test's operator for phonetic comparison. Works only for type String */
054        PHONETIC("phonetic"),
055        /** Constant of test's operator for 'less than' comparison. Works only for type String */
056        NGRAM("ngram"),
057        /** Constant of test's operator for 'less than' comparison */
058        LT("lt"),
059        /** Constant of test's operator for 'less than or equals to' comparison */
060        LE("le"),
061        /** Constant of test's operator for 'greater than' comparison */
062        GT("gt"),
063        /** Constant of test's operator for 'greater than or equals to' comparison */
064        GE("ge"),
065        /** Constant of test's operator for 'equals to' comparison */
066        EQ("eq"),
067        /** Constant of test's operator for 'not equals to' comparison */
068        NE("ne");
069        
070        private static Map<String, Operator> _OP_NAMES = new HashMap<>();
071        static 
072        {
073            for (Operator op : values())
074            {
075                _OP_NAMES.put(op.getName(), op);
076            }
077        }
078        
079        private final String _name;
080        
081        Operator(String name)
082        {
083            _name = name;
084        }
085        
086        /**
087         * Get the operator name.
088         * @return the operator name.
089         */
090        public String getName()
091        {
092            return _name;
093        }
094        
095        /**
096         * Get an Operator object from its name.
097         * @param name the operator name.
098         * @return the operator object, or null if not found.
099         */
100        public static Operator fromName(String name)
101        {
102            if (_OP_NAMES.containsKey(name))
103            {
104                return _OP_NAMES.get(name);
105            }
106            throw new IllegalArgumentException("No operator with code '" + name + "'");
107        }
108    }
109    
110    /** Enumeration of available logical operators in {@link Query} */
111    public enum LogicalOperator 
112    {
113        /** Logical operator AND */
114        AND,
115        /** Logical operator OR */
116        OR
117    }
118    
119    /**
120     * Build the solr query string representing the Query object.
121     * @return the solr query string representing the Query object.
122     * @throws QuerySyntaxException if the query can't be built because of a syntax error.
123     */
124    String build() throws QuerySyntaxException;
125    
126    /**
127     * Build the solr query representing the Query object.<br>
128     * The return type may be either String or Map&lt;String, Object> following the Solr JSON Query DSL.
129     * @return the solr query representing the Query object. Can be empty in case of empty query.
130     * @throws QuerySyntaxException if the query can't be built because of a syntax error.
131     */
132    default Optional<Object> buildAsJson() throws QuerySyntaxException
133    {
134        return Optional.of(build());
135    }
136    
137    /**
138     * Rewrite this Query to allow further optimisations.
139     * @return a rewritten Query, sementically identical. Can be empty in case of empty query
140     */
141    default Optional<Query> rewrite()
142    {
143        return Optional.of(this);
144    }
145    
146    /**
147     * Gets a representation of this {@link Query}, for pretty-printing for logging and debugging purposes
148     * @param indent The current indentation. Base indentation is 2 (for printing a sub-level)
149     * @return a representation of this {@link Query}
150     */
151    default String toString(int indent)
152    {
153        return QueryDebugger.toDebugString(this, indent);
154    }
155}