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.lang.reflect.Modifier; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.stream.Collectors; 023import java.util.stream.Stream; 024 025import org.apache.commons.lang3.StringUtils; 026 027/** 028 * Represents a search predicate. 029 */ 030@FunctionalInterface 031public interface Query 032{ 033 034 /** Enumeration of available operators in {@link Query} */ 035 public enum Operator 036 { 037 /** Operator testing the existence of a property. */ 038 EXISTS("exists"), 039 /** Constant of test's operator for textual search, on unstemmed terms. Works only for type String */ 040 SEARCH("search"), 041 /** Constant of test's operator for textual search, on stemmed terms. Works only for type String */ 042 SEARCH_STEMMED("searchStemmed"), 043 /** Constant of test's operator for 'like' comparison. Works only for type String */ 044 LIKE("like"), 045 /** Constant of test's operator for 'less than' comparison */ 046 LT("lt"), 047 /** Constant of test's operator for 'less than or equals to' comparison */ 048 LE("le"), 049 /** Constant of test's operator for 'greater than' comparison */ 050 GT("gt"), 051 /** Constant of test's operator for 'greater than or equals to' comparison */ 052 GE("ge"), 053 /** Constant of test's operator for 'equals to' comparison */ 054 EQ("eq"), 055 /** Constant of test's operator for 'not equals to' comparison */ 056 NE("ne"); 057 058 private static Map<String, Operator> _OP_NAMES = new HashMap<>(); 059 static 060 { 061 for (Operator op : values()) 062 { 063 _OP_NAMES.put(op.getName(), op); 064 } 065 } 066 067 private final String _name; 068 069 Operator(String name) 070 { 071 _name = name; 072 } 073 074 /** 075 * Get the operator name. 076 * @return the operator name. 077 */ 078 public String getName() 079 { 080 return _name; 081 } 082 083 /** 084 * Get an Operator object from its name. 085 * @param name the operator name. 086 * @return the operator object, or null if not found. 087 */ 088 public static Operator fromName(String name) 089 { 090 if (_OP_NAMES.containsKey(name)) 091 { 092 return _OP_NAMES.get(name); 093 } 094 throw new IllegalArgumentException("No operator with code '" + name + "'"); 095 } 096 } 097 098 /** Enumeration of available logical operators in {@link Query} */ 099 public enum LogicalOperator 100 { 101 /** Logical operator AND */ 102 AND, 103 /** Logical operator OR */ 104 OR 105 } 106 107 /** 108 * Build the solr query string representing the Query object. 109 * @return the solr query string representing the Query object. 110 * @throws QuerySyntaxException if the query can't be built because of a syntax error. 111 */ 112 String build() throws QuerySyntaxException; 113 114 /** 115 * Gets a representation of this {@link Query}, for pretty-printing for logging and debugging purposes 116 * @param indent The current indentation. Base indentation is 2 (for printing a sub-level) 117 * @return a representation of this {@link Query} 118 */ 119 default String toString(int indent) 120 { 121 String toStr; 122 try 123 { 124 toStr = this.getClass().getSimpleName() 125 + "(" 126 + Stream.of(this.getClass().getDeclaredFields()) 127 .filter(f -> !Modifier.isStatic(f.getModifiers())) 128 .map(f -> 129 { 130 try 131 { 132 f.setAccessible(true); 133 Object val = f.get(this); 134 String strVal = val != null && val.getClass().isArray() ? Arrays.toString((Object[]) val) : String.valueOf(val); 135 return f.getName() + ":" + strVal; 136 } 137 catch (Exception e) 138 { 139 return ""; 140 } 141 }) 142 .collect(Collectors.joining(",")) 143 + ")"; 144 } 145 catch (Exception e) 146 { 147 toStr = toString(); 148 } 149 return StringUtils.repeat(' ', indent) + toStr; 150 } 151}