diff --git a/jpa/pom.xml b/jpa/pom.xml
index f5a0818ac..2451c0fa2 100644
--- a/jpa/pom.xml
+++ b/jpa/pom.xml
@@ -25,6 +25,7 @@
security
multiple-datasources
eclipselink
+ query-by-example
diff --git a/jpa/query-by-example/README.md b/jpa/query-by-example/README.md
new file mode 100644
index 000000000..c4f459201
--- /dev/null
+++ b/jpa/query-by-example/README.md
@@ -0,0 +1,11 @@
+# Spring Data JPA - Query-by-Example (QBE) example
+
+This project contains samples of Query-by-Example of Spring Data JPA.
+
+## Support for Query-by-Example
+
+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 JPA-QL at all.
+
+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 with JPA Repositories.
+
+This example contains a test class to illustrate Query-by-Example with a Repository in `UserRepositoryIntegrationTests`.
diff --git a/jpa/query-by-example/pom.xml b/jpa/query-by-example/pom.xml
new file mode 100644
index 000000000..0a2ee1437
--- /dev/null
+++ b/jpa/query-by-example/pom.xml
@@ -0,0 +1,14 @@
+
+ 4.0.0
+
+
+ org.springframework.data.examples
+ spring-data-jpa-examples
+ 1.0.0.BUILD-SNAPSHOT
+
+
+ spring-data-jpa-query-by-example
+ Spring Data JPA - Query-by-Example (QBE)
+
+
diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java
new file mode 100644
index 000000000..2f3006ffb
--- /dev/null
+++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/ApplicationConfiguration.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.jpa.querybyexample;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.orm.jpa.EntityScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+
+/**
+ * @author Mark Paluch
+ */
+@Configuration
+@EnableAutoConfiguration
+@EntityScan(basePackageClasses = { ApplicationConfiguration.class })
+@EnableJpaAuditing
+public class ApplicationConfiguration {
+
+}
diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java
new file mode 100644
index 000000000..5b46915bb
--- /dev/null
+++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/SpecialUser.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.jpa.querybyexample;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+
+/**
+ * Sample class that extends {@link User}.
+ *
+ * @author Mark Paluch
+ */
+@Entity
+@Data
+@NoArgsConstructor
+public class SpecialUser extends User {
+
+ public SpecialUser(String firstname, String lastname, Integer age) {
+ super(firstname, lastname, age);
+ }
+}
diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java
new file mode 100644
index 000000000..fc5d7a1ac
--- /dev/null
+++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/User.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.jpa.querybyexample;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Sample user class.
+ *
+ * @author Mark Paluch
+ */
+@Entity
+@Data
+@NoArgsConstructor
+public class User {
+
+ @Id @GeneratedValue //
+ private Long id;
+ private String firstname;
+ private String lastname;
+ private Integer age;
+
+ public User(String firstname, String lastname, Integer age) {
+
+ super();
+
+ this.firstname = firstname;
+ this.lastname = lastname;
+ this.age = age;
+ }
+
+}
diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java
new file mode 100644
index 000000000..f5decc44b
--- /dev/null
+++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/UserRepository.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.jpa.querybyexample;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+/**
+ * Simple repository interface for {@link User} instances. The interface implements {@link QueryByExampleExecutor} and
+ * allows execution of methods accepting {@link org.springframework.data.domain.Example}.
+ *
+ * @author Mark Paluch
+ */
+public interface UserRepository extends CrudRepository, QueryByExampleExecutor {
+
+}
diff --git a/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java
new file mode 100644
index 000000000..39edfd595
--- /dev/null
+++ b/jpa/query-by-example/src/main/java/example/springdata/jpa/querybyexample/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Sample showing Query-by-Example related features of Spring Data JPA.
+ *
+ * @author Mark Paluch
+ */
+package example.springdata.jpa.querybyexample;
diff --git a/jpa/query-by-example/src/main/resources/application.properties b/jpa/query-by-example/src/main/resources/application.properties
new file mode 100644
index 000000000..d98d035f1
--- /dev/null
+++ b/jpa/query-by-example/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.datasource.separator=/;
\ No newline at end of file
diff --git a/jpa/query-by-example/src/main/resources/logback.xml b/jpa/query-by-example/src/main/resources/logback.xml
new file mode 100644
index 000000000..40a8f3394
--- /dev/null
+++ b/jpa/query-by-example/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d %5p %40.40c:%4L - %m%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java
new file mode 100644
index 000000000..1d8f2cf41
--- /dev/null
+++ b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryInheritanceIntegrationTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.jpa.querybyexample;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleSpec;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories.
+ *
+ * @author Mark Paluch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@Transactional
+@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
+public class UserRepositoryInheritanceIntegrationTests {
+
+ @Autowired UserRepository repository;
+
+ User skyler, walter, flynn;
+
+ @Before
+ public void setUp() {
+
+ repository.deleteAll();
+
+ this.skyler = repository.save(new User("Skyler", "White", 45));
+ this.walter = repository.save(new SpecialUser("Walter", "White", 50));
+ this.flynn = repository.save(new SpecialUser("Walter Jr. (Flynn)", "White", 17));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void countBySimpleExample() {
+
+ Example example = Example.of(new SpecialUser(null, "White", null));
+
+ assertThat(repository.count(example), is(3L));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void countUserByTypedExample() {
+
+ Example example = Example.of(new SpecialUser(null, "White", null), //
+ ExampleSpec.typed(User.class));
+
+ assertThat(repository.count(example), is(3L));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void countSpecialUserByTypedExample() {
+
+ Example example = Example.of(new SpecialUser(null, "White", null), //
+ ExampleSpec.typed(SpecialUser.class));
+
+ assertThat(repository.count(example), is(2L));
+ }
+
+}
diff --git a/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java
new file mode 100644
index 000000000..4a784f2b2
--- /dev/null
+++ b/jpa/query-by-example/src/test/java/example/springdata/jpa/querybyexample/UserRepositoryIntegrationTests.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package example.springdata.jpa.querybyexample;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleSpec;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Integration test showing the usage of JPA Query-by-Example support through Spring Data repositories.
+ *
+ * @author Mark Paluch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@Transactional
+@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
+public class UserRepositoryIntegrationTests {
+
+ @Autowired
+ UserRepository repository;
+
+ User skyler, walter, flynn, marie, hank;
+
+ @Before
+ public void setUp() {
+
+ repository.deleteAll();
+
+ this.skyler = repository.save(new User("Skyler", "White", 45));
+ this.walter = repository.save(new User("Walter", "White", 50));
+ this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17));
+ this.marie = repository.save(new User("Marie", "Schrader", 38));
+ this.hank = repository.save(new User("Hank", "Schrader", 43));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void countBySimpleExample() {
+
+ Example example = Example.of(new User(null, "White", null));
+
+ assertThat(repository.count(example), is(3L));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void ignorePropertiesAndMatchByAge() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped(). //
+ withIgnorePaths("firstname", "lastname");
+
+ assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void substringMatching() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped().//
+ withStringMatcherEnding();
+
+ assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void matchStartingStringsIgnoreCase() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped(). //
+ withIgnorePaths("age").//
+ withMatcher("firstname", startsWith()).//
+ withMatcher("lastname", ignoreCase());
+
+ assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void configuringMatchersUsingLambdas() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped().withIgnorePaths("age"). //
+ withMatcher("firstname", matcher -> matcher.startsWith()). //
+ withMatcher("lastname", matcher -> matcher.ignoreCase());
+
+ assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter));
+ }
+
+ /**
+ * @see DATAJPA-218
+ */
+ @Test
+ public void valueTransformer() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped(). //
+ withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50)));
+
+ assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter));
+ }
+
+}
diff --git a/mongodb/pom.xml b/mongodb/pom.xml
index 0c2854e2d..35e2bab4a 100644
--- a/mongodb/pom.xml
+++ b/mongodb/pom.xml
@@ -23,6 +23,7 @@
java8
security
geo-json
+ query-by-example
diff --git a/mongodb/query-by-example/README.md b/mongodb/query-by-example/README.md
new file mode 100644
index 000000000..01ad499cb
--- /dev/null
+++ b/mongodb/query-by-example/README.md
@@ -0,0 +1,12 @@
+# Spring Data MongoDB - Query-by-Example (QBE) example
+
+This project contains samples of Query-by-Example of Spring Data MongoDB.
+
+## Support for Query-by-Example
+
+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 JPA-QL at all.
+
+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 with `MongoOperations` and Repositories.
+
+This example contains two test classes to illustrate Query-by-Example with `MongoOperations` in `MongoOperationsIntegrationTests` and the usage with a Repository in `UserRepositoryIntegrationTests`.
+
diff --git a/mongodb/query-by-example/pom.xml b/mongodb/query-by-example/pom.xml
new file mode 100644
index 000000000..a0652f4cb
--- /dev/null
+++ b/mongodb/query-by-example/pom.xml
@@ -0,0 +1,14 @@
+
+ 4.0.0
+
+
+ org.springframework.data.examples
+ spring-data-mongodb-examples
+ 1.0.0.BUILD-SNAPSHOT
+
+
+ spring-data-mongodb-query-by-example
+ Spring Data MongoDB - Query-by-Example (QBE)
+
+
diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java
new file mode 100644
index 000000000..3b121ff68
--- /dev/null
+++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ApplicationConfiguration.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author Mark Paluch
+ */
+@SpringBootApplication
+public class ApplicationConfiguration {
+
+}
diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java
new file mode 100644
index 000000000..90d4651ed
--- /dev/null
+++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/Contact.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+import org.bson.types.ObjectId;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * Sample contact class.
+ *
+ * @author Mark Paluch
+ */
+@Data
+@RequiredArgsConstructor
+@Document(collection = "collectionStoringTwoTypes")
+public class Contact {
+
+ @Id //
+ private ObjectId id;
+ private final String firstname;
+ private final String lastname;
+ private final Integer age;
+
+}
diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java
new file mode 100644
index 000000000..6c7083e54
--- /dev/null
+++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/ContactRepository.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+/**
+ * Simple repository interface for {@link Contact} instances. The interface implements {@link QueryByExampleExecutor} and
+ * allows execution of methods accepting {@link org.springframework.data.domain.Example}.
+ *
+ * @author Mark Paluch
+ */
+public interface ContactRepository extends CrudRepository, QueryByExampleExecutor {
+
+}
diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java
new file mode 100644
index 000000000..56b84fe74
--- /dev/null
+++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/User.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
+
+import org.bson.types.ObjectId;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * Sample user class.
+ *
+ * @author Mark Paluch
+ */
+@Data
+@RequiredArgsConstructor
+@Document(collection = "collectionStoringTwoTypes")
+public class User {
+
+ @Id //
+ private ObjectId id;
+ private final String firstname;
+ private final String lastname;
+ private final Integer age;
+
+}
diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java
new file mode 100644
index 000000000..14241543c
--- /dev/null
+++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/UserRepository.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.QueryByExampleExecutor;
+
+/**
+ * Simple repository interface for {@link User} instances. The interface implements {@link QueryByExampleExecutor} and
+ * allows execution of methods accepting {@link org.springframework.data.domain.Example}.
+ *
+ * @author Mark Paluch
+ */
+public interface UserRepository extends CrudRepository, QueryByExampleExecutor {
+
+}
diff --git a/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java
new file mode 100644
index 000000000..3b8fa0cc2
--- /dev/null
+++ b/mongodb/query-by-example/src/main/java/example/springdata/mongodb/querybyexample/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Sample showing Query-by-Example related features of Spring Data MongoDB.
+ *
+ * @author Mark Paluch
+ */
+package example.springdata.mongodb.querybyexample;
diff --git a/mongodb/query-by-example/src/main/resources/logback.xml b/mongodb/query-by-example/src/main/resources/logback.xml
new file mode 100644
index 000000000..40a8f3394
--- /dev/null
+++ b/mongodb/query-by-example/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d %5p %40.40c:%4L - %m%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java
new file mode 100644
index 000000000..b3a87af6a
--- /dev/null
+++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/ContactRepositoryIntegrationTests.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleSpec;
+import org.springframework.data.domain.ExampleSpec.StringMatcher;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories for a case where two domain types are stored in one collection.
+ *
+ * @author Mark Paluch
+ * @soundtrack Paul van Dyk - VONYC Sessions Episode 496 with guest Armin van Buuren
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
+public class ContactRepositoryIntegrationTests {
+
+ @Autowired UserRepository userRepository;
+ @Autowired ContactRepository contactRepository;
+ @Autowired MongoOperations mongoOperations;
+
+ User skyler, walter, flynn;
+ Contact marie, hank;
+
+ @Before
+ public void setUp() {
+
+ userRepository.deleteAll();
+
+ this.skyler = userRepository.save(new User("Skyler", "White", 45));
+ this.walter = userRepository.save(new User("Walter", "White", 50));
+ this.flynn = userRepository.save(new User("Walter Jr. (Flynn)", "White", 17));
+ this.marie = contactRepository.save(new Contact("Marie", "Schrader", 38));
+ this.hank = contactRepository.save(new Contact("Hank", "Schrader", 43));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void countUsersByUntypedExample() {
+
+ Example example = Example.of(new User(null, null, null));
+
+ // the count is 5 because untyped examples do not restrict the type
+ assertThat(userRepository.count(example), is(5L));
+ assertThat(mongoOperations.count(new Query(), "collectionStoringTwoTypes"), is(5L));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void countByTypedExample() {
+
+ Example example = Example.of(new User(null, null, null), ExampleSpec.typed(User.class));
+
+ assertThat(userRepository.count(example), is(3L));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void findAllUsersBySimpleExample() {
+
+ Example example = Example.of(new User(".*", null, null), //
+ ExampleSpec.typed(User.class).withStringMatcher(StringMatcher.REGEX));
+
+ assertThat(userRepository.findAll(example), containsInAnyOrder(skyler, walter, flynn));
+ assertThat(userRepository.findAll(example), not(containsInAnyOrder(hank, marie)));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void findAllContactsBySimpleExample() {
+
+ Example example = Example.of(new Contact(".*", null, null), //
+ ExampleSpec.typed(Contact.class).withStringMatcher(StringMatcher.REGEX));
+
+ assertThat(contactRepository.findAll(example), containsInAnyOrder(hank, marie));
+ assertThat(contactRepository.findAll(example), not(containsInAnyOrder(skyler, walter, flynn)));
+ }
+
+}
diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java
new file mode 100644
index 000000000..fb2a2bded
--- /dev/null
+++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/MongoOperationsIntegrationTests.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleSpec;
+import org.springframework.data.domain.TypedExampleSpec;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories.
+ *
+ * @author Mark Paluch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
+public class MongoOperationsIntegrationTests {
+
+ @Autowired MongoOperations operations;
+
+ User skyler, walter, flynn, marie, hank;
+
+ @Before
+ public void setUp() {
+
+ operations.remove(new Query(), User.class);
+
+ this.skyler = new User("Skyler", "White", 45);
+ this.walter = new User("Walter", "White", 50);
+ this.flynn = new User("Walter Jr. (Flynn)", "White", 17);
+ this.marie = new User("Marie", "Schrader", 38);
+ this.hank = new User("Hank", "Schrader", 43);
+
+ operations.save(this.skyler);
+ operations.save(this.walter);
+ operations.save(this.flynn);
+ operations.save(this.marie);
+ operations.save(this.hank);
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void ignoreNullProperties() {
+
+ assertThat(operations.findByExample(new User(null, null, 17)), hasItems(flynn));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void substringMatching() {
+
+ TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class).//
+ withStringMatcherEnding();
+
+ assertThat(operations.findByExample(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void regexMatching() {
+
+ TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class).//
+ withMatcher("firstname", matcher -> matcher.regex());
+
+ assertThat(operations.findByExample(Example.of(new User("(Skyl|Walt)er", null, null), exampleSpec)),
+ hasItems(skyler, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void matchStartingStringsIgnoreCase() {
+
+ TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class). //
+ withIgnorePaths("age").//
+ withMatcher("firstname", startsWith()).//
+ withMatcher("lastname", ignoreCase());
+
+ assertThat(operations.findByExample(Example.of(new User("Walter", "WHITE", null), exampleSpec)),
+ hasItems(flynn, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void configuringMatchersUsingLambdas() {
+
+ TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class).withIgnorePaths("age"). //
+ withMatcher("firstname", matcher -> matcher.startsWith()). //
+ withMatcher("lastname", matcher -> matcher.ignoreCase());
+
+ assertThat(operations.findByExample(Example.of(new User("Walter", "WHITE", null), exampleSpec)),
+ hasItems(flynn, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void valueTransformer() {
+
+ TypedExampleSpec exampleSpec = ExampleSpec.typed(User.class). //
+ withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50)));
+
+ assertThat(operations.findByExample(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter));
+ }
+
+}
diff --git a/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java
new file mode 100644
index 000000000..575fb2b27
--- /dev/null
+++ b/mongodb/query-by-example/src/test/java/example/springdata/mongodb/querybyexample/UserRepositoryIntegrationTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.springdata.mongodb.querybyexample;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.ignoreCase;
+import static org.springframework.data.domain.ExampleSpec.GenericPropertyMatchers.startsWith;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleSpec;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * Integration test showing the usage of MongoDB Query-by-Example support through Spring Data repositories.
+ *
+ * @author Mark Paluch
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = ApplicationConfiguration.class)
+public class UserRepositoryIntegrationTests {
+
+ @Autowired UserRepository repository;
+
+ User skyler, walter, flynn, marie, hank;
+
+ @Before
+ public void setUp() {
+
+ repository.deleteAll();
+
+ this.skyler = repository.save(new User("Skyler", "White", 45));
+ this.walter = repository.save(new User("Walter", "White", 50));
+ this.flynn = repository.save(new User("Walter Jr. (Flynn)", "White", 17));
+ this.marie = repository.save(new User("Marie", "Schrader", 38));
+ this.hank = repository.save(new User("Hank", "Schrader", 43));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void countBySimpleExample() {
+
+ Example example = Example.of(new User(null, "White", null));
+
+ assertThat(repository.count(example), is(3L));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void ignorePropertiesAndMatchByAge() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped(). //
+ withIgnorePaths("firstname", "lastname");
+
+ assertThat(repository.findOne(Example.of(flynn, exampleSpec)), is(flynn));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void substringMatching() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped().//
+ withStringMatcherEnding();
+
+ assertThat(repository.findAll(Example.of(new User("er", null, null), exampleSpec)), hasItems(skyler, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void regexMatching() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped().//
+ withMatcher("firstname", matcher -> matcher.regex());
+
+ assertThat(repository.findAll(Example.of(new User("(Skyl|Walt)er", null, null), exampleSpec)),
+ hasItems(skyler, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void matchStartingStringsIgnoreCase() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped(). //
+ withIgnorePaths("age").//
+ withMatcher("firstname", startsWith()).//
+ withMatcher("lastname", ignoreCase());
+
+ assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void configuringMatchersUsingLambdas() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped().withIgnorePaths("age"). //
+ withMatcher("firstname", matcher -> matcher.startsWith()). //
+ withMatcher("lastname", matcher -> matcher.ignoreCase());
+
+ assertThat(repository.findAll(Example.of(new User("Walter", "WHITE", null), exampleSpec)), hasItems(flynn, walter));
+ }
+
+ /**
+ * @see DATAMONGO-1245
+ */
+ @Test
+ public void valueTransformer() {
+
+ ExampleSpec exampleSpec = ExampleSpec.untyped(). //
+ withMatcher("age", matcher -> matcher.transform(value -> Integer.valueOf(50)));
+
+ assertThat(repository.findAll(Example.of(new User(null, "White", 99), exampleSpec)), hasItems(walter));
+ }
+
+}