1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.allanbank.mongodb.builder;
22
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.LinkedHashMap;
26 import java.util.Map;
27 import java.util.Set;
28
29 import com.allanbank.mongodb.bson.Document;
30 import com.allanbank.mongodb.bson.DocumentAssignable;
31 import com.allanbank.mongodb.bson.Element;
32 import com.allanbank.mongodb.bson.builder.ArrayBuilder;
33 import com.allanbank.mongodb.bson.builder.BuilderFactory;
34 import com.allanbank.mongodb.bson.builder.DocumentBuilder;
35 import com.allanbank.mongodb.bson.element.DocumentElement;
36 import com.allanbank.mongodb.bson.element.StringElement;
37 import com.allanbank.mongodb.bson.impl.EmptyDocument;
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 public class QueryBuilder implements DocumentAssignable {
74
75
76
77
78
79
80
81
82
83 public static Document and(final DocumentAssignable... criteria) {
84 if (criteria.length <= 0) {
85 return EmptyDocument.INSTANCE;
86 }
87 else if (criteria.length == 1) {
88 return criteria[0].asDocument();
89 }
90 else {
91
92
93
94
95 final Set<String> seen = new HashSet<String>();
96 DocumentBuilder optimized = BuilderFactory.start();
97 final DocumentBuilder docBuilder = BuilderFactory.start();
98 final ArrayBuilder arrayBuilder = docBuilder
99 .pushArray(LogicalOperator.AND.getToken());
100
101 for (final DocumentAssignable criterion : criteria) {
102 final Document subQuery = criterion.asDocument();
103
104 final Iterator<Element> iter = subQuery.iterator();
105 if (iter.hasNext()) {
106 arrayBuilder.addDocument(subQuery);
107
108 while ((optimized != null) && iter.hasNext()) {
109 final Element subQueryElement = iter.next();
110 if (seen.add(subQueryElement.getName())) {
111 optimized.add(subQueryElement);
112 }
113 else {
114 optimized = null;
115 }
116 }
117 }
118 }
119
120 if (optimized != null) {
121 return optimized.build();
122 }
123 return docBuilder.build();
124 }
125 }
126
127
128
129
130
131
132
133
134
135 public static Document nor(final DocumentAssignable... criteria) {
136
137 final DocumentBuilder docBuilder = BuilderFactory.start();
138 final ArrayBuilder arrayBuilder = docBuilder
139 .pushArray(LogicalOperator.NOR.getToken());
140
141 for (final DocumentAssignable criterion : criteria) {
142 final Document subQuery = criterion.asDocument();
143 if (subQuery.iterator().hasNext()) {
144 arrayBuilder.addDocument(subQuery);
145 }
146 }
147
148 return docBuilder.build();
149 }
150
151
152
153
154
155
156
157
158
159 public static Document not(final DocumentAssignable... criteria) {
160 final DocumentBuilder docBuilder = BuilderFactory.start();
161 final ArrayBuilder arrayBuilder = docBuilder
162 .pushArray(LogicalOperator.NOT.getToken());
163
164 for (final DocumentAssignable criterion : criteria) {
165 final Document subQuery = criterion.asDocument();
166 if (subQuery.iterator().hasNext()) {
167 arrayBuilder.addDocument(subQuery);
168 }
169 }
170
171 return docBuilder.build();
172 }
173
174
175
176
177
178
179
180
181
182 public static Document or(final DocumentAssignable... criteria) {
183 if (criteria.length <= 0) {
184 return EmptyDocument.INSTANCE;
185 }
186 else if (criteria.length == 1) {
187 return criteria[0].asDocument();
188 }
189 else {
190 final DocumentBuilder docBuilder = BuilderFactory.start();
191 final ArrayBuilder arrayBuilder = docBuilder
192 .pushArray(LogicalOperator.OR.getToken());
193
194 for (final DocumentAssignable criterion : criteria) {
195 final Document subQuery = criterion.asDocument();
196 if (subQuery.iterator().hasNext()) {
197 arrayBuilder.addDocument(subQuery);
198 }
199 }
200
201 return docBuilder.build();
202 }
203 }
204
205
206
207
208
209
210
211
212 public static ConditionBuilder where(final String field) {
213 return new QueryBuilder().whereField(field);
214 }
215
216
217 private final Map<String, ConditionBuilder> myConditions;
218
219
220 private String myQueryComment;
221
222
223 private Element myTextQuery;
224
225
226 private String myWhere;
227
228
229
230
231 public QueryBuilder() {
232 myConditions = new LinkedHashMap<String, ConditionBuilder>();
233
234 reset();
235 }
236
237
238
239
240
241
242
243
244
245 @Override
246 public Document asDocument() {
247 return build();
248 }
249
250
251
252
253
254
255 public Document build() {
256 final DocumentBuilder builder = BuilderFactory.start();
257
258 if (myQueryComment != null) {
259 builder.add(MiscellaneousOperator.COMMENT.getToken(),
260 myQueryComment);
261 }
262
263 if (myTextQuery != null) {
264 builder.add(myTextQuery);
265 }
266
267 for (final ConditionBuilder condBuilder : myConditions.values()) {
268 final Element condElement = condBuilder.buildFieldCondition();
269
270 if (condElement != null) {
271 builder.add(condElement);
272 }
273 }
274
275 if (myWhere != null) {
276 builder.addJavaScript(MiscellaneousOperator.WHERE.getToken(),
277 myWhere);
278 }
279
280 return builder.build();
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298 public QueryBuilder comment(final String comment) {
299 myQueryComment = comment;
300
301 return this;
302 }
303
304
305
306
307 public void reset() {
308 myConditions.clear();
309 myTextQuery = null;
310 myQueryComment = null;
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328 public QueryBuilder text(final String textSearchExpression) {
329 myTextQuery = new DocumentElement(
330 MiscellaneousOperator.TEXT.getToken(), new StringElement(
331 MiscellaneousOperator.SEARCH_MODIFIER,
332 textSearchExpression));
333
334 return this;
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357 public QueryBuilder text(final String textSearchExpression,
358 final String language) {
359 myTextQuery = new DocumentElement(
360 MiscellaneousOperator.TEXT.getToken(), new StringElement(
361 MiscellaneousOperator.SEARCH_MODIFIER,
362 textSearchExpression), new StringElement(
363 MiscellaneousOperator.LANGUAGE_MODIFIER, language));
364
365 return this;
366 }
367
368
369
370
371
372
373
374
375
376 public ConditionBuilder whereField(final String fieldName) {
377 ConditionBuilder builder = myConditions.get(fieldName);
378 if (builder == null) {
379 builder = new ConditionBuilder(fieldName, this);
380 myConditions.put(fieldName, builder);
381 }
382 return builder;
383 }
384
385
386
387
388
389
390
391
392 public QueryBuilder whereJavaScript(final String javaScript) {
393 myWhere = javaScript;
394
395 return this;
396 }
397 }