627d125ae2804b7767cb65a904b4edea57c041e1
[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.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.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         Filter nodeFilter = getFilter(filterList, nodeKey);
120         if (nodeFilter != null) {
121             SearchRequest request = new SearchRequest(index, dataType);
122             request.setQuery(
123                     QueryBuilders.matchQuery(nodeKey, nodeFilter.getFiltervalue()).aggregations(uuidKey).size(0));
124             return request;
125         } else {
126             String msg = "no nodename in filter found ";
127             LOG.debug(msg);
128             throw new IllegalArgumentException(msg);
129         }
130     }
131
132     public SearchRequest getSearchRequestBySortOrder(String nodeKey, String uuidKey, String index, String dataType) {
133         Sortorder soNode = getSortOrder(sortOrder, nodeKey);
134         SearchRequest request = new SearchRequest(index, dataType);
135         QueryBuilder query = null;
136         if (soNode != null) {
137             query = QueryBuilders.matchAllQuery().aggregations(nodeKey, convert(soNode.getSortorder())).size(0);
138         } else {
139             query = QueryBuilders.matchAllQuery().aggregations(nodeKey).size(0);
140         }
141         request.setQuery(query);
142         return request;
143     }
144
145     public long getPage() {
146         return page;
147     }
148
149     public long getPageSize() {
150         return pageSize;
151     }
152
153     public long getPageStartIndex() {
154         return fromPage;
155     }
156
157     @Override
158     public String toString() {
159         return "QueryByFilter [page=" + page + ", pageSize=" + pageSize + ", fromPage=" + fromPage + ", filterList="
160                 + filterList + ", sortOrder=" + sortOrder + "]";
161     }
162
163     /*
164      * Private and static implementations
165      */
166     private static QueryBuilder setSortOrder(QueryBuilder query, @Nullable List<Sortorder> sortorder, String prefix) {
167         if (sortorder != null && sortorder.size() > 0) {
168             for (Sortorder so : sortorder) {
169                 query.sort(handlePrefix(prefix, so.getProperty()), convert(so.getSortorder()));
170             }
171         }
172         return query;
173     }
174
175     private static org.onap.ccsdk.features.sdnr.wt.common.database.queries.SortOrder convert(SortOrder sortOrder) {
176         return sortOrder == SortOrder.Ascending
177                 ? org.onap.ccsdk.features.sdnr.wt.common.database.queries.SortOrder.ASCENDING
178                 : org.onap.ccsdk.features.sdnr.wt.common.database.queries.SortOrder.DESCENDING;
179     };
180
181     private static Sortorder getSortOrder(@Nullable List<Sortorder> list, String prop) {
182         if (list == null) {
183             return null;
184         }
185         for (Sortorder o : list) {
186             if (prop.equals(o.getProperty())) {
187                 return o;
188             }
189         }
190         return null;
191     }
192
193     private static Filter getFilter(@Nullable List<Filter> list, String prop) {
194         if (list == null) {
195             return null;
196         }
197         for (Filter f : list) {
198             if (prop.equals(f.getProperty())) {
199                 return f;
200             }
201         }
202         return null;
203     }
204
205     private static String fillTimeStamp(String value) {
206         int idx = value.lastIndexOf("*");
207         final String REPLACE = "0000-00-00T00:00:00.0Z";
208         String s = value.substring(0, idx) + REPLACE.substring(idx);
209         if (Integer.parseInt(s.substring(5, 7)) == 0) {
210             s = s.substring(0, 5) + "01-" + s.substring(8);
211         }
212         if (Integer.parseInt(s.substring(8, 10)) == 0) {
213             s = s.substring(0, 8) + "01" + s.substring(10);
214         }
215
216         return s;
217     }
218
219     /**
220      * convert timestamp with ending placeholder in filter to elasticsearch filter e.g. 2017* => gte:
221      * 2017-01-01T00:00:00Z, lt:2018-01-01T00:00:00Z
222      *
223      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
224      *
225      */
226     private static @Nullable QueryBuilder fromTimestampSearchFilter(String property, String value) {
227         if (!value.endsWith("*")) {
228             return null;
229         }
230         int idx = value.lastIndexOf("*");
231         String lowerEnd = fillTimeStamp(value);
232         String upperEnd = null;
233         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
234         Date dt = null;
235         try {
236             dt = converter.getDateFromNetconf(lowerEnd);
237         } catch (Exception e) {
238
239         }
240         if (dt == null) {
241             return null;
242         }
243         // property.substring(0,idx)+REPLACE.substring(idx+1);
244         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
245         c.setTime(dt);
246         int tmpvalue;
247         switch (idx) {
248             case 1: // (2*)
249                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
250                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
251                 break;
252             case 2: // (20*)
253                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
254                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
255                 break;
256             case 3: // (200*)
257                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
258                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
259                 break;
260             case 4: // (2000*)
261             case 5: // (2000-*)
262                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
263                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
264                 break;
265             case 6: // switch 10 months (2000-0* or 2000-1*)
266                 tmpvalue = c.get(Calendar.MONTH);
267                 if (tmpvalue < 9) {
268                     c.set(Calendar.MONTH, 9);
269                 } else {
270                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
271                     c.set(Calendar.MONTH, 0);
272                 }
273                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
274
275                 break;
276             case 7: // switch one month (2018-01* or 2018-01-*)
277             case 8:
278                 c.add(Calendar.MONTH, 1);
279                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
280                 break;
281             case 9: // (2018-01-0*)
282                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
283                 if (tmpvalue == 1) {
284                     c.set(Calendar.DAY_OF_MONTH, 10);
285                 } else if (tmpvalue == 10) {
286                     c.set(Calendar.DAY_OF_MONTH, 20);
287                 } else if (tmpvalue == 20) {
288                     if (c.getActualMaximum(Calendar.DAY_OF_MONTH) < 30) {
289                         c.set(Calendar.DAY_OF_MONTH, 1);
290                         c.add(Calendar.MONTH, 1);
291                     } else {
292                         c.set(Calendar.DAY_OF_MONTH, 30);
293                     }
294                 } else if (tmpvalue == 30) {
295                     c.set(Calendar.DAY_OF_MONTH, 1);
296                     c.add(Calendar.MONTH, 1);
297                 } else {
298                     break;
299                 }
300                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
301                 break;
302             case 10: // (2018-01-01*)
303             case 11: // (2018-01-01T*)
304                 c.add(Calendar.DAY_OF_MONTH, 1);
305                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
306                 break;
307             case 12: // (2018-01-01T1*)
308                 tmpvalue = c.get(Calendar.HOUR_OF_DAY);
309                 if (tmpvalue == 20) {
310                     c.set(Calendar.HOUR_OF_DAY, 0);
311                     c.add(Calendar.DAY_OF_MONTH, 1);
312                 } else {
313                     c.add(Calendar.HOUR_OF_DAY, 10);
314                 }
315                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
316                 break;
317             case 13: // (2018-01-01T11*)
318             case 14: // (2018-01-01T11-*)
319                 c.add(Calendar.HOUR_OF_DAY, 1);
320                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
321                 break;
322             case 15: // (2018-01-01T11-3*)
323                 c.add(Calendar.MINUTE, 10);
324                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
325                 break;
326             case 16: // (2018-01-01T11-32*)
327             case 17: // (2018-01-01T11-32-*)
328                 c.add(Calendar.MINUTE, 1);
329                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
330                 break;
331             case 18: // (2018-01-01T11-32-1*)
332                 c.add(Calendar.SECOND, 10);
333                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
334                 break;
335             case 19: // (2018-01-01T11-32-11*)
336             case 20: // (2018-01-01T11-32-11.*)
337                 c.add(Calendar.SECOND, 1);
338                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
339                 break;
340
341             default:
342                 break;
343         }
344
345         if (upperEnd == null) {
346             return null;
347         }
348         return QueryBuilders.rangeQuery(property).gte(lowerEnd).lt(upperEnd);
349
350     }
351
352     private static List<String> collectValues(Filter filter) {
353         List<String> values = new ArrayList<String>();
354         if (filter.getFiltervalue() != null) {
355             values.add(filter.getFiltervalue());
356         }
357         if (filter.getFiltervalues() != null) {
358             values.addAll(filter.getFiltervalues());
359         }
360         return values;
361     }
362
363     private static QueryBuilder fromFilter(@Nullable List<Filter> filters, String prefix) {
364         if (filters == null || filters.size() == 0) {
365             return QueryBuilders.matchAllQuery();
366
367         } else if (filters.size() == 1) {
368             String property = filters.get(0).getProperty();
369             List<String> values = collectValues(filters.get(0));
370             if ("id".equals(property)) {
371                 property = "_id";
372             }
373             if (values.size() == 1) {
374                 return getSinglePropertyQuery(property, values.get(0), prefix);
375             } else {
376                 BoolQueryBuilder bquery = new BoolQueryBuilder();
377                 for (String v : values) {
378                     bquery.should(getSinglePropertyQuery(property, v, prefix));
379                 }
380                 return bquery;
381
382             }
383         } else {
384             BoolQueryBuilder query = new BoolQueryBuilder();
385             for (Filter fi : filters) {
386                 String p = fi.getProperty();
387                 List<String> values = collectValues(fi);
388                 if ("id".equals(p)) {
389                     p = "_id";
390                 }
391                 if (values.size() == 1) {
392                     query.must(getSinglePropertyQuery(p, values.get(0), prefix));
393                 } else {
394                     BoolQueryBuilder tmpQuery = QueryBuilders.boolQuery();
395                     for (String v : values) {
396                         tmpQuery.should(getSinglePropertyQuery(p, v, prefix));
397                     }
398                     query.must(tmpQuery);
399                     tmpQuery = QueryBuilders.boolQuery();
400                 }
401             }
402             LOG.trace("Query result. {}", query.toJSON());
403             return query;
404         }
405     }
406
407     private static QueryBuilder getSinglePropertyQuery(String p, String v, String prefix) {
408         QueryBuilder query = null;
409         if (DbFilter.hasSearchParams(v)) {
410             if (p != null && timestampValueNames.contains(p.toLowerCase())) {
411                 query = fromTimestampSearchFilter(p, v);
412                 if (query == null) {
413                     query = QueryBuilders.regex(handlePrefix(prefix, p), DbFilter.createDatabaseRegex(v));
414                 }
415             } else {
416                 query = QueryBuilders.regex(handlePrefix(prefix, p), DbFilter.createDatabaseRegex(v));
417             }
418         } else if (DbFilter.isComparisonValid(v)) {
419             query = DbFilter.getRangeQuery(handlePrefix(prefix, p), v);
420             if (query == null) {
421                 query = QueryBuilders.matchQuery(handlePrefix(prefix, p), v);
422             }
423         } else {
424             query = QueryBuilders.matchQuery(handlePrefix(prefix, p), v);
425         }
426         return query;
427     }
428
429     private static String handlePrefix(String prefix, String p) {
430         return (prefix != null ? prefix : "") + p;
431     }
432
433 }