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