eef61e09c683eeecbdb007f16b64757711297ad0
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
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  */
22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database.elasticsearch.data.rpctypehelper;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.List;
29 import java.util.TimeZone;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.onap.ccsdk.features.sdnr.wt.common.YangHelper;
32 import org.onap.ccsdk.features.sdnr.wt.common.database.data.DbFilter;
33 import org.onap.ccsdk.features.sdnr.wt.common.database.queries.BoolQueryBuilder;
34 import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilder;
35 import org.onap.ccsdk.features.sdnr.wt.common.database.queries.QueryBuilders;
36 import org.onap.ccsdk.features.sdnr.wt.common.database.requests.SearchRequest;
37 import org.onap.ccsdk.features.sdnr.wt.dataprovider.database.elasticsearch.data.acessor.DataObjectAcessorPm;
38 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.NetconfTimeStamp;
39 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.NetconfTimeStampImpl;
40 import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.types.YangHelper2;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.EntityInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.SortOrder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.entity.input.Filter;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.entity.input.Pagination;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.data.provider.rev201110.entity.input.Sortorder;
46 import org.opendaylight.yangtools.yang.common.Uint32;
47 import org.opendaylight.yangtools.yang.common.Uint64;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public class QueryByFilter {
52
53     private static final Logger LOG = LoggerFactory.getLogger(DataObjectAcessorPm.class);
54     private static final List<String> timestampValueNames = Arrays.asList("timestamp", "start", "end");
55
56     private static List<Sortorder> emptySortOrderList = new ArrayList<>();
57     private static List<Filter> emptyFilterList = new ArrayList<>();
58
59     // Derived from input
60     private long page;
61     private long pageSize;
62     private long fromPage;
63     private List<Filter> filterList;
64     private List<Sortorder> sortOrder;
65
66     /**
67      * Process input from RPC into Queries to database
68      *
69      * @param input Input from RPC, for test it could be null
70      */
71     public QueryByFilter(EntityInput input) {
72         page = -1;
73         pageSize = -1;
74         if (input != null) {
75             @Nullable
76             Pagination pagination = input.getPagination();
77             if (pagination != null) {
78                 @Nullable
79                 Uint64 pageOrNull = YangHelper2.getUint64(pagination.getPage());
80                 if (pageOrNull != null) {
81                     page = pageOrNull.longValue();
82                 }
83                 @Nullable
84                 Uint32 pageSizeOrNull = YangHelper2.getUint32(pagination.getSize());
85                 if (pageSizeOrNull != null) {
86                     pageSize = pageSizeOrNull.longValue();
87                 }
88             }
89         }
90         if (page < 0)
91             page = 1;
92         if (pageSize < 0)
93             pageSize = 1;
94
95         fromPage = (page - 1) * pageSize;
96         if (fromPage < 0 || pageSize > 10000)
97             throw new IllegalArgumentException("mismatching input parameters. From:" + fromPage + " size:" + pageSize);
98
99         filterList = YangHelper.getList(input.getFilter());
100         if (filterList == null)
101             filterList = emptyFilterList;
102         sortOrder = YangHelper.getList(input.getSortorder());
103         if (sortOrder == null)
104             sortOrder = emptySortOrderList;
105
106     }
107
108     public QueryBuilder getQueryBuilderByFilter() {
109         return getQueryBuilderByFilter("");
110     }
111
112     public QueryBuilder getQueryBuilderByFilter(String prefix) {
113         QueryBuilder queryBuilder = fromFilter(filterList, prefix).from(fromPage).size(pageSize);
114         setSortOrder(queryBuilder, sortOrder, prefix);
115         return queryBuilder;
116     }
117
118     public SearchRequest getSearchRequestByFilter(String nodeKey, String uuidKey, String index, String dataType,
119             boolean doFullsizeRequest) {
120         Filter nodeFilter = getFilter(filterList, nodeKey);
121         if (nodeFilter != null) {
122             SearchRequest request = new SearchRequest(index, dataType);
123             request.setQuery(QueryBuilders.matchQuery(nodeKey, nodeFilter.getFiltervalue())
124                     .setFullsizeRequest(doFullsizeRequest).aggregations(uuidKey).size(0));
125             return request;
126         } else {
127             String msg = "no nodename in filter found ";
128             LOG.debug(msg);
129             throw new IllegalArgumentException(msg);
130         }
131     }
132
133     public SearchRequest getSearchRequestBySortOrder(String nodeKey, String uuidKey, String index, String dataType,
134             boolean doFullsizeRequest) {
135         Sortorder soNode = getSortOrder(sortOrder, nodeKey);
136         SearchRequest request = new SearchRequest(index, dataType);
137         QueryBuilder query = null;
138         if (soNode != null) {
139             query = QueryBuilders.matchAllQuery().setFullsizeRequest(doFullsizeRequest)
140                     .aggregations(nodeKey, convert(soNode.getSortorder())).size(0);
141         } else {
142             query = QueryBuilders.matchAllQuery().setFullsizeRequest(doFullsizeRequest).aggregations(nodeKey).size(0);
143         }
144         request.setQuery(query);
145         return request;
146     }
147
148     public long getPage() {
149         return page;
150     }
151
152     public long getPageSize() {
153         return pageSize;
154     }
155
156     public long getPageStartIndex() {
157         return fromPage;
158     }
159
160     @Override
161     public String toString() {
162         return "QueryByFilter [page=" + page + ", pageSize=" + pageSize + ", fromPage=" + fromPage + ", filterList="
163                 + filterList + ", sortOrder=" + sortOrder + "]";
164     }
165
166     /*
167      * Private and static implementations
168      */
169     private static QueryBuilder setSortOrder(QueryBuilder query, @Nullable List<Sortorder> sortorder, String prefix) {
170         if (sortorder != null && sortorder.size() > 0) {
171             for (Sortorder so : sortorder) {
172                 query.sort(handlePrefix(prefix, so.getProperty()), convert(so.getSortorder()));
173             }
174         }
175         return query;
176     }
177
178     private static org.onap.ccsdk.features.sdnr.wt.common.database.queries.SortOrder convert(SortOrder sortOrder) {
179         return sortOrder == SortOrder.Ascending
180                 ? org.onap.ccsdk.features.sdnr.wt.common.database.queries.SortOrder.ASCENDING
181                 : org.onap.ccsdk.features.sdnr.wt.common.database.queries.SortOrder.DESCENDING;
182     };
183
184     private static Sortorder getSortOrder(@Nullable List<Sortorder> list, String prop) {
185         if (list == null) {
186             return null;
187         }
188         for (Sortorder o : list) {
189             if (prop.equals(o.getProperty())) {
190                 return o;
191             }
192         }
193         return null;
194     }
195
196     private static Filter getFilter(@Nullable List<Filter> list, String prop) {
197         if (list == null) {
198             return null;
199         }
200         for (Filter f : list) {
201             if (prop.equals(f.getProperty())) {
202                 return f;
203             }
204         }
205         return null;
206     }
207
208     private static String fillTimeStamp(String value) {
209         int idx = value.lastIndexOf("*");
210         final String REPLACE = "0000-00-00T00:00:00.0Z";
211         String s = value.substring(0, idx) + REPLACE.substring(idx);
212         if (Integer.parseInt(s.substring(5, 7)) == 0) {
213             s = s.substring(0, 5) + "01-" + s.substring(8);
214         }
215         if (Integer.parseInt(s.substring(8, 10)) == 0) {
216             s = s.substring(0, 8) + "01" + s.substring(10);
217         }
218
219         return s;
220     }
221
222     /**
223      * convert timestamp with ending placeholder in filter to elasticsearch filter e.g. 2017* => gte:
224      * 2017-01-01T00:00:00Z, lt:2018-01-01T00:00:00Z
225      *
226      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
227      *
228      */
229     private static @Nullable QueryBuilder fromTimestampSearchFilter(String property, String value) {
230         if (!value.endsWith("*")) {
231             return null;
232         }
233         int idx = value.lastIndexOf("*");
234         String lowerEnd = fillTimeStamp(value);
235         String upperEnd = null;
236         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
237         Date dt = null;
238         try {
239             dt = converter.getDateFromNetconf(lowerEnd);
240         } catch (Exception e) {
241
242         }
243         if (dt == null) {
244             return null;
245         }
246         // property.substring(0,idx)+REPLACE.substring(idx+1);
247         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
248         c.setTime(dt);
249         int tmpvalue;
250         switch (idx) {
251             case 1: // (2*)
252                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
253                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
254                 break;
255             case 2: // (20*)
256                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
257                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
258                 break;
259             case 3: // (200*)
260                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
261                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
262                 break;
263             case 4: // (2000*)
264             case 5: // (2000-*)
265                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
266                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
267                 break;
268             case 6: // switch 10 months (2000-0* or 2000-1*)
269                 tmpvalue = c.get(Calendar.MONTH);
270                 if (tmpvalue < 9) {
271                     c.set(Calendar.MONTH, 9);
272                 } else {
273                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
274                     c.set(Calendar.MONTH, 0);
275                 }
276                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
277
278                 break;
279             case 7: // switch one month (2018-01* or 2018-01-*)
280             case 8:
281                 c.add(Calendar.MONTH, 1);
282                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
283                 break;
284             case 9: // (2018-01-0*)
285                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
286                 if (tmpvalue == 1) {
287                     c.set(Calendar.DAY_OF_MONTH, 10);
288                 } else if (tmpvalue == 10) {
289                     c.set(Calendar.DAY_OF_MONTH, 20);
290                 } else if (tmpvalue == 20) {
291                     if (c.getActualMaximum(Calendar.DAY_OF_MONTH) < 30) {
292                         c.set(Calendar.DAY_OF_MONTH, 1);
293                         c.add(Calendar.MONTH, 1);
294                     } else {
295                         c.set(Calendar.DAY_OF_MONTH, 30);
296                     }
297                 } else if (tmpvalue == 30) {
298                     c.set(Calendar.DAY_OF_MONTH, 1);
299                     c.add(Calendar.MONTH, 1);
300                 } else {
301                     break;
302                 }
303                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
304                 break;
305             case 10: // (2018-01-01*)
306             case 11: // (2018-01-01T*)
307                 c.add(Calendar.DAY_OF_MONTH, 1);
308                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
309                 break;
310             case 12: // (2018-01-01T1*)
311                 tmpvalue = c.get(Calendar.HOUR_OF_DAY);
312                 if (tmpvalue == 20) {
313                     c.set(Calendar.HOUR_OF_DAY, 0);
314                     c.add(Calendar.DAY_OF_MONTH, 1);
315                 } else {
316                     c.add(Calendar.HOUR_OF_DAY, 10);
317                 }
318                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
319                 break;
320             case 13: // (2018-01-01T11*)
321             case 14: // (2018-01-01T11-*)
322                 c.add(Calendar.HOUR_OF_DAY, 1);
323                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
324                 break;
325             case 15: // (2018-01-01T11-3*)
326                 c.add(Calendar.MINUTE, 10);
327                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
328                 break;
329             case 16: // (2018-01-01T11-32*)
330             case 17: // (2018-01-01T11-32-*)
331                 c.add(Calendar.MINUTE, 1);
332                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
333                 break;
334             case 18: // (2018-01-01T11-32-1*)
335                 c.add(Calendar.SECOND, 10);
336                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
337                 break;
338             case 19: // (2018-01-01T11-32-11*)
339             case 20: // (2018-01-01T11-32-11.*)
340                 c.add(Calendar.SECOND, 1);
341                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
342                 break;
343
344             default:
345                 break;
346         }
347
348         if (upperEnd == null) {
349             return null;
350         }
351         return QueryBuilders.rangeQuery(property).gte(lowerEnd).lt(upperEnd);
352
353     }
354
355     private static List<String> collectValues(Filter filter) {
356         List<String> values = new ArrayList<String>();
357         if (filter.getFiltervalue() != null) {
358             values.add(filter.getFiltervalue());
359         }
360         if (filter.getFiltervalues() != null) {
361             values.addAll(filter.getFiltervalues());
362         }
363         return values;
364     }
365
366     private static QueryBuilder fromFilter(@Nullable List<Filter> filters, String prefix) {
367         if (filters == null || filters.size() == 0) {
368             return QueryBuilders.matchAllQuery();
369
370         } else if (filters.size() == 1) {
371             String property = filters.get(0).getProperty();
372             List<String> values = collectValues(filters.get(0));
373             if ("id".equals(property)) {
374                 property = "_id";
375             }
376             if (values.size() == 1) {
377                 return getSinglePropertyQuery(property, values.get(0), prefix);
378             } else {
379                 BoolQueryBuilder bquery = new BoolQueryBuilder();
380                 for (String v : values) {
381                     bquery.should(getSinglePropertyQuery(property, v, prefix));
382                 }
383                 return bquery;
384
385             }
386         } else {
387             BoolQueryBuilder query = new BoolQueryBuilder();
388             for (Filter fi : filters) {
389                 String p = fi.getProperty();
390                 List<String> values = collectValues(fi);
391                 if ("id".equals(p)) {
392                     p = "_id";
393                 }
394                 if (values.size() == 1) {
395                     query.must(getSinglePropertyQuery(p, values.get(0), prefix));
396                 } else {
397                     BoolQueryBuilder tmpQuery = QueryBuilders.boolQuery();
398                     for (String v : values) {
399                         tmpQuery.should(getSinglePropertyQuery(p, v, prefix));
400                     }
401                     query.must(tmpQuery);
402                     tmpQuery = QueryBuilders.boolQuery();
403                 }
404             }
405             LOG.trace("Query result. {}", query.toJSON());
406             return query;
407         }
408     }
409
410     private static QueryBuilder getSinglePropertyQuery(String p, String v, String prefix) {
411         QueryBuilder query = null;
412         if (DbFilter.hasSearchParams(v)) {
413             if (p != null && timestampValueNames.contains(p.toLowerCase())) {
414                 query = fromTimestampSearchFilter(p, v);
415                 if (query == null) {
416                     query = QueryBuilders.regex(handlePrefix(prefix, p), DbFilter.createDatabaseRegex(v));
417                 }
418             } else {
419                 query = QueryBuilders.regex(handlePrefix(prefix, p), DbFilter.createDatabaseRegex(v));
420             }
421         } else if (DbFilter.isComparisonValid(v)) {
422             query = DbFilter.getRangeQuery(handlePrefix(prefix, p), v);
423             if (query == null) {
424                 query = QueryBuilders.matchQuery(handlePrefix(prefix, p), v);
425             }
426         } else {
427             query = QueryBuilders.matchQuery(handlePrefix(prefix, p), v);
428         }
429         return query;
430     }
431
432     private static String handlePrefix(String prefix, String p) {
433         return (prefix != null ? prefix : "") + p;
434     }
435
436 }