PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

http://github.com/SpringSource/spring-data-mongodb
Java | 3541 lines | 2135 code | 736 blank | 670 comment | 257 complexity | 5371130db57092c99f8e93476fd99e82 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright 2010-2021 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. * https://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.mongodb.core;
  17. import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
  18. import java.io.IOException;
  19. import java.math.BigDecimal;
  20. import java.math.RoundingMode;
  21. import java.util.*;
  22. import java.util.concurrent.TimeUnit;
  23. import java.util.stream.Collectors;
  24. import org.bson.Document;
  25. import org.bson.conversions.Bson;
  26. import org.slf4j.Logger;
  27. import org.slf4j.LoggerFactory;
  28. import org.springframework.beans.BeansException;
  29. import org.springframework.context.ApplicationContext;
  30. import org.springframework.context.ApplicationContextAware;
  31. import org.springframework.context.ApplicationEventPublisher;
  32. import org.springframework.context.ApplicationEventPublisherAware;
  33. import org.springframework.context.ApplicationListener;
  34. import org.springframework.context.ConfigurableApplicationContext;
  35. import org.springframework.core.io.Resource;
  36. import org.springframework.core.io.ResourceLoader;
  37. import org.springframework.dao.DataAccessException;
  38. import org.springframework.dao.InvalidDataAccessApiUsageException;
  39. import org.springframework.dao.OptimisticLockingFailureException;
  40. import org.springframework.dao.support.PersistenceExceptionTranslator;
  41. import org.springframework.data.convert.EntityReader;
  42. import org.springframework.data.geo.Distance;
  43. import org.springframework.data.geo.GeoResult;
  44. import org.springframework.data.geo.GeoResults;
  45. import org.springframework.data.geo.Metric;
  46. import org.springframework.data.mapping.MappingException;
  47. import org.springframework.data.mapping.callback.EntityCallbacks;
  48. import org.springframework.data.mapping.context.MappingContext;
  49. import org.springframework.data.mongodb.MongoDatabaseFactory;
  50. import org.springframework.data.mongodb.MongoDatabaseUtils;
  51. import org.springframework.data.mongodb.SessionSynchronization;
  52. import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
  53. import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
  54. import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
  55. import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
  56. import org.springframework.data.mongodb.core.QueryOperations.CountContext;
  57. import org.springframework.data.mongodb.core.QueryOperations.DeleteContext;
  58. import org.springframework.data.mongodb.core.QueryOperations.DistinctQueryContext;
  59. import org.springframework.data.mongodb.core.QueryOperations.QueryContext;
  60. import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
  61. import org.springframework.data.mongodb.core.aggregation.Aggregation;
  62. import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
  63. import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
  64. import org.springframework.data.mongodb.core.aggregation.AggregationResults;
  65. import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
  66. import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
  67. import org.springframework.data.mongodb.core.convert.DbRefResolver;
  68. import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
  69. import org.springframework.data.mongodb.core.convert.JsonSchemaMapper;
  70. import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
  71. import org.springframework.data.mongodb.core.convert.MongoConverter;
  72. import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
  73. import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper;
  74. import org.springframework.data.mongodb.core.convert.MongoWriter;
  75. import org.springframework.data.mongodb.core.convert.QueryMapper;
  76. import org.springframework.data.mongodb.core.convert.UpdateMapper;
  77. import org.springframework.data.mongodb.core.index.IndexOperations;
  78. import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
  79. import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
  80. import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
  81. import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
  82. import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
  83. import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
  84. import org.springframework.data.mongodb.core.mapping.event.*;
  85. import org.springframework.data.mongodb.core.mapreduce.GroupBy;
  86. import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
  87. import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
  88. import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
  89. import org.springframework.data.mongodb.core.query.BasicQuery;
  90. import org.springframework.data.mongodb.core.query.Collation;
  91. import org.springframework.data.mongodb.core.query.Criteria;
  92. import org.springframework.data.mongodb.core.query.Meta;
  93. import org.springframework.data.mongodb.core.query.Meta.CursorOption;
  94. import org.springframework.data.mongodb.core.query.NearQuery;
  95. import org.springframework.data.mongodb.core.query.Query;
  96. import org.springframework.data.mongodb.core.query.UpdateDefinition;
  97. import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
  98. import org.springframework.data.mongodb.core.timeseries.Granularity;
  99. import org.springframework.data.mongodb.core.validation.Validator;
  100. import org.springframework.data.mongodb.util.BsonUtils;
  101. import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
  102. import org.springframework.data.util.CloseableIterator;
  103. import org.springframework.data.util.Optionals;
  104. import org.springframework.lang.Nullable;
  105. import org.springframework.util.Assert;
  106. import org.springframework.util.ClassUtils;
  107. import org.springframework.util.CollectionUtils;
  108. import org.springframework.util.NumberUtils;
  109. import org.springframework.util.ObjectUtils;
  110. import org.springframework.util.ResourceUtils;
  111. import org.springframework.util.StringUtils;
  112. import com.mongodb.ClientSessionOptions;
  113. import com.mongodb.MongoException;
  114. import com.mongodb.ReadPreference;
  115. import com.mongodb.WriteConcern;
  116. import com.mongodb.client.AggregateIterable;
  117. import com.mongodb.client.ClientSession;
  118. import com.mongodb.client.DistinctIterable;
  119. import com.mongodb.client.FindIterable;
  120. import com.mongodb.client.MapReduceIterable;
  121. import com.mongodb.client.MongoClient;
  122. import com.mongodb.client.MongoCollection;
  123. import com.mongodb.client.MongoCursor;
  124. import com.mongodb.client.MongoDatabase;
  125. import com.mongodb.client.MongoIterable;
  126. import com.mongodb.client.model.*;
  127. import com.mongodb.client.result.DeleteResult;
  128. import com.mongodb.client.result.UpdateResult;
  129. /**
  130. * Primary implementation of {@link MongoOperations}.
  131. *
  132. * @author Thomas Risberg
  133. * @author Graeme Rocher
  134. * @author Mark Pollack
  135. * @author Oliver Gierke
  136. * @author Amol Nayak
  137. * @author Patryk Wasik
  138. * @author Tobias Trelle
  139. * @author Sebastian Herold
  140. * @author Thomas Darimont
  141. * @author Chuong Ngo
  142. * @author Christoph Strobl
  143. * @author Doménique Tilleuil
  144. * @author Niko Schmuck
  145. * @author Mark Paluch
  146. * @author Laszlo Csontos
  147. * @author Maninder Singh
  148. * @author Borislav Rangelov
  149. * @author duozhilin
  150. * @author Andreas Zink
  151. * @author Cimon Lucas
  152. * @author Michael J. Simons
  153. * @author Roman Puchkovskiy
  154. * @author Yadhukrishna S Pai
  155. * @author Anton Barkan
  156. * @author Bartłomiej Mazur
  157. */
  158. public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider {
  159. private static final Logger LOGGER = LoggerFactory.getLogger(MongoTemplate.class);
  160. private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
  161. private final MongoConverter mongoConverter;
  162. private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
  163. private final MongoDatabaseFactory mongoDbFactory;
  164. private final PersistenceExceptionTranslator exceptionTranslator;
  165. private final QueryMapper queryMapper;
  166. private final UpdateMapper updateMapper;
  167. private final JsonSchemaMapper schemaMapper;
  168. private final SpelAwareProxyProjectionFactory projectionFactory;
  169. private final EntityOperations operations;
  170. private final PropertyOperations propertyOperations;
  171. private final QueryOperations queryOperations;
  172. private @Nullable WriteConcern writeConcern;
  173. private WriteConcernResolver writeConcernResolver = DefaultWriteConcernResolver.INSTANCE;
  174. private WriteResultChecking writeResultChecking = WriteResultChecking.NONE;
  175. private @Nullable ReadPreference readPreference;
  176. private @Nullable ApplicationEventPublisher eventPublisher;
  177. private @Nullable EntityCallbacks entityCallbacks;
  178. private @Nullable ResourceLoader resourceLoader;
  179. private @Nullable MongoPersistentEntityIndexCreator indexCreator;
  180. private SessionSynchronization sessionSynchronization = SessionSynchronization.ON_ACTUAL_TRANSACTION;
  181. /**
  182. * Constructor used for a basic template configuration.
  183. *
  184. * @param mongoClient must not be {@literal null}.
  185. * @param databaseName must not be {@literal null} or empty.
  186. * @since 2.1
  187. */
  188. public MongoTemplate(MongoClient mongoClient, String databaseName) {
  189. this(new SimpleMongoClientDatabaseFactory(mongoClient, databaseName), (MongoConverter) null);
  190. }
  191. /**
  192. * Constructor used for a basic template configuration.
  193. *
  194. * @param mongoDbFactory must not be {@literal null}.
  195. */
  196. public MongoTemplate(MongoDatabaseFactory mongoDbFactory) {
  197. this(mongoDbFactory, (MongoConverter) null);
  198. }
  199. /**
  200. * Constructor used for a basic template configuration.
  201. *
  202. * @param mongoDbFactory must not be {@literal null}.
  203. * @param mongoConverter
  204. */
  205. public MongoTemplate(MongoDatabaseFactory mongoDbFactory, @Nullable MongoConverter mongoConverter) {
  206. Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!");
  207. this.mongoDbFactory = mongoDbFactory;
  208. this.exceptionTranslator = mongoDbFactory.getExceptionTranslator();
  209. this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
  210. this.queryMapper = new QueryMapper(this.mongoConverter);
  211. this.updateMapper = new UpdateMapper(this.mongoConverter);
  212. this.schemaMapper = new MongoJsonSchemaMapper(this.mongoConverter);
  213. this.projectionFactory = new SpelAwareProxyProjectionFactory();
  214. this.operations = new EntityOperations(this.mongoConverter.getMappingContext());
  215. this.propertyOperations = new PropertyOperations(this.mongoConverter.getMappingContext());
  216. this.queryOperations = new QueryOperations(queryMapper, updateMapper, operations, propertyOperations,
  217. mongoDbFactory);
  218. // We always have a mapping context in the converter, whether it's a simple one or not
  219. mappingContext = this.mongoConverter.getMappingContext();
  220. // We create indexes based on mapping events
  221. if (mappingContext instanceof MongoMappingContext) {
  222. MongoMappingContext mappingContext = (MongoMappingContext) this.mappingContext;
  223. if (mappingContext.isAutoIndexCreation()) {
  224. indexCreator = new MongoPersistentEntityIndexCreator(mappingContext, this);
  225. eventPublisher = new MongoMappingEventPublisher(indexCreator);
  226. mappingContext.setApplicationEventPublisher(eventPublisher);
  227. }
  228. }
  229. }
  230. private MongoTemplate(MongoDatabaseFactory dbFactory, MongoTemplate that) {
  231. this.mongoDbFactory = dbFactory;
  232. this.exceptionTranslator = that.exceptionTranslator;
  233. this.sessionSynchronization = that.sessionSynchronization;
  234. // we need to (re)create the MappingMongoConverter as we need to have it use a DbRefResolver that operates within
  235. // the sames session. Otherwise loading referenced objects would happen outside of it.
  236. if (that.mongoConverter instanceof MappingMongoConverter) {
  237. this.mongoConverter = ((MappingMongoConverter) that.mongoConverter).with(dbFactory);
  238. } else {
  239. this.mongoConverter = that.mongoConverter;
  240. }
  241. this.queryMapper = that.queryMapper;
  242. this.updateMapper = that.updateMapper;
  243. this.schemaMapper = that.schemaMapper;
  244. this.projectionFactory = that.projectionFactory;
  245. this.mappingContext = that.mappingContext;
  246. this.operations = that.operations;
  247. this.propertyOperations = that.propertyOperations;
  248. this.queryOperations = that.queryOperations;
  249. }
  250. /**
  251. * Configures the {@link WriteResultChecking} to be used with the template. Setting {@literal null} will reset the
  252. * default of {@link #DEFAULT_WRITE_RESULT_CHECKING}.
  253. *
  254. * @param resultChecking
  255. */
  256. public void setWriteResultChecking(@Nullable WriteResultChecking resultChecking) {
  257. this.writeResultChecking = resultChecking == null ? DEFAULT_WRITE_RESULT_CHECKING : resultChecking;
  258. }
  259. /**
  260. * Configures the {@link WriteConcern} to be used with the template. If none is configured the {@link WriteConcern}
  261. * configured on the {@link MongoDatabaseFactory} will apply.
  262. *
  263. * @param writeConcern
  264. */
  265. public void setWriteConcern(@Nullable WriteConcern writeConcern) {
  266. this.writeConcern = writeConcern;
  267. }
  268. /**
  269. * Configures the {@link WriteConcernResolver} to be used with the template.
  270. *
  271. * @param writeConcernResolver
  272. */
  273. public void setWriteConcernResolver(@Nullable WriteConcernResolver writeConcernResolver) {
  274. this.writeConcernResolver = writeConcernResolver == null ? DefaultWriteConcernResolver.INSTANCE
  275. : writeConcernResolver;
  276. }
  277. /**
  278. * Used by @{link {@link #prepareCollection(MongoCollection)} to set the {@link ReadPreference} before any operations
  279. * are performed.
  280. *
  281. * @param readPreference
  282. */
  283. public void setReadPreference(@Nullable ReadPreference readPreference) {
  284. this.readPreference = readPreference;
  285. }
  286. /*
  287. * (non-Javadoc)
  288. * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
  289. */
  290. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  291. prepareIndexCreator(applicationContext);
  292. eventPublisher = applicationContext;
  293. if (entityCallbacks == null) {
  294. setEntityCallbacks(EntityCallbacks.create(applicationContext));
  295. }
  296. if (mappingContext instanceof ApplicationEventPublisherAware) {
  297. ((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
  298. }
  299. resourceLoader = applicationContext;
  300. projectionFactory.setBeanFactory(applicationContext);
  301. projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
  302. }
  303. /**
  304. * Set the {@link EntityCallbacks} instance to use when invoking
  305. * {@link org.springframework.data.mapping.callback.EntityCallback callbacks} like the {@link BeforeSaveCallback}.
  306. * <br />
  307. * Overrides potentially existing {@link EntityCallbacks}.
  308. *
  309. * @param entityCallbacks must not be {@literal null}.
  310. * @throws IllegalArgumentException if the given instance is {@literal null}.
  311. * @since 2.2
  312. */
  313. public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
  314. Assert.notNull(entityCallbacks, "EntityCallbacks must not be null!");
  315. this.entityCallbacks = entityCallbacks;
  316. }
  317. /**
  318. * Inspects the given {@link ApplicationContext} for {@link MongoPersistentEntityIndexCreator} and those in turn if
  319. * they were registered for the current {@link MappingContext}. If no creator for the current {@link MappingContext}
  320. * can be found we manually add the internally created one as {@link ApplicationListener} to make sure indexes get
  321. * created appropriately for entity types persisted through this {@link MongoTemplate} instance.
  322. *
  323. * @param context must not be {@literal null}.
  324. */
  325. private void prepareIndexCreator(ApplicationContext context) {
  326. String[] indexCreators = context.getBeanNamesForType(MongoPersistentEntityIndexCreator.class);
  327. for (String creator : indexCreators) {
  328. MongoPersistentEntityIndexCreator creatorBean = context.getBean(creator, MongoPersistentEntityIndexCreator.class);
  329. if (creatorBean.isIndexCreatorFor(mappingContext)) {
  330. return;
  331. }
  332. }
  333. if (context instanceof ConfigurableApplicationContext && indexCreator != null) {
  334. ((ConfigurableApplicationContext) context).addApplicationListener(indexCreator);
  335. }
  336. }
  337. /**
  338. * Returns the default {@link org.springframework.data.mongodb.core.convert.MongoConverter}.
  339. *
  340. * @return
  341. */
  342. public MongoConverter getConverter() {
  343. return this.mongoConverter;
  344. }
  345. /*
  346. * (non-Javadoc)
  347. * @see org.springframework.data.mongodb.core.MongoOperations#executeAsStream(org.springframework.data.mongodb.core.query.Query, java.lang.Class)
  348. */
  349. @Override
  350. public <T> CloseableIterator<T> stream(Query query, Class<T> entityType) {
  351. return stream(query, entityType, getCollectionName(entityType));
  352. }
  353. /*
  354. * (non-Javadoc)
  355. * @see org.springframework.data.mongodb.core.MongoOperations#stream(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
  356. */
  357. @Override
  358. public <T> CloseableIterator<T> stream(Query query, Class<T> entityType, String collectionName) {
  359. return doStream(query, entityType, collectionName, entityType);
  360. }
  361. @SuppressWarnings("ConstantConditions")
  362. protected <T> CloseableIterator<T> doStream(Query query, Class<?> entityType, String collectionName,
  363. Class<T> returnType) {
  364. Assert.notNull(query, "Query must not be null!");
  365. Assert.notNull(entityType, "Entity type must not be null!");
  366. Assert.hasText(collectionName, "Collection name must not be null or empty!");
  367. Assert.notNull(returnType, "ReturnType must not be null!");
  368. return execute(collectionName, (CollectionCallback<CloseableIterator<T>>) collection -> {
  369. MongoPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityType);
  370. QueryContext queryContext = queryOperations.createQueryContext(query);
  371. Document mappedQuery = queryContext.getMappedQuery(persistentEntity);
  372. Document mappedFields = queryContext.getMappedFields(persistentEntity, returnType, projectionFactory);
  373. FindIterable<Document> cursor = new QueryCursorPreparer(query, entityType).initiateFind(collection,
  374. col -> col.find(mappedQuery, Document.class).projection(mappedFields));
  375. return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator,
  376. new ProjectingReadCallback<>(mongoConverter, entityType, returnType, collectionName));
  377. });
  378. }
  379. @Override
  380. public String getCollectionName(Class<?> entityClass) {
  381. return this.operations.determineCollectionName(entityClass);
  382. }
  383. /*
  384. * (non-Javadoc)
  385. * @see org.springframework.data.mongodb.core.MongoOperations#executeCommand(java.lang.String)
  386. */
  387. @Override
  388. @SuppressWarnings("ConstantConditions")
  389. public Document executeCommand(String jsonCommand) {
  390. Assert.hasText(jsonCommand, "JsonCommand must not be null nor empty!");
  391. return execute(db -> db.runCommand(Document.parse(jsonCommand), Document.class));
  392. }
  393. /*
  394. * (non-Javadoc)
  395. * @see org.springframework.data.mongodb.core.MongoOperations#executeCommand(org.bson.Document)
  396. */
  397. @Override
  398. @SuppressWarnings("ConstantConditions")
  399. public Document executeCommand(Document command) {
  400. Assert.notNull(command, "Command must not be null!");
  401. return execute(db -> db.runCommand(command, Document.class));
  402. }
  403. /*
  404. * (non-Javadoc)
  405. * @see org.springframework.data.mongodb.core.MongoOperations#executeCommand(org.bson.Document, com.mongodb.ReadPreference)
  406. */
  407. @Override
  408. @SuppressWarnings("ConstantConditions")
  409. public Document executeCommand(Document command, @Nullable ReadPreference readPreference) {
  410. Assert.notNull(command, "Command must not be null!");
  411. return execute(db -> readPreference != null //
  412. ? db.runCommand(command, readPreference, Document.class) //
  413. : db.runCommand(command, Document.class));
  414. }
  415. /*
  416. * (non-Javadoc)
  417. * @see org.springframework.data.mongodb.core.MongoOperations#executeQuery(org.springframework.data.mongodb.core.query.Query, java.lang.String, org.springframework.data.mongodb.core.DocumentCallbackHandler)
  418. */
  419. @Override
  420. public void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch) {
  421. executeQuery(query, collectionName, dch, new QueryCursorPreparer(query, null));
  422. }
  423. /**
  424. * Execute a MongoDB query and iterate over the query results on a per-document basis with a
  425. * {@link DocumentCallbackHandler} using the provided CursorPreparer.
  426. *
  427. * @param query the query class that specifies the criteria used to find a record and also an optional fields
  428. * specification, must not be {@literal null}.
  429. * @param collectionName name of the collection to retrieve the objects from
  430. * @param documentCallbackHandler the handler that will extract results, one document at a time
  431. * @param preparer allows for customization of the {@link FindIterable} used when iterating over the result set,
  432. * (apply limits, skips and so on).
  433. */
  434. protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler documentCallbackHandler,
  435. @Nullable CursorPreparer preparer) {
  436. Assert.notNull(query, "Query must not be null!");
  437. Assert.notNull(collectionName, "CollectionName must not be null!");
  438. Assert.notNull(documentCallbackHandler, "DocumentCallbackHandler must not be null!");
  439. Document queryObject = queryMapper.getMappedObject(query.getQueryObject(), Optional.empty());
  440. Document sortObject = query.getSortObject();
  441. Document fieldsObject = query.getFieldsObject();
  442. if (LOGGER.isDebugEnabled()) {
  443. LOGGER.debug("Executing query: {} sort: {} fields: {} in collection: {}", serializeToJsonSafely(queryObject),
  444. sortObject, fieldsObject, collectionName);
  445. }
  446. this.executeQueryInternal(new FindCallback(queryObject, fieldsObject, null),
  447. preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, documentCallbackHandler, collectionName);
  448. }
  449. /*
  450. * (non-Javadoc)
  451. * @see org.springframework.data.mongodb.core.MongoOperations#execute(org.springframework.data.mongodb.core.DbCallback)
  452. */
  453. public <T> T execute(DbCallback<T> action) {
  454. Assert.notNull(action, "DbCallback must not be null!");
  455. try {
  456. MongoDatabase db = prepareDatabase(this.doGetDatabase());
  457. return action.doInDB(db);
  458. } catch (RuntimeException e) {
  459. throw potentiallyConvertRuntimeException(e, exceptionTranslator);
  460. }
  461. }
  462. /*
  463. * (non-Javadoc)
  464. * @see org.springframework.data.mongodb.core.MongoOperations#execute(java.lang.Class, org.springframework.data.mongodb.core.DbCallback)
  465. */
  466. public <T> T execute(Class<?> entityClass, CollectionCallback<T> callback) {
  467. Assert.notNull(entityClass, "EntityClass must not be null!");
  468. return execute(getCollectionName(entityClass), callback);
  469. }
  470. /*
  471. * (non-Javadoc)
  472. * @see org.springframework.data.mongodb.core.MongoOperations#execute(java.lang.String, org.springframework.data.mongodb.core.DbCallback)
  473. */
  474. public <T> T execute(String collectionName, CollectionCallback<T> callback) {
  475. Assert.notNull(collectionName, "CollectionName must not be null!");
  476. Assert.notNull(callback, "CollectionCallback must not be null!");
  477. try {
  478. MongoCollection<Document> collection = getAndPrepareCollection(doGetDatabase(), collectionName);
  479. return callback.doInCollection(collection);
  480. } catch (RuntimeException e) {
  481. throw potentiallyConvertRuntimeException(e, exceptionTranslator);
  482. }
  483. }
  484. /*
  485. * (non-Javadoc)
  486. * @see org.springframework.data.mongodb.core.MongoOperations#withSession(com.mongodb.ClientSessionOptions)
  487. */
  488. @Override
  489. public SessionScoped withSession(ClientSessionOptions options) {
  490. Assert.notNull(options, "ClientSessionOptions must not be null!");
  491. return withSession(() -> mongoDbFactory.getSession(options));
  492. }
  493. /*
  494. * (non-Javadoc)
  495. * @see org.springframework.data.mongodb.core.MongoOperations#withSession(com.mongodb.session.ClientSession)
  496. */
  497. @Override
  498. public MongoTemplate withSession(ClientSession session) {
  499. Assert.notNull(session, "ClientSession must not be null!");
  500. return new SessionBoundMongoTemplate(session, MongoTemplate.this);
  501. }
  502. /**
  503. * Define if {@link MongoTemplate} should participate in transactions. Default is set to
  504. * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION}.<br />
  505. * <strong>NOTE:</strong> MongoDB transactions require at least MongoDB 4.0.
  506. *
  507. * @since 2.1
  508. */
  509. public void setSessionSynchronization(SessionSynchronization sessionSynchronization) {
  510. this.sessionSynchronization = sessionSynchronization;
  511. }
  512. /*
  513. * (non-Javadoc)
  514. * @see org.springframework.data.mongodb.core.MongoOperations#createCollection(java.lang.Class)
  515. */
  516. public <T> MongoCollection<Document> createCollection(Class<T> entityClass) {
  517. return createCollection(entityClass, operations.forType(entityClass).getCollectionOptions());
  518. }
  519. /*
  520. * (non-Javadoc)
  521. * @see org.springframework.data.mongodb.core.MongoOperations#createCollection(java.lang.Class, org.springframework.data.mongodb.core.CollectionOptions)
  522. */
  523. public <T> MongoCollection<Document> createCollection(Class<T> entityClass,
  524. @Nullable CollectionOptions collectionOptions) {
  525. Assert.notNull(entityClass, "EntityClass must not be null!");
  526. CollectionOptions options = collectionOptions != null ? collectionOptions : CollectionOptions.empty();
  527. options = Optionals
  528. .firstNonEmpty(() -> Optional.ofNullable(collectionOptions).flatMap(CollectionOptions::getCollation),
  529. () -> operations.forType(entityClass).getCollation()) //
  530. .map(options::collation).orElse(options);
  531. return doCreateCollection(getCollectionName(entityClass), convertToDocument(options, entityClass));
  532. }
  533. /*
  534. * (non-Javadoc)
  535. * @see org.springframework.data.mongodb.core.MongoOperations#createCollection(java.lang.String)
  536. */
  537. public MongoCollection<Document> createCollection(String collectionName) {
  538. Assert.notNull(collectionName, "CollectionName must not be null!");
  539. return doCreateCollection(collectionName, new Document());
  540. }
  541. /*
  542. * (non-Javadoc)
  543. * @see org.springframework.data.mongodb.core.MongoOperations#createCollection(java.lang.String, org.springframework.data.mongodb.core.CollectionOptions)
  544. */
  545. public MongoCollection<Document> createCollection(String collectionName,
  546. @Nullable CollectionOptions collectionOptions) {
  547. Assert.notNull(collectionName, "CollectionName must not be null!");
  548. return doCreateCollection(collectionName, convertToDocument(collectionOptions));
  549. }
  550. /*
  551. * (non-Javadoc)
  552. * @see org.springframework.data.mongodb.core.MongoOperations#getCollection(java.lang.String)
  553. */
  554. @SuppressWarnings("ConstantConditions")
  555. public MongoCollection<Document> getCollection(String collectionName) {
  556. Assert.notNull(collectionName, "CollectionName must not be null!");
  557. return execute(db -> db.getCollection(collectionName, Document.class));
  558. }
  559. /*
  560. * (non-Javadoc)
  561. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#getCollection(java.lang.Class)
  562. */
  563. public <T> boolean collectionExists(Class<T> entityClass) {
  564. return collectionExists(getCollectionName(entityClass));
  565. }
  566. /*
  567. * (non-Javadoc)
  568. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#getCollection(java.lang.String)
  569. */
  570. @SuppressWarnings("ConstantConditions")
  571. public boolean collectionExists(String collectionName) {
  572. Assert.notNull(collectionName, "CollectionName must not be null!");
  573. return execute(db -> {
  574. for (String name : db.listCollectionNames()) {
  575. if (name.equals(collectionName)) {
  576. return true;
  577. }
  578. }
  579. return false;
  580. });
  581. }
  582. /*
  583. * (non-Javadoc)
  584. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#dropCollection(java.lang.Class)
  585. */
  586. public <T> void dropCollection(Class<T> entityClass) {
  587. dropCollection(getCollectionName(entityClass));
  588. }
  589. /*
  590. * (non-Javadoc)
  591. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#dropCollection(java.lang.String)
  592. */
  593. public void dropCollection(String collectionName) {
  594. Assert.notNull(collectionName, "CollectionName must not be null!");
  595. execute(collectionName, (CollectionCallback<Void>) collection -> {
  596. collection.drop();
  597. if (LOGGER.isDebugEnabled()) {
  598. LOGGER.debug("Dropped collection [{}]",
  599. collection.getNamespace() != null ? collection.getNamespace().getCollectionName() : collectionName);
  600. }
  601. return null;
  602. });
  603. }
  604. @Override
  605. public IndexOperations indexOps(String collectionName) {
  606. return indexOps(collectionName, null);
  607. }
  608. /*
  609. * (non-Javadoc)
  610. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#indexOps(java.lang.String)
  611. */
  612. public IndexOperations indexOps(String collectionName, @Nullable Class<?> type) {
  613. return new DefaultIndexOperations(this, collectionName, type);
  614. }
  615. /*
  616. * (non-Javadoc)
  617. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#indexOps(java.lang.Class)
  618. */
  619. public IndexOperations indexOps(Class<?> entityClass) {
  620. return indexOps(getCollectionName(entityClass), entityClass);
  621. }
  622. /*
  623. * (non-Javadoc)
  624. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#bulkOps(org.springframework.data.mongodb.core.BulkMode, java.lang.String)
  625. */
  626. public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) {
  627. return bulkOps(bulkMode, null, collectionName);
  628. }
  629. /*
  630. * (non-Javadoc)
  631. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#bulkOps(org.springframework.data.mongodb.core.BulkMode, java.lang.Class)
  632. */
  633. public BulkOperations bulkOps(BulkMode bulkMode, Class<?> entityClass) {
  634. return bulkOps(bulkMode, entityClass, getCollectionName(entityClass));
  635. }
  636. /*
  637. * (non-Javadoc)
  638. * @see org.springframework.data.mongodb.core.ExecutableInsertOperation#bulkOps(org.springframework.data.mongodb.core.BulkMode, java.lang.Class, java.lang.String)
  639. */
  640. public BulkOperations bulkOps(BulkMode mode, @Nullable Class<?> entityType, String collectionName) {
  641. Assert.notNull(mode, "BulkMode must not be null!");
  642. Assert.hasText(collectionName, "Collection name must not be null or empty!");
  643. DefaultBulkOperations operations = new DefaultBulkOperations(this, collectionName,
  644. new BulkOperationContext(mode, Optional.ofNullable(getPersistentEntity(entityType)), queryMapper, updateMapper,
  645. eventPublisher, entityCallbacks));
  646. operations.setDefaultWriteConcern(writeConcern);
  647. return operations;
  648. }
  649. /*
  650. * (non-Javadoc)
  651. * @see org.springframework.data.mongodb.core.MongoOperations#scriptOps()
  652. */
  653. @Override
  654. public ScriptOperations scriptOps() {
  655. return new DefaultScriptOperations(this);
  656. }
  657. // Find methods that take a Query to express the query and that return a single object.
  658. @Nullable
  659. @Override
  660. public <T> T findOne(Query query, Class<T> entityClass) {
  661. return findOne(query, entityClass, getCollectionName(entityClass));
  662. }
  663. @Nullable
  664. @Override
  665. public <T> T findOne(Query query, Class<T> entityClass, String collectionName) {
  666. Assert.notNull(query, "Query must not be null!");
  667. Assert.notNull(entityClass, "EntityClass must not be null!");
  668. Assert.notNull(collectionName, "CollectionName must not be null!");
  669. if (ObjectUtils.isEmpty(query.getSortObject())) {
  670. return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(),
  671. new QueryCursorPreparer(query, entityClass), entityClass);
  672. } else {
  673. query.limit(1);
  674. List<T> results = find(query, entityClass, collectionName);
  675. return results.isEmpty() ? null : results.get(0);
  676. }
  677. }
  678. @Override
  679. public boolean exists(Query query, Class<?> entityClass) {
  680. return exists(query, entityClass, getCollectionName(entityClass));
  681. }
  682. @Override
  683. public boolean exists(Query query, String collectionName) {
  684. return exists(query, null, collectionName);
  685. }
  686. @Override
  687. @SuppressWarnings("ConstantConditions")
  688. public boolean exists(Query query, @Nullable Class<?> entityClass, String collectionName) {
  689. if (query == null) {
  690. throw new InvalidDataAccessApiUsageException("Query passed in to exist can't be null");
  691. }
  692. Assert.notNull(collectionName, "CollectionName must not be null!");
  693. QueryContext queryContext = queryOperations.createQueryContext(query);
  694. Document mappedQuery = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
  695. return execute(collectionName,
  696. new ExistsCallback(mappedQuery, queryContext.getCollation(entityClass).orElse(null)));
  697. }
  698. // Find methods that take a Query to express the query and that return a List of objects.
  699. /*
  700. * (non-Javadoc)
  701. * @see org.springframework.data.mongodb.core.MongoOperations#findOne(org.springframework.data.mongodb.core.query.Query, java.lang.Class)
  702. */
  703. @Override
  704. public <T> List<T> find(Query query, Class<T> entityClass) {
  705. return find(query, entityClass, getCollectionName(entityClass));
  706. }
  707. /*
  708. * (non-Javadoc)
  709. * @see org.springframework.data.mongodb.core.MongoOperations#findOne(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
  710. */
  711. @Override
  712. public <T> List<T> find(Query query, Class<T> entityClass, String collectionName) {
  713. Assert.notNull(query, "Query must not be null!");
  714. Assert.notNull(collectionName, "CollectionName must not be null!");
  715. Assert.notNull(entityClass, "EntityClass must not be null!");
  716. return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
  717. new QueryCursorPreparer(query, entityClass));
  718. }
  719. @Nullable
  720. @Override
  721. public <T> T findById(Object id, Class<T> entityClass) {
  722. return findById(id, entityClass, getCollectionName(entityClass));
  723. }
  724. @Nullable
  725. @Override
  726. public <T> T findById(Object id, Class<T> entityClass, String collectionName) {
  727. Assert.notNull(id, "Id must not be null!");
  728. Assert.notNull(entityClass, "EntityClass must not be null!");
  729. Assert.notNull(collectionName, "CollectionName must not be null!");
  730. String idKey = operations.getIdPropertyName(entityClass);
  731. return doFindOne(collectionName, new Document(idKey, id), new Document(), entityClass);
  732. }
  733. /*
  734. * (non-Javadoc)
  735. * @see org.springframework.data.mongodb.core.MongoOperations#findDistinct(org.springframework.data.mongodb.core.query.Query, java.lang.String, java.lang.Class, java.lang.Class)
  736. */
  737. @Override
  738. public <T> List<T> findDistinct(Query query, String field, Class<?> entityClass, Class<T> resultClass) {
  739. return findDistinct(query, field, getCollectionName(entityClass), entityClass, resultClass);
  740. }
  741. /*
  742. * (non-Javadoc)
  743. * @see org.springframework.data.mongodb.core.MongoOperations#findDistinct(org.springframework.data.mongodb.core.query.Query, java.lang.String, java.lang.String, java.lang.Class, java.lang.Class)
  744. */
  745. @Override
  746. @SuppressWarnings("unchecked")
  747. public <T> List<T> findDistinct(Query query, String field, String collectionName, Class<?> entityClass,
  748. Class<T> resultClass) {
  749. Assert.notNull(query, "Query must not be null!");
  750. Assert.notNull(field, "Field must not be null!");
  751. Assert.notNull(collectionName, "CollectionName must not be null!");
  752. Assert.notNull(entityClass, "EntityClass must not be null!");
  753. Assert.notNull(resultClass, "ResultClass must not be null!");
  754. MongoPersistentEntity<?> entity = entityClass != Object.class ? getPersistentEntity(entityClass) : null;
  755. DistinctQueryContext distinctQueryContext = queryOperations.distinctQueryContext(query, field);
  756. Document mappedQuery = distinctQueryContext.getMappedQuery(entity);
  757. String mappedFieldName = distinctQueryContext.getMappedFieldName(entity);
  758. Class<T> mongoDriverCompatibleType = distinctQueryContext.getDriverCompatibleClass(resultClass);
  759. MongoIterable<?> result = execute(collectionName, (collection) -> {
  760. if (LOGGER.isDebugEnabled()) {
  761. LOGGER.debug("Executing findDistinct using query {} for field: {} in collection: {}",
  762. serializeToJsonSafely(mappedQuery), field, collectionName);
  763. }
  764. QueryCursorPreparer preparer = new QueryCursorPreparer(query, entityClass);
  765. if (preparer.hasReadPreference()) {
  766. collection = collection.withReadPreference(preparer.getReadPreference());
  767. }
  768. DistinctIterable<T> iterable = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
  769. distinctQueryContext.applyCollation(entityClass, iterable::collation);
  770. return iterable;
  771. });
  772. if (resultClass == Object.class || mongoDriverCompatibleType != resultClass) {
  773. MongoConverter converter = getConverter();
  774. DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
  775. result = result.map((source) -> converter.mapValueToTargetType(source,
  776. distinctQueryContext.getMostSpecificConversionTargetType(resultClass, entityClass), dbRefResolver));
  777. }
  778. try {
  779. return (List<T>) result.into(new ArrayList<>());
  780. } catch (RuntimeException e) {
  781. throw potentiallyConvertRuntimeException(e, exceptionTranslator);
  782. }
  783. }
  784. @Override
  785. public <T> GeoResults<T> geoNear(NearQuery near, Class<T> entityClass) {
  786. return geoNear(near, entityClass, getCollectionName(entityClass));
  787. }
  788. @Override
  789. public <T> GeoResults<T> geoNear(NearQuery near, Class<T> domainType, String collectionName) {
  790. return geoNear(near, domainType, collectionName, domainType);
  791. }
  792. public <T> GeoResults<T> geoNear(NearQuery near, Class<?> domainType, String collectionName, Class<T> returnType) {
  793. if (near == null) {
  794. throw new InvalidDataAccessApiUsageException("NearQuery must not be null!");
  795. }
  796. if (domainType == null) {
  797. throw new InvalidDataAccessApiUsageException("Entity class must not be null!");
  798. }
  799. Assert.notNull(collectionName, "CollectionName must not be null!");
  800. Assert.notNull(returnType, "ReturnType must not be null!");
  801. String collection = StringUtils.hasText(collectionName) ? collectionName : getCollectionName(domainType);
  802. String distanceField = operations.nearQueryDistanceFieldName(domainType);
  803. Aggregation $geoNear = TypedAggregation.newAggregation(domainType, Aggregation.geoNear(near, distanceField))
  804. .withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
  805. AggregationResults<Document> results = aggregate($geoNear, collection, Document.class);
  806. DocumentCallback<GeoResult<T>> callback = new GeoNearResultDocumentCallback<>(distanceField,
  807. new ProjectingReadCallback<>(mongoConverter, domainType, returnType, collection), near.getMetric());
  808. List<GeoResult<T>> result = new ArrayList<>();
  809. BigDecimal aggregate = BigDecimal.ZERO;
  810. for (Document element : results) {
  811. GeoResult<T> geoResult = callback.doWith(element);
  812. aggregate = aggregate.add(BigDecimal.valueOf(geoResult.getDistance().getValue()));
  813. result.add(geoResult);
  814. }
  815. Distance avgDistance = new Distance(
  816. result.size() == 0 ? 0 : aggregate.divide(new BigDecimal(result.size()), RoundingMode.HALF_UP).doubleValue(),
  817. near.getMetric());
  818. return new GeoResults<>(result, avgDistance);
  819. }
  820. @Nullable
  821. @Override
  822. public <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass) {
  823. return findAndModify(query, update, new FindAndModifyOptions(), entityClass, getCollectionName(entityClass));
  824. }
  825. @Nullable
  826. @Override
  827. public <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName) {
  828. return findAndModify(query, update, new FindAndModifyOptions(), entityClass, collectionName);
  829. }
  830. @Nullable
  831. @Override
  832. public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass) {
  833. return findAndModify(query, update, options, entityClass, getCollectionName(entityClass));
  834. }
  835. @Nullable
  836. @Override
  837. public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
  838. String collectionName) {
  839. Assert.notNull(query, "Query must not be null!");
  840. Assert.notNull(update, "Update must not be null!");
  841. Assert.notNull(options, "Options must not be null!");
  842. Assert.notNull(entityClass, "EntityClass must not be null!");
  843. Assert.notNull(collectionName, "CollectionName must not be null!");
  844. FindAndModifyOptions optionsToUse = FindAndModifyOptions.of(options);
  845. Optionals.ifAllPresent(query.getCollation(), optionsToUse.getCollation(), (l, r) -> {
  846. throw new IllegalArgumentException(
  847. "Both Query and FindAndModifyOptions define a collation. Please provide the collation only via one of the two.");
  848. });
  849. if (!options.getCollation().isPresent()) {
  850. operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
  851. }
  852. return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(),
  853. getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
  854. }
  855. /*
  856. * (non-Javadoc)
  857. * @see org.springframework.data.mongodb.core.MongoOperations#findAndReplace(org.springframework.data.mongodb.core.query.Query, java.lang.Object, org.springframework.data.mongodb.core.FindAndReplaceOptions, java.lang.Class, java.lang.String, java.lang.Class)
  858. */
  859. @Override
  860. public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
  861. String collectionName, Class<T> resultType) {
  862. Assert.notNull(query, "Query must not be null!");
  863. Assert.notNull(replacement, "Replacement must not be null!");
  864. Assert.notNull(options, "Options must not be null! Use FindAndReplaceOptions#empty() instead.");
  865. Assert.notNull(entityType, "EntityType must not be null!");
  866. Assert.notNull(collectionName, "CollectionName must not be null!");
  867. Assert.notNull(resultType, "ResultType must not be null! Use Object.class instead.");
  868. Assert.isTrue(query.getLimit() <= 1, "Query must not define a limit other than 1 ore none!");
  869. Assert.isTrue(query.getSkip() <= 0, "Query must not define skip.");
  870. MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType);
  871. QueryContext queryContext = queryOperations.createQueryContext(query);
  872. Document mappedQuery = queryContext.getMappedQuery(entity);
  873. Document mappedFields = queryContext.getMappedFields(entity, resultType, projectionFactory);
  874. Document mappedSort = queryContext.getMappedSort(entity);
  875. replacement = maybeCallBeforeConvert(replacement, collectionName);
  876. Document mappedReplacement = operations.forEntity(replacement).toMappedDocument(this.mongoConverter).getDocument();
  877. maybeEmitEvent(new BeforeSaveEvent<>(replacement, mappedReplacement, collectionName));
  878. maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
  879. T saved = doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort,
  880. queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, resultType);
  881. if (saved != null) {
  882. maybeEmitEvent(new AfterSaveEvent<>(saved, mappedReplacement, collectionName));
  883. return maybeCallAfterSave(saved, mappedReplacement, collectionName);
  884. }
  885. return saved;
  886. }
  887. // Find methods that take a Query to express the query and that return a single object that is also removed from the
  888. // collection in the database.
  889. @Nullable
  890. @Override
  891. public <T> T findAndRemove(Query query, Class<T> entityClass) {
  892. return findAndRemove(query, entityClass, getCollectionName(entityClass));
  893. }
  894. @Nullable
  895. @Override
  896. public <T> T findAndRemove(Query query, Class<T> entityClass, String collectionName) {
  897. Assert.notNull(query, "Query must not be null!");
  898. Assert.notNull(entityClass, "EntityClass must not be null!");
  899. Assert.notNull(collectionName, "CollectionName must not be null!");
  900. return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(),
  901. getMappedSortObject(query, entityClass), operations.forType(entityClass).getCollation(query).orElse(null),
  902. entityClass);
  903. }
  904. @Override
  905. public long count(Query query, Class<?> entityClass) {
  906. Assert.notNull(entityClass, "Entity class must not be null!");
  907. return count(query, entityClass, getCollectionName(entityClass));
  908. }
  909. @Override
  910. public long count(Query query, String collectionName) {
  911. return count(query, null, collectionName);
  912. }
  913. /*
  914. * (non-Javadoc)
  915. * @see org.springframework.data.mongodb.core.MongoOperations#count(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
  916. */
  917. public long count(Query query, @Nullable Class<?> entityClass, String collectionName) {
  918. Assert.notNull(query, "Query must not be null!");
  919. Assert.hasText(collectionName, "Collection name must not be null or empty!");
  920. CountContext countContext = queryOperations.countQueryContext(query);
  921. CountOptions options = countContext.getCountOptions(entityClass);
  922. Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
  923. return doCount(collectionName, mappedQuery, options);
  924. }
  925. @SuppressWarnings("ConstantConditions")
  926. protected long doCount(String collectionName, Document filter, CountOptions options) {
  927. if (LOGGER.isDebugEnabled()) {
  928. LOGGER.debug("Executing count: {} in collection: {}", serializeToJsonSafely(filter), collectionName);
  929. }
  930. return execute(collectionName,
  931. collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
  932. }
  933. /*
  934. * (non-Javadoc)
  935. * @see org.springframework.data.mongodb.core.MongoOperations#estimatedCount(java.lang.String)
  936. */
  937. @Override
  938. public long estimatedCount(String collectionName) {
  939. return doEstimatedCount(collectionName, new EstimatedDocumentCountOptions());
  940. }
  941. protected long doEstimatedCount(String collectionName, EstimatedDocumentCountOptions options) {
  942. return execute(collectionName, collection -> collection.estimatedDocumentCount(options));
  943. }
  944. /*
  945. * (non-Javadoc)
  946. * @see org.springframework.data.mongodb.core.MongoOperations#insert(java.lang.Object)
  947. */
  948. @Override
  949. public <T> T insert(T objectToSave) {
  950. Assert.notNull(objectToSave, "ObjectToSave must not be null!");
  951. ensureNotIterable(objectToSave);
  952. return insert(objectToSave, getCollectionName(ClassUtils.getUserClass(objectToSave)));
  953. }
  954. /*
  955. * (non-Javadoc)
  956. * @see org.springframework.data.mongodb.core.MongoOperations#insert(java.lang.Object, java.lang.String)
  957. */
  958. @Override
  959. @SuppressWarnings("unchecked")
  960. public <T> T insert(T objectToSave, String collectionName) {
  961. Assert.notNull(objectToSave, "ObjectToSave must not be null!");
  962. Assert.notNull(collectionName, "CollectionName must not be null!");
  963. ensureNotIterable(objectToSave);
  964. return (T) doInsert(collectionName, objectToSave, this.mongoConverter);
  965. }
  966. /**
  967. * Ensure the given {@literal source} is not an {@link java.lang.reflect.Array}, {@link Collection} or
  968. * {@link Iterator}.
  969. *
  970. * @param source can be {@literal null}.
  971. * @deprecated since 3.2. Call {@link #ensureNotCollectionLike(Object)} instead.
  972. */
  973. protected void ensureNotIterable(@Nullable Object source) {
  974. ensureNotCollectionLike(source);
  975. }
  976. /**
  977. * Ensure the given {@literal source} is not an {@link java.lang.reflect.Array}, {@link Collection} or
  978. * {@link Iterator}.
  979. *
  980. * @param source can be {@literal null}.
  981. * @since 3.2.
  982. */
  983. protected void ensureNotCollectionLike(@Nullable Object source) {
  984. if (EntityOperations.isCollectionLike(source)) {
  985. throw new IllegalArgumentException("Cannot use a collection here.");
  986. }
  987. }
  988. /**
  989. * Prepare the collection before any processing is done using it. This allows a convenient way to apply settings like
  990. * withCodecRegistry() etc. Can be overridden in sub-classes.
  991. *
  992. * @param collection
  993. */
  994. protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
  995. if (this.readPreference != null) {
  996. collection = collection.withReadPreference(readPreference);
  997. }
  998. return collection;
  999. }
  1000. /**
  1001. * Prepare the WriteConcern before any processing is done using it. This allows a convenient way to apply custom
  1002. * settings in sub-classes. <br />
  1003. * In case of using MongoDB Java driver version 3 the returned {@link WriteConcern} will be defaulted to
  1004. * {@link WriteConcern#ACKNOWLEDGED} when {@link WriteResultChecking} is set to {@link WriteResultChecking#EXCEPTION}.
  1005. *
  1006. * @param mongoAction any MongoAction already configured or null
  1007. * @return The prepared WriteConcern or null
  1008. */
  1009. @Nullable
  1010. protected WriteConcern prepareWriteConcern(MongoAction mongoAction) {
  1011. WriteConcern wc = writeConcernResolver.resolve(mongoAction);
  1012. return potentiallyForceAcknowledgedWrite(wc);
  1013. }
  1014. @Nullable
  1015. private WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc) {
  1016. if (ObjectUtils.nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking)) {
  1017. if (wc == null || wc.getWObject() == null
  1018. || (wc.getWObject() instanceof Number && ((Number) wc.getWObject()).intValue() < 1)) {
  1019. return WriteConcern.ACKNOWLEDGED;
  1020. }
  1021. }
  1022. return wc;
  1023. }
  1024. protected <T> T doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
  1025. BeforeConvertEvent<T> event = new BeforeConvertEvent<>(objectToSave, collectionName);
  1026. T toConvert = maybeEmitEvent(event).getSource();
  1027. toConvert = maybeCallBeforeConvert(toConvert, collectionName);
  1028. AdaptibleEntity<T> entity = operations.forEntity(toConvert, mongoConverter.getConversionService());
  1029. entity.assertUpdateableIdIfNotSet();
  1030. T initialized = entity.initializeVersionProperty();
  1031. Document dbDoc = entity.toMappedDocument(writer).getDocument();
  1032. maybeEmitEvent(new BeforeSaveEvent<>(initialized, dbDoc, collectionName));
  1033. initialized = maybeCallBeforeSave(initialized, dbDoc, collectionName);
  1034. Object id = insertDocument(collectionName, dbDoc, initialized.getClass());
  1035. T saved = populateIdIfNecessary(initialized, id);
  1036. maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName));
  1037. return maybeCallAfterSave(saved, dbDoc, collectionName);
  1038. }
  1039. @Override
  1040. @SuppressWarnings("unchecked")
  1041. public <T> Collection

Large files files are truncated, but you can click here to view the full file