Organise imports to ONAP Java standards
[aai/search-data-service.git] / src / main / java / org / onap / aai / sa / searchdbabstraction / searchapi / SearchStatement.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 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.concurrent.atomic.AtomicBoolean;
27 import org.radeox.util.logging.Logger;
28
29 /**
30  * This class represents the structure of a search statement.
31  *
32  * <p>The expected JSON structure to represent a search statement is as follows:
33  *
34  * <p><pre>
35  *     {
36  *         "results-start": int,  - Optional: index of starting point in result set.
37  *         "results-size": int,   - Optional: maximum number of documents to include in result set.
38  *
39  *         "filter": {
40  *             { filter structure - see {@link Filter} }
41  *         },
42  *
43  *         "queries": [
44  *             { query structure - see {@link QueryStatement} },
45  *             { query structure - see {@link QueryStatement} },
46  *                              .
47  *                              .
48  *             { query structure - see {@link QueryStatement} },
49  *         ],
50  *
51  *         "aggregations": [
52  *             { aggregation structure - see {@link AggregationStatement} },
53  *             { aggregation structure - see {@link AggregationStatement} },
54  *                              .
55  *                              .
56  *             { aggregation structure - see {@link AggregationStatement} },
57  *         ]
58  *     }
59  * </pre>
60  */
61 public class SearchStatement {
62
63   /**
64    * Defines the filters that should be applied before running the
65    * actual queries.  This is optional.
66    */
67   private Filter filter;
68
69   /**
70    * The list of queries to be applied to the document store.
71    */
72   private Query[] queries;
73
74   /**
75    * The list of aggregations to be applied to the search
76    */
77   private Aggregation[] aggregations;
78
79   /**
80    * Defines the sort criteria to apply to the query result set.
81    * This is optional.
82    */
83   private Sort sort;
84
85   @JsonProperty("results-start")
86   private Integer resultsStart;
87
88   @JsonProperty("results-size")
89   private Integer size;
90
91   public Filter getFilter() {
92     return filter;
93   }
94
95   public void setFilter(Filter filter) {
96     this.filter = filter;
97   }
98
99   public Query[] getQueries() {
100     return queries;
101   }
102
103   public void setQueries(Query[] queries) {
104     this.queries = queries;
105   }
106
107   public Sort getSort() {
108     return sort;
109   }
110
111   public void setSort(Sort sort) {
112     this.sort = sort;
113   }
114
115   public boolean isFiltered() {
116     return filter != null;
117   }
118
119   public Aggregation[] getAggregations() {
120     return aggregations;
121   }
122
123   public void setAggregations(Aggregation[] aggregations) {
124     this.aggregations = aggregations;
125   }
126
127   public boolean hasAggregations() {
128     return aggregations != null && aggregations.length > 0;
129   }
130
131   public Integer getFrom() {
132     return resultsStart;
133   }
134
135   public void setFrom(Integer from) {
136     this.resultsStart = from;
137   }
138
139   public Integer getSize() {
140     return size;
141   }
142
143   public void setSize(Integer size) {
144     this.size = size;
145   }
146
147   /**
148    * This method returns a string which represents this statement in syntax
149    * that is understandable by ElasticSearch and is suitable for inclusion
150    * in an ElasticSearch query string.
151    *
152    * @return - ElasticSearch syntax string.
153    */
154   public String toElasticSearch() {
155
156     StringBuilder sb = new StringBuilder();
157     List<QueryStatement> notMatchQueries = new ArrayList<QueryStatement>();
158     List<QueryStatement> mustQueries = new ArrayList<QueryStatement>();
159     List<QueryStatement> shouldQueries = new ArrayList<QueryStatement>();
160
161     createQueryLists(queries, mustQueries, shouldQueries, notMatchQueries);
162
163     sb.append("{");
164
165     sb.append("\"version\": true,");
166
167     // If the client has specified an index into the results for the first
168     // document in the result set then include that in the ElasticSearch
169     // query.
170     if (resultsStart != null) {
171       sb.append("\"from\": ").append(resultsStart).append(", ");
172     }
173
174     // If the client has specified a maximum number of documents to be returned
175     // in the result set then include that in the ElasticSearch query.
176     if (size != null) {
177       sb.append("\"size\": ").append(size).append(", ");
178     }
179
180     sb.append("\"query\": {");
181     sb.append("\"bool\": {");
182
183     sb.append("\"must\": [");
184     AtomicBoolean firstQuery = new AtomicBoolean(true);
185     for (QueryStatement query : mustQueries) {
186
187       if (!firstQuery.compareAndSet(true, false)) {
188         sb.append(", ");
189       }
190
191       sb.append(query.toElasticSearch());
192     }
193     sb.append("], ");
194
195     sb.append("\"should\": [");
196
197     firstQuery = new AtomicBoolean(true);
198     for (QueryStatement query : shouldQueries) {
199
200       if (!firstQuery.compareAndSet(true, false)) {
201         sb.append(", ");
202       }
203
204       sb.append(query.toElasticSearch());
205     }
206
207     sb.append("],"); // close should list
208
209     sb.append("\"must_not\": [");
210     firstQuery.set(true);
211     for (QueryStatement query : notMatchQueries) {
212       sb.append(query.toElasticSearch());
213     }
214     sb.append("]");
215
216     // Add the filter stanza, if one is required.
217     if (isFiltered()) {
218       sb.append(", \"filter\": ").append(filter.toElasticSearch());
219     }
220
221     sb.append("}"); // close bool clause
222     sb.append("}"); // close query clause
223
224     // Add the sort directive, if one is required.
225     if (sort != null) {
226       sb.append(", \"sort\": ").append(sort.toElasticSearch());
227     }
228
229     // Add aggregations
230     if (hasAggregations()) {
231       sb.append(", \"aggs\": {");
232
233       for (int i = 0; i < aggregations.length; i++) {
234         if (i > 0) {
235           sb.append(",");
236         }
237         sb.append(aggregations[i].toElasticSearch());
238       }
239
240       sb.append("}");
241     }
242
243     sb.append("}");
244
245     Logger.debug("Generated raw ElasticSearch query statement: " + sb.toString());
246     return sb.toString();
247   }
248
249   private void createQueryLists(Query[] queries, List<QueryStatement> mustList,
250                                 List<QueryStatement> mayList, List<QueryStatement> mustNotList) {
251
252     for (Query query : queries) {
253
254       if (query.isMust()) {
255
256         if (query.getQueryStatement().isNotMatch()) {
257           mustNotList.add(query.getQueryStatement());
258         } else {
259           mustList.add(query.getQueryStatement());
260         }
261       } else {
262
263         if (query.getQueryStatement().isNotMatch()) {
264           mustNotList.add(query.getQueryStatement());
265         } else {
266           mayList.add(query.getQueryStatement());
267         }
268       }
269     }
270   }
271
272
273   @Override
274   public String toString() {
275
276     StringBuilder sb = new StringBuilder();
277
278     sb.append("SEARCH STATEMENT: {");
279
280     if (size != null) {
281       sb.append("from: ").append(resultsStart).append(", size: ").append(size).append(", ");
282     }
283
284     if (filter != null) {
285       sb.append("filter: ").append(filter.toString()).append(", ");
286     }
287
288     sb.append("queries: [");
289     AtomicBoolean firstQuery = new AtomicBoolean(true);
290     if (queries != null) {
291       for (Query query : queries) {
292
293         if (!firstQuery.compareAndSet(true, false)) {
294           sb.append(", ");
295         }
296         sb.append(query.toString());
297       }
298     }
299     sb.append("]");
300
301     sb.append("aggregations: [");
302     firstQuery = new AtomicBoolean(true);
303
304     if (aggregations != null) {
305       for (Aggregation agg : aggregations) {
306
307         if (!firstQuery.compareAndSet(true, false)) {
308           sb.append(", ");
309         }
310         sb.append(agg.toString());
311       }
312     }
313     sb.append("]");
314
315     sb.append("]}");
316
317     return sb.toString();
318   }
319
320 }