-
Notifications
You must be signed in to change notification settings - Fork 215
/
QueryParser.java
148 lines (133 loc) · 6.57 KB
/
QueryParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.thingsearch.service.persistence.query;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.rql.model.ParserException;
import org.eclipse.ditto.rql.model.predicates.PredicateParser;
import org.eclipse.ditto.rql.parser.RqlPredicateParser;
import org.eclipse.ditto.rql.parser.thingsearch.RqlOptionParser;
import org.eclipse.ditto.rql.query.Query;
import org.eclipse.ditto.rql.query.QueryBuilder;
import org.eclipse.ditto.rql.query.QueryBuilderFactory;
import org.eclipse.ditto.rql.query.criteria.Criteria;
import org.eclipse.ditto.rql.query.criteria.CriteriaFactory;
import org.eclipse.ditto.rql.query.expression.ThingsFieldExpressionFactory;
import org.eclipse.ditto.rql.query.filter.QueryFilterCriteriaFactory;
import org.eclipse.ditto.thingsearch.api.commands.sudo.StreamThings;
import org.eclipse.ditto.thingsearch.api.commands.sudo.SudoCountThings;
import org.eclipse.ditto.thingsearch.api.query.filter.ParameterOptionVisitor;
import org.eclipse.ditto.thingsearch.model.signals.commands.exceptions.InvalidOptionException;
import org.eclipse.ditto.thingsearch.model.signals.commands.query.QueryThings;
import org.eclipse.ditto.thingsearch.model.signals.commands.query.ThingSearchQueryCommand;
import org.eclipse.ditto.thingsearch.service.persistence.query.validation.QueryCriteriaValidator;
/**
* Create Query objects from search commands.
*/
public final class QueryParser {
private final QueryFilterCriteriaFactory queryFilterCriteriaFactory;
private final ThingsFieldExpressionFactory fieldExpressionFactory;
private final QueryBuilderFactory queryBuilderFactory;
private final RqlOptionParser rqlOptionParser;
private final QueryCriteriaValidator queryCriteriaValidator;
private QueryParser(final ThingsFieldExpressionFactory fieldExpressionFactory,
final PredicateParser predicateParser,
final QueryBuilderFactory queryBuilderFactory,
final QueryCriteriaValidator queryCriteriaValidator) {
this.queryFilterCriteriaFactory = QueryFilterCriteriaFactory.of(fieldExpressionFactory, predicateParser);
this.fieldExpressionFactory = fieldExpressionFactory;
this.queryBuilderFactory = queryBuilderFactory;
this.queryCriteriaValidator = queryCriteriaValidator;
rqlOptionParser = new RqlOptionParser();
}
/**
* Create a QueryFactory.
*
* @param fieldExpressionFactory a factory to retrieve things field expressions.
* @param queryBuilderFactory a factory to create a query builder.
* @param queryCriteriaValidator a validator for queries.
* @return the query factory.
*/
public static QueryParser of(final ThingsFieldExpressionFactory fieldExpressionFactory,
final QueryBuilderFactory queryBuilderFactory,
final QueryCriteriaValidator queryCriteriaValidator) {
return new QueryParser(fieldExpressionFactory, RqlPredicateParser.getInstance(), queryBuilderFactory,
queryCriteriaValidator);
}
/**
* Parses a search command into a query.
*
* @param command the search command.
* @return the query.
*/
public CompletionStage<Query> parse(final ThingSearchQueryCommand<?> command) {
final Criteria criteria = parseCriteria(command);
final Query query;
if (command instanceof final QueryThings queryThings) {
final QueryBuilder queryBuilder = queryBuilderFactory.newBuilder(criteria);
queryThings.getOptions()
.map(optionStrings -> String.join(",", optionStrings))
.ifPresent(options -> setOptions(options, queryBuilder, command.getDittoHeaders()));
query = queryBuilder.build();
} else if (command instanceof final StreamThings streamThings) {
final QueryBuilder queryBuilder = queryBuilderFactory.newUnlimitedBuilder(criteria);
streamThings.getSort().ifPresent(sort -> setOptions(sort, queryBuilder, command.getDittoHeaders()));
query = queryBuilder.build();
} else {
query = queryBuilderFactory.newUnlimitedBuilder(criteria).build();
}
return queryCriteriaValidator.validateQuery(command, query);
}
private Criteria parseCriteria(final ThingSearchQueryCommand<?> command) {
final DittoHeaders headers = command.getDittoHeaders();
final Set<String> namespaces = command.getNamespaces().orElse(null);
final String filter = command.getFilter().orElse(null);
if (namespaces == null) {
return queryFilterCriteriaFactory.filterCriteria(filter, command.getDittoHeaders());
} else {
return queryFilterCriteriaFactory.filterCriteriaRestrictedByNamespaces(filter, headers, namespaces);
}
}
/**
* Parses a SudoCountThings command into a query.
*
* @param sudoCountThings the command.
* @return the query.
*/
public CompletionStage<Query> parseSudoCountThings(final SudoCountThings sudoCountThings) {
final DittoHeaders headers = sudoCountThings.getDittoHeaders();
final String filters = sudoCountThings.getFilter().orElse(null);
final Criteria criteria = queryFilterCriteriaFactory.filterCriteria(filters, headers);
return CompletableFuture.completedFuture(queryBuilderFactory.newUnlimitedBuilder(criteria).build());
}
/**
* @return the criteria factory.
*/
public CriteriaFactory getCriteriaFactory() {
return queryFilterCriteriaFactory.toCriteriaFactory();
}
private void setOptions(final String options, final QueryBuilder queryBuilder, final DittoHeaders headers) {
try {
final ParameterOptionVisitor visitor = new ParameterOptionVisitor(fieldExpressionFactory, queryBuilder);
visitor.visitAll(rqlOptionParser.parse(options));
} catch (final ParserException | IllegalArgumentException e) {
throw InvalidOptionException.newBuilder()
.message(e.getMessage())
.cause(e)
.dittoHeaders(headers)
.build();
}
}
}