Cleanup project's name in Sonar
[aai/search-data-service.git] / src / main / java / org / openecomp / sa / searchdbabstraction / searchapi / RangeQuery.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23 package org.openecomp.sa.searchdbabstraction.searchapi;
24
25 import com.fasterxml.jackson.annotation.JsonProperty;
26
27 /**
28  * This class represents a simple range query.
29  *
30  * <p>A range query is composed of one or more operator/value pairs which define
31  * the upper and lower bounds of the range, and a field to apply the query to.
32  *
33  * <p>Operators may be one of the following:
34  * <ul>
35  * <li>gt  - Greater than. </li>
36  * <li>gte - Greater than or equal to. </li>
37  * <li>lt  - Less than. </li>
38  * <li>lte - Less than or equal to. </li>
39  * </ul>
40  * Values may be either numeric values (Integer or Double) or Strings representing
41  * dates.
42  *
43  * <p>The following examples illustrate a couple of variants of the range query:
44  *
45  * <p><pre>
46  *     // A simple numeric range query:
47  *     {
48  *         "range": {
49  *             "field": "fieldname",
50  *             "gte": 5,
51  *             "lte": 10
52  *         }
53  *     }
54  *
55  *     // A simple date range query:
56  *     {
57  *         "range": {
58  *             "field": "fieldname",
59  *             "gt": "2016-10-06T00:00:00.558+03:00",
60  *             "lt": "2016-10-06T23:59:59.558+03:00"
61  *         }
62  *     }
63  * </pre>
64  */
65 public class RangeQuery {
66
67   /**
68    * The name of the field to apply the range query against.
69    */
70   private String field;
71
72   /**
73    * The value of the field must be greater than this value to be a match.<br>
74    * NOTE: Only one of 'gt' or 'gte' should be set on any single {@link RangeQuery}
75    * instance.
76    */
77   private Object gt;
78
79   /**
80    * The value of the field must be greater than or equal to this value to be a match.<br>
81    * NOTE: Only one of 'gt' or 'gte' should be set on any single {@link RangeQuery}
82    * instance.
83    */
84   private Object gte;
85
86   /**
87    * The value of the field must be less than this value to be a match.<br>
88    * NOTE: Only one of 'lt' or 'lte' should be set on any single {@link RangeQuery}
89    * instance.
90    */
91   private Object lt;
92
93   /**
94    * The value of the field must be less than or equal to than this value to be a match.<br>
95    * NOTE: Only one of 'lt' or 'lte' should be set on any single {@link RangeQuery}
96    * instance.
97    */
98   private Object lte;
99
100   private String format;
101
102   @JsonProperty("time-zone")
103   private String timeZone;
104
105   public String getField() {
106     return field;
107   }
108
109   public void setField(String field) {
110     this.field = field;
111   }
112
113   public Object getGt() {
114     return gt;
115   }
116
117   public void setGt(Object gt) {
118
119     // It does not make sense to assign a value to both the 'greater than'
120     // and 'greater than or equal' operations, so make sure we are not
121     // trying to do that.
122     if (gte == null) {
123
124       // Make sure that we are not trying to mix both numeric and date
125       // type values in the same queries.
126       if (((lt != null) && !typesMatch(gt, lt))
127           || ((lte != null) && !typesMatch(gt, lte))) {
128         throw new IllegalArgumentException("Cannot mix date and numeric values in the same ranged query");
129       }
130
131       // If we made it here, then we're all good.  Store the value.
132       this.gt = gt;
133     } else {
134       throw new IllegalArgumentException("Cannot assign both 'gt' and 'gte' fields in the same ranged query");
135     }
136   }
137
138
139   public Object getGte() {
140     return gte;
141   }
142
143   public void setGte(Object gte) {
144
145     // It does not make sense to assign a value to both the 'greater than'
146     // and 'greater than or equal' operations, so make sure we are not
147     // trying to do that.
148     if (gt == null) {
149
150       // Make sure that we are not trying to mix both numeric and date
151       // type values in the same queries.
152       if (((lt != null) && !typesMatch(gte, lt))
153           || ((lte != null) && !typesMatch(gte, lte))) {
154         throw new IllegalArgumentException("Cannot mix date and numeric values in the same ranged query");
155       }
156
157       // If we made it here, then we're all good.  Store the value.
158       this.gte = gte;
159
160     } else {
161       throw new IllegalArgumentException("Cannot assign both 'gt' and 'gte' fields in the same ranged query");
162     }
163   }
164
165   public Object getLt() {
166     return lt;
167   }
168
169   public void setLt(Object lt) {
170
171     // It does not make sense to assign a value to both the 'less than'
172     // and 'less than or equal' operations, so make sure we are not
173     // trying to do that.
174     if (lte == null) {
175
176       // Make sure that we are not trying to mix both numeric and date
177       // type values in the same queries.
178       if (((gt != null) && !typesMatch(lt, gt))
179           || ((gte != null) && !typesMatch(lt, gte))) {
180         throw new IllegalArgumentException("Cannot mix date and numeric values in the same ranged query");
181       }
182
183       // If we made it here, then we're all good.  Store the value.
184
185       this.lt = lt;
186     } else {
187       throw new IllegalArgumentException("Cannot assign both 'lt' and 'lte' fields in the same ranged query");
188     }
189   }
190
191   public Object getLte() {
192     return lte;
193   }
194
195   public void setLte(Object lte) {
196
197     // It does not make sense to assign a value to both the 'greater than'
198     // and 'greater than or equal' operations, so make sure we are not
199     // trying to do that.
200     if (lt == null) {
201
202       // Make sure that we are not trying to mix both numeric and date
203       // type values in the same queries.
204       if (((gt != null) && !typesMatch(lte, gt))
205           || ((gte != null) && !typesMatch(lte, gte))) {
206         throw new IllegalArgumentException("Cannot mix date and numeric values in the same ranged query");
207       }
208
209       // If we made it here, then we're all good.  Store the value.
210
211       this.lte = lte;
212     } else {
213       throw new IllegalArgumentException("Cannot assign both 'lt' and 'lte' fields in the same ranged query");
214     }
215   }
216
217   public String getFormat() {
218     return format;
219   }
220
221   public void setFormat(String format) {
222     this.format = format;
223   }
224
225   public String getTimeZone() {
226     return timeZone;
227   }
228
229   public void setTimeZone(String timeZone) {
230     this.timeZone = timeZone;
231   }
232
233   /**
234    * This convenience method determines whether or not the supplied
235    * value needs to be enclosed in '"' characters when generating
236    * ElasticSearch compatible syntax.
237    *
238    * @param val - The value to check.
239    * @return - A string representation of the value for inclusion
240    *     in an ElasticSearch syntax string.
241    */
242   private String formatStringOrNumericVal(Object val) {
243
244     if (val instanceof String) {
245       return "\"" + val.toString() + "\"";
246     } else {
247       return val.toString();
248     }
249   }
250
251
252   /**
253    * This convenience method verifies that the supplied objects are
254    * of classes considered to be compatible for a ranged query.
255    *
256    * @param value1 - The first value to check.
257    * @param value2 - The second value to check.
258    * @return - True if the two objects are compatible for inclusion in the
259    *     same ranged query, False, otherwise.
260    */
261   boolean typesMatch(Object value1, Object value2) {
262
263     return ((value1 instanceof String) && (value2 instanceof String))
264         || (!(value1 instanceof String) && !(value2 instanceof String));
265   }
266
267
268   /**
269    * This method returns a string which represents this query in syntax
270    * that is understandable by ElasticSearch and is suitable for inclusion
271    * in an ElasticSearch query string.
272    *
273    * @return - ElasticSearch syntax string.
274    */
275   public String toElasticSearch() {
276
277     StringBuilder sb = new StringBuilder();
278
279     sb.append("{");
280     sb.append("\"range\": {");
281     sb.append("\"").append(field).append("\": {");
282
283     // We may have one or zero of 'greater than' or 'greater
284     // than or equal'
285     boolean needComma = false;
286     if (gte != null) {
287       sb.append("\"gte\": ").append(formatStringOrNumericVal(gte));
288       needComma = true;
289     } else if (gt != null) {
290       sb.append("\"gt\": ").append(formatStringOrNumericVal(gt));
291       needComma = true;
292     }
293
294     // We may have one or zero of 'less than' or 'less
295     // than or equal'
296     if (lte != null) {
297       if (needComma) {
298         sb.append(", ");
299       }
300       sb.append("\"lte\": ").append(formatStringOrNumericVal(lte));
301     } else if (lt != null) {
302       if (needComma) {
303         sb.append(", ");
304       }
305       sb.append("\"lt\": ").append(formatStringOrNumericVal(lt));
306     }
307
308     // Append the format specifier if one was provided.
309     if (format != null) {
310       sb.append(", \"format\": \"").append(format).append("\"");
311     }
312
313     // Append the time zone specifier if one was provided.
314     if (timeZone != null) {
315       sb.append(", \"time_zone\": \"").append(timeZone).append("\"");
316     }
317
318     sb.append("}");
319     sb.append("}");
320     sb.append("}");
321
322     return sb.toString();
323   }
324
325   @Override
326   public String toString() {
327
328     String str = "{ field: " + field + ", ";
329
330     if (gt != null) {
331       str += "gt: " + gt;
332     } else if (gte != null) {
333       str += "gte: " + gte;
334     }
335
336     if (lt != null) {
337       str += (((gt != null) || (gte != null)) ? ", " : "") + "lt: " + lt;
338     } else if (lte != null) {
339       str += (((gt != null) || (gte != null)) ? ", " : "") + "lte: " + lte;
340     }
341
342     str += "}";
343
344     return str;
345   }
346 }