2  * ============LICENSE_START=======================================================
 
   3  * ONAP : ccsdk features
 
   4  * ================================================================================
 
   5  * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property.
 
   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
 
  12  *     http://www.apache.org/licenses/LICENSE-2.0
 
  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=========================================================
 
  22 package org.onap.ccsdk.features.sdnr.wt.dataprovider.database.elasticsearch.data.rpctypehelper;
 
  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;
 
  51 public class QueryByFilter {
 
  53     private static final Logger LOG = LoggerFactory.getLogger(DataObjectAcessorPm.class);
 
  54     private static final List<String> timestampValueNames = Arrays.asList("timestamp", "start", "end");
 
  56     private static List<Sortorder> emptySortOrderList = new ArrayList<>();
 
  57     private static List<Filter> emptyFilterList = new ArrayList<>();
 
  61     private long pageSize;
 
  62     private long fromPage;
 
  63     private List<Filter> filterList;
 
  64     private List<Sortorder> sortOrder;
 
  67      * Process input from RPC into Queries to database
 
  69      * @param input Input from RPC, for test it could be null
 
  71     public QueryByFilter(EntityInput input) {
 
  76             Pagination pagination = input.getPagination();
 
  77             if (pagination != null) {
 
  79                 Uint64 pageOrNull = YangHelper2.getUint64(pagination.getPage());
 
  80                 if (pageOrNull != null) {
 
  81                     page = pageOrNull.longValue();
 
  84                 Uint32 pageSizeOrNull = YangHelper2.getUint32(pagination.getSize());
 
  85                 if (pageSizeOrNull != null) {
 
  86                     pageSize = pageSizeOrNull.longValue();
 
  95         fromPage = (page - 1) * pageSize;
 
  96         if (fromPage < 0 || pageSize > 10000)
 
  97             throw new IllegalArgumentException("mismatching input parameters. From:" + fromPage + " size:" + pageSize);
 
  99         filterList = input == null ? null : YangHelper.getList(input.getFilter());
 
 100         if (filterList == null)
 
 101             filterList = emptyFilterList;
 
 102         sortOrder = input == null ? null : YangHelper.getList(input.getSortorder());
 
 103         if (sortOrder == null)
 
 104             sortOrder = emptySortOrderList;
 
 108     public QueryBuilder getQueryBuilderByFilter() {
 
 109         return getQueryBuilderByFilter("");
 
 112     public QueryBuilder getQueryBuilderByFilter(String prefix) {
 
 113         QueryBuilder queryBuilder = fromFilter(filterList, prefix).from(fromPage).size(pageSize);
 
 114         setSortOrder(queryBuilder, sortOrder, prefix);
 
 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));
 
 127             String msg = "no nodename in filter found ";
 
 129             throw new IllegalArgumentException(msg);
 
 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);
 
 142             query = QueryBuilders.matchAllQuery().setFullsizeRequest(doFullsizeRequest).aggregations(nodeKey).size(0);
 
 144         request.setQuery(query);
 
 148     public long getPage() {
 
 152     public long getPageSize() {
 
 156     public long getPageStartIndex() {
 
 161     public String toString() {
 
 162         return "QueryByFilter [page=" + page + ", pageSize=" + pageSize + ", fromPage=" + fromPage + ", filterList="
 
 163                 + filterList + ", sortOrder=" + sortOrder + "]";
 
 167      * Private and static implementations
 
 169     private static QueryBuilder setSortOrder(QueryBuilder query, @Nullable List<Sortorder> sortorder, String prefix) {
 
 170         if (sortorder != null && !sortorder.isEmpty()) {
 
 171             for (Sortorder so : sortorder) {
 
 172                 query.sort(handlePrefix(prefix, so.getProperty()), convert(so.getSortorder()));
 
 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;
 
 184     private static Sortorder getSortOrder(@Nullable List<Sortorder> list, String prop) {
 
 188         for (Sortorder o : list) {
 
 189             if (prop.equals(o.getProperty())) {
 
 196     private static Filter getFilter(@Nullable List<Filter> list, String prop) {
 
 200         for (Filter f : list) {
 
 201             if (prop.equals(f.getProperty())) {
 
 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);
 
 215         if (Integer.parseInt(s.substring(8, 10)) == 0) {
 
 216             s = s.substring(0, 8) + "01" + s.substring(10);
 
 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
 
 226      * 201* => 2010-01... 2020 .. 2018-* => 2018-01... <=> 2019-01
 
 229     private static @Nullable QueryBuilder fromTimestampSearchFilter(String property, String value) {
 
 230         if (!value.endsWith("*")) {
 
 233         int idx = value.lastIndexOf("*");
 
 234         String lowerEnd = fillTimeStamp(value);
 
 235         String upperEnd = null;
 
 236         NetconfTimeStamp converter = NetconfTimeStampImpl.getConverter();
 
 239             dt = converter.getDateFromNetconf(lowerEnd);
 
 240         } catch (Exception e) {
 
 246         // property.substring(0,idx)+REPLACE.substring(idx+1);
 
 247         Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
 
 252                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1000);
 
 253                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 256                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 100);
 
 257                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 260                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 10);
 
 261                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 265                 c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
 
 266                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 268             case 6: // switch 10 months (2000-0* or 2000-1*)
 
 269                 tmpvalue = c.get(Calendar.MONTH);
 
 271                     c.set(Calendar.MONTH, 9);
 
 273                     c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
 
 274                     c.set(Calendar.MONTH, 0);
 
 276                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 279             case 7: // switch one month (2018-01* or 2018-01-*)
 
 281                 c.add(Calendar.MONTH, 1);
 
 282                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 284             case 9: // (2018-01-0*)
 
 285                 tmpvalue = c.get(Calendar.DAY_OF_MONTH);
 
 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);
 
 295                         c.set(Calendar.DAY_OF_MONTH, 30);
 
 297                 } else if (tmpvalue == 30) {
 
 298                     c.set(Calendar.DAY_OF_MONTH, 1);
 
 299                     c.add(Calendar.MONTH, 1);
 
 303                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 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());
 
 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);
 
 316                     c.add(Calendar.HOUR_OF_DAY, 10);
 
 318                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 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());
 
 325             case 15: // (2018-01-01T11-3*)
 
 326                 c.add(Calendar.MINUTE, 10);
 
 327                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 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());
 
 334             case 18: // (2018-01-01T11-32-1*)
 
 335                 c.add(Calendar.SECOND, 10);
 
 336                 upperEnd = converter.getTimeStampAsNetconfString(c.getTime());
 
 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());
 
 348         if (upperEnd == null) {
 
 351         return QueryBuilders.rangeQuery(property).gte(lowerEnd).lt(upperEnd);
 
 355     private static List<String> collectValues(Filter filter) {
 
 356         List<String> values = new ArrayList<>();
 
 357         if (filter.getFiltervalue() != null) {
 
 358             values.add(filter.getFiltervalue());
 
 360         if (filter.getFiltervalues() != null) {
 
 361             values.addAll(filter.getFiltervalues());
 
 366     private static QueryBuilder fromFilter(@Nullable List<Filter> filters, String prefix) {
 
 367         if (filters == null || filters.isEmpty()) {
 
 368             return QueryBuilders.matchAllQuery();
 
 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)) {
 
 376             if (values.size() == 1) {
 
 377                 return getSinglePropertyQuery(property, values.get(0), prefix);
 
 379                 BoolQueryBuilder bquery = new BoolQueryBuilder();
 
 380                 for (String v : values) {
 
 381                     bquery.should(getSinglePropertyQuery(property, v, prefix));
 
 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)) {
 
 394                 if (values.size() == 1) {
 
 395                     query.must(getSinglePropertyQuery(p, values.get(0), prefix));
 
 397                     BoolQueryBuilder tmpQuery = QueryBuilders.boolQuery();
 
 398                     for (String v : values) {
 
 399                         tmpQuery.should(getSinglePropertyQuery(p, v, prefix));
 
 401                     query.must(tmpQuery);
 
 404             LOG.trace("Query result. {}", query.toJSON());
 
 409     private static QueryBuilder getSinglePropertyQuery(String p, String v, String prefix) {
 
 410         QueryBuilder query = null;
 
 411         if (DbFilter.hasSearchParams(v)) {
 
 412             if (p != null && timestampValueNames.contains(p.toLowerCase())) {
 
 413                 query = fromTimestampSearchFilter(p, v);
 
 415                     query = QueryBuilders.regex(handlePrefix(prefix, p), DbFilter.createDatabaseRegex(v));
 
 418                 query = QueryBuilders.regex(handlePrefix(prefix, p), DbFilter.createDatabaseRegex(v));
 
 420         } else if (DbFilter.isComparisonValid(v)) {
 
 421             query = DbFilter.getRangeQuery(handlePrefix(prefix, p), v);
 
 423                 query = QueryBuilders.matchQuery(handlePrefix(prefix, p), v);
 
 426             query = QueryBuilders.matchQuery(handlePrefix(prefix, p), v);
 
 431     private static String handlePrefix(String prefix, String p) {
 
 432         return (prefix != null ? prefix : "") + p;