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