-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathcriteria.html
588 lines (456 loc) · 71.4 KB
/
criteria.html
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"-->
<link rel="stylesheet" href="/gfx/bootstrap.min.css">
<link rel="stylesheet" href="/gfx/main.css">
<link rel="stylesheet" href="/gfx/code.css">
<title>Criteria</title>
</head>
<body class="page">
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-PMJSKV"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-PMJSKV');</script>
<!-- End Google Tag Manager -->
<header>
<div class="container">
<a href="/">Immutables</a> ←
<h1>Criteria <iframe src="https://ghbtns.com/github-btn.html?user=immutables&repo=immutables&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
</h1>
</div>
</header>
<aside id="toc"></aside>
<section class="documentation">
<h2 id="overview">Overview</h2>
<p>The focus of Immutables Criteria is to provide database agnostic and efficient API for storing, querying and modifying documents expressed as immutable objects.</p>
<h3 id="features">Features</h3>
<ul>
<li><strong>Expressive and type-safe API</strong> Compile-type validation of the query</li>
<li><strong>Dynamic</strong> Combine predicates at runtime based on some logic</li>
<li><strong>Data-source agnostic</strong> Define criteria once and apply to different data-sources (Map, JDBC, Mongo, Elastic etc.)</li>
<li><strong>Blocking / asynchronous operations</strong> Generated repositories allow querying data in blocking, non-blocking and <a href="https://www.reactive-streams.org/">reactive</a> fashion</li>
</ul>
<h3 id="requirements">Requirements</h3>
<p>Criteria API requires JDK 8 (or later) plus backend specific dependencies (like mongo java driver).</p>
<h2 id="quick-start">Quick Start</h2>
<p>1) Add criteria module dependency (on the top of <a href="getstarted.html">existing</a> immutables annotation processor)</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- Maven dependency --></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.immutables<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>criteria-inmemory<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.10.1<span class="nt"></version></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
<p>or gradle</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Gradle dependecy</span>
<span class="n">dependencies</span> <span class="o">{</span>
<span class="n">implementation</span> <span class="s1">'org.immutables:criteria-inmemory:2.10.1'</span>
<span class="o">}</span>
</code></pre></div></div>
<p>2) Define a model with two annotations present <code class="language-plaintext highlighter-rouge">@Criteria</code> and <code class="language-plaintext highlighter-rouge">@Criteria.Repository</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Value</span><span class="o">.</span><span class="na">Immutable</span>
<span class="nd">@Criteria</span> <span class="c1">// generate criteria</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Repository</span> <span class="c1">// means generate repository (different from @Criteria)</span>
<span class="kd">interface</span> <span class="nc">Person</span> <span class="o">{</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Id</span>
<span class="nc">String</span> <span class="nf">id</span><span class="o">();</span>
<span class="nc">String</span> <span class="nf">fullName</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>3) Instantiate a backend (we’ll use simple <code class="language-plaintext highlighter-rouge">InMemoryBackend</code>) and perform some CRUD operations:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// instantiate a backend</span>
<span class="nc">Backend</span> <span class="n">backend</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">InMemoryBackend</span><span class="o">();</span>
<span class="c1">// attach repository to the backend</span>
<span class="nc">PersonRepository</span> <span class="n">repository</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PersonRepository</span><span class="o">(</span><span class="n">backend</span><span class="o">);</span>
<span class="c1">// insert some documents</span>
<span class="n">repository</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="nc">ImmutablePerson</span><span class="o">.</span><span class="na">builder</span><span class="o">().</span><span class="na">id</span><span class="o">(</span><span class="s">"id1"</span><span class="o">).</span><span class="na">fullName</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">build</span><span class="o">());</span>
<span class="n">repository</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="nc">ImmutablePerson</span><span class="o">.</span><span class="na">builder</span><span class="o">().</span><span class="na">id</span><span class="o">(</span><span class="s">"id2"</span><span class="o">).</span><span class="na">fullName</span><span class="o">(</span><span class="s">"Mary"</span><span class="o">).</span><span class="na">build</span><span class="o">());</span>
<span class="c1">// query</span>
<span class="nc">Person</span> <span class="n">john</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">)).</span><span class="na">fetch</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="nc">Person</span> <span class="n">mary</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">isNot</span><span class="o">(</span><span class="s">"John"</span><span class="o">)).</span><span class="na">fetch</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
</code></pre></div></div>
<h2 id="introduction">Introduction</h2>
<p>Criteria module uses several abstractions which are useful to understand. Below are the most important ones:</p>
<ul>
<li><a href="#criteria">Criteria</a> code-generated DSL to query a model.</li>
<li><a href="#repository">Repository</a> Data Access API to perform queries, updates, pub/sub or other operations. Uses <em>Backend</em> and <em>Criteria</em>.</li>
<li><a href="#backend">Backend</a> adapter to a data-source (database). Uses vendor specific API and transforms queries/operations into native calls.</li>
</ul>
<h2 id="criteria">Criteria</h2>
<p>In order to enable criteria generation add <code class="language-plaintext highlighter-rouge">@Criteria</code> annotation to any existing immutable interface or abstract class. Criteria will be generated as a class with a <code class="language-plaintext highlighter-rouge">Criteria</code> suffix in the same package.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Value</span><span class="o">.</span><span class="na">Immutable</span>
<span class="nd">@Criteria</span> <span class="c1">// generate criteria</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Repository</span> <span class="c1">// means generate repository (different from @Criteria)</span>
<span class="kd">interface</span> <span class="nc">Person</span> <span class="o">{</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Id</span>
<span class="nc">String</span> <span class="nf">id</span><span class="o">();</span>
<span class="nc">String</span> <span class="nf">fullName</span><span class="o">();</span>
<span class="nc">Optional</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="nf">nickName</span><span class="o">();</span>
<span class="kt">int</span> <span class="nf">age</span><span class="o">();</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">Pet</span><span class="o">></span> <span class="nf">pets</span><span class="o">();</span>
<span class="nc">Optional</span><span class="o"><</span><span class="nc">Friend</span><span class="o">></span> <span class="nf">bestFriend</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Value</span><span class="o">.</span><span class="na">Immutable</span>
<span class="nd">@Criteria</span>
<span class="kd">interface</span> <span class="nc">Pet</span> <span class="o">{</span>
<span class="kd">enum</span> <span class="nc">PetType</span> <span class="o">{</span><span class="n">parrot</span><span class="o">,</span> <span class="n">panda</span><span class="o">,</span> <span class="n">iguana</span><span class="o">,</span> <span class="n">gecko</span><span class="o">}</span>
<span class="nc">PetType</span> <span class="nf">type</span><span class="o">();</span>
<span class="nc">String</span> <span class="nf">name</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Value</span><span class="o">.</span><span class="na">Immutable</span>
<span class="nd">@Criteria</span>
<span class="kd">interface</span> <span class="nc">Friend</span> <span class="o">{</span>
<span class="nc">String</span> <span class="nf">hobby</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Generated <code class="language-plaintext highlighter-rouge">PersonCriteria</code> class closely follows <code class="language-plaintext highlighter-rouge">Person</code> model and allows type-safe queries. Criteria objects are immutable and can be stored as constants, serialized or otherwise safely passed around. They have methods corresponding to document attributes and relevant matchers (attribute predicates).</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// basic query by id</span>
<span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">in</span><span class="o">(</span><span class="s">"id1"</span><span class="o">,</span> <span class="s">"id2"</span><span class="o">,</span> <span class="s">"id3"</span><span class="o">);</span>
<span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">notIn</span><span class="o">(</span><span class="s">"bad_id"</span><span class="o">);</span>
<span class="c1">// query on Strings, Comparables, Optionals and other Criterias</span>
<span class="n">person</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">)</span> <span class="c1">// basic equal</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">isNot</span><span class="o">(</span><span class="s">"Mary"</span><span class="o">)</span> <span class="c1">// not equal</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="s">"Smith"</span><span class="o">)</span> <span class="c1">// string condition</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="mf">3.1415</span><span class="no">D</span><span class="o">)</span> <span class="c1">// ERROR! will not compile since fullName is String (not double)</span>
<span class="o">.</span><span class="na">nickName</span><span class="o">.</span><span class="na">isPresent</span><span class="o">()</span> <span class="c1">// for Optional attribute</span>
<span class="o">.</span><span class="na">nickName</span><span class="o">.</span><span class="na">startsWith</span><span class="o">(</span><span class="s">"Adam"</span><span class="o">)</span> <span class="c1">// special matcher Optional<String> which is intersetion type between OptionalMatcher and StringMatcher</span>
<span class="o">.</span><span class="na">pets</span><span class="o">.</span><span class="na">notEmpty</span><span class="o">()</span> <span class="c1">// condition on an Iterable</span>
<span class="o">.</span><span class="na">active</span><span class="o">.</span><span class="na">isTrue</span><span class="o">()</span> <span class="c1">// boolean</span>
<span class="o">.</span><span class="na">or</span><span class="o">()</span> <span class="c1">// disjunction (equivalent to logical OR)</span>
<span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">atLeast</span><span class="o">(</span><span class="mi">21</span><span class="o">)</span> <span class="c1">// comparable attribute</span>
<span class="o">.</span><span class="na">or</span><span class="o">()</span>
<span class="o">.</span><span class="na">not</span><span class="o">(</span><span class="n">p</span> <span class="o">-></span> <span class="n">p</span><span class="o">.</span><span class="na">nickName</span><span class="o">.</span><span class="na">hasLength</span><span class="o">(</span><span class="mi">4</span><span class="o">));</span> <span class="c1">// negation on a Optional<String> attribute</span>
<span class="o">.</span><span class="na">bestFriend</span><span class="o">.</span><span class="na">value</span><span class="o">().</span><span class="na">hobby</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="s">"ing"</span><span class="o">)</span> <span class="c1">// chaining criterias on other entities like Optional<Friend></span>
<span class="c1">// apply specific predicate to elements of a collection</span>
<span class="n">person</span>
<span class="o">.</span><span class="na">pets</span><span class="o">.</span><span class="na">none</span><span class="o">().</span><span class="na">type</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="nc">Pet</span><span class="o">.</span><span class="na">PetType</span><span class="o">.</span><span class="na">iguana</span><span class="o">)</span> <span class="c1">// no Iguanas</span>
<span class="o">.</span><span class="na">or</span><span class="o">()</span>
<span class="o">.</span><span class="na">pets</span><span class="o">.</span><span class="na">any</span><span class="o">().</span><span class="na">name</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"fluffy"</span><span class="o">);</span> <span class="c1">// person has a pet which sounds like fluffy</span>
</code></pre></div></div>
<p>You will need to add <code class="language-plaintext highlighter-rouge">@Criteria</code> to all classes to be queried. For example, to filter on <code class="language-plaintext highlighter-rouge">Person.pets.name</code>,
<code class="language-plaintext highlighter-rouge">Pet</code> class needs to have <code class="language-plaintext highlighter-rouge">@Criteria</code> annotation (otherwise generic <code class="language-plaintext highlighter-rouge">ObjectMatcher</code> is used).</p>
<h3 id="query-dsl-syntax">Query DSL Syntax</h3>
<p>In the previous query example you will notice that there are no <code class="language-plaintext highlighter-rouge">and</code> statements (conjunctions) that is because criteria uses
<a href="https://en.wikipedia.org/wiki/Disjunctive_normal_form">Disjunctive Normal Form</a> (in short DNF). By default, statements are
combined using logical <code class="language-plaintext highlighter-rouge">and</code> (<a href="https://en.wikipedia.org/wiki/Logical_conjunction">conjunction</a>) unless <code class="language-plaintext highlighter-rouge">or</code> (<a href="https://en.wikipedia.org/wiki/Logical_disjunction">disjunction</a>) is explicitly used.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Some examples of Query DSL</span>
<span class="c1">// left side (DSL) // right side SQL equivalent</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">)</span> <span class="c1">// fullName = 'John'</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">21</span><span class="o">)</span> <span class="c1">// fullName = 'John' AND age > 21</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">21</span><span class="o">).</span><span class="na">nickName</span><span class="o">.</span><span class="na">isPresent</span><span class="o">()</span> <span class="c1">// fullName = 'John' AND age > 21 AND nickName != null</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">or</span><span class="o">().</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"Mary"</span><span class="o">)</span> <span class="c1">// fullName = 'John' OR fullName = 'Mary'</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">21</span><span class="o">)</span>
<span class="o">.</span><span class="na">or</span><span class="o">()</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"Mary"</span><span class="o">)</span> <span class="c1">// (fullName = 'John' AND age > 21) OR fullName = 'Mary'</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">21</span><span class="o">)</span>
<span class="o">.</span><span class="na">or</span><span class="o">()</span>
<span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"Mary"</span><span class="o">).</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">22</span><span class="o">)</span> <span class="c1">// (fullName = 'John' AND age > 21) OR (fullName = 'Mary' AND age > 22)</span>
</code></pre></div></div>
<h4 id="more-complex-logical-expressions">More complex logical expressions</h4>
<p>For more complex expressions, one can still combine criterias using <code class="language-plaintext highlighter-rouge">and</code>s / <code class="language-plaintext highlighter-rouge">or</code>s / <code class="language-plaintext highlighter-rouge">not</code>s. Boolean algebra methods
allow composition of existing criterias with each other using <code class="language-plaintext highlighter-rouge">and</code> / <code class="language-plaintext highlighter-rouge">or</code> / <code class="language-plaintext highlighter-rouge">not</code> logic.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// fullName = 'John' AND (age > 22 OR nickName != null)</span>
<span class="n">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John"</span><span class="o">).</span><span class="na">and</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">22</span><span class="o">).</span><span class="na">or</span><span class="o">().</span><span class="na">nickName</span><span class="o">.</span><span class="na">isPresent</span><span class="o">())</span>
</code></pre></div></div>
<p>Since criteria objects are immutable one can safely pass them as variables, constants or function arguments.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">PersonCriteria</span> <span class="n">crit</span> <span class="o">=</span> <span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">;</span>
<span class="n">drinkingAge</span> <span class="o">=</span> <span class="n">crit</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">atLeast</span><span class="o">(</span><span class="mi">21</span><span class="o">);</span>
<span class="n">hasNickname</span> <span class="o">=</span> <span class="n">crit</span><span class="o">.</span><span class="na">nickName</span><span class="o">.</span><span class="na">isPresent</span><span class="o">()</span>
<span class="n">isActive</span> <span class="o">=</span> <span class="n">crit</span><span class="o">.</span><span class="na">isActive</span><span class="o">.</span><span class="na">isTrue</span><span class="o">();</span>
<span class="c1">// ...</span>
<span class="c1">// (age > 21 OR nickName != null) AND isActive = true</span>
<span class="k">return</span> <span class="n">drinkingAge</span><span class="o">.</span><span class="na">or</span><span class="o">(</span><span class="n">hasNickname</span><span class="o">).</span><span class="na">and</span><span class="o">(</span><span class="n">isActive</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="repository">Repository</h3>
<p>Repository is a User facing API to perform queries, updates, pub/sub or other CRUD operations (think data-access abstraction).
Similarly to criteria, repositories are auto-generated when <code class="language-plaintext highlighter-rouge">@Criteria.Repository</code> annotation is added to immutables class.
User has the option to customize repository generation by using facets.</p>
<p>Repositories delegate all operations to the Backend (more on that later).</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// add insert / find / delete / watch operations which return rxjava types</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Repository</span><span class="o">(</span><span class="n">facets</span> <span class="o">=</span> <span class="o">{</span><span class="nc">RxJavaReadable</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="nc">RxJavaWritable</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="nc">RxJavaWatchable</span><span class="o">.</span><span class="na">class</span><span class="o">})</span>
<span class="kd">interface</span> <span class="nc">Person</span> <span class="o">{</span>
<span class="o">}</span>
<span class="c1">// query datasource and return reactive type: Flowable</span>
<span class="nc">Flowable</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="n">persons</span> <span class="o">=</span> <span class="n">repository</span>
<span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">atLeast</span><span class="o">(</span><span class="mi">33</span><span class="o">))</span>
<span class="o">.</span><span class="na">orderBy</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">asc</span><span class="o">())</span>
<span class="o">.</span><span class="na">offset</span><span class="o">(</span><span class="mi">20</span><span class="o">)</span>
<span class="o">.</span><span class="na">limit</span><span class="o">(</span><span class="mi">10</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">();</span> <span class="c1">// return rxjava flowable because of RxJavaReadable facet</span>
<span class="c1">// unbounded stream of events using watch API (if backend supports it)</span>
<span class="nc">Flowable</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="n">persons</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">watcher</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">active</span><span class="o">.</span><span class="na">isFalse</span><span class="o">()).</span><span class="na">watch</span><span class="o">();</span>
</code></pre></div></div>
<p>By default, table (collection, index etc.) name is derived from simple class name (<code class="language-plaintext highlighter-rouge">MyClass</code> resolves to <code class="language-plaintext highlighter-rouge">myClass</code> table). It is possible to override this behaviour using annotation (<code class="language-plaintext highlighter-rouge">@Criteria.Repository(name ="custom")</code>) as well as
by registring custom name resolution strategy (see <a href="https://github.com/immutables/immutables/blob/master/criteria/common/src/org/immutables/criteria/backend/ContainerNaming.java">ContainerNaming</a> interface). The later is done during backend instantiation.</p>
<h3 id="facet">Facet</h3>
<p>Facets allow fine-tuning of repository behaviour. They (mostly) serve two purposes: define a set of operations supported by repository (like
read, write, watch) and control execution model of the repository (sync / async / reactive).</p>
<p>Several implementatins for execution model are available out of the box:</p>
<ul>
<li>Reactive streams. Returning <a href="https://www.reactive-streams.org/reactive-streams-1.0.2-javadoc/org/reactivestreams/Publisher.html">Publisher</a></li>
<li>Synchronous. Returning List / Optional / void / etc.</li>
<li>Asyncronous. Returning <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html">CompletionStage</a></li>
<li><a href="https://github.com/ReactiveX/RxJava">RxJava</a>. Returning <a href="http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Flowable.html">Flowable</a> / <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html">Single</a> / <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Maybe.html">Maybe</a>.</li>
<li><a href="https://projectreactor.io">Project Reactor</a>. Returning <a href="https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html">Flux</a> / <a href="https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html">Mono</a>.</li>
</ul>
<h3 id="querying">Querying</h3>
<p>Add one of the <code class="language-plaintext highlighter-rouge">*Readable</code> facets for query operations to become available.</p>
<p>Currently <code class="language-plaintext highlighter-rouge">Readable</code> allows filter / select / order / limit / offset operations.</p>
<h4 id="projections">Projections</h4>
<p>Use <code class="language-plaintext highlighter-rouge">select</code> operation to reduce number of attributes returned by the backend. The concept is similar to <a href="https://en.wikipedia.org/wiki/Projection_(relational_algebra)">projection</a>
in relational algebra.</p>
<p>To preserve type-safety, basic projection requires a mapping function. Mapping function argument types match individual types of the projection(s) in <code class="language-plaintext highlighter-rouge">select</code> operation (eg. <code class="language-plaintext highlighter-rouge">Optional<String></code>).
One can pass lambda function or method reference to transform incoming value(s). Currently mapping function can have up to 5 arguments. If projection on more than 5 fields is necessary use <code class="language-plaintext highlighter-rouge">Tuple</code> (see below).</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">repository</span>
<span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">atLeast</span><span class="o">(</span><span class="mi">33</span><span class="o">))</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">nickName</span><span class="o">,</span> <span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">)</span> <span class="c1">// project two fields of person: nickName and age</span>
<span class="o">.</span><span class="na">map</span><span class="o">((</span><span class="n">nickName</span><span class="o">,</span> <span class="n">age</span><span class="o">)</span> <span class="o">-></span> <span class="n">nickName</span><span class="o">.</span><span class="na">orElse</span><span class="o">(</span><span class="kc">null</span><span class="o">)</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="o">(</span><span class="n">age</span> <span class="o">-</span> <span class="mi">10</span><span class="o">))</span> <span class="c1">// map operator required after projection. Note that nickName is Optional<String> and age is of type Integer</span>
<span class="c1">//.map((nickName, age) -> NickNameAndAge::new) // alternative with method reference</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">();</span>
</code></pre></div></div>
<p>When list of attributes is unknown at compile time or when default mapping function can’t be used (eg. due to number of arguments threshold) use generic <code class="language-plaintext highlighter-rouge">Tuple</code> in projection. <code class="language-plaintext highlighter-rouge">select(Iterable)</code> method overload will return <code class="language-plaintext highlighter-rouge">Tuple</code>.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Projection</span><span class="o"><?>></span> <span class="n">projections</span> <span class="o">=</span> <span class="o">....;</span> <span class="c1">// build list of projections</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">repository</span>
<span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">atLeast</span><span class="o">(</span><span class="mi">33</span><span class="o">))</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">nickName</span><span class="o">,</span> <span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">))</span> <span class="c1">// select(Iterable) method overload</span>
<span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">tuple</span> <span class="o">-></span> <span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">nickName</span><span class="o">).</span><span class="na">orElse</span><span class="o">(</span><span class="kc">null</span><span class="o">)</span> <span class="o">+</span> <span class="o">(</span><span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">)</span> <span class="o">-</span> <span class="mi">10</span><span class="o">))</span> <span class="c1">// using single argument mapper with Tuple API</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">();</span>
</code></pre></div></div>
<p>When possible, prefer using basic <code class="language-plaintext highlighter-rouge">select</code> variant of projection (as opposed to <code class="language-plaintext highlighter-rouge">Tuple</code>) since it enforces type-safety.</p>
<h4 id="aggregations">Aggregations</h4>
<p>Standard aggregations like <code class="language-plaintext highlighter-rouge">count</code> / <code class="language-plaintext highlighter-rouge">min</code> / <code class="language-plaintext highlighter-rouge">max</code> / <code class="language-plaintext highlighter-rouge">sum</code> / <code class="language-plaintext highlighter-rouge">avg</code> on specific attributes are also supported. Aggregation is a projection combined with <code class="language-plaintext highlighter-rouge">groupBy()</code> operator.</p>
<p><code class="language-plaintext highlighter-rouge">count</code> operator is available on all types. For <code class="language-plaintext highlighter-rouge">min</code> / <code class="language-plaintext highlighter-rouge">max</code> attribute needs to be of type <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html">Comparable</a>. For <code class="language-plaintext highlighter-rouge">sum</code> / <code class="language-plaintext highlighter-rouge">avg</code> attribute needs to be of type <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Number.html">Number</a>.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">list</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">findAll</span><span class="o">()</span>
<span class="o">.</span><span class="na">orderBy</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">nickName</span><span class="o">.</span><span class="na">desc</span><span class="o">())</span>
<span class="o">.</span><span class="na">groupBy</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">nickName</span><span class="o">)</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">nickName</span><span class="o">,</span> <span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">max</span><span class="o">(),</span> <span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">min</span><span class="o">(),</span> <span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">count</span><span class="o">(),</span> <span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">sum</span><span class="o">())</span>
<span class="o">.</span><span class="na">map</span><span class="o">((</span><span class="n">nickName</span><span class="o">,</span> <span class="n">max</span><span class="o">,</span> <span class="n">min</span><span class="o">,</span> <span class="n">count</span><span class="o">,</span> <span class="n">sum</span><span class="o">)</span> <span class="o">-></span> <span class="o">(</span><span class="s">"nick="</span> <span class="o">+</span> <span class="n">nickName</span><span class="o">.</span><span class="na">orElse</span><span class="o">(</span><span class="kc">null</span><span class="o">)</span> <span class="o">+</span> <span class="s">" max="</span> <span class="o">+</span> <span class="n">max</span> <span class="o">+</span> <span class="s">" min="</span> <span class="o">+</span> <span class="n">min</span> <span class="o">+</span> <span class="s">" count="</span> <span class="o">+</span> <span class="n">count</span> <span class="o">+</span> <span class="s">" sum="</span> <span class="o">+</span> <span class="n">sum</span><span class="o">)))</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">();</span>
</code></pre></div></div>
<h4 id="distinct--limit--offset">Distinct / Limit / Offset</h4>
<p>To reduce number of returned elements use <code class="language-plaintext highlighter-rouge">limit / offset</code> statements (eg. for pagination). When using projections you can also request result to be de-duplicated with <code class="language-plaintext highlighter-rouge">distinct</code> keyword (note: distinct is available
only after projection).</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">persion</span><span class="o">.</span><span class="na">active</span><span class="o">.</span><span class="na">isTrue</span><span class="o">())</span>
<span class="o">.</span><span class="na">orderBy</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">asc</span><span class="o">())</span>
<span class="o">.</span><span class="na">select</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">)</span>
<span class="o">.</span><span class="na">distinct</span><span class="o">()</span>
<span class="o">.</span><span class="na">limit</span><span class="o">(</span><span class="mi">10</span><span class="o">)</span>
<span class="o">.</span><span class="na">offset</span><span class="o">(</span><span class="mi">10</span><span class="o">)</span>
<span class="o">.</span><span class="na">fetch</span><span class="o">();</span>
</code></pre></div></div>
<h4 id="fetching-variations">Fetching Variations</h4>
<p>Common way to return all results is to use <code class="language-plaintext highlighter-rouge">fetch()</code> function, however typical <a href="https://github.com/immutables/immutables/blob/master/criteria/common/src/org/immutables/criteria/repository/sync/SyncFetcher.java">Fetcher</a> has a richer API.</p>
<p>Use <code class="language-plaintext highlighter-rouge">fetch()</code> when you want to return full result set (which can have zero, one or multiple elements):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// get all results (size of the result can be 0, 1, 2 or more)</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="n">result</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">atLeast</span><span class="o">(</span><span class="mi">33</span><span class="o">)).</span><span class="na">limit</span><span class="o">(</span><span class="mi">10</span><span class="o">).</span><span class="na">fetch</span><span class="o">();</span>
</code></pre></div></div>
<p>Use <code class="language-plaintext highlighter-rouge">one()</code> when you expect <em>exactly one</em> element in the result set. Conveniently, the method will return only that element. Exception is thrown (usually with query information) when there is zero, two
or more elements. Depending on the facet, method will return <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html">Single</a> or [CompletionStage<T>](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html):</T></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// throw exception if there is no exactly one match</span>
<span class="c1">// may return Single / CompletionStage depending on the facet</span>
<span class="nc">Person</span> <span class="n">person</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John Doe"</span><span class="o">)).</span><span class="na">one</span><span class="o">();</span>
</code></pre></div></div>
<p>Use <code class="language-plaintext highlighter-rouge">oneOrNone()</code> when you require <em>at most one</em> element in the result set. Depending on facet sync / rxjava / async etc. the return type will be <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html">Optional</a> / <a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Maybe.html">Maybe</a> or [CompletionStage<Optional>](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html):</Optional></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// may return Maybe / CompletionStage<Optional> depending on facet</span>
<span class="nc">Optional</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="n">maybeOne</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John Doe2"</span><span class="o">)).</span><span class="na">oneOrNone</span><span class="o">();</span>
</code></pre></div></div>
<p>Use <code class="language-plaintext highlighter-rouge">exists()</code> when you need to check if criteria filter matches any records.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// may return Single<Boolean> / CompletionStage<Boolean> depending on the facet</span>
<span class="kt">boolean</span> <span class="n">exists</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">active</span><span class="o">.</span><span class="na">isTrue</span><span class="o">().</span><span class="na">fullName</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="s">"John Doe3"</span><span class="o">)).</span><span class="na">exists</span><span class="o">();</span>
</code></pre></div></div>
<p>Simple result-set counting can be achieved using <code class="language-plaintext highlighter-rouge">count()</code> operator. This operator is considered a terminal operator similar to <code class="language-plaintext highlighter-rouge">fetch()</code> and will return <code class="language-plaintext highlighter-rouge">long</code> / <code class="language-plaintext highlighter-rouge">Mono<Long></code> / <code class="language-plaintext highlighter-rouge">Single<Long></code> / <code class="language-plaintext highlighter-rouge">Future<Long></code> types depending on facet used.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// count all records (assumes RxJava facet)</span>
<span class="nc">Single</span><span class="o"><</span><span class="nc">Long</span><span class="o">></span> <span class="n">count</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">findAll</span><span class="o">().</span><span class="na">count</span><span class="o">();</span>
<span class="c1">// or apply a filter before count</span>
<span class="nc">Single</span><span class="o"><</span><span class="nc">Long</span><span class="o">></span> <span class="n">count</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">age</span><span class="o">.</span><span class="na">greaterThan</span><span class="o">(</span><span class="mi">33</span><span class="o">)).</span><span class="na">count</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="inserting--updating--deleting">Inserting / Updating / Deleting</h3>
<p><code class="language-plaintext highlighter-rouge">*Writable</code> facet is required to enable write operations. Examples of write / delete operations:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// one of RxJavaWritable / SyncWritable / AsyncWritable etc.</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Repository</span><span class="o">(</span><span class="n">facets</span><span class="o">=</span><span class="nc">RxJavaWritable</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">interface</span> <span class="nc">Person</span> <span class="o">{}</span>
<span class="nc">WriteResult</span> <span class="n">result</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">insertAll</span><span class="o">(</span><span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="n">person1</span><span class="o">,</span> <span class="n">person2</span><span class="o">));</span> <span class="c1">// for sync</span>
<span class="nc">Single</span><span class="o"><</span><span class="nc">WriteResult</span><span class="o">></span> <span class="n">result</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="n">person1</span><span class="o">);</span> <span class="c1">// for rxjava2</span>
<span class="nc">CompletionStage</span><span class="o"><</span><span class="nc">WriteResult</span><span class="o">></span> <span class="n">result</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="n">person1</span><span class="o">);</span> <span class="c1">// for async</span>
<span class="nc">Publisher</span><span class="o"><</span><span class="nc">WriteResult</span><span class="o">></span> <span class="n">result</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">insert</span><span class="o">(</span><span class="n">person1</span><span class="o">);</span> <span class="c1">// for reactive</span>
<span class="n">repository</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">active</span><span class="o">.</span><span class="na">isTrue</span><span class="o">());</span> <span class="c1">// delete by query</span>
</code></pre></div></div>
<h4 id="in-place-updates-by-query">In-place updates by query</h4>
<p>If supported by back-end, one can also perform in-place updates by query:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">repository</span><span class="o">.</span><span class="na">update</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">is</span><span class="o">(</span><span class="mi">123</span><span class="o">))</span>
<span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">person</span><span class="o">.</span><span class="na">fullName</span><span class="o">,</span> <span class="s">"Changed Name"</span><span class="o">)</span>
<span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">peson</span><span class="o">.</span><span class="na">age</span><span class="o">,</span> <span class="mi">33</span><span class="o">)</span>
<span class="o">.</span><span class="na">execute</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="pubsub-aka-watching">Pub/Sub (aka Watching)</h3>
<p>Watching allows to continuously observe events on the backend in real-time. When available, it is built on the top of existing oferring like
<a href="https://docs.mongodb.com/manual/changeStreams">change streams</a> in mongo or
<a href="https://geode.apache.org/docs/guide/19/developing/continuous_querying/how_continuous_querying_works.html">continuous querying</a> in Geode.</p>
<p>Use <code class="language-plaintext highlighter-rouge">*Watchable</code> facet to enable this functionality on a repository.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Criteria</span><span class="o">.</span><span class="na">Repository</span><span class="o">(</span><span class="n">facets</span><span class="o">=</span><span class="nc">RxJavaWatchable</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">interface</span> <span class="nc">Person</span> <span class="o">{}</span>
<span class="c1">// if remote database allows filtering in real-time</span>
<span class="nc">Flowable</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="n">flow</span> <span class="o">=</span> <span class="n">repository</span><span class="o">.</span><span class="na">watcher</span><span class="o">(</span><span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">active</span><span class="o">.</span><span class="na">isFalse</span><span class="o">()).</span><span class="na">watch</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="custom-repositories">Custom Repositories</h3>
<p>While <code class="language-plaintext highlighter-rouge">@Criteria.Repository</code> will auto-generate repository class based on facets, one can also write repository implementation manually. Facets are just
classes which can be leveraged to compose functionality.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyCustomRepository</span> <span class="kd">implements</span> <span class="nc">Repository</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">RxJavaReadable</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="n">readable</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">MyCustomRepository</span><span class="o">(</span><span class="nc">Backend</span> <span class="n">backend</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// open backend session for Person class</span>
<span class="nc">Backend</span><span class="o">.</span><span class="na">Session</span> <span class="n">session</span> <span class="o">=</span> <span class="n">backend</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="nc">Person</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="k">this</span><span class="o">.</span><span class="na">readable</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">RxJavaReadable</span><span class="o">(</span><span class="n">session</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">Flowable</span><span class="o"><</span><span class="nc">Person</span><span class="o">></span> <span class="nf">findPeopleOlderThan</span><span class="o">(</span><span class="nc">Period</span> <span class="n">period</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">PersonCriteria</span> <span class="n">criteria</span> <span class="o">=</span> <span class="nc">PersonCriteria</span><span class="o">.</span><span class="na">person</span><span class="o">.</span><span class="na">dateOfBirth</span><span class="o">.</span><span class="na">atMost</span><span class="o">(</span><span class="nc">LocalDate</span><span class="o">.</span><span class="na">now</span><span class="o">().</span><span class="na">minus</span><span class="o">(</span><span class="n">period</span><span class="o">));</span>
<span class="k">return</span> <span class="n">readable</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">criteria</span><span class="o">).</span><span class="na">fetch</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<hr />
<p>Backend
—-</p>
<p>Backend is responsible for interpreting expressions and operations into native queries and API calls using vendor drivers. It is the adapter
between criteria abstraction and native API.</p>
<p>Usually it is instantiated using vendor API (eg. MongoDatabase)</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Backend</span> <span class="n">backend</span> <span class="o">=</span> <span class="o">...</span> <span class="c1">// can be Mongo / Elasticsearch or any other backend</span>
<span class="c1">// instantiate repository using existing backend</span>
<span class="nc">PersonRepository</span> <span class="n">repository</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PersonRepository</span><span class="o">(</span><span class="n">backend</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="inmemory">InMemory</h3>
<p><code class="language-plaintext highlighter-rouge">InMemoryBackend</code> is the simplest form of backend which doesn’t have any external dependencies. Internally it uses <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html">ConcurrentMap</a> and reflections
to evaluate expressions and perform CRUD operations.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// instantiate InMemoryBackend</span>
<span class="nc">Backend</span> <span class="n">backend</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">InMemoryBackend</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="mongo">Mongo</h3>
<p>Mongo backend uses <a href="https://mongodb.github.io/mongo-java-driver-reactivestreams/">reactive streams</a> driver. There is always an
option for repository to expose synchronous (or other) API by using facets.</p>
<p>To instantiate mongo backend use <code class="language-plaintext highlighter-rouge">CollectionResolver</code>. The later is responsible for mapping an entity class eg. <code class="language-plaintext highlighter-rouge">Person</code> to <code class="language-plaintext highlighter-rouge">MongoCollection<Person></code>.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">MongoDatabase</span> <span class="n">database</span> <span class="o">=</span> <span class="o">...</span> <span class="c1">// get database (with correct CodecRegistry)</span>
<span class="nc">MongoBackend</span> <span class="n">backend</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">MongoBackend</span><span class="o">(</span><span class="nc">MongoSetup</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">database</span><span class="o">));</span>
<span class="nc">PersonRepository</span> <span class="n">repository</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PersonRepository</span><span class="o">(</span><span class="n">backend</span><span class="o">);</span>
</code></pre></div></div>
<h4 id="jacksonbson-integration">Jackson/Bson integration</h4>
<p>Out of box, criteria provides integration with <a href="https://github.com/FasterXML/jackson">jackson</a> library. This allows
use of standard jackson binding infrastructure but still serializing documents in <a href="http://bsonspec.org/">BSON format</a> (including non-JSON types like Decimal128,
timestamp or date). Jackson (BSON) adapter will delegate calls to native <a href="http://mongodb.github.io/mongo-java-driver/3.9/bson/readers-and-writers/">BsonReader and BsonWriter</a> without intermediate object transformation (eg. <code class="language-plaintext highlighter-rouge">BSON -> String -> POJO</code>) thus avoiding
extra parsing and memory allocation.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ObjectMapper</span> <span class="n">mapper</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ObjectMapper</span><span class="o">()</span>
<span class="o">.</span><span class="na">registerModule</span><span class="o">(</span><span class="k">new</span> <span class="nc">BsonModule</span><span class="o">())</span> <span class="c1">// register default codecs like Jsr310, BsonValueCodec, ValueCodecProvider etc.</span>
<span class="o">.</span><span class="na">registerModule</span><span class="o">(</span><span class="k">new</span> <span class="nc">GuavaModule</span><span class="o">())</span> <span class="c1">// for Immutable* classes from Guava (eg. ImmutableList)</span>
<span class="o">.</span><span class="na">registerModule</span><span class="o">(</span><span class="k">new</span> <span class="nc">Jdk8Module</span><span class="o">())</span> <span class="c1">// used for java 8 types like Optional / OptionalDouble etc.</span>
<span class="o">.</span><span class="na">registerModule</span><span class="o">(</span><span class="k">new</span> <span class="nc">IdAnnotationModule</span><span class="o">());</span> <span class="c1">// used for Criteria.Id to '_id' attribute mapping</span>
<span class="nc">CodecRegistry</span> <span class="n">registry</span> <span class="o">=</span> <span class="nc">JacksonCodecs</span><span class="o">.</span><span class="na">registryFromMapper</span><span class="o">(</span><span class="n">mapper</span><span class="o">);</span> <span class="c1">// create CodecRegistry (adapter) from ObjectMapper</span>
<span class="nc">MongoClient</span> <span class="n">client</span> <span class="o">=</span> <span class="o">...</span> <span class="c1">// "connect" / get client</span>
<span class="nc">MongoDatabase</span> <span class="n">database</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="na">getDatabase</span><span class="o">(</span><span class="s">"myDB"</span><span class="o">).</span><span class="na">withCodecRegistry</span><span class="o">(</span><span class="n">registry</span><span class="o">);</span> <span class="c1">// override with "jackson" CodecRegistry</span>
<span class="nc">MongoBackend</span> <span class="n">backend</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">MongoBackend</span><span class="o">(</span><span class="nc">MongoSetup</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">database</span><span class="o">));</span> <span class="c1">// create backend instance</span>
</code></pre></div></div>
<p>Don’t forget to add <code class="language-plaintext highlighter-rouge">@JsonSerialize</code> and <code class="language-plaintext highlighter-rouge">@JsonDeserialize</code> to your model. Admittedly, number of annotations is becoming noticeable.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Value</span><span class="o">.</span><span class="na">Immutable</span>
<span class="nd">@Criteria</span>
<span class="nd">@Criteria</span><span class="o">.</span><span class="na">Repository</span>
<span class="nd">@JsonSerialize</span><span class="o">(</span><span class="n">as</span> <span class="o">=</span> <span class="nc">ImmutablePerson</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="nd">@JsonDeserialize</span><span class="o">(</span><span class="n">as</span> <span class="o">=</span> <span class="nc">ImmutablePerson</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Person</span> <span class="o">{}</span>
</code></pre></div></div>
<h3 id="elastic-search">Elastic Search</h3>
<p><code class="language-plaintext highlighter-rouge">ElasticsearchBackend</code> leverages <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low.html">low-level rest client</a>
to communicate with elastic search cluster. Because of object binding and JSON parsing <a href="https://github.com/FasterXML/jackson">Jackson</a>
is currently a hard dependency of this module.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">RestClient</span> <span class="n">restClient</span> <span class="o">=</span> <span class="o">...</span> <span class="c1">// provided</span>
<span class="c1">// use default resolver which maps entity (class) to index name</span>
<span class="nc">ElasticsearchBackend</span> <span class="n">backend</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ElasticsearchBackend</span><span class="o">(</span><span class="nc">ElasticsearchSetup</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">restClient</span><span class="o">));</span>
</code></pre></div></div>
<p>The only required depedency of <code class="language-plaintext highlighter-rouge">ElasticsearchSetup</code> is <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-low.html">RestClient</a> however you can also override default instances<br />
of <code class="language-plaintext highlighter-rouge">ObjectMapper</code>, <code class="language-plaintext highlighter-rouge">scrollSize</code>, <code class="language-plaintext highlighter-rouge">indexResolver</code> etc.</p>
<p>By default, <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html#request-body-search-scroll">scrolling</a> is
used for all queries unless it is an aggregation or <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html#request-body-search-from-size">offset/from</a> request.</p>
<p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html">Mapping types</a> are not supported (to be removed by vendor in 7.x).</p>
<p>At least version 6.2 of Elastic is recommended for criteria API. Generally we follow official <a href="https://www.elastic.co/support/eol">EoL schedule</a></p>
<h3 id="geode">Geode</h3>
<p>The only required dependency of <code class="language-plaintext highlighter-rouge">GeodeBackend</code> is <a href="https://geode.apache.org/releases/latest/javadoc/org/apache/geode/cache/GemFireCache.html">GemFireCache</a>. Below is an example of how to instantiate <code class="language-plaintext highlighter-rouge">GeodeBackend</code>.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">GemFireCache</span> <span class="n">cache</span> <span class="o">=</span> <span class="o">...</span> <span class="c1">// provided</span>
<span class="nc">GeodeBackend</span> <span class="n">backend</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GeodeBackend</span><span class="o">(</span><span class="nc">GeodeSetup</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">cache</span><span class="o">));</span>
</code></pre></div></div>
<hr />
<p>JavaBeans
—-</p>
<p>Limited support for <a href="https://en.wikipedia.org/wiki/JavaBeans">JavaBeans</a> is provided.
It is intended for projects which may want to leverage criteria (runtime and DSL) as data access layer but are not yet fully migrated to immutables model.
You may be able to generate Criteria DSL assuming your class follows <a href="https://www.oracle.com/technetwork/articles/javaee/spec-136004.html">JavaBeans spec</a>.</p>
<p>Requirements for JavaBean classes:</p>
<ol>
<li>Has to be non-abstract and not an enum.</li>
<li>Both getters (<code class="language-plaintext highlighter-rouge">get*</code> / <code class="language-plaintext highlighter-rouge">is*</code>) and setters (<code class="language-plaintext highlighter-rouge">set*</code>) should be present for the same attribute.</li>
<li>Class should have a (non-static) field derived from getter/setter. Example <code class="language-plaintext highlighter-rouge">name</code> for <code class="language-plaintext highlighter-rouge">getName/setName</code>, <code class="language-plaintext highlighter-rouge">URL</code> for <code class="language-plaintext highlighter-rouge">getURL/setURL</code>, <code class="language-plaintext highlighter-rouge">a</code> for <code class="language-plaintext highlighter-rouge">getA/setA</code>
For more details see <em>8.8 Capitalization of inferred names</em> in JavaBeans spec</li>
</ol>
<p>Just annotate your existing bean with <code class="language-plaintext highlighter-rouge">@Criteria</code> and immutables will generate Criteria DSL for it.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Example of a valid JavaBean</span>
<span class="nd">@Criteria</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyBean</span> <span class="o">{</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">name</span><span class="o">;</span> <span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setName</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span> <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Note that all attributes are considered nullable by default in JavaBean model.</p>
</section>
<footer class="jumbotron">
<div class="container">
<h2 id="guides">Guides</h2>
<ul>
<li><a href="/getstarted.html">Get started!</a></li>
<li><a href="/intro.html">Inception</a></li>
<li><a href="/immutable.html">Immutable objects</a></li>
<li><a href="/factory.html">Factory builders</a></li>
<li><a href="/functional.html">Functions and Predicates (for Java 7)</a></li>
<li><a href="/style.html">Style customization</a></li>
<li><a href="/json.html">JSON serialization</a></li>
<li><a href="/criteria.html">Criteria</a></li>
<li><a href="/mongo.html">MongoDB repositories</a></li>
<li><a href="/dynamodb.html">DynamoDB integration</a></li>
<li><a href="/encoding.html">Encoding: Customizing attributes and builders (experimental)</a></li>
<li><a href="/apt.html">Using annotation processor in IDE</a></li>
</ul>
<h2 id="get-involved">Get involved</h2>
<ul>
<li>Clone source repository, contribute bug reports and fixes on <a href="https://github.com/immutables/immutables">GitHub immutables/immutables</a></li>
<li>Issue reports, questions and feedback is welcome on issue tracker <a href="https://github.com/immutables/immutables/issues">GitHub immutables/immutables/issues</a></li>
<li>News and announcements on twitter <a href="https://twitter.com/ImmutablesOrg">@ImmutablesOrg</a></li>
</ul>
<p><a href="/license.html">Apache License 2.0</a></p>
<!--<div><h2>Posts</h2>
<ul>
</ul>
</div>-->
</div>
</footer>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script defer src="/gfx/jquery.toc.min.js"></script>
<script>
$(function() {
$('#toc').toc({
container: '.documentation',
selectors: 'h1,h2,h3,h4',
anchorName: function(i, heading, prefix) {
heading = $(heading).text();
if (heading.trim) heading = heading.trim();
return heading.toLowerCase().replace(/ /g, '-').replace(/[^a-z^\-]+/g, '');
},
})
})
</script>
</body>
</html>