1 | /* |
2 | * Copyright 2012 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 | public class SimpleElasticsearchRepository<T> implements ElasticsearchRepository<T, String> { |
46 | |
47 | |
48 | private ElasticsearchOperations elasticsearchOperations; |
49 | private Class<T> entityClass; |
50 | private ElasticsearchEntityInformation<T, String> entityInformation; |
51 | |
52 | public SimpleElasticsearchRepository() { |
53 | } |
54 | |
55 | public SimpleElasticsearchRepository(ElasticsearchOperations elasticsearchOperations) { |
56 | Assert.notNull(elasticsearchOperations); |
57 | this.setElasticsearchOperations(elasticsearchOperations); |
58 | } |
59 | |
60 | public SimpleElasticsearchRepository(ElasticsearchEntityInformation<T, String> metadata, ElasticsearchOperations elasticsearchOperations) { |
61 | this(elasticsearchOperations); |
62 | Assert.notNull(metadata); |
63 | this.entityInformation = metadata; |
64 | setEntityClass(this.entityInformation.getJavaType()); |
65 | } |
66 | |
67 | @PostConstruct |
68 | public void createIndex(){ |
69 | elasticsearchOperations.createIndex(getEntityClass()); |
70 | } |
71 | |
72 | @Override |
73 | public T findOne(String id) { |
74 | GetQuery query = new GetQuery(); |
75 | query.setId(id); |
76 | return elasticsearchOperations.queryForObject(query, getEntityClass()); |
77 | } |
78 | |
79 | @Override |
80 | public Iterable<T> findAll() { |
81 | int itemCount = (int) this.count(); |
82 | if (itemCount == 0) { |
83 | return new PageImpl<T>(Collections.<T> emptyList()); |
84 | } |
85 | return this.findAll(new PageRequest(0, Math.max(1, itemCount))); |
86 | } |
87 | |
88 | @Override |
89 | public Page<T> findAll(Pageable pageable) { |
90 | SearchQuery query = new SearchQuery(); |
91 | query.setElasticsearchQuery(matchAllQuery()); |
92 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
93 | } |
94 | |
95 | @Override |
96 | public Iterable<T> findAll(Sort sort) { |
97 | SearchQuery query = new SearchQuery(); |
98 | query.setElasticsearchQuery(matchAllQuery()); |
99 | query.setPageable(new PageRequest(0,Integer.MAX_VALUE, sort)); |
100 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
101 | } |
102 | |
103 | @Override |
104 | public Iterable<T> findAll(Iterable<String> ids) { |
105 | SearchQuery query = new SearchQuery(); |
106 | query.setElasticsearchQuery(inQuery(entityInformation.getIdAttribute(), ids)); |
107 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
108 | } |
109 | |
110 | @Override |
111 | public long count() { |
112 | SearchQuery query = new SearchQuery(); |
113 | return elasticsearchOperations.count(query,getEntityClass()); |
114 | } |
115 | |
116 | @Override |
117 | public <S extends T> S save(S entity) { |
118 | Assert.notNull(entity, "Cannot save 'null' entity."); |
119 | elasticsearchOperations.index(createIndexQuery(entity)); |
120 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
121 | return entity; |
122 | } |
123 | |
124 | public <S extends T> List<S> save(List<S> entities) { |
125 | Assert.notNull(entities, "Cannot insert 'null' as a List."); |
126 | Assert.notEmpty(entities,"Cannot insert empty List."); |
127 | List<IndexQuery> queries = new ArrayList<IndexQuery>(); |
128 | for(S s:entities){ |
129 | queries.add(createIndexQuery(s)); |
130 | } |
131 | elasticsearchOperations.bulkIndex(queries); |
132 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
133 | return entities; |
134 | } |
135 | |
136 | @Override |
137 | public <S extends T> S index(S entity) { |
138 | return save(entity); |
139 | } |
140 | |
141 | @Override |
142 | public <S extends T> Iterable<S> save(Iterable<S> entities) { |
143 | Assert.notNull(entities, "Cannot insert 'null' as a List."); |
144 | if (!(entities instanceof Collection<?>)) { |
145 | throw new InvalidDataAccessApiUsageException("Entities have to be inside a collection"); |
146 | } |
147 | List<IndexQuery> queries = new ArrayList<IndexQuery>(); |
148 | for(S s: entities){ |
149 | queries.add(createIndexQuery(s)); |
150 | } |
151 | elasticsearchOperations.bulkIndex(queries); |
152 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
153 | return entities; |
154 | } |
155 | |
156 | @Override |
157 | public boolean exists(String id) { |
158 | return findOne(id) != null; |
159 | } |
160 | |
161 | @Override |
162 | public Iterable<T> search(QueryBuilder elasticsearchQuery) { |
163 | SearchQuery query = new SearchQuery(); |
164 | query.setElasticsearchQuery(elasticsearchQuery); |
165 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
166 | } |
167 | |
168 | @Override |
169 | public Page<T> search(QueryBuilder elasticsearchQuery, Pageable pageable) { |
170 | SearchQuery query = new SearchQuery(); |
171 | query.setElasticsearchQuery(elasticsearchQuery); |
172 | query.setPageable(pageable); |
173 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
174 | } |
175 | |
176 | @Override |
177 | public Page<T> search(SearchQuery query){ |
178 | return elasticsearchOperations.queryForPage(query, getEntityClass()); |
179 | } |
180 | |
181 | @Override |
182 | public void delete(String id) { |
183 | Assert.notNull(id, "Cannot delete entity with id 'null'."); |
184 | elasticsearchOperations.delete(entityInformation.getIndexName(), entityInformation.getType(),id); |
185 | elasticsearchOperations.refresh(entityInformation.getIndexName(),true); |
186 | } |
187 | |
188 | @Override |
189 | public void delete(T entity) { |
190 | Assert.notNull(entity, "Cannot delete 'null' entity."); |
191 | delete(extractIdFromBean(entity)); |
192 | elasticsearchOperations.refresh(entityInformation.getIndexName(), true); |
193 | } |
194 | |
195 | @Override |
196 | public void delete(Iterable<? extends T> entities) { |
197 | Assert.notNull(entities, "Cannot delete 'null' list."); |
198 | for (T entity : entities) { |
199 | delete(entity); |
200 | } |
201 | } |
202 | |
203 | @Override |
204 | public void deleteAll() { |
205 | DeleteQuery query = new DeleteQuery(); |
206 | query.setElasticsearchQuery(matchAllQuery()); |
207 | elasticsearchOperations.delete(query, getEntityClass()); |
208 | elasticsearchOperations.refresh(entityInformation.getIndexName(),true); |
209 | } |
210 | |
211 | private IndexQuery createIndexQuery(T entity){ |
212 | IndexQuery query = new IndexQuery(); |
213 | query.setObject(entity); |
214 | query.setId(extractIdFromBean(entity)); |
215 | return query; |
216 | } |
217 | |
218 | @SuppressWarnings("unchecked") |
219 | private Class<T> resolveReturnedClassFromGenericType() { |
220 | ParameterizedType parameterizedType = resolveReturnedClassFromGenericType(getClass()); |
221 | return (Class<T>) parameterizedType.getActualTypeArguments()[0]; |
222 | } |
223 | |
224 | private ParameterizedType resolveReturnedClassFromGenericType(Class<?> clazz) { |
225 | Object genericSuperclass = clazz.getGenericSuperclass(); |
226 | if (genericSuperclass instanceof ParameterizedType) { |
227 | ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; |
228 | Type rawtype = parameterizedType.getRawType(); |
229 | if (SimpleElasticsearchRepository.class.equals(rawtype)) { |
230 | return parameterizedType; |
231 | } |
232 | } |
233 | return resolveReturnedClassFromGenericType(clazz.getSuperclass()); |
234 | } |
235 | |
236 | public Class<T> getEntityClass() { |
237 | if (!isEntityClassSet()) { |
238 | try { |
239 | this.entityClass = resolveReturnedClassFromGenericType(); |
240 | } catch (Exception e) { |
241 | throw new InvalidDataAccessApiUsageException("Unable to resolve EntityClass. Please use according setter!", e); |
242 | } |
243 | } |
244 | return entityClass; |
245 | } |
246 | |
247 | private boolean isEntityClassSet() { |
248 | return entityClass != null; |
249 | } |
250 | |
251 | public final void setEntityClass(Class<T> entityClass) { |
252 | Assert.notNull(entityClass, "EntityClass must not be null."); |
253 | this.entityClass = entityClass; |
254 | } |
255 | |
256 | public final void setElasticsearchOperations(ElasticsearchOperations elasticsearchOperations) { |
257 | Assert.notNull(elasticsearchOperations, "ElasticsearchOperations must not be null."); |
258 | this.elasticsearchOperations = elasticsearchOperations; |
259 | } |
260 | |
261 | |
262 | private String extractIdFromBean(T entity) { |
263 | if (entityInformation != null) { |
264 | return entityInformation.getId(entity); |
265 | } |
266 | return null; |
267 | } |
268 | |
269 | } |