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