Skip to content

Commit ff60457

Browse files
committed
DATAMONGO-1245 - Initial documentation for Query by Example.
1 parent bc0a58b commit ff60457

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

src/main/asciidoc/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ include::{spring-data-commons-docs}/repositories.adoc[]
2727
include::reference/introduction.adoc[]
2828
include::reference/mongodb.adoc[]
2929
include::reference/mongo-repositories.adoc[]
30+
include::reference/query-by-example.adoc[]
3031
include::{spring-data-commons-docs}/auditing.adoc[]
3132
include::reference/mongo-auditing.adoc[]
3233
include::reference/mapping.adoc[]
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
[[query.by.example]]
2+
= Query by Example
3+
4+
== Introduction
5+
6+
This chapter will give you an introduction to Query by Example and will explain how to use example specifications.
7+
8+
Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require to write queries containing field names. In fact, Query by Example does not require to write queries using store-specific query languages at all.
9+
10+
== Usage
11+
12+
An `Example` takes a data object (usually the entity object or a subtype of it) and a specification how to match properties. You can use Query by Example in the `MongoTemplate` and within `Repositories`.
13+
14+
Query by Example is suited for several use-cases but also comes with limitations:
15+
16+
**When to use**
17+
18+
* Querying your data store with a set of static or dynamic constraints
19+
* Frequent refactoring of the entities without worrying about breaking existing queries
20+
* Works independently from the data store API
21+
22+
**Limitations**
23+
24+
* Query predicates are combined using the `AND` keyword
25+
* No support for nested/grouped property constraints like `firstname = ?0 or (firstname = ?1 and lastname = ?2)`
26+
* Limited to starts/contains/ends/regex matching for strings and exact matching for other property types
27+
28+
29+
Before getting started with Query by Example you need to have your entities set up.
30+
31+
.Sample Person entity
32+
====
33+
[source,java]
34+
----
35+
public class Person {
36+
37+
@Id
38+
private String id;
39+
private String firstname;
40+
private String lastname;
41+
private Address address;
42+
43+
// … getters and setters omitted
44+
}
45+
----
46+
====
47+
48+
We have a quite simple entity here that is mapped to the data store. You can use this entity to create an Example specification. By default, fields having `null` values are ignored, and strings are matched using the store specific defaults. Examples can be built by either using the `exampleOf` factory method or by using the <<query.by.example.builder,Example builder>>. Once the `Example` is constructed it becomes immutable.
49+
50+
.The first Example specification
51+
====
52+
[source,xml]
53+
----
54+
Person person = new Person(); <1>
55+
56+
person.setFirstname("Dave"); <2>
57+
58+
Example<Person> example = Example.exampleOf(person); <3>
59+
----
60+
<1> Create a new instance of the entity
61+
<2> Set the properties to query
62+
<3> Create an `Example`
63+
====
64+
65+
66+
NOTE: Property names of the sample object must correlate with the property names of the queried entity.
67+
68+
.Query by Example using the MongoTemplate
69+
====
70+
[source,xml]
71+
----
72+
@Autowired
73+
MongoTemplate template;
74+
75+
public List<Person> findPeople(Person sampleObject) {
76+
return template.findByExample(Example.exampleOf(person));
77+
}
78+
----
79+
====
80+
81+
The `findByExample` method accepts either the sample object or an `Example` object to query the data store. Spring Data uses the `Example` to create and execute a query.
82+
83+
84+
.Query by Example using the Repositories
85+
====
86+
[source, java]
87+
----
88+
public interface MongoRepository<Person, String> {
89+
90+
<S extends T> List<T> findAllByExample(Example<S> example);
91+
92+
<S extends T> List<T> findAllByExample(Example<S> example, Sort sort);
93+
94+
<S extends T> Page<T> findAllByExample(Example<S> example, Pageable pageable);
95+
96+
// … more functionality omitted.
97+
}
98+
----
99+
====
100+
101+
[[query.by.example.builder]]
102+
== Example builder
103+
104+
Examples are not limited to default settings. You can specify own defaults for string matching, null handling and property-specific settings using the example builder.
105+
106+
.Query by Example builder
107+
====
108+
[source, java]
109+
----
110+
Example.newExampleOf(person)
111+
.withStringMatcher(StringMatcher.ENDING)
112+
.includeNullValues()
113+
.withPropertySpecifier(
114+
newPropertySpecifier("firstname").matchString(StringMatcher.CONTAINING).get())
115+
.withPropertySpecifier(
116+
newPropertySpecifier("lastname").matchStringsWithIgnoreCase().get())
117+
.withPropertySpecifier(
118+
newPropertySpecifier("address.city").matchStringStartingWith().get())
119+
.get();
120+
----
121+
====
122+
123+
Property specifier accepts property names and property paths separated by dots that are contained within the sample object. A `PropertySpecifier` allows setting string matching options, case-sensitivity,
124+
125+
[cols="1,2", options="header"]
126+
.Supported string matching options of `StringMatcher`
127+
|===
128+
| Matching
129+
| Logical result
130+
131+
| `DEFAULT` (case-sensitive)
132+
| `{"firstname" : firstname}`
133+
134+
| `DEFAULT` (case-insensitive)
135+
| `{"firstname" : { $regex: firstname, $options: 'i'}}`
136+
137+
| `EXACT` (case-sensitive)
138+
| `{"firstname" : { $regex: /^firstname$/}}`
139+
140+
| `EXACT` (case-insensitive)
141+
| `{"firstname" : { $regex: /^firstname$/, $options: 'i'}}`
142+
143+
| `STARTING` (case-sensitive)
144+
| `{"firstname" : { $regex: /^firstname/}}`
145+
146+
| `STARTING` (case-insensitive)
147+
| `{"firstname" : { $regex: /^firstname/, $options: 'i'}}`
148+
149+
| `ENDING` (case-sensitive)
150+
| `{"firstname" : { $regex: /firstname$/}}`
151+
152+
| `ENDING` (case-insensitive)
153+
| `{"firstname" : { $regex: /firstname$/, $options: 'i'}}`
154+
155+
| `CONTAINING` (case-sensitive)
156+
| `{"firstname" : { $regex: /.\*firstname.*/}}`
157+
158+
| `CONTAINING` (case-insensitive)
159+
| `{"firstname" : { $regex: /.\*firstname.*/, $options: 'i'}}`
160+
161+
| `REGEX` (case-sensitive)
162+
| `{"firstname" : { $regex: /firstname/}}`
163+
164+
| `REGEX` (case-insensitive)
165+
| `{"firstname" : { $regex: /firstname/, $options: 'i'}}`
166+
167+
|===

0 commit comments

Comments
 (0)