├── .gitignore
├── .project
├── .rultor.yml
├── .settings
├── org.eclipse.core.resources.prefs
└── org.eclipse.m2e.core.prefs
├── .travis.yml
├── LICENSE
├── README.md
├── db-conversion-api
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── jeromeloisel
│ └── db
│ └── conversion
│ └── api
│ ├── JsonDeserializer.java
│ ├── JsonSerializationFactory.java
│ ├── JsonSerializer.java
│ └── package-info.java
├── db-conversion-jackson
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── jeromeloisel
│ │ └── db
│ │ └── conversion
│ │ └── jackson
│ │ ├── JacksonConversionException.java
│ │ ├── JacksonJsonDeserializer.java
│ │ ├── JacksonJsonSerializer.java
│ │ ├── JacksonSerializationFactory.java
│ │ └── package-info.java
│ └── test
│ └── java
│ └── com
│ └── jeromeloisel
│ └── db
│ └── conversion
│ └── jackson
│ ├── Animal.java
│ ├── ConversionTest.java
│ ├── JacksonConversionExceptionTest.java
│ ├── JacksonJsonDeserializerTest.java
│ ├── JacksonJsonSerializerTest.java
│ └── JacksonSerializationFactoryTest.java
├── db-entity
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── jeromeloisel
│ └── db
│ └── entity
│ ├── Document.java
│ ├── Entity.java
│ └── package-info.java
├── db-integration-test
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── jeromeloisel
│ │ ├── Application.java
│ │ └── db
│ │ └── integration
│ │ └── test
│ │ ├── NodeTestConfig.java
│ │ ├── SpringElasticSearchTest.java
│ │ ├── TestConversionConfig.java
│ │ └── package-info.java
│ └── test
│ └── java
│ └── com
│ └── jeromeloisel
│ └── db
│ └── integration
│ └── test
│ ├── ApplicationTest.java
│ └── ElasticTest.java
├── db-repository-api
├── .gitignore
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── jeromeloisel
│ └── db
│ └── repository
│ └── api
│ ├── DatabaseRepository.java
│ ├── DatabaseRepositoryFactory.java
│ └── package-info.java
├── db-repository-elasticsearch
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── jeromeloisel
│ │ └── db
│ │ └── repository
│ │ └── elasticsearch
│ │ ├── ElasticSearchRepository.java
│ │ ├── ElasticSearchRepositoryFactory.java
│ │ ├── EntityScroll.java
│ │ └── package-info.java
│ └── test
│ ├── java
│ └── com
│ │ └── jeromeloisel
│ │ └── db
│ │ └── repository
│ │ └── elasticsearch
│ │ ├── ElasticSearchRepositoryFactoryTest.java
│ │ ├── ElasticSearchRepositoryTest.java
│ │ ├── EntityScrollTest.java
│ │ ├── Person.java
│ │ ├── RepositoryIntegrationTest.java
│ │ └── TestRepositoryConfig.java
│ └── resources
│ └── datas.json
├── db-scroll-api
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── jeromeloisel
│ │ └── db
│ │ └── scroll
│ │ └── api
│ │ ├── DatabaseScroll.java
│ │ ├── DatabaseScrolling.java
│ │ └── DatabaseScrollingFactory.java
│ └── test
│ └── java
│ └── com
│ └── jeromeloisel
│ └── db
│ └── scroll
│ └── api
│ ├── DatabaseScrollTest.java
│ └── MockDatabaseScroll.java
├── db-scroll-elastic
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── jeromeloisel
│ │ └── database
│ │ └── scroll
│ │ └── elastic
│ │ ├── BulkDelete.java
│ │ ├── BulkIndex.java
│ │ ├── CompoundScroll.java
│ │ ├── ElasticScroll.java
│ │ ├── ElasticScrollingFactory.java
│ │ └── package-info.java
│ └── test
│ └── java
│ └── com
│ └── jeromeloisel
│ └── database
│ └── scroll
│ └── elastic
│ ├── BulkDeleteEsTest.java
│ ├── BulkDeleteTest.java
│ ├── BulkIndexEsTest.java
│ ├── BulkIndexTest.java
│ ├── CompoundScrollTest.java
│ ├── ElasticScrollTest.java
│ ├── ElasticScrollingFactoryEsTest.java
│ ├── ElasticScrollingFactoryTest.java
│ └── Person.java
├── db-spring-elasticsearch-starter
├── .gitignore
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── jeromeloisel
│ │ └── repository
│ │ └── elasticsearch
│ │ └── starter
│ │ ├── ElasticSearchRepositoryFactoryAutoConfiguration.java
│ │ ├── JacksonConversionAutoConfiguration.java
│ │ └── package-info.java
│ └── test
│ └── java
│ └── com
│ └── jeromeloisel
│ ├── Application.java
│ └── repository
│ └── elasticsearch
│ └── starter
│ ├── TestConfig.java
│ └── WiringTest.java
├── pom.xml
├── pubring.gpg.asc
├── secring.gpg.asc
└── settings.xml.asc
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | release.properties
7 | dependency-reduced-pom.xml
8 | buildNumber.properties
9 | *.iml
10 | .idea/
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | elasticsearch-crud
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.m2e.core.maven2Builder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.m2e.core.maven2Nature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.rultor.yml:
--------------------------------------------------------------------------------
1 | docker:
2 | image: "maven:3-jdk-8"
3 | decrypt:
4 | settings.xml: "repo/settings.xml.asc"
5 | pubring.gpg: "repo/pubring.gpg.asc"
6 | secring.gpg: "repo/secring.gpg.asc"
7 | release:
8 | script: |
9 | mvn versions:set "-DnewVersion=${tag}"
10 | git commit -am "${tag}"
11 | mvn clean deploy -Pelastic-crud --settings /home/r/settings.xml
12 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
4 | after_success:
5 | - mvn clean test jacoco:report coveralls:report
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/jloisel/elastic-crud)
2 | [](https://www.versioneye.com/user/projects/568d2e269c1b98002b000030)
3 | [](https://coveralls.io/github/jloisel/elastic-crud?branch=master)
4 | [](https://maven-badges.herokuapp.com/maven-central/com.jeromeloisel/db-spring-elasticsearch-starter)
5 | [](http://www.javadoc.io/doc/com.jeromeloisel/elastic-crud)
6 |
7 | ## Elasticsearch Simple CRUD Repository
8 |
9 | Easily perform Create / Read / Update / Delete operations on beans stored in Elasticsearch. [Spring Data Elasticsearch](https://github.com/spring-projects/spring-data-elasticsearch) lacks maintenance and is already a few Elasticsearch versions behind the latest version.
10 |
11 | This project powers our [JMeter Load Testing platform](https://octoperf.com).
12 |
13 | ### Versions
14 |
15 | The following table shows the correspondance between our versions and Elasticsearch versions:
16 |
17 | | Version | ElasticSearch Version |
18 | | ------------- |:---------------------:|
19 | | 1.1.x | 2.1.x |
20 | | 2.2.x | 2.2.x |
21 | | 2.3.x | 2.3.x |
22 | | 5.1.x | 5.1.x |
23 | | 5.6.x | 5.6.x |
24 |
25 | As of 2.2.x, the project is going to strictly follow the same versioning as [elasticsearch](https://github.com/elastic/elasticsearch).
26 |
27 | ### Spring
28 |
29 | Add the following Maven dependency to get started quickly with Spring:
30 |
31 | ```xml
32 |
33 | com.jeromeloisel
34 | db-spring-elasticsearch-starter
35 | 5.6.3
36 |
37 | ```
38 | ### Vanilla Java
39 |
40 | To get started with Vanilla Java application, you need to add two dependencies:
41 |
42 | ```xml
43 |
44 | com.jeromeloisel
45 | db-conversion-jackson
46 | 5.6.3
47 |
48 | ```
49 | This dependency provides the Jackson Json serialization mechanism.
50 |
51 | ```xml
52 |
53 | com.jeromeloisel
54 | db-repository-elasticsearch
55 | 5.6.3
56 |
57 | ```
58 |
59 | This dependency provides the **ElasticSearchRepositoryFactory** to create **ElasticRepository**.
60 |
61 | ### Java Example
62 |
63 | Suppose we would like to persist the following Bean in Elasticsearch:
64 |
65 | ```java
66 | @Value
67 | @Builder
68 | @Document(indexName="datas", type="person")
69 | public class Person implements Entity {
70 | @Wither
71 | String id;
72 | String firstname;
73 | String lastname;
74 |
75 | @JsonCreator
76 | Person(
77 | @JsonProperty("id") final String id,
78 | @JsonProperty("firstname") final String firstname,
79 | @JsonProperty("lastname") final String lastname) {
80 | super();
81 | this.id = id;
82 | this.firstname = checkNotNull(firstname);
83 | this.lastname = checkNotNull(lastname);
84 | }
85 | }
86 | ```
87 |
88 | The following code shows how to use the CRUD repository:
89 |
90 | ```java
91 | @Autowired
92 | private ElasticSearchRepositoryFactory factory;
93 |
94 | public void method() {
95 | final ElasticRepository repository = factory.create(Person.class);
96 |
97 | final Person person = Person.builder().id("").firstname("John").lastname("Smith").build();
98 | final Person withId = repository.save(person);
99 |
100 | // Find by id
101 | final Optional byId = repository.findOne(withId.getId());
102 | assertTrue(repository.exists(byId));
103 |
104 | // Search by firstname (with "not_analyzed" string mapping)
105 | final TermQueryBuilder term = new TermQueryBuilder("firstname", PERSON.getFirstname());
106 | final List found = repository.search(term);
107 | assertTrue(found.contains(byId));
108 |
109 | // Delete from Elasticsearch definitively
110 | repository.delete(withId.getId());
111 | assertFalse(repository.exists(byId));
112 | }
113 | ```
114 |
115 | Also, scrolling through massive amount of results is made dead easy with the scrolling API:
116 | ```java
117 | @Autowired
118 | private DatabaseScrollingFactory factory;
119 |
120 | public void example() {
121 | // Incorporated bulk delete
122 | factory
123 | .newScroll("myIndex")
124 | .withQuery(new MatchAllQueryBuilder())
125 | .scroll(factory.bulkDelete());
126 |
127 | }
128 | ```
129 | You simply have to implement the `DatabaseScroll` interface:
130 |
131 | ```java
132 | @FunctionalInterface
133 | public interface DatabaseScroll {
134 |
135 | default void onStartBatch() throws IOException {
136 |
137 | }
138 |
139 | void accept(SearchHit hit) throws IOException;
140 |
141 | default void onEndBatch() throws IOException {
142 |
143 | }
144 | }
145 |
146 | ```
147 |
148 | ### Type mapping
149 |
150 | Beans stored in Elasticsearch must have **_source** field enabled: see https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html. The following example Json shows how to enable _source field:
151 |
152 | ```json
153 | {
154 | "template": "datas",
155 | "settings": {
156 | "number_of_shards": 5,
157 | "number_of_replicas": 1,
158 | "index.refresh_interval": -1,
159 | },
160 | "mappings": {
161 | "_default_": {
162 | "_all": {
163 | "enabled": false
164 | },
165 | "_source": {
166 | "enabled": true
167 | }
168 | }
169 | }
170 | }
171 | ```
172 |
173 | ### Index refresh
174 |
175 | Every mutating query (insert, delete) performed on the index automatically refreshes it. I would recommend to disable index refresh as shows in the Json above.
176 |
177 | ### Json Serialization
178 |
179 | The Json serialization is configured to use [Jackson](https://github.com/FasterXML/jackson) by default. To use Jackson Json serialization, simply add Jackson as dependency:
180 |
181 | ```xml
182 |
183 | com.fasterxml.jackson.core
184 | jackson-databind
185 | ${jackson.version}
186 |
187 | ```
188 |
189 | Replace **${jackson.version}** with the version you are using.
190 |
191 | If you intend to use your own Json serialization mechanism (like Gson), please provide an implementation for the **JsonSerializationFactory** interface.
192 |
193 | ### Elasticsearch Client
194 |
195 | An instance of the [Elasticsearch Client](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/client.html) must be provided.
196 |
--------------------------------------------------------------------------------
/db-conversion-api/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-conversion-api/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-conversion-api
10 |
11 |
12 |
13 | com.jeromeloisel
14 | db-entity
15 | ${project.version}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/db-conversion-api/src/main/java/com/jeromeloisel/db/conversion/api/JsonDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.api;
2 |
3 | import java.util.function.Function;
4 |
5 | import com.jeromeloisel.db.entity.Entity;
6 |
7 | public interface JsonDeserializer extends Function {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/db-conversion-api/src/main/java/com/jeromeloisel/db/conversion/api/JsonSerializationFactory.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.api;
2 |
3 | import com.jeromeloisel.db.entity.Entity;
4 |
5 | public interface JsonSerializationFactory {
6 |
7 | JsonSerializer serializer(Class clazz);
8 |
9 | JsonDeserializer deserializer(Class clazz);
10 | }
11 |
--------------------------------------------------------------------------------
/db-conversion-api/src/main/java/com/jeromeloisel/db/conversion/api/JsonSerializer.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.api;
2 |
3 | import java.util.function.Function;
4 |
5 | import com.jeromeloisel.db.entity.Entity;
6 |
7 | public interface JsonSerializer extends Function {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/db-conversion-api/src/main/java/com/jeromeloisel/db/conversion/api/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Converts beans back and forth to/from Json.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.db.conversion.api;
8 |
--------------------------------------------------------------------------------
/db-conversion-jackson/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-conversion-jackson/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-conversion-jackson
10 |
11 |
12 |
13 | com.jeromeloisel
14 | db-conversion-api
15 | ${project.version}
16 |
17 |
18 | com.jeromeloisel
19 | db-entity
20 | ${project.version}
21 |
22 |
23 | com.fasterxml.jackson.core
24 | jackson-databind
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/main/java/com/jeromeloisel/db/conversion/jackson/JacksonConversionException.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static java.util.Objects.requireNonNull;
4 |
5 | class JacksonConversionException extends RuntimeException {
6 | private static final long serialVersionUID = -4917703394134871581L;
7 |
8 | JacksonConversionException(final String message, final Throwable e) {
9 | super(requireNonNull(message), requireNonNull(e));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/main/java/com/jeromeloisel/db/conversion/jackson/JacksonJsonDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static lombok.AccessLevel.PACKAGE;
4 | import static lombok.AccessLevel.PRIVATE;
5 |
6 | import java.io.IOException;
7 |
8 | import com.fasterxml.jackson.databind.ObjectMapper;
9 | import com.jeromeloisel.db.conversion.api.JsonDeserializer;
10 | import com.jeromeloisel.db.entity.Entity;
11 |
12 | import lombok.AllArgsConstructor;
13 | import lombok.NonNull;
14 | import lombok.experimental.FieldDefaults;
15 |
16 |
17 | @AllArgsConstructor(access=PACKAGE)
18 | @FieldDefaults(level=PRIVATE, makeFinal=true)
19 | final class JacksonJsonDeserializer implements JsonDeserializer {
20 | @NonNull
21 | ObjectMapper mapper;
22 | @NonNull
23 | Class clazz;
24 |
25 | @Override
26 | public T apply(final String json) {
27 | try {
28 | return mapper.readerFor(clazz).readValue(json);
29 | } catch (final IOException e) {
30 | throw new JacksonConversionException("Could not deserialize json", e);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/main/java/com/jeromeloisel/db/conversion/jackson/JacksonJsonSerializer.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static lombok.AccessLevel.PACKAGE;
4 | import static lombok.AccessLevel.PRIVATE;
5 |
6 | import com.fasterxml.jackson.core.JsonProcessingException;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
9 | import com.jeromeloisel.db.entity.Entity;
10 |
11 | import lombok.AllArgsConstructor;
12 | import lombok.NonNull;
13 | import lombok.experimental.FieldDefaults;
14 |
15 |
16 | @AllArgsConstructor(access=PACKAGE)
17 | @FieldDefaults(level=PRIVATE, makeFinal=true)
18 | final class JacksonJsonSerializer implements JsonSerializer {
19 | @NonNull
20 | ObjectMapper mapper;
21 |
22 | @Override
23 | public String apply(final T t) {
24 | try {
25 | return mapper.writeValueAsString(t);
26 | } catch (final JsonProcessingException e) {
27 | throw new JacksonConversionException("Could not serialize entity=" + t.getClass(), e);
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/main/java/com/jeromeloisel/db/conversion/jackson/JacksonSerializationFactory.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static lombok.AccessLevel.PRIVATE;
4 |
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import com.jeromeloisel.db.conversion.api.JsonDeserializer;
7 | import com.jeromeloisel.db.conversion.api.JsonSerializationFactory;
8 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
9 | import com.jeromeloisel.db.entity.Entity;
10 |
11 | import lombok.AllArgsConstructor;
12 | import lombok.NonNull;
13 | import lombok.experimental.FieldDefaults;
14 |
15 | @FieldDefaults(level=PRIVATE, makeFinal=true)
16 | @AllArgsConstructor
17 | public final class JacksonSerializationFactory implements JsonSerializationFactory {
18 | @NonNull
19 | ObjectMapper mapper;
20 |
21 | @Override
22 | public JsonSerializer serializer(final Class clazz) {
23 | return new JacksonJsonSerializer<>(mapper);
24 | }
25 |
26 | @Override
27 | public JsonDeserializer deserializer(final Class clazz) {
28 | return new JacksonJsonDeserializer<>(mapper, clazz);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/main/java/com/jeromeloisel/db/conversion/jackson/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Json Conversion using Jackson.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.db.conversion.jackson;
8 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/test/java/com/jeromeloisel/db/conversion/jackson/Animal.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static com.google.common.base.Preconditions.checkNotNull;
4 |
5 | import com.fasterxml.jackson.annotation.JsonCreator;
6 | import com.fasterxml.jackson.annotation.JsonProperty;
7 | import com.jeromeloisel.db.entity.Entity;
8 |
9 | import lombok.Builder;
10 | import lombok.Value;
11 | import lombok.experimental.Wither;
12 |
13 | @Value
14 | @Builder
15 | public class Animal implements Entity {
16 |
17 | @Wither
18 | String id;
19 | String name;
20 | String specy;
21 | boolean isNude;
22 |
23 | @JsonCreator
24 | public Animal(
25 | @JsonProperty("id") final String id,
26 | @JsonProperty("name") final String name,
27 | @JsonProperty("specy") final String specy,
28 | @JsonProperty("nude") final boolean isNude) {
29 | super();
30 | this.id = checkNotNull(id);
31 | this.name = checkNotNull(name);
32 | this.specy = checkNotNull(specy);
33 | this.isNude = checkNotNull(isNude);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/test/java/com/jeromeloisel/db/conversion/jackson/ConversionTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
4 | import static org.junit.Assert.assertEquals;
5 |
6 | import org.junit.Test;
7 |
8 | import com.fasterxml.jackson.databind.ObjectMapper;
9 | import com.google.common.testing.NullPointerTester;
10 | import com.jeromeloisel.db.conversion.api.JsonDeserializer;
11 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
12 |
13 | public class ConversionTest {
14 |
15 | private final ObjectMapper mapper = new ObjectMapper();
16 | private final JsonSerializer serializer = new JacksonJsonSerializer<>(mapper);
17 | private final JsonDeserializer deserializer = new JacksonJsonDeserializer<>(mapper, Animal.class);
18 |
19 | @Test
20 | public void shouldBijectivelyConvert() {
21 | final Animal animal = new Animal("id", "Tiger", "Panthera Tigris", false);
22 | final String json = serializer.apply(animal);
23 | final Animal deserialized = deserializer.apply(json);
24 | assertEquals(animal, deserialized);
25 | }
26 |
27 | @Test
28 | public void shouldPassNPETesterDeserializer() {
29 | new NullPointerTester().testConstructors(JacksonJsonDeserializer.class, PACKAGE);
30 | }
31 |
32 | @Test
33 | public void shouldPassNPETesterSerializer() {
34 | new NullPointerTester().testConstructors(JacksonJsonSerializer.class, PACKAGE);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/test/java/com/jeromeloisel/db/conversion/jackson/JacksonConversionExceptionTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
4 | import static org.junit.Assert.assertNotNull;
5 |
6 | import java.io.IOException;
7 |
8 | import org.junit.Test;
9 |
10 | import com.google.common.testing.NullPointerTester;
11 |
12 | public class JacksonConversionExceptionTest {
13 |
14 | @Test
15 | public void shouldPassNPETester() {
16 | new NullPointerTester().testConstructors(JacksonConversionException.class, PACKAGE);
17 | }
18 |
19 | @Test
20 | public void shouldThrow() {
21 | final Exception e = new JacksonConversionException("", new IOException());
22 | assertNotNull(e);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/test/java/com/jeromeloisel/db/conversion/jackson/JacksonJsonDeserializerTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.fasterxml.jackson.databind.ObjectReader;
6 | import com.google.common.testing.NullPointerTester;
7 | import com.jeromeloisel.db.entity.Entity;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.mockito.Mock;
12 | import org.mockito.runners.MockitoJUnitRunner;
13 |
14 | import java.io.IOException;
15 |
16 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
17 | import static org.mockito.Mockito.mock;
18 | import static org.mockito.Mockito.when;
19 |
20 | @RunWith(MockitoJUnitRunner.class)
21 | public class JacksonJsonDeserializerTest {
22 |
23 | @Mock
24 | private ObjectMapper mapper;
25 | @Mock
26 | private Entity entity;
27 | @Mock
28 | private ObjectReader reader;
29 |
30 | private JacksonJsonDeserializer function;
31 |
32 | @Before
33 | public void before() {
34 | when(mapper.readerFor(Entity.class)).thenReturn(reader);
35 | function = new JacksonJsonDeserializer<>(mapper, Entity.class);
36 | }
37 |
38 | @Test
39 | public void shouldPassNPETesterSerializer() {
40 | new NullPointerTester().testConstructors(JacksonJsonDeserializer.class, PACKAGE);
41 | }
42 |
43 | @Test(expected=JacksonConversionException.class)
44 | public void shouldThrow() throws IOException {
45 | when(reader.readValue("")).thenThrow(mock(JsonProcessingException.class));
46 | function.apply("");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/test/java/com/jeromeloisel/db/conversion/jackson/JacksonJsonSerializerTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.google.common.testing.NullPointerTester;
6 | import com.jeromeloisel.db.entity.Entity;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 | import org.mockito.Mock;
11 | import org.mockito.runners.MockitoJUnitRunner;
12 |
13 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
14 | import static org.mockito.Mockito.mock;
15 | import static org.mockito.Mockito.when;
16 |
17 | @RunWith(MockitoJUnitRunner.class)
18 | public class JacksonJsonSerializerTest {
19 |
20 | @Mock
21 | private ObjectMapper mapper;
22 | @Mock
23 | private Entity entity;
24 |
25 | private JacksonJsonSerializer serializer;
26 |
27 | @Before
28 | public void before() {
29 | serializer = new JacksonJsonSerializer<>(mapper);
30 | }
31 |
32 | @Test
33 | public void shouldPassNPETesterSerializer() {
34 | new NullPointerTester().testConstructors(JacksonJsonSerializer.class, PACKAGE);
35 | }
36 |
37 | @Test(expected=JacksonConversionException.class)
38 | public void shouldThrow() throws JsonProcessingException {
39 | when(mapper.writeValueAsString(entity)).thenThrow(mock(JsonProcessingException.class));
40 | serializer.apply(entity);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/db-conversion-jackson/src/test/java/com/jeromeloisel/db/conversion/jackson/JacksonSerializationFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.conversion.jackson;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.google.common.testing.NullPointerTester;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.mockito.Mock;
9 | import org.mockito.runners.MockitoJUnitRunner;
10 |
11 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
12 | import static org.junit.Assert.assertEquals;
13 |
14 | @RunWith(MockitoJUnitRunner.class)
15 | public class JacksonSerializationFactoryTest {
16 |
17 | @Mock
18 | private ObjectMapper mapper;
19 |
20 | private JacksonSerializationFactory factory;
21 |
22 | @Before
23 | public void before() {
24 | factory = new JacksonSerializationFactory(mapper);
25 | }
26 |
27 | @Test
28 | public void shouldCreateDeserializer() {
29 | assertEquals(JacksonJsonDeserializer.class, factory.deserializer(Animal.class).getClass());
30 | }
31 |
32 | @Test
33 | public void shouldCreateSerializer() {
34 | assertEquals(JacksonJsonSerializer.class, factory.serializer(Animal.class).getClass());
35 | }
36 |
37 | @Test
38 | public void shouldPassNPETesterSerializer() {
39 | new NullPointerTester().testConstructors(JacksonSerializationFactory.class, PACKAGE);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/db-entity/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-entity/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-entity
10 |
11 |
--------------------------------------------------------------------------------
/db-entity/src/main/java/com/jeromeloisel/db/entity/Document.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.entity;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.Target;
6 |
7 | import static java.lang.annotation.ElementType.TYPE;
8 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
9 |
10 | /**
11 | * Defines the index name and the type in which the document is stored.
12 | *
13 | * @author jerome
14 | *
15 | */
16 | @Documented
17 | @Retention(RUNTIME)
18 | @Target(TYPE)
19 | public @interface Document {
20 |
21 | /**
22 | * ElasticSearch index name.
23 | *
24 | * @return
25 | */
26 | String indexName();
27 |
28 | /**
29 | * Elasticsearch type associated to the annotated bean.
30 | *
31 | * @return
32 | */
33 | String type();
34 | }
35 |
--------------------------------------------------------------------------------
/db-entity/src/main/java/com/jeromeloisel/db/entity/Entity.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.entity;
2 |
3 | /**
4 | * Representation of an immutable entity.
5 | *
6 | * @author jerome
7 | *
8 | */
9 | public interface Entity {
10 |
11 | /**
12 | * Inject this field into the bean by using the {@link Id} annotation.
13 | *
14 | * @return id of the entity
15 | */
16 | String getId();
17 |
18 | /**
19 | * Returns a copy of the entity with this {@code id} set.
20 | *
21 | * @param id new id to set
22 | * @return
23 | */
24 | Entity withId(String id);
25 | }
26 |
--------------------------------------------------------------------------------
/db-entity/src/main/java/com/jeromeloisel/db/entity/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Beans stored on ElasticSearch implement this API.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.db.entity;
8 |
--------------------------------------------------------------------------------
/db-integration-test/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-integration-test/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-integration-test
10 |
11 |
12 |
13 | org.elasticsearch.client
14 | transport
15 |
16 |
17 | org.springframework
18 | spring-context
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter
23 | ${spring.boot.version}
24 |
25 |
26 | com.fasterxml.jackson.core
27 | jackson-databind
28 |
29 |
30 |
31 | org.springframework
32 | spring-test
33 | compile
34 |
35 |
36 | com.jeromeloisel
37 | db-conversion-jackson
38 | ${project.version}
39 |
40 |
41 | junit
42 | junit
43 | compile
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-test
48 | ${spring.boot.version}
49 | compile
50 |
51 |
52 | org.apache.logging.log4j
53 | log4j-core
54 | ${log4j.version}
55 |
56 |
57 |
--------------------------------------------------------------------------------
/db-integration-test/src/main/java/com/jeromeloisel/Application.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(final String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
--------------------------------------------------------------------------------
/db-integration-test/src/main/java/com/jeromeloisel/db/integration/test/NodeTestConfig.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.integration.test;
2 |
3 | import org.elasticsearch.client.Client;
4 | import org.elasticsearch.cluster.ClusterName;
5 | import org.elasticsearch.common.network.NetworkModule;
6 | import org.elasticsearch.common.settings.Settings;
7 | import org.elasticsearch.common.util.concurrent.EsExecutors;
8 | import org.elasticsearch.env.Environment;
9 | import org.elasticsearch.node.Node;
10 | import org.elasticsearch.node.NodeValidationException;
11 | import org.elasticsearch.script.ScriptService;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.context.annotation.Bean;
14 | import org.springframework.context.annotation.Configuration;
15 |
16 | import java.nio.file.Path;
17 |
18 | import static com.google.common.io.Files.createTempDir;
19 | import static org.elasticsearch.env.NodeEnvironment.NODE_ID_SEED_SETTING;
20 |
21 | @Configuration
22 | class NodeTestConfig {
23 |
24 | @Bean(destroyMethod="close")
25 | Node newNode() throws NodeValidationException {
26 | final Path tempDir = createTempDir().toPath();
27 | final Settings settings = Settings.builder()
28 | .put(ClusterName.CLUSTER_NAME_SETTING.getKey(), new ClusterName("single-node-cluster" + System.nanoTime()))
29 | .put(Environment.PATH_HOME_SETTING.getKey(), tempDir)
30 | .put(Environment.PATH_REPO_SETTING.getKey(), tempDir.resolve("repo"))
31 | .put(Environment.PATH_SHARED_DATA_SETTING.getKey(), createTempDir().getParent())
32 | .put("node.name", "single-node")
33 | .put("script.inline", "true")
34 | .put("script.stored", "true")
35 | .put(ScriptService.SCRIPT_MAX_COMPILATIONS_PER_MINUTE.getKey(), 1000)
36 | .put(EsExecutors.PROCESSORS_SETTING.getKey(), 1)
37 | .put(NetworkModule.HTTP_ENABLED.getKey(), false)
38 | .put("discovery.type", "zen")
39 | .put("transport.type", "local")
40 | .put(Node.NODE_DATA_SETTING.getKey(), true)
41 | .put(NODE_ID_SEED_SETTING.getKey(), System.nanoTime())
42 | .build();
43 | return new Node(settings).start(); // NOSONAR
44 | }
45 |
46 | @Bean(destroyMethod = "close")
47 | @Autowired
48 | Client client(final Node node) {
49 | return node.client();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/db-integration-test/src/main/java/com/jeromeloisel/db/integration/test/SpringElasticSearchTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.integration.test;
2 |
3 | import com.jeromeloisel.Application;
4 | import org.elasticsearch.client.Client;
5 | import org.elasticsearch.client.IndicesAdminClient;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.test.context.junit4.SpringRunner;
11 |
12 | import static org.junit.Assert.assertNotNull;
13 |
14 | @RunWith(SpringRunner.class)
15 | @SpringBootTest(classes=Application.class)
16 | public abstract class SpringElasticSearchTest {
17 | @Autowired
18 | protected Client client;
19 |
20 | @Test
21 | public void shouldAutowireClient() {
22 | assertNotNull(client);
23 | }
24 |
25 | protected final Client elasticClient() {
26 | return client;
27 | }
28 |
29 | protected final void flush(final String index) {
30 | final IndicesAdminClient indices = elasticClient().admin().indices();
31 | indices.prepareFlush(index).execute().actionGet();
32 | indices.prepareRefresh(index).execute().actionGet();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/db-integration-test/src/main/java/com/jeromeloisel/db/integration/test/TestConversionConfig.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.integration.test;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import com.jeromeloisel.db.conversion.api.JsonSerializationFactory;
9 | import com.jeromeloisel.db.conversion.jackson.JacksonSerializationFactory;
10 |
11 | @Configuration
12 | class TestConversionConfig {
13 |
14 | @Bean
15 | ObjectMapper objectMapper() {
16 | final ObjectMapper mapper = new ObjectMapper();
17 | mapper.findAndRegisterModules();
18 | return mapper;
19 | }
20 |
21 | @Bean
22 | @Autowired
23 | JsonSerializationFactory jsonSerializationFactory(final ObjectMapper mapper) {
24 | return new JacksonSerializationFactory(mapper);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/db-integration-test/src/main/java/com/jeromeloisel/db/integration/test/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Database Repository Test module.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.db.integration.test;
8 |
--------------------------------------------------------------------------------
/db-integration-test/src/test/java/com/jeromeloisel/db/integration/test/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.integration.test;
2 |
3 | import com.jeromeloisel.Application;
4 | import org.junit.Test;
5 |
6 | public class ApplicationTest {
7 |
8 | @Test
9 | public void shouldRunMain() throws Exception {
10 | Application.main(new String[0]);
11 | }
12 | }
--------------------------------------------------------------------------------
/db-integration-test/src/test/java/com/jeromeloisel/db/integration/test/ElasticTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.integration.test;
2 |
3 | import org.elasticsearch.client.IndicesAdminClient;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertNotNull;
7 |
8 | public class ElasticTest extends SpringElasticSearchTest {
9 |
10 | @Test
11 | public void shouldAutowire() {
12 | assertNotNull(client);
13 | final IndicesAdminClient indices = client.admin().indices();
14 | indices.prepareCreate("test").execute().actionGet();
15 | flush("test");
16 | indices.prepareDelete("test").execute().actionGet();
17 | }
18 |
19 | @Test
20 | @Override
21 | public void shouldAutowireClient() {
22 | super.shouldAutowireClient();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/db-repository-api/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-repository-api/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-repository-api
10 |
11 |
12 |
13 | com.jeromeloisel
14 | db-entity
15 | ${project.version}
16 |
17 |
18 | org.elasticsearch.client
19 | transport
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/db-repository-api/src/main/java/com/jeromeloisel/db/repository/api/DatabaseRepository.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.api;
2 |
3 | import com.jeromeloisel.db.entity.Entity;
4 | import org.elasticsearch.action.support.WriteRequest;
5 | import org.elasticsearch.index.query.QueryBuilder;
6 |
7 | import java.util.Collection;
8 | import java.util.List;
9 | import java.util.Optional;
10 | import java.util.function.Consumer;
11 |
12 | /**
13 | * Basic Crud Database repository.
14 | *
15 | * @author jerome
16 | *
17 | * @param type of entity to store
18 | */
19 | public interface DatabaseRepository {
20 |
21 | /**
22 | * Sets the index refresh setting.
23 | *
24 | * @param refresh refresh policy
25 | */
26 | void refreshPolicy(WriteRequest.RefreshPolicy refresh);
27 |
28 | /**
29 | * Searches using the given request.
30 | *
31 | * @param query
32 | * @return matching items
33 | */
34 | List search(QueryBuilder query);
35 |
36 | /**
37 | * Saves a given result. Use the returned instance for further operations as the save operation might have changed the
38 | * result instance completely.
39 | *
40 | * @param entity
41 | * @return the saved result
42 | */
43 | T save(T entity);
44 |
45 | /**
46 | * Saves all given entities.
47 | *
48 | * @param entities
49 | * @return the saved entities
50 | * @throws IllegalArgumentException in case the given result is (@literal null}.
51 | */
52 | List saveAll(List entities);
53 |
54 | /**
55 | * Retrieves an result by its id.
56 | *
57 | * @param id must not be {@literal null}.
58 | * @return the result with the given id or {@literal null} if none found
59 | * @throws IllegalArgumentException if {@code id} is {@literal null}
60 | */
61 | Optional findOne(String id);
62 |
63 | /**
64 | * Returns whether an result with the given id exists.
65 | *
66 | * @param id must not be {@literal null}.
67 | * @return true if an result with the given id exists, {@literal false} otherwise
68 | * @throws IllegalArgumentException if {@code id} is {@literal null}
69 | */
70 | boolean exists(String id);
71 |
72 | /**
73 | * Returns whether an result exists.
74 | *
75 | * @param entity must not be {@code null}
76 | * @return true if an result exists, {@literal false} otherwise
77 | * @throws IllegalArgumentException if {@code id} is {@literal null}
78 | */
79 | boolean exists(T entity);
80 |
81 | /**
82 | * Returns all instances of the type with the given IDs.
83 | *
84 | * @param ids
85 | * @return
86 | */
87 | List findAll(List ids);
88 |
89 | /**
90 | * Deletes the result with the given id.
91 | *
92 | * @param id must not be {@literal null}.
93 | * @return the deleted result id
94 | * @throws IllegalArgumentException in case the given {@code id} is {@literal null}
95 | */
96 | String delete(String id);
97 |
98 | /**
99 | * Deletes a given result.
100 | *
101 | * @param entity
102 | * @return the deleted result id
103 | * @throws IllegalArgumentException in case the given result is (@literal null}.
104 | */
105 | String delete(T entity);
106 |
107 | /**
108 | * Deletes all the given entities at once.
109 | *
110 | * @param entities
111 | */
112 | List deleteAll(Collection entities);
113 |
114 | /**
115 | * Deletes all the given entities at once.
116 | *
117 | * @param ids
118 | */
119 | List deleteAllIds(Collection ids);
120 |
121 | /**
122 | * Deletes all the entities matching the given query.
123 | *
124 | * @param query query
125 | * @return matching items
126 | */
127 | void deleteAllByQuery(QueryBuilder query);
128 |
129 | /**
130 | * Scrolls over the whole results and consume them.
131 | *
132 | * @param query elastic query
133 | * @param consumer consumes each item
134 | */
135 | void scroll(QueryBuilder query, Consumer consumer);
136 | }
137 |
--------------------------------------------------------------------------------
/db-repository-api/src/main/java/com/jeromeloisel/db/repository/api/DatabaseRepositoryFactory.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.api;
2 |
3 | import com.jeromeloisel.db.entity.Entity;
4 |
5 | public interface DatabaseRepositoryFactory {
6 |
7 | DatabaseRepository create(Class clazz);
8 | }
9 |
--------------------------------------------------------------------------------
/db-repository-api/src/main/java/com/jeromeloisel/db/repository/api/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Elasticsearch Repository API.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.db.repository.api;
8 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-repository-elasticsearch
10 |
11 |
12 |
13 | com.jeromeloisel
14 | db-repository-api
15 | ${project.version}
16 |
17 |
18 | com.jeromeloisel
19 | db-conversion-api
20 | ${project.version}
21 |
22 |
23 | com.jeromeloisel
24 | db-scroll-api
25 | ${project.version}
26 |
27 |
28 |
29 | org.elasticsearch.client
30 | transport
31 |
32 |
33 |
34 | com.jeromeloisel
35 | db-integration-test
36 | ${project.version}
37 | test
38 |
39 |
40 | com.jeromeloisel
41 | db-scroll-elastic
42 | ${project.version}
43 | test
44 |
45 |
46 | commons-io
47 | commons-io
48 | test
49 |
50 |
51 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/main/java/com/jeromeloisel/db/repository/elasticsearch/ElasticSearchRepository.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import com.google.common.annotations.VisibleForTesting;
4 | import com.google.common.collect.ImmutableList;
5 | import com.google.common.collect.ImmutableList.Builder;
6 | import com.google.common.collect.ImmutableSet;
7 | import com.jeromeloisel.db.conversion.api.JsonDeserializer;
8 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
9 | import com.jeromeloisel.db.entity.Entity;
10 | import com.jeromeloisel.db.repository.api.DatabaseRepository;
11 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
12 | import lombok.AllArgsConstructor;
13 | import lombok.NonNull;
14 | import lombok.experimental.FieldDefaults;
15 | import org.elasticsearch.action.bulk.BulkItemResponse;
16 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
17 | import org.elasticsearch.action.bulk.BulkResponse;
18 | import org.elasticsearch.action.get.GetResponse;
19 | import org.elasticsearch.action.get.MultiGetItemResponse;
20 | import org.elasticsearch.action.get.MultiGetResponse;
21 | import org.elasticsearch.action.index.IndexRequestBuilder;
22 | import org.elasticsearch.action.support.WriteRequest;
23 | import org.elasticsearch.client.Client;
24 | import org.elasticsearch.index.query.QueryBuilder;
25 |
26 | import java.util.Collection;
27 | import java.util.List;
28 | import java.util.Optional;
29 | import java.util.concurrent.atomic.AtomicReference;
30 | import java.util.function.Consumer;
31 |
32 | import static com.google.common.base.Strings.emptyToNull;
33 | import static java.util.Optional.ofNullable;
34 | import static java.util.stream.Collectors.toList;
35 | import static lombok.AccessLevel.PACKAGE;
36 | import static lombok.AccessLevel.PRIVATE;
37 | import static org.elasticsearch.common.xcontent.XContentType.JSON;
38 |
39 | @AllArgsConstructor(access = PACKAGE)
40 | @FieldDefaults(level = PRIVATE, makeFinal = true)
41 | final class ElasticSearchRepository implements DatabaseRepository {
42 | @VisibleForTesting
43 | public static final int SCROLL_SIZE = 100;
44 |
45 | @NonNull
46 | String index;
47 | @NonNull
48 | String type;
49 | @NonNull
50 | Client client;
51 | @NonNull
52 | JsonSerializer serializer;
53 | @NonNull
54 | JsonDeserializer deserializer;
55 | @NonNull
56 | DatabaseScrollingFactory scrolling;
57 | @NonNull
58 | AtomicReference policy;
59 |
60 | @Override
61 | public List search(final QueryBuilder query) {
62 | final Builder builder = ImmutableList.builder();
63 | scroll(query, builder::add);
64 | return builder.build();
65 | }
66 |
67 | @Override
68 | public void deleteAllByQuery(final QueryBuilder query) {
69 | final Builder builder = ImmutableList.builder();
70 | scroll(query, e -> builder.add(e.getId()));
71 | final List ids = builder.build();
72 | if(ids.isEmpty()) {
73 | return;
74 | }
75 |
76 | deleteAllIds(ids);
77 | }
78 |
79 | @Override
80 | public void scroll(final QueryBuilder query, final Consumer consumer) {
81 | scrolling
82 | .newScroll(index)
83 | .withQuery(query)
84 | .withFetchSource(true)
85 | .withTypes(type)
86 | .scroll(new EntityScroll<>(deserializer, consumer));
87 | }
88 |
89 | @Override
90 | public T save(final T entity) {
91 | return saveAll(ImmutableList.of(entity)).get(0);
92 | }
93 |
94 | @Override
95 | public List saveAll(final List entities) {
96 | if(entities.isEmpty()) {
97 | return entities;
98 | }
99 |
100 | final BulkRequestBuilder bulk = client
101 | .prepareBulk()
102 | .setRefreshPolicy(policy.get());
103 |
104 | for(final T entity : entities) {
105 | final String source = serializer.apply(entity);
106 | final IndexRequestBuilder request = client
107 | .prepareIndex(index, type)
108 | .setSource(source, JSON);
109 | ofNullable(emptyToNull(entity.getId())).ifPresent(request::setId);
110 | bulk.add(request);
111 | }
112 |
113 | final BulkResponse response = bulk.execute().actionGet();
114 | final BulkItemResponse[] items = response.getItems();
115 |
116 | final ImmutableList.Builder saved = ImmutableList.builder();
117 | for(int i=0; i findOne(final String id) {
144 | final GetResponse response = client
145 | .prepareGet(index, type, id)
146 | .setFetchSource(true)
147 | .execute()
148 | .actionGet();
149 | final String json = response.getSourceAsString();
150 | return ofNullable(json)
151 | .map(deserializer)
152 | .map(e -> (T) e.withId(id));
153 | }
154 |
155 | @SuppressWarnings("unchecked")
156 | @Override
157 | public List findAll(final List ids) {
158 | if (ids.isEmpty()) {
159 | return ImmutableList.of();
160 | }
161 |
162 | final Builder builder = ImmutableList.builder();
163 |
164 | final MultiGetResponse response = client
165 | .prepareMultiGet()
166 | .add(index, type, ids)
167 | .execute()
168 | .actionGet();
169 |
170 | for(final MultiGetItemResponse item : response.getResponses()) {
171 | final GetResponse get = item.getResponse();
172 | if(get.isSourceEmpty()) {
173 | continue;
174 | }
175 |
176 | final String json = get.getSourceAsString();
177 | final T entity = deserializer.apply(json);
178 | builder.add((T) entity.withId(get.getId()));
179 | }
180 |
181 | return builder.build();
182 | }
183 |
184 | @Override
185 | public String delete(final T entity) {
186 | return deleteAll(ImmutableList.of(entity)).get(0);
187 | }
188 |
189 | @Override
190 | public List deleteAll(final Collection entities) {
191 | return deleteAllIds(entities.stream().map(Entity::getId).collect(toList()));
192 | }
193 |
194 | @Override
195 | public String delete(final String id) {
196 | return deleteAllIds(ImmutableSet.of(id)).get(0);
197 | }
198 |
199 | @Override
200 | public List deleteAllIds(final Collection ids) {
201 | if (ids.isEmpty()) {
202 | return ImmutableList.of();
203 | }
204 |
205 | final BulkRequestBuilder bulk = client
206 | .prepareBulk()
207 | .setRefreshPolicy(policy.get());
208 |
209 | for (final String id : ids) {
210 | bulk.add(client.prepareDelete(index, type, id));
211 | }
212 |
213 | final BulkResponse response = bulk.execute().actionGet();
214 |
215 | final ImmutableList.Builder builder = ImmutableList.builder();
216 | for (final BulkItemResponse item : response.getItems()) {
217 | builder.add(item.getId());
218 | }
219 | return builder.build();
220 | }
221 |
222 | @Override
223 | public void refreshPolicy(final WriteRequest.RefreshPolicy refresh) {
224 | policy.set(refresh);
225 | }
226 | }
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/main/java/com/jeromeloisel/db/repository/elasticsearch/ElasticSearchRepositoryFactory.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonDeserializer;
4 | import com.jeromeloisel.db.conversion.api.JsonSerializationFactory;
5 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
6 | import com.jeromeloisel.db.entity.Document;
7 | import com.jeromeloisel.db.entity.Entity;
8 | import com.jeromeloisel.db.repository.api.DatabaseRepository;
9 | import com.jeromeloisel.db.repository.api.DatabaseRepositoryFactory;
10 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
11 | import lombok.AllArgsConstructor;
12 | import lombok.NonNull;
13 | import lombok.experimental.FieldDefaults;
14 | import org.elasticsearch.client.Client;
15 |
16 | import java.util.concurrent.atomic.AtomicReference;
17 |
18 | import static com.google.common.base.Preconditions.checkState;
19 | import static lombok.AccessLevel.PRIVATE;
20 | import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
21 |
22 | @AllArgsConstructor
23 | @FieldDefaults(level=PRIVATE, makeFinal = true)
24 | public final class ElasticSearchRepositoryFactory implements DatabaseRepositoryFactory {
25 | @NonNull
26 | JsonSerializationFactory factory;
27 | @NonNull
28 | Client client;
29 | @NonNull
30 | DatabaseScrollingFactory scrolling;
31 |
32 | @Override
33 | public DatabaseRepository create(final Class clazz) {
34 | checkState(
35 | clazz.isAnnotationPresent(Document.class),
36 | "%s must be annotated with @Document",
37 | clazz.getName());
38 |
39 | final Document document = clazz.getDeclaredAnnotation(Document.class);
40 | final JsonSerializer serializer = factory.serializer(clazz);
41 | final JsonDeserializer deserializer = factory.deserializer(clazz);
42 |
43 | final String index = document.indexName();
44 | final String type = document.type();
45 |
46 | return new ElasticSearchRepository<>(
47 | index,
48 | type,
49 | client,
50 | serializer,
51 | deserializer,
52 | scrolling,
53 | new AtomicReference<>(IMMEDIATE)
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/main/java/com/jeromeloisel/db/repository/elasticsearch/EntityScroll.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonDeserializer;
4 | import com.jeromeloisel.db.entity.Entity;
5 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
6 | import lombok.AllArgsConstructor;
7 | import lombok.NonNull;
8 | import lombok.experimental.FieldDefaults;
9 | import org.elasticsearch.search.SearchHit;
10 |
11 | import java.util.function.Consumer;
12 |
13 | import static lombok.AccessLevel.PACKAGE;
14 | import static lombok.AccessLevel.PRIVATE;
15 |
16 | @AllArgsConstructor(access = PACKAGE)
17 | @FieldDefaults(level = PRIVATE, makeFinal = true)
18 | final class EntityScroll implements DatabaseScroll {
19 | @NonNull
20 | JsonDeserializer deserializer;
21 | @NonNull
22 | Consumer consumer;
23 |
24 | @Override
25 | public void accept(final SearchHit hit) {
26 | final String source = hit.getSourceAsString();
27 | @SuppressWarnings("unchecked")
28 | final T entity = (T) deserializer.apply(source).withId(hit.getId());
29 | consumer.accept(entity);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/main/java/com/jeromeloisel/db/repository/elasticsearch/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Elasticsearch Database Repository.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.db.repository.elasticsearch;
8 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/java/com/jeromeloisel/db/repository/elasticsearch/ElasticSearchRepositoryFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
4 |
5 | import org.junit.Test;
6 |
7 | import com.google.common.testing.NullPointerTester;
8 |
9 | public class ElasticSearchRepositoryFactoryTest {
10 |
11 | @Test
12 | public void shouldPassNPETester() {
13 | new NullPointerTester().testConstructors(ElasticSearchRepositoryFactory.class, PACKAGE);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/java/com/jeromeloisel/db/repository/elasticsearch/ElasticSearchRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
4 |
5 | import org.junit.Test;
6 |
7 | import com.google.common.testing.NullPointerTester;
8 |
9 | public class ElasticSearchRepositoryTest {
10 |
11 | @Test
12 | public void shouldPassNPETester() {
13 | new NullPointerTester().testConstructors(ElasticSearchRepository.class, PACKAGE);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/java/com/jeromeloisel/db/repository/elasticsearch/EntityScrollTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import com.google.common.testing.NullPointerTester;
4 | import org.junit.Test;
5 |
6 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
7 |
8 | public class EntityScrollTest {
9 |
10 | @Test
11 | public void shouldPassNPETester() {
12 | new NullPointerTester().testConstructors(EntityScroll.class, PACKAGE);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/java/com/jeromeloisel/db/repository/elasticsearch/Person.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import static com.google.common.base.Preconditions.checkNotNull;
4 |
5 | import com.fasterxml.jackson.annotation.JsonCreator;
6 | import com.fasterxml.jackson.annotation.JsonProperty;
7 | import com.jeromeloisel.db.entity.Document;
8 | import com.jeromeloisel.db.entity.Entity;
9 |
10 | import lombok.Builder;
11 | import lombok.Value;
12 | import lombok.experimental.Wither;
13 |
14 | @Value
15 | @Builder
16 | @Document(indexName="datas", type="person")
17 | public class Person implements Entity {
18 | @Wither
19 | String id;
20 | String firstname;
21 | String lastname;
22 |
23 | @JsonCreator
24 | Person(
25 | @JsonProperty("id") final String id,
26 | @JsonProperty("firstname") final String firstname,
27 | @JsonProperty("lastname") final String lastname) {
28 | super();
29 | this.id = id;
30 | this.firstname = checkNotNull(firstname);
31 | this.lastname = checkNotNull(lastname);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/java/com/jeromeloisel/db/repository/elasticsearch/RepositoryIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.jeromeloisel.Application;
5 | import com.jeromeloisel.db.repository.api.DatabaseRepository;
6 | import com.jeromeloisel.db.repository.api.DatabaseRepositoryFactory;
7 | import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
8 | import org.elasticsearch.client.Client;
9 | import org.elasticsearch.client.IndicesAdminClient;
10 | import org.elasticsearch.index.query.BoolQueryBuilder;
11 | import org.elasticsearch.index.query.MatchAllQueryBuilder;
12 | import org.elasticsearch.index.query.TermQueryBuilder;
13 | import org.junit.After;
14 | import org.junit.Before;
15 | import org.junit.Test;
16 | import org.junit.runner.RunWith;
17 | import org.springframework.beans.factory.annotation.Autowired;
18 | import org.springframework.boot.test.context.SpringBootTest;
19 | import org.springframework.test.context.junit4.SpringRunner;
20 |
21 | import java.io.IOException;
22 | import java.util.List;
23 |
24 | import static com.google.common.base.Preconditions.checkState;
25 | import static org.apache.commons.io.IOUtils.toByteArray;
26 | import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
27 | import static org.elasticsearch.common.xcontent.XContentType.JSON;
28 | import static org.junit.Assert.assertEquals;
29 | import static org.junit.Assert.assertFalse;
30 | import static org.junit.Assert.assertNotEquals;
31 | import static org.junit.Assert.assertTrue;
32 |
33 | @RunWith(SpringRunner.class)
34 | @SpringBootTest(classes=Application.class)
35 | public class RepositoryIntegrationTest {
36 |
37 | private static final Person PERSON = Person
38 | .builder()
39 | .id("")
40 | .firstname("John")
41 | .lastname("Smith")
42 | .build();
43 |
44 | @Autowired
45 | private Client client;
46 | @Autowired
47 | private DatabaseRepositoryFactory factory;
48 |
49 | private DatabaseRepository repository;
50 |
51 | @Before
52 | public void before() throws IOException {
53 | repository = factory.create(Person.class);
54 | repository.refreshPolicy(IMMEDIATE);
55 | final IndicesAdminClient indices = client.admin().indices();
56 |
57 | final PutIndexTemplateRequest datas = indices.preparePutTemplate("datas")
58 | .setSource(toByteArray(getClass().getResourceAsStream("/datas.json")), JSON)
59 | .request();
60 | checkState(indices.putTemplate(datas).actionGet().isAcknowledged());
61 | }
62 |
63 | @Test
64 | public void shouldSaveTwice() {
65 | final ImmutableList.Builder builder = ImmutableList.builder();
66 | for(int i=0; i< ElasticSearchRepository.SCROLL_SIZE * 2;i++) {
67 | builder.add(PERSON);
68 | }
69 | final List persons = builder.build();
70 |
71 | final List saved = repository.saveAll(persons);
72 | assertEquals(persons.size(), saved.size());
73 |
74 | final List found = repository.search(new MatchAllQueryBuilder());
75 | assertEquals(persons.size(), found.size());
76 |
77 | repository.deleteAll(saved);
78 | }
79 |
80 | @Test
81 | public void shouldDeleteAllByQuery() {
82 | final Person saved = repository.save(PERSON);
83 | final TermQueryBuilder byFirstname = new TermQueryBuilder("firstname", PERSON.getFirstname());
84 | repository.deleteAllByQuery(byFirstname);
85 | assertFalse(repository.exists(saved));
86 | }
87 |
88 | @Test
89 | public void shouldNotDeleteAllByIds() {
90 | final List ids = repository.deleteAllIds(ImmutableList.of());
91 | assertTrue(ids.isEmpty());
92 | }
93 |
94 | @Test
95 | public void shouldNotDeleteAllByQuery() {
96 | final Person saved = repository.save(PERSON);
97 | final TermQueryBuilder byFirstname = new TermQueryBuilder("firstname", PERSON.getLastname());
98 | repository.deleteAllByQuery(byFirstname);
99 | assertTrue(repository.exists(saved));
100 | repository.delete(saved);
101 | }
102 |
103 | @Test
104 | public void shouldNotDeleteById() {
105 | final Person saved = repository.save(PERSON);
106 | repository.delete(saved.getId() + "invalid");
107 | assertTrue(repository.exists(saved));
108 | repository.delete(saved.getId());
109 | assertFalse(repository.exists(saved));
110 | }
111 |
112 | @Test
113 | public void shouldScanAndScroll() {
114 | final Person saved = repository.save(PERSON);
115 | final Person anotherSave = repository.save(saved);
116 | assertEquals(anotherSave.getId(), saved.getId());
117 | repository.delete(saved);
118 | }
119 |
120 | @Test
121 | public void shouldDeleteById() {
122 | final Person saved = repository.save(PERSON);
123 | repository.delete(saved.getId());
124 | assertFalse(repository.exists(saved));
125 | }
126 |
127 | @Test
128 | public void shouldExistsById() {
129 | final Person saved = repository.save(PERSON);
130 | assertTrue(repository.exists(saved.getId()));
131 | repository.delete(saved.getId());
132 | assertFalse(repository.exists(saved.getId()));
133 | }
134 |
135 | @Test
136 | public void shouldFindOne() {
137 | final Person save = repository.save(PERSON);
138 | assertEquals(save, repository.findOne(save.getId()).get());
139 | repository.delete(save);
140 | }
141 |
142 | @Test
143 | public void shouldFindAll() {
144 | final Person save = repository.save(PERSON);
145 | assertEquals(ImmutableList.of(save), repository.findAll(ImmutableList.of(save.getId())));
146 | repository.delete(save);
147 | }
148 |
149 | @Test
150 | public void shouldNotFindAll() {
151 | assertEquals(ImmutableList.of(), repository.findAll(ImmutableList.of()));
152 | }
153 |
154 | @Test
155 | public void shouldNotFindAllIfInvalid() {
156 | assertEquals(ImmutableList.of(), repository.findAll(ImmutableList.of("invalid")));
157 | }
158 |
159 | @Test
160 | public void shouldSaveAll() {
161 | final List saved = repository.saveAll(ImmutableList.of(PERSON));
162 | assertEquals(1, saved.size());
163 | repository.delete(saved.get(0));
164 | }
165 |
166 | @Test
167 | public void shouldNotSaveAll() {
168 | final List saved = repository.saveAll(ImmutableList.of());
169 | assertEquals(0, saved.size());
170 | }
171 |
172 | @Test
173 | public void shouldFindByFirstname() {
174 | final Person saved = repository.save(PERSON);
175 | final TermQueryBuilder term = new TermQueryBuilder("firstname", PERSON.getFirstname());
176 |
177 | final List search = repository.search(term);
178 | assertEquals(1, search.size());
179 | assertEquals(saved, search.get(0));
180 | repository.delete(saved);
181 | }
182 |
183 | @Test
184 | public void shouldFindByFirstnameAndLastname() {
185 | final Person saved = repository.save(PERSON);
186 | final TermQueryBuilder byFirstname = new TermQueryBuilder("firstname", PERSON.getFirstname());
187 | final TermQueryBuilder byLastname = new TermQueryBuilder("lastname", PERSON.getLastname());
188 | final BoolQueryBuilder bool = new BoolQueryBuilder()
189 | .must(byFirstname)
190 | .must(byLastname);
191 |
192 | final List search = repository.search(bool);
193 | assertEquals(1, search.size());
194 | assertEquals(saved, search.get(0));
195 | repository.delete(saved);
196 | }
197 |
198 | @Test
199 | public void shouldTest() {
200 | final Person saved = repository.save(PERSON);
201 | assertNotEquals("", saved.getId());
202 | final String id = repository.delete(saved);
203 | assertEquals(saved.getId(), id);
204 | }
205 |
206 | @After
207 | public void after() {
208 | repository.delete(PERSON);
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/java/com/jeromeloisel/db/repository/elasticsearch/TestRepositoryConfig.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.repository.elasticsearch;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonSerializationFactory;
4 | import com.jeromeloisel.db.repository.api.DatabaseRepositoryFactory;
5 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
6 | import org.elasticsearch.client.Client;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | @Configuration
11 | class TestRepositoryConfig {
12 |
13 | @Bean
14 | DatabaseRepositoryFactory databaseRepositoryFactory(
15 | final Client client,
16 | final JsonSerializationFactory serialization,
17 | final DatabaseScrollingFactory factory) {
18 | return new ElasticSearchRepositoryFactory(serialization, client, factory);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/db-repository-elasticsearch/src/test/resources/datas.json:
--------------------------------------------------------------------------------
1 | {
2 | "template": "datas",
3 | "settings": {
4 | "number_of_shards": 1,
5 | "number_of_replicas": 1,
6 | "index.refresh_interval": -1
7 | },
8 | "mappings": {
9 | "_default_": {
10 | "_all": {
11 | "enabled": false
12 | },
13 | "_source": {
14 | "enabled": true
15 | },
16 | "dynamic_templates": [
17 | {
18 | "strings": {
19 | "match_mapping_type": "string",
20 | "mapping": {
21 | "type": "keyword"
22 | }
23 | }
24 | }
25 | ]
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/db-scroll-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | elasticsearch-crud
7 | com.jeromeloisel
8 | 5.6.4-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | db-scroll-api
13 |
14 |
15 |
16 | org.elasticsearch.client
17 | transport
18 |
19 |
20 |
--------------------------------------------------------------------------------
/db-scroll-api/src/main/java/com/jeromeloisel/db/scroll/api/DatabaseScroll.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.scroll.api;
2 |
3 | import org.elasticsearch.search.SearchHit;
4 |
5 | import java.io.IOException;
6 |
7 | @FunctionalInterface
8 | public interface DatabaseScroll {
9 |
10 | default void onStartBatch() throws IOException {
11 |
12 | }
13 |
14 | void accept(SearchHit hit) throws IOException;
15 |
16 | default void onEndBatch() throws IOException {
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/db-scroll-api/src/main/java/com/jeromeloisel/db/scroll/api/DatabaseScrolling.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.scroll.api;
2 |
3 | import org.elasticsearch.index.query.QueryBuilder;
4 | import org.elasticsearch.search.sort.FieldSortBuilder;
5 |
6 | import java.util.concurrent.TimeUnit;
7 |
8 | public interface DatabaseScrolling {
9 |
10 | /**
11 | * Defaults to _doc sort.
12 | *
13 | * @param sort
14 | * @return
15 | */
16 | DatabaseScrolling withSort(FieldSortBuilder sort);
17 |
18 | /**
19 | * Defaults to 100.
20 | *
21 | * @param size
22 | * @return
23 | */
24 | DatabaseScrolling withScrollSize(int size);
25 |
26 | /**
27 | * Defaults to 1 minute.
28 | *
29 | * @param time
30 | * @param unit
31 | * @return
32 | */
33 | DatabaseScrolling withKeepAlive(long time, TimeUnit unit);
34 |
35 | /**
36 | * Defaults to all index types.
37 | * @param types
38 | * @return
39 | */
40 | DatabaseScrolling withTypes(String... types);
41 |
42 | /**
43 | * Defaults to true.
44 | *
45 | * @param fetchSource if source should be fetched
46 | * @return
47 | */
48 | DatabaseScrolling withFetchSource(boolean fetchSource);
49 |
50 | /**
51 | * By default, uses {@link org.elasticsearch.index.query.MatchAllQueryBuilder}.
52 | *
53 | * @param query the query
54 | */
55 | DatabaseScrolling withQuery(QueryBuilder query);
56 |
57 | void scroll(DatabaseScroll scroll);
58 | }
59 |
--------------------------------------------------------------------------------
/db-scroll-api/src/main/java/com/jeromeloisel/db/scroll/api/DatabaseScrollingFactory.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.scroll.api;
2 |
3 | import org.elasticsearch.action.search.SearchRequestBuilder;
4 |
5 | public interface DatabaseScrollingFactory {
6 |
7 | DatabaseScrolling newScroll(String index);
8 |
9 | DatabaseScrolling newScroll(SearchRequestBuilder search);
10 |
11 | DatabaseScroll bulkDelete();
12 |
13 | DatabaseScroll bulkIndex();
14 |
15 | DatabaseScroll combine(DatabaseScroll... first);
16 | }
17 |
--------------------------------------------------------------------------------
/db-scroll-api/src/test/java/com/jeromeloisel/db/scroll/api/DatabaseScrollTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.scroll.api;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import org.junit.Test;
5 |
6 | import java.io.IOException;
7 |
8 | public class DatabaseScrollTest {
9 |
10 | private DatabaseScroll scroll = hit -> {
11 |
12 | };
13 |
14 | @Test
15 | public void shouldOnStart() throws IOException {
16 | scroll.onStartBatch();
17 | }
18 |
19 | @Test
20 | public void shouldOnend() throws IOException {
21 | scroll.onEndBatch();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/db-scroll-api/src/test/java/com/jeromeloisel/db/scroll/api/MockDatabaseScroll.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.db.scroll.api;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import com.jeromeloisel.db.scroll.api.DatabaseScrolling;
5 | import org.elasticsearch.index.query.QueryBuilder;
6 | import org.elasticsearch.search.sort.FieldSortBuilder;
7 |
8 | import java.util.concurrent.TimeUnit;
9 |
10 | public final class MockDatabaseScroll implements DatabaseScrolling {
11 | @Override
12 | public DatabaseScrolling withSort(FieldSortBuilder sort) {
13 | return this;
14 | }
15 |
16 | @Override
17 | public DatabaseScrolling withScrollSize(int size) {
18 | return this;
19 | }
20 |
21 | @Override
22 | public DatabaseScrolling withKeepAlive(long time, TimeUnit unit) {
23 | return this;
24 | }
25 |
26 | @Override
27 | public DatabaseScrolling withTypes(String... types) {
28 | return this;
29 | }
30 |
31 | @Override
32 | public DatabaseScrolling withFetchSource(boolean fetchSource) {
33 | return this;
34 | }
35 |
36 | @Override
37 | public DatabaseScrolling withQuery(QueryBuilder query) {
38 | return this;
39 | }
40 |
41 | @Override
42 | public void scroll(final DatabaseScroll scroll) {
43 | // does nothing
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/db-scroll-elastic/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | elasticsearch-crud
7 | com.jeromeloisel
8 | 5.6.4-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | db-scroll-elastic
13 |
14 |
15 |
16 | com.jeromeloisel
17 | db-scroll-api
18 | ${project.version}
19 |
20 |
21 | org.apache.logging.log4j
22 | log4j-api
23 |
24 |
25 | org.springframework
26 | spring-context
27 |
28 |
29 |
30 | org.apache.commons
31 | commons-lang3
32 | test
33 |
34 |
35 | com.jeromeloisel
36 | db-integration-test
37 | ${project.version}
38 | test
39 |
40 |
41 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/main/java/com/jeromeloisel/database/scroll/elastic/BulkDelete.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import lombok.AllArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.FieldDefaults;
7 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
8 | import org.elasticsearch.action.delete.DeleteRequest;
9 | import org.elasticsearch.client.Client;
10 | import org.elasticsearch.search.SearchHit;
11 |
12 | import java.util.concurrent.atomic.AtomicReference;
13 |
14 | import static lombok.AccessLevel.PACKAGE;
15 | import static lombok.AccessLevel.PRIVATE;
16 |
17 | @AllArgsConstructor(access = PACKAGE)
18 | @FieldDefaults(level = PRIVATE, makeFinal = true)
19 | final class BulkDelete implements DatabaseScroll {
20 | @NonNull
21 | Client client;
22 |
23 | AtomicReference request = new AtomicReference<>();
24 |
25 | @Override
26 | public void onStartBatch() {
27 | request.set(client.prepareBulk());
28 | }
29 |
30 | @Override
31 | public void accept(final SearchHit hit) {
32 | final DeleteRequest delete = client
33 | .prepareDelete()
34 | .setIndex(hit.getIndex())
35 | .setType(hit.getType())
36 | .setId(hit.getId())
37 | .request();
38 |
39 | request.get().add(delete);
40 | }
41 |
42 | @Override
43 | public void onEndBatch() {
44 | request.get().execute().actionGet();
45 | }
46 | }
--------------------------------------------------------------------------------
/db-scroll-elastic/src/main/java/com/jeromeloisel/database/scroll/elastic/BulkIndex.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import lombok.AllArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.FieldDefaults;
7 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
8 | import org.elasticsearch.action.index.IndexRequest;
9 | import org.elasticsearch.client.Client;
10 | import org.elasticsearch.search.SearchHit;
11 |
12 | import java.util.concurrent.atomic.AtomicReference;
13 |
14 | import static lombok.AccessLevel.PACKAGE;
15 | import static lombok.AccessLevel.PRIVATE;
16 | import static org.elasticsearch.common.xcontent.XContentType.JSON;
17 |
18 | @AllArgsConstructor(access = PACKAGE)
19 | @FieldDefaults(level = PRIVATE, makeFinal = true)
20 | final class BulkIndex implements DatabaseScroll {
21 | @NonNull
22 | Client client;
23 |
24 | AtomicReference request = new AtomicReference<>();
25 |
26 | @Override
27 | public void onStartBatch() {
28 | request.set(client.prepareBulk());
29 | }
30 |
31 | @Override
32 | public void accept(final SearchHit hit) {
33 | final IndexRequest index = client
34 | .prepareIndex(hit.getIndex(), hit.getType(), hit.getId())
35 | .setSource(hit.getSourceRef(), JSON)
36 | .request();
37 |
38 | request.get().add(index);
39 | }
40 |
41 | @Override
42 | public void onEndBatch() {
43 | request.get().execute().actionGet();
44 | }
45 | }
--------------------------------------------------------------------------------
/db-scroll-elastic/src/main/java/com/jeromeloisel/database/scroll/elastic/CompoundScroll.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import lombok.AllArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.FieldDefaults;
7 | import org.elasticsearch.search.SearchHit;
8 |
9 | import java.io.IOException;
10 |
11 | import static lombok.AccessLevel.PACKAGE;
12 | import static lombok.AccessLevel.PRIVATE;
13 |
14 | @AllArgsConstructor(access = PACKAGE)
15 | @FieldDefaults(level = PRIVATE, makeFinal = true)
16 | final class CompoundScroll implements DatabaseScroll {
17 | @NonNull
18 | Iterable scrolls;
19 |
20 | @Override
21 | public void onStartBatch() throws IOException {
22 | for (final DatabaseScroll scroll : scrolls) {
23 | scroll.onStartBatch();
24 | }
25 | }
26 |
27 | @Override
28 | public void accept(final SearchHit hit) throws IOException {
29 | for (final DatabaseScroll consumer : scrolls) {
30 | consumer.accept(hit);
31 | }
32 | }
33 |
34 | @Override
35 | public void onEndBatch() throws IOException {
36 | for (final DatabaseScroll scroll : scrolls) {
37 | scroll.onEndBatch();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/main/java/com/jeromeloisel/database/scroll/elastic/ElasticScroll.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import com.jeromeloisel.db.scroll.api.DatabaseScrolling;
5 | import lombok.experimental.FieldDefaults;
6 | import org.elasticsearch.action.search.SearchRequestBuilder;
7 | import org.elasticsearch.action.search.SearchResponse;
8 | import org.elasticsearch.client.Client;
9 | import org.elasticsearch.common.unit.TimeValue;
10 | import org.elasticsearch.index.query.QueryBuilder;
11 | import org.elasticsearch.search.SearchHit;
12 | import org.elasticsearch.search.sort.FieldSortBuilder;
13 |
14 | import java.io.IOException;
15 | import java.util.concurrent.TimeUnit;
16 | import java.util.concurrent.atomic.AtomicReference;
17 |
18 | import static java.util.Objects.requireNonNull;
19 | import static lombok.AccessLevel.PRIVATE;
20 | import static org.elasticsearch.search.sort.SortBuilders.fieldSort;
21 |
22 | @FieldDefaults(level = PRIVATE, makeFinal = true)
23 | final class ElasticScroll implements DatabaseScrolling {
24 | private static final FieldSortBuilder DEFAULT_SORT = fieldSort("_doc");
25 |
26 | Client client;
27 | SearchRequestBuilder search;
28 | AtomicReference sort;
29 | AtomicReference scrollTime;
30 |
31 | ElasticScroll(
32 | final Client client,
33 | final SearchRequestBuilder search,
34 | final TimeValue timeValue) {
35 | super();
36 | this.client = requireNonNull(client);
37 | this.search = requireNonNull(search);
38 | this.sort = new AtomicReference<>(DEFAULT_SORT);
39 | this.scrollTime = new AtomicReference<>(requireNonNull(timeValue));
40 | }
41 |
42 | @Override
43 | public DatabaseScrolling withSort(final FieldSortBuilder s) {
44 | this.sort.set(requireNonNull(s));
45 | return this;
46 | }
47 |
48 | @Override
49 | public DatabaseScrolling withScrollSize(final int size) {
50 | search.setSize(size);
51 | return this;
52 | }
53 |
54 | @Override
55 | public DatabaseScrolling withKeepAlive(final long time, final TimeUnit unit) {
56 | scrollTime.set(TimeValue.timeValueMillis(unit.toMillis(time)));
57 | return this;
58 | }
59 |
60 | @Override
61 | public DatabaseScrolling withTypes(final String... types) {
62 | search.setTypes(types);
63 | return this;
64 | }
65 |
66 | @Override
67 | public DatabaseScrolling withFetchSource(final boolean fetchSource) {
68 | search.setFetchSource(fetchSource);
69 | return this;
70 | }
71 |
72 | @Override
73 | public DatabaseScrolling withQuery(final QueryBuilder query) {
74 | search.setQuery(query);
75 | return this;
76 | }
77 |
78 | @Override
79 | public void scroll(final DatabaseScroll scroll) {
80 | SearchResponse response = search
81 | .addSort(sort.get())
82 | .setScroll(scrollTime.get())
83 | .execute()
84 | .actionGet();
85 |
86 | while (true) {
87 | final String scrollId = response.getScrollId();
88 | final SearchHit[] hits = response.getHits().getHits();
89 | if(hits.length == 0) {
90 | break;
91 | }
92 |
93 | try {
94 | scroll.onStartBatch();
95 | try {
96 | for (final SearchHit hit : hits) {
97 | scroll.accept(hit);
98 | }
99 | } finally {
100 | scroll.onEndBatch();
101 | }
102 | } catch (final IOException e) {
103 | // Ignore
104 | }
105 |
106 | response = client
107 | .prepareSearchScroll(scrollId)
108 | .setScroll(scrollTime.get())
109 | .execute()
110 | .actionGet();
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/main/java/com/jeromeloisel/database/scroll/elastic/ElasticScrollingFactory.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
4 | import com.jeromeloisel.db.scroll.api.DatabaseScrolling;
5 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
6 | import lombok.AllArgsConstructor;
7 | import lombok.NonNull;
8 | import lombok.experimental.FieldDefaults;
9 | import org.elasticsearch.action.search.SearchRequestBuilder;
10 | import org.elasticsearch.client.Client;
11 | import org.elasticsearch.common.unit.TimeValue;
12 | import org.springframework.stereotype.Service;
13 |
14 | import static com.google.common.collect.ImmutableList.copyOf;
15 | import static java.util.Objects.requireNonNull;
16 | import static lombok.AccessLevel.PACKAGE;
17 | import static lombok.AccessLevel.PRIVATE;
18 | import static org.elasticsearch.common.unit.TimeValue.timeValueMinutes;
19 |
20 | @Service
21 | @AllArgsConstructor(access = PACKAGE)
22 | @FieldDefaults(level = PRIVATE, makeFinal = true)
23 | public final class ElasticScrollingFactory implements DatabaseScrollingFactory {
24 | private static final TimeValue DEFAULT_SCROLL_TIME = timeValueMinutes(1);
25 | public static final int DEFAULT_SCROLL_SIZE = 100;
26 |
27 | @NonNull
28 | Client client;
29 |
30 | @Override
31 | public DatabaseScrolling newScroll(final String index) {
32 | final SearchRequestBuilder search = client
33 | .prepareSearch(requireNonNull(index))
34 | .setFetchSource(true)
35 | .setSize(DEFAULT_SCROLL_SIZE);
36 | return newScroll(search);
37 | }
38 |
39 | @Override
40 | public DatabaseScrolling newScroll(final SearchRequestBuilder search) {
41 | return new ElasticScroll(client, search, DEFAULT_SCROLL_TIME);
42 | }
43 |
44 | @Override
45 | public DatabaseScroll bulkDelete() {
46 | return new BulkDelete(client);
47 | }
48 |
49 | @Override
50 | public DatabaseScroll bulkIndex() {
51 | return new BulkIndex(client);
52 | }
53 |
54 | @Override
55 | public DatabaseScroll combine(final DatabaseScroll... scrolls) {
56 | return new CompoundScroll(copyOf(scrolls));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/main/java/com/jeromeloisel/database/scroll/elastic/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Elasticsearch Database Scrolling.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.database.scroll.elastic;
8 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/BulkDeleteEsTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
4 | import com.jeromeloisel.db.conversion.jackson.JacksonSerializationFactory;
5 | import com.jeromeloisel.db.integration.test.SpringElasticSearchTest;
6 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
7 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
8 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
9 | import org.elasticsearch.action.index.IndexRequest;
10 | import org.elasticsearch.client.IndicesAdminClient;
11 | import org.junit.After;
12 | import org.junit.Assert;
13 | import org.junit.Before;
14 | import org.junit.Test;
15 | import org.mockito.Mockito;
16 | import org.springframework.beans.factory.annotation.Autowired;
17 |
18 | import java.io.IOException;
19 | import java.util.UUID;
20 | import java.util.concurrent.atomic.AtomicLong;
21 |
22 | import static org.elasticsearch.common.xcontent.XContentType.JSON;
23 | import static org.mockito.Mockito.doThrow;
24 |
25 | public class BulkDeleteEsTest extends SpringElasticSearchTest {
26 | private static final String INDEX = "data";
27 | private static final String TYPE = "person";
28 | private static final int SIZE = ElasticScrollingFactory.DEFAULT_SCROLL_SIZE + 1;
29 |
30 | @Autowired
31 | private JacksonSerializationFactory mapper;
32 | @Autowired
33 | private DatabaseScrollingFactory scroll;
34 |
35 | @Before
36 | public void before() throws IOException {
37 | if(!client.admin().indices().prepareExists(INDEX).execute().actionGet().isExists()) {
38 | client.admin().indices().prepareCreate(INDEX).execute().actionGet();
39 | }
40 | final BulkRequestBuilder bulk = client.prepareBulk();
41 | final JsonSerializer serializer = mapper.serializer(Person.class);
42 | for (int i = 0; i < SIZE; i++) {
43 |
44 | final String name = UUID.randomUUID().toString();
45 | final IndexRequest request = new IndexRequest(INDEX, TYPE);
46 | request.source(serializer.apply(Person.builder().id("").firstname(name).lastname(name).build()), JSON);
47 | bulk.add(request);
48 | }
49 |
50 | client.bulk(bulk.request()).actionGet();
51 | flush(INDEX);
52 | }
53 |
54 | @Test
55 | public void shouldScroll() {
56 | scroll.newScroll(INDEX).scroll(scroll.bulkDelete());
57 | flush(INDEX);
58 | final AtomicLong atomic = new AtomicLong();
59 | scroll.newScroll(INDEX).scroll(hit -> atomic.incrementAndGet());
60 | Assert.assertEquals(
61 | 0L, atomic.longValue());
62 | }
63 |
64 | @Test
65 | public void shouldNotScroll() throws IOException {
66 | final DatabaseScroll mock = Mockito.mock(DatabaseScroll.class);
67 | doThrow(new IOException("")).when(mock).onStartBatch();
68 | scroll.newScroll(INDEX).scroll(mock);
69 | }
70 |
71 | @After
72 | public void after() {
73 | final IndicesAdminClient indices = client.admin().indices();
74 | if(indices.prepareExists(INDEX).execute().actionGet().isExists()) {
75 | indices.prepareDelete(INDEX).execute().actionGet();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/BulkDeleteTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 |
4 | import com.google.common.testing.NullPointerTester;
5 | import org.junit.Test;
6 |
7 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
8 |
9 | public class BulkDeleteTest {
10 |
11 | @Test
12 | public void shouldPassNPETester() {
13 | new NullPointerTester().testConstructors(BulkDelete.class, PACKAGE);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/BulkIndexEsTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
4 | import com.jeromeloisel.db.conversion.jackson.JacksonSerializationFactory;
5 | import com.jeromeloisel.db.integration.test.SpringElasticSearchTest;
6 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
7 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
8 | import org.elasticsearch.action.index.IndexRequest;
9 | import org.elasticsearch.client.IndicesAdminClient;
10 | import org.junit.After;
11 | import org.junit.Assert;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 |
16 | import java.io.IOException;
17 | import java.util.UUID;
18 | import java.util.concurrent.atomic.AtomicLong;
19 |
20 | import static org.elasticsearch.common.xcontent.XContentType.JSON;
21 |
22 | public class BulkIndexEsTest extends SpringElasticSearchTest {
23 | private static final String INDEX = "data";
24 | private static final String TYPE = "person";
25 | private static final long SIZE = ElasticScrollingFactory.DEFAULT_SCROLL_SIZE + 1;
26 |
27 | @Autowired
28 | private JacksonSerializationFactory mapper;
29 | @Autowired
30 | private DatabaseScrollingFactory scroll;
31 |
32 | @Before
33 | public void before() throws IOException {
34 | final IndicesAdminClient indices = client.admin().indices();
35 | if(!indices.prepareExists(INDEX).execute().actionGet().isExists()) {
36 | indices.prepareCreate(INDEX).execute().actionGet();
37 | }
38 | final JsonSerializer serializer = mapper.serializer(Person.class);
39 | final BulkRequestBuilder bulk = client.prepareBulk();
40 | for (int i = 0; i < SIZE; i++) {
41 |
42 | final String name = UUID.randomUUID().toString();
43 | final IndexRequest request = new IndexRequest(INDEX, TYPE);
44 | request.source(serializer.apply(Person.builder().id("").firstname(name).lastname(name).build()), JSON);
45 | bulk.add(request);
46 | }
47 |
48 | client.bulk(bulk.request()).actionGet();
49 | flush(INDEX);
50 | }
51 |
52 | @Test
53 | public void shouldScroll() {
54 | scroll.newScroll(INDEX).scroll(scroll.bulkIndex());
55 | flush(INDEX);
56 | final AtomicLong atomic = new AtomicLong();
57 | scroll.newScroll(INDEX).scroll(hit -> {
58 | atomic.incrementAndGet();
59 | });
60 | Assert.assertEquals(
61 | SIZE, atomic.longValue());
62 | }
63 |
64 | @After
65 | public void after() {
66 | final IndicesAdminClient indices = client.admin().indices();
67 | if(indices.prepareExists(INDEX).execute().actionGet().isExists()) {
68 | indices.prepareDelete(INDEX).execute().actionGet();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/BulkIndexTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 |
4 | import com.google.common.testing.NullPointerTester;
5 | import org.junit.Test;
6 |
7 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
8 |
9 | public class BulkIndexTest {
10 |
11 | @Test
12 | public void shouldPassNPETester() {
13 | new NullPointerTester().testConstructors(BulkIndex.class, PACKAGE);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/CompoundScrollTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 |
4 | import com.google.common.collect.ImmutableSet;
5 | import com.google.common.testing.NullPointerTester;
6 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
7 | import org.elasticsearch.search.SearchHit;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.mockito.Mock;
12 | import org.mockito.runners.MockitoJUnitRunner;
13 |
14 | import java.io.IOException;
15 |
16 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
17 | import static org.mockito.Mockito.verify;
18 |
19 | @RunWith(MockitoJUnitRunner.class)
20 | public class CompoundScrollTest {
21 |
22 | @Mock
23 | DatabaseScroll first;
24 | @Mock
25 | DatabaseScroll second;
26 | @Mock
27 | SearchHit hit;
28 |
29 | CompoundScroll scroll;
30 |
31 | @Before
32 | public void before() {
33 | scroll = new CompoundScroll(ImmutableSet.of(first, second));
34 | }
35 |
36 | @Test
37 | public void shouldOnStart() throws IOException {
38 | scroll.onStartBatch();
39 | verify(first).onStartBatch();
40 | verify(second).onStartBatch();
41 | }
42 |
43 | @Test
44 | public void shouldOnEnd() throws IOException {
45 | scroll.onEndBatch();
46 | verify(first).onEndBatch();
47 | verify(second).onEndBatch();
48 | }
49 |
50 | @Test
51 | public void shouldAccept() throws IOException {
52 | final CompoundScroll scroll = new CompoundScroll(ImmutableSet.of(first, second));
53 | scroll.accept(hit);
54 | verify(first).accept(hit);
55 | verify(second).accept(hit);
56 | }
57 |
58 | @Test
59 | public void shouldPassNPETester() {
60 | new NullPointerTester().testConstructors(CompoundScroll.class, PACKAGE);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/ElasticScrollTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.google.common.testing.NullPointerTester;
4 | import org.elasticsearch.action.search.SearchRequestBuilder;
5 | import org.elasticsearch.client.Client;
6 | import org.elasticsearch.common.unit.TimeValue;
7 | import org.elasticsearch.index.query.MatchAllQueryBuilder;
8 | import org.elasticsearch.index.query.QueryBuilder;
9 | import org.elasticsearch.search.sort.FieldSortBuilder;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 | import org.mockito.Mock;
14 | import org.mockito.runners.MockitoJUnitRunner;
15 |
16 | import java.util.concurrent.TimeUnit;
17 |
18 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
19 | import static org.mockito.Mockito.mock;
20 | import static org.mockito.Mockito.verify;
21 |
22 | @RunWith(MockitoJUnitRunner.class)
23 | public class ElasticScrollTest {
24 |
25 | @Mock
26 | Client client;
27 | @Mock
28 | SearchRequestBuilder search;
29 |
30 | private ElasticScroll scroll;
31 |
32 | @Before
33 | public void before() {
34 | scroll = new ElasticScroll(client, search, TimeValue.ZERO);
35 | }
36 |
37 | @Test
38 | public void shouldFetchSource() {
39 | scroll.withFetchSource(true);
40 | verify(search).setFetchSource(true);
41 | }
42 |
43 | @Test
44 | public void shouldWithKeepAlive() {
45 | scroll.withKeepAlive(1, TimeUnit.SECONDS);
46 | }
47 |
48 | @Test
49 | public void shouldWithQuery() {
50 | final QueryBuilder query = new MatchAllQueryBuilder();
51 | scroll.withQuery(query);
52 | verify(search).setQuery(query);
53 | }
54 |
55 | @Test
56 | public void shouldWithSort() {
57 | final FieldSortBuilder sort = new FieldSortBuilder("_doc");
58 | scroll.withSort(sort);
59 | }
60 |
61 | @Test
62 | public void shouldWithSize() {
63 | scroll.withScrollSize(123);
64 | verify(search).setSize(123);
65 | }
66 |
67 | @Test
68 | public void shoudlWithTypes() {
69 | scroll.withTypes("type");
70 | verify(search).setTypes("type");
71 | }
72 |
73 | @Test
74 | public void shouldPassNPETester() {
75 | new NullPointerTester()
76 | .setDefault(SearchRequestBuilder.class, mock(SearchRequestBuilder.class))
77 | .testConstructors(ElasticScroll.class, PACKAGE);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/ElasticScrollingFactoryEsTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonSerializer;
4 | import com.jeromeloisel.db.conversion.jackson.JacksonSerializationFactory;
5 | import com.jeromeloisel.db.integration.test.SpringElasticSearchTest;
6 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
7 | import org.elasticsearch.action.bulk.BulkRequestBuilder;
8 | import org.elasticsearch.action.index.IndexRequest;
9 | import org.elasticsearch.client.IndicesAdminClient;
10 | import org.junit.After;
11 | import org.junit.Assert;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 |
16 | import java.io.IOException;
17 | import java.util.UUID;
18 | import java.util.concurrent.atomic.AtomicLong;
19 |
20 | import static org.elasticsearch.common.xcontent.XContentType.JSON;
21 |
22 | public class ElasticScrollingFactoryEsTest extends SpringElasticSearchTest {
23 | private static final String INDEX = "data";
24 | private static final String TYPE = "person";
25 | private static final int SIZE = ElasticScrollingFactory.DEFAULT_SCROLL_SIZE + 1;
26 |
27 | @Autowired
28 | private JacksonSerializationFactory mapper;
29 | @Autowired
30 | private DatabaseScrollingFactory scroll;
31 |
32 | @Before
33 | public void before() throws IOException {
34 | if(!client.admin().indices().prepareExists(INDEX).execute().actionGet().isExists()) {
35 | client.admin().indices().prepareCreate(INDEX).execute().actionGet();
36 | }
37 | final JsonSerializer serializer = mapper.serializer(Person.class);
38 | final BulkRequestBuilder bulk = client.prepareBulk();
39 | for (int i = 0; i < SIZE; i++) {
40 |
41 | final String name = UUID.randomUUID().toString();
42 | final IndexRequest request = new IndexRequest(INDEX, TYPE);
43 | request.source(serializer.apply(Person.builder().id("").firstname(name).lastname(name).build()), JSON);
44 | bulk.add(request);
45 | }
46 |
47 | client.bulk(bulk.request()).actionGet();
48 | flush(INDEX);
49 | }
50 |
51 | @Test
52 | public void shouldScroll() {
53 | final AtomicLong atomic = new AtomicLong();
54 | scroll.newScroll(INDEX).scroll(hit -> atomic.incrementAndGet());
55 | Assert.assertEquals(
56 | SIZE, atomic.longValue());
57 | }
58 |
59 | @After
60 | public void after() {
61 | final IndicesAdminClient indices = client.admin().indices();
62 | if(indices.prepareExists(INDEX).execute().actionGet().isExists()) {
63 | indices.prepareDelete(INDEX).execute().actionGet();
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/ElasticScrollingFactoryTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.google.common.testing.NullPointerTester;
4 | import com.jeromeloisel.db.scroll.api.DatabaseScroll;
5 | import org.elasticsearch.client.Client;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.mockito.Mock;
9 | import org.mockito.runners.MockitoJUnitRunner;
10 |
11 | import static com.google.common.testing.NullPointerTester.Visibility.PACKAGE;
12 | import static org.junit.Assert.assertEquals;
13 |
14 | @RunWith(MockitoJUnitRunner.class)
15 | public class ElasticScrollingFactoryTest {
16 |
17 | @Mock
18 | Client client;
19 | @Mock
20 | DatabaseScroll scroll;
21 |
22 | @Test
23 | public void shouldCombine() {
24 | final ElasticScrollingFactory factory = new ElasticScrollingFactory(client);
25 | final DatabaseScroll combine = factory.combine(scroll);
26 | assertEquals(CompoundScroll.class, combine.getClass());
27 | }
28 |
29 | @Test
30 | public void shouldPassNPETester() {
31 | new NullPointerTester().testConstructors(ElasticScrollingFactory.class, PACKAGE);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/db-scroll-elastic/src/test/java/com/jeromeloisel/database/scroll/elastic/Person.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.database.scroll.elastic;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import com.jeromeloisel.db.entity.Entity;
6 | import lombok.Builder;
7 | import lombok.Value;
8 | import lombok.experimental.Wither;
9 |
10 | import static com.google.common.base.Preconditions.checkNotNull;
11 |
12 | @Value
13 | @Builder
14 | public class Person implements Entity {
15 | @Wither
16 | String id;
17 | String firstname;
18 | String lastname;
19 |
20 | @JsonCreator
21 | Person(
22 | @JsonProperty("id") final String id,
23 | @JsonProperty("firstname") final String firstname,
24 | @JsonProperty("lastname") final String lastname) {
25 | super();
26 | this.id = checkNotNull(id);
27 | this.firstname = checkNotNull(firstname);
28 | this.lastname = checkNotNull(lastname);
29 | }
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jeromeloisel
6 | elasticsearch-crud
7 | 5.6.4-SNAPSHOT
8 |
9 | db-spring-elasticsearch-starter
10 | Spring Boot Elasticsearch DB starter
11 | ElasticSearch Repository Spring Boot autoconfiguration with Jackson serialization.
12 |
13 |
14 |
15 | com.jeromeloisel
16 | db-conversion-jackson
17 | ${project.version}
18 |
19 |
20 | com.jeromeloisel
21 | db-repository-elasticsearch
22 | ${project.version}
23 |
24 |
25 | com.jeromeloisel
26 | db-scroll-elastic
27 | ${project.version}
28 |
29 |
30 | com.fasterxml.jackson.core
31 | jackson-databind
32 | true
33 |
34 |
35 | org.springframework
36 | spring-context
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-autoconfigure
41 | ${spring.boot.version}
42 |
43 |
44 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/src/main/java/com/jeromeloisel/repository/elasticsearch/starter/ElasticSearchRepositoryFactoryAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.repository.elasticsearch.starter;
2 |
3 | import com.jeromeloisel.db.conversion.api.JsonSerializationFactory;
4 | import com.jeromeloisel.db.repository.api.DatabaseRepositoryFactory;
5 | import com.jeromeloisel.db.repository.elasticsearch.ElasticSearchRepositoryFactory;
6 | import com.jeromeloisel.db.scroll.api.DatabaseScrollingFactory;
7 | import org.elasticsearch.client.Client;
8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
10 | import org.springframework.context.annotation.Bean;
11 | import org.springframework.context.annotation.Configuration;
12 |
13 | /**
14 | * Elasticsearch Repository autoconfiguration.
15 | *
16 | * @author jerome
17 | *
18 | */
19 | @Configuration
20 | public class ElasticSearchRepositoryFactoryAutoConfiguration {
21 |
22 | @Bean
23 | @ConditionalOnMissingBean(DatabaseRepositoryFactory.class)
24 | @ConditionalOnBean(value={Client.class, JsonSerializationFactory.class, DatabaseScrollingFactory.class})
25 | DatabaseRepositoryFactory databaseRepositoryFactory(
26 | final Client client,
27 | final JsonSerializationFactory serialization,
28 | final DatabaseScrollingFactory factory) {
29 | return new ElasticSearchRepositoryFactory(serialization, client, factory);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/src/main/java/com/jeromeloisel/repository/elasticsearch/starter/JacksonConversionAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.repository.elasticsearch.starter;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | import com.fasterxml.jackson.databind.ObjectMapper;
11 | import com.jeromeloisel.db.conversion.api.JsonSerializationFactory;
12 | import com.jeromeloisel.db.conversion.jackson.JacksonSerializationFactory;
13 |
14 | /**
15 | * Jackson Json Conversion autoconfiguration.
16 | *
17 | * @author jerome
18 | *
19 | */
20 | @Configuration
21 | @ConditionalOnClass(ObjectMapper.class)
22 | public class JacksonConversionAutoConfiguration {
23 |
24 | @Bean
25 | @Autowired
26 | @ConditionalOnBean(ObjectMapper.class)
27 | @ConditionalOnMissingBean(JsonSerializationFactory.class)
28 | JsonSerializationFactory jsonSerializationFactory(final ObjectMapper mapper) {
29 | return new JacksonSerializationFactory(mapper);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/src/main/java/com/jeromeloisel/repository/elasticsearch/starter/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Spring Elasticsearch DB configuration.
3 | *
4 | * @author jerome
5 | *
6 | */
7 | package com.jeromeloisel.repository.elasticsearch.starter;
8 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/src/test/java/com/jeromeloisel/Application.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(final String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/src/test/java/com/jeromeloisel/repository/elasticsearch/starter/TestConfig.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.repository.elasticsearch.starter;
2 |
3 | import static org.mockito.Mockito.mock;
4 |
5 | import org.elasticsearch.client.Client;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import com.fasterxml.jackson.databind.ObjectMapper;
10 |
11 | @Configuration
12 | class TestConfig {
13 |
14 | @Bean
15 | ObjectMapper objectMapper() {
16 | return new ObjectMapper();
17 | }
18 |
19 | @Bean
20 | Client client() {
21 | return mock(Client.class);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/db-spring-elasticsearch-starter/src/test/java/com/jeromeloisel/repository/elasticsearch/starter/WiringTest.java:
--------------------------------------------------------------------------------
1 | package com.jeromeloisel.repository.elasticsearch.starter;
2 |
3 | import com.jeromeloisel.Application;
4 | import com.jeromeloisel.db.repository.api.DatabaseRepositoryFactory;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 |
11 | import static org.junit.Assert.assertNotNull;
12 |
13 | @RunWith(SpringRunner.class)
14 | @SpringBootTest(classes=Application.class)
15 | public class WiringTest {
16 |
17 | @Autowired
18 | DatabaseRepositoryFactory factory;
19 |
20 | @Test
21 | public void shouldAutowire() {
22 | assertNotNull(factory);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.jcabi
6 | parent
7 | 0.49.2
8 |
9 |
10 | com.jeromeloisel
11 | elasticsearch-crud
12 | 5.6.4-SNAPSHOT
13 | pom
14 | ElasticSearch CRUD Repository
15 | Easily perform CRUD operations on Beans stored on Elasticsearch.
16 |
17 |
18 | yyyy-MM-dd'T'HH:mm:ss
19 | **/*IntegrationTest.java
20 |
21 | 2.6
22 | 5.6.3
23 | 23.2-jre
24 | 2.9.2
25 | 4.12
26 | 2.9.1
27 | 1.16.12
28 | 6.3.0
29 | 1.10.19
30 | 4.3.12.RELEASE
31 | 1.5.8.RELEASE
32 | 1.7.25
33 |
34 |
35 |
36 |
37 |
38 | org.slf4j
39 | slf4j-api
40 | ${slf4j.version}
41 |
42 |
43 | org.springframework
44 | spring-context
45 | ${spring.version}
46 |
47 |
48 | org.springframework
49 | spring-test
50 | ${spring.version}
51 | test
52 |
53 |
54 | commons-io
55 | commons-io
56 | ${commons.io.version}
57 |
58 |
59 | com.fasterxml.jackson.core
60 | jackson-annotations
61 | ${jackson.version}
62 |
63 |
64 | com.fasterxml.jackson.core
65 | jackson-databind
66 | ${jackson.version}
67 |
68 |
69 | com.fasterxml.jackson.datatype
70 | jackson-datatype-jdk8
71 | ${jackson.version}
72 |
73 |
74 | org.elasticsearch
75 | elasticsearch
76 | ${elasticsearch.version}
77 |
78 |
79 | org.elasticsearch.client
80 | transport
81 | ${elasticsearch.version}
82 |
83 |
84 | org.elasticsearch.test
85 | framework
86 | ${elasticsearch.version}
87 | test
88 |
89 |
90 |
91 | com.google.guava
92 | guava-testlib
93 | ${guava.version}
94 | test
95 |
96 |
97 | org.mockito
98 | mockito-all
99 | ${mockito.version}
100 | test
101 |
102 |
103 | junit
104 | junit
105 | ${junit.version}
106 | test
107 |
108 |
109 | org.mockito
110 | mockito-core
111 | ${mockito.version}
112 | test
113 |
114 |
115 | org.apache.logging.log4j
116 | log4j-api
117 | ${log4j.version}
118 |
119 |
120 | org.apache.logging.log4j
121 | log4j-core
122 | ${log4j.version}
123 |
124 |
125 |
126 |
127 |
128 |
129 | org.mockito
130 | mockito-all
131 | test
132 |
133 |
134 | org.projectlombok
135 | lombok
136 | provided
137 |
138 |
139 | org.springframework.boot
140 | spring-boot-starter-test
141 | ${spring.boot.version}
142 | test
143 |
144 |
145 | com.google.guava
146 | guava-testlib
147 |
148 |
149 | junit
150 | junit
151 |
152 |
153 | org.springframework
154 | spring-test
155 | ${spring.version}
156 | test
157 |
158 |
159 | com.google.guava
160 | guava
161 |
162 |
163 |
164 |
165 |
166 |
167 | org.apache.maven.plugins
168 | maven-failsafe-plugin
169 | 2.20.1
170 |
171 | suites
172 |
173 |
174 |
175 | org.apache.maven.plugins
176 | maven-surefire-plugin
177 | 2.20.1
178 |
179 | suites
180 | false
181 | 1
182 |
183 | file:/dev/./urandom
184 | true
185 |
186 |
187 |
188 |
189 | org.apache.maven.plugins
190 | maven-compiler-plugin
191 | 3.7.0
192 |
193 | 1.8
194 | 1.8
195 | true
196 |
197 |
198 |
199 | org.apache.maven.plugins
200 | maven-source-plugin
201 | 3.0.1
202 |
203 |
204 | attach-sources
205 |
206 | jar
207 |
208 |
209 |
210 |
211 |
212 | org.apache.maven.plugins
213 | maven-javadoc-plugin
214 | 2.10.4
215 |
216 |
217 | attach-javadocs
218 |
219 | jar
220 |
221 |
222 | -Xdoclint:none
223 |
224 |
225 |
226 |
227 |
228 | org.apache.maven.plugins
229 | maven-jar-plugin
230 | 3.0.2
231 |
232 |
233 |
234 | test-jar
235 |
236 |
237 |
238 |
239 | true
240 |
241 |
242 |
243 | org.eluder.coveralls
244 | coveralls-maven-plugin
245 | 4.3.0
246 |
247 | ${maven.build.timestamp.format}
248 |
249 |
250 |
251 | org.jacoco
252 | jacoco-maven-plugin
253 | 0.7.9
254 |
255 |
256 | prepare-agent
257 |
258 | prepare-agent
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 | scm:git:git@github.com:jloisel/elastic-crud.git
268 | scm:git:git@github.com:jloisel/elastic-crud.git
269 | git@github.com:jloisel/elastic-crud.git
270 |
271 |
272 |
273 |
274 | Jerome Loisel
275 | loisel.jerome@gmail.com
276 | https://jeromeloisel.com
277 |
278 |
279 |
280 |
281 |
282 | Apache License 2.0
283 | http://www.spdx.org/licenses/Apache-2.0
284 |
285 |
286 |
287 |
288 | db-repository-api
289 | db-entity
290 | db-conversion-api
291 | db-conversion-jackson
292 | db-repository-elasticsearch
293 | db-spring-elasticsearch-starter
294 | db-integration-test
295 | db-scroll-api
296 | db-scroll-elastic
297 |
298 |
299 |
--------------------------------------------------------------------------------
/pubring.gpg.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP MESSAGE-----
2 | Version: GnuPG v1
3 |
4 | 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
65 | =fQ+w
66 | -----END PGP MESSAGE-----
67 |
68 |
--------------------------------------------------------------------------------
/secring.gpg.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP MESSAGE-----
2 | Version: GnuPG v1
3 |
4 | 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
70 | =Abq4
71 | -----END PGP MESSAGE-----
72 |
73 |
--------------------------------------------------------------------------------
/settings.xml.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP MESSAGE-----
2 | Version: GnuPG v1
3 |
4 | hQEMA5qETcGag5w6AQf/R2COfUM8cbBoLq/OB6L6b5btkxfJ5a3fWiKBT18+Ow2I
5 | WVhUy7yZlkANnb2k72dJuQCRRyQuqgsouESAj6dnlaUkcZBTA3wYU4N6SRkzMMik
6 | 2AmnsX7vkhITf3v0Dcg4M/0BYN4+tuJsF9quUDtaAW+slunmipMQ55dr4OKnTPjy
7 | QAmdwQTmIVqo1zByS+SJz3lMARt/+1MWAAgny2MoxKN3AhTR8k1mQGlMIrDw++q5
8 | 7/venCMtXdCMey7PbvvCWthAbgr6Sv/AjWWNlBS8fXjaFoA9holONd5FWRK1UuB2
9 | 3Bjmued3220+BpVIvNWBvadWz96G6bdOO6BsvC47HNLpAb6Bgry9wghGdJjT1Aw3
10 | 6M8Ez9RPG7XJ2o3d2scxTv21pu+z0mze08HSVQeWByfLrIY6TkBA9BgTiG2kccUS
11 | j/cONqaO3sS9/SxOnUNtkIXa6oThj5bG0x6+9/FdTq5S+qoro1zwKrgTl629il3r
12 | k0B32nk+fOM3xdGbXu6ZFrNAMMRdOkq5fSExRtNc157EM8oXoDtWGc/CHjIjLsJo
13 | 1R0sV3558G1yDBLjPEaMPaM7coVN+Ylm0X+TtEOfRv/bu7cvGvRiAsMf0xJv6Osu
14 | kgqeHGNeGF3IqQEh9k8oXdhWLJifjZc24Dep1iuOTkrKuZbPa9/usQtOwKEicsDj
15 | DTQubx1aiVIXldpN1UB5IOkXOKgN5wq0qigtoS/+tlcY98IGsloz7m6foED0axO7
16 | RO4HvODshep2Hix3G5XYxAFoKUeTbXGwLNq7a+84FdOu2Nmo+6+6gxHfvjKPDrvK
17 | rtNIql+ju2lhcOd2QWIXy+bkVbjDyHDIO5gvc5imQuy9kj8+yMIdIQ3tl7vnNcpc
18 | 4WRNF56K6134k3GvFPNPOn4wNunHZ0kXbH1/O93/4WoI9+6+MV6dxS+LGKwn9oa1
19 | EFyhVNVCAgdCQoT4YJ4Ty45/i0pMLGeUkT07qzvoQ4B+N9q+Y9LSu7Ymj8leZlVm
20 | cQd86CupxG7XKjcyD2HJOVwM5sNlD1eINd5GoPAE
21 | =VVe2
22 | -----END PGP MESSAGE-----
23 |
--------------------------------------------------------------------------------