1 | /* |
2 | * Copyright 2013 the original author or authors. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.springframework.data.elasticsearch.repository.support; |
17 | |
18 | import org.elasticsearch.index.query.QueryBuilder; |
19 | import org.springframework.dao.InvalidDataAccessApiUsageException; |
20 | import org.springframework.data.domain.*; |
21 | import org.springframework.data.elasticsearch.core.ElasticsearchOperations; |
22 | import org.springframework.data.elasticsearch.core.query.DeleteQuery; |
23 | import org.springframework.data.elasticsearch.core.query.GetQuery; |
24 | import org.springframework.data.elasticsearch.core.query.IndexQuery; |
25 | import org.springframework.data.elasticsearch.core.query.SearchQuery; |
26 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; |
27 | import org.springframework.util.Assert; |
28 | |
29 | import javax.annotation.PostConstruct; |
30 | import java.lang.reflect.ParameterizedType; |
31 | import java.lang.reflect.Type; |
32 | import java.util.ArrayList; |
33 | import java.util.Collection; |
34 | import java.util.Collections; |
35 | import java.util.List; |
36 | |
37 | import static org.elasticsearch.index.query.QueryBuilders.inQuery; |
38 | import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; |
39 | |
40 | /** |
41 | * Elasticsearch specific repository implementation. Likely to be used as target within {@link ElasticsearchRepositoryFactory} |
42 | * |
43 | * @param <T> |
44 | * |
45 | * @author Rizwan Idrees |
46 | * @author Mohsin Husen |
47 | */ |
48 | public class SimpleElasticsearchRepository<T> implements ElasticsearchRepository<T, String> { |
49 | |
50 | |
51 | private ElasticsearchOperations elasticsearchOperations; |
52 | private Class<T> entityClass; |
53 | private ElasticsearchEntityInformation<T, String> entityInformation; |
54 | |
55 | public SimpleElasticsearchRepository() { |
56 | } |
57 | |
58 | public SimpleElasticsearchRepository(ElasticsearchOperations elasticsearchOperations) { |
59 | Assert.notNull(elasticsearchOperations); |
60 | this.setElasticsearchOperations(elasticsearchOperations); |
61 | } |
62 | |
63 | public SimpleElasticsearchRepository(ElasticsearchEntityInformation<T, String> metadata, ElasticsearchOperations elasticsearchOperations) { |
64 | this(elasticsearchOperations); |
65 | Assert.notNull(metadata); |
66 | this.entityInformation = metadata; |
67 | setEntityClass(this.entityInformation.getJavaType()); |
68 | createIndex(); |
69 | } |
70 | |
71 | private void createIndex(){ |
72 | elasticsearchOperations.createIndex(getEntityClass()); |
73 | } |
74 | |
75 | @Override |
76 | public T findOne(String id) { |
77 | GetQuery query = new GetQuery(); |
78 | query.setId(id); |
79 | return elasticsearchOperations.queryForObject(query, getEntityClass()); |
80 | } |
81 | |
82 | @Override |
83 | public Iterable<T> findAll() { |
84 | int itemCount = (int) this.count(); |
85 | if (itemCount == 0) { |
86 | return new PageImpl<T>(Collections.<T> emptyList()); |
87 | } |
88 | return this.findAll(new PageRequest(0, Math.max(1, itemCount))); |
89 | } |
90 | |
91 | @Override |
92 | public Page<T> findAll(Pageable pageable) { |
93 | SearchQuery query = new SearchQuery(); |
94 | query.setElasticsearchQuery(matchAllQuery()); |
95 | query.setPageable(pageable); |
96 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
97 | } |
98 | |
99 | @Override |
100 | public Iterable<T> findAll(Sort sort) { |
101 | SearchQuery query = new SearchQuery(); |
102 | query.setElasticsearchQuery(matchAllQuery()); |
103 | query.setPageable(new PageRequest(0,Integer.MAX_VALUE, sort)); |
104 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
105 | } |
106 | |
107 | @Override |
108 | public Iterable<T> findAll(Iterable<String> ids) { |
109 | SearchQuery query = new SearchQuery(); |
110 | query.setElasticsearchQuery(inQuery(entityInformation.getIdAttribute(), ids)); |
111 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
112 | } |
113 | |
114 | @Override |
115 | public long count() { |
116 | SearchQuery query = new SearchQuery(); |
117 | return elasticsearchOperations.count(query,getEntityClass()); |
118 | } |
119 | |
120 | @Override |
121 | public <S extends T> S save(S entity) { |
122 | Assert.notNull(entity, "Cannot save 'null' entity."); |
123 | elasticsearchOperations.index(createIndexQuery(entity)); |
124 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
125 | return entity; |
126 | } |
127 | |
128 | public <S extends T> List<S> save(List<S> entities) { |
129 | Assert.notNull(entities, "Cannot insert 'null' as a List."); |
130 | Assert.notEmpty(entities,"Cannot insert empty List."); |
131 | List<IndexQuery> queries = new ArrayList<IndexQuery>(); |
132 | for(S s:entities){ |
133 | queries.add(createIndexQuery(s)); |
134 | } |
135 | elasticsearchOperations.bulkIndex(queries); |
136 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
137 | return entities; |
138 | } |
139 | |
140 | @Override |
141 | public <S extends T> S index(S entity) { |
142 | return save(entity); |
143 | } |
144 | |
145 | @Override |
146 | public <S extends T> Iterable<S> save(Iterable<S> entities) { |
147 | Assert.notNull(entities, "Cannot insert 'null' as a List."); |
148 | if (!(entities instanceof Collection<?>)) { |
149 | throw new InvalidDataAccessApiUsageException("Entities have to be inside a collection"); |
150 | } |
151 | List<IndexQuery> queries = new ArrayList<IndexQuery>(); |
152 | for(S s: entities){ |
153 | queries.add(createIndexQuery(s)); |
154 | } |
155 | elasticsearchOperations.bulkIndex(queries); |
156 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
157 | return entities; |
158 | } |
159 | |
160 | @Override |
161 | public boolean exists(String id) { |
162 | return findOne(id) != null; |
163 | } |
164 | |
165 | @Override |
166 | public Iterable<T> search(QueryBuilder elasticsearchQuery) { |
167 | SearchQuery query = new SearchQuery(); |
168 | int count = (int) elasticsearchOperations.count(query, getEntityClass()); |
169 | if(count == 0){ |
170 | return new PageImpl<T>(Collections.<T>emptyList()); |
171 | } |
172 | query.setPageable(new PageRequest(0,count)); |
173 | query.setElasticsearchQuery(elasticsearchQuery); |
174 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
175 | } |
176 | |
177 | @Override |
178 | public Page<T> search(QueryBuilder elasticsearchQuery, Pageable pageable) { |
179 | SearchQuery query = new SearchQuery(); |
180 | query.setElasticsearchQuery(elasticsearchQuery); |
181 | query.setPageable(pageable); |
182 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
183 | } |
184 | |
185 | @Override |
186 | public Page<T> search(SearchQuery query){ |
187 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
188 | } |
189 | |
190 | @Override |
191 | public void delete(String id) { |
192 | Assert.notNull(id, "Cannot delete entity with id 'null'."); |
193 | elasticsearchOperations.delete(entityInformation.getIndexName(), entityInformation.getType(),id); |
194 | elasticsearchOperations.refresh(entityInformation.getIndexName(),true); |
195 | } |
196 | |
197 | @Override |
198 | public void delete(T entity) { |
199 | Assert.notNull(entity, "Cannot delete 'null' entity."); |
200 | delete(extractIdFromBean(entity)); |
201 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
202 | } |
203 | |
204 | @Override |
205 | public void delete(Iterable<? extends T> entities) { |
206 | Assert.notNull(entities, "Cannot delete 'null' list."); |
207 | for (T entity : entities) { |
208 | delete(entity); |
209 | } |
210 | } |
211 | |
212 | @Override |
213 | public void deleteAll() { |
214 | DeleteQuery query = new DeleteQuery(); |
215 | query.setElasticsearchQuery(matchAllQuery()); |
216 | elasticsearchOperations.delete(query, getEntityClass()); |
217 | elasticsearchOperations.refresh(entityInformation.getIndexName(),true); |
218 | } |
219 | |
220 | private IndexQuery createIndexQuery(T entity){ |
221 | IndexQuery query = new IndexQuery(); |
222 | query.setObject(entity); |
223 | query.setId(extractIdFromBean(entity)); |
224 | query.setVersion(extractVersionFromBean(entity)); |
225 | return query; |
226 | } |
227 | |
228 | @SuppressWarnings("unchecked") |
229 | private Class<T> resolveReturnedClassFromGenericType() { |
230 | ParameterizedType parameterizedType = resolveReturnedClassFromGenericType(getClass()); |
231 | return (Class<T>) parameterizedType.getActualTypeArguments()[0]; |
232 | } |
233 | |
234 | private ParameterizedType resolveReturnedClassFromGenericType(Class<?> clazz) { |
235 | Object genericSuperclass = clazz.getGenericSuperclass(); |
236 | if (genericSuperclass instanceof ParameterizedType) { |
237 | ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; |
238 | Type rawtype = parameterizedType.getRawType(); |
239 | if (SimpleElasticsearchRepository.class.equals(rawtype)) { |
240 | return parameterizedType; |
241 | } |
242 | } |
243 | return resolveReturnedClassFromGenericType(clazz.getSuperclass()); |
244 | } |
245 | |
246 | public Class<T> getEntityClass() { |
247 | if (!isEntityClassSet()) { |
248 | try { |
249 | this.entityClass = resolveReturnedClassFromGenericType(); |
250 | } catch (Exception e) { |
251 | throw new InvalidDataAccessApiUsageException("Unable to resolve EntityClass. Please use according setter!", e); |
252 | } |
253 | } |
254 | return entityClass; |
255 | } |
256 | |
257 | private boolean isEntityClassSet() { |
258 | return entityClass != null; |
259 | } |
260 | |
261 | public final void setEntityClass(Class<T> entityClass) { |
262 | Assert.notNull(entityClass, "EntityClass must not be null."); |
263 | this.entityClass = entityClass; |
264 | } |
265 | |
266 | public final void setElasticsearchOperations(ElasticsearchOperations elasticsearchOperations) { |
267 | Assert.notNull(elasticsearchOperations, "ElasticsearchOperations must not be null."); |
268 | this.elasticsearchOperations = elasticsearchOperations; |
269 | } |
270 | |
271 | |
272 | private String extractIdFromBean(T entity) { |
273 | if (entityInformation != null) { |
274 | return entityInformation.getId(entity); |
275 | } |
276 | return null; |
277 | } |
278 | |
279 | private Long extractVersionFromBean(T entity){ |
280 | if (entityInformation != null) { |
281 | return entityInformation.getVersion(entity); |
282 | } |
283 | return null; |
284 | } |
285 | |
286 | } |