Compare commits

...

116 Commits

Author SHA1 Message Date
Oliver Gierke
678e967318 DATAMONGO-1597 - Release version 1.9.8 (Hopper SR8). 2017-03-02 11:30:05 +01:00
Oliver Gierke
ce6193a541 DATAMONGO-1597 - Prepare 1.9.8 (Hopper SR8). 2017-03-02 11:29:33 +01:00
Oliver Gierke
a3c21bf3af DATAMONGO-1597 - Updated changelog. 2017-03-02 11:29:27 +01:00
Oliver Gierke
850e95946a DATAMONGO-1598 - Updated changelog. 2017-03-02 11:11:02 +01:00
Mark Paluch
c316aceda1 DATAMONGO-1605 - Polishing.
Remove additional quoting around JSON serialization because JSON serialization adds quotes to a string. Reformat code.
2017-03-01 16:01:49 +01:00
Christoph Strobl
3385d96213 DATAMONGO-1605 - Retain type of SpEL expression result when used in @Query.
Fix issue where any result of a SpEL expression had been treated as quoted String within the resulting MongoDB query.
2017-03-01 16:01:48 +01:00
Mark Paluch
fa7d783940 DATAMONGO-1603 - Polishing.
Remove code that became unused. Reformat code. Extend years in copyright header.

Original pull request: #441.
2017-03-01 14:15:45 +01:00
Christoph Strobl
39ba28cdae DATAMONGO-1603 - Fix Placeholder not replaced correctly in @Query.
Fix issues when placeholders are appended with other chars eg. '?0xyz' or have been reused multiple times within the query. Additional tests and fixes for complex quoted replacements eg. in regex query. Rely on placeholder quotation indication instead of binding one. Might be misleading when placeholder is used more than once.

This backport contains elements from DATAMONGO-1575.

Original pull request: #441.
Related ticket: DATAMONGO-1575.
2017-03-01 14:15:37 +01:00
Oliver Gierke
7a32d40dd0 DATAMONGO-1617 - Reinstantiate version property initialization before BeforeConvertEvent publication.
Related pull request: #443.
2017-02-28 20:06:02 +01:00
Oliver Gierke
6593fef803 DATAMONGO-1617 - Polishing.
Some cleanups in MongoTemplateTests. Removed manual ID assignment in general id handling test to make sure we use the id generation. Removed unneccessary code from domain type in favor of Lombok.

Original pull request: #443.
2017-02-28 18:35:06 +01:00
Laszlo Csontos
5de3cb9ba3 DATAMONGO-1617 - BeforeConvertEvent is now emitted before updatable idendifier assertion.
We now make sure the BeforeConvertEvent is published before we check for identifier types that can potentially be auto-generated. That allows the event listeners to populate identifiers. Previously the identifier check kicked in before that and thus caused the listener not being able to populate the property.

Original pull request: #443.
2017-02-28 18:16:10 +01:00
Christoph Strobl
cd42b19718 DATAMONGO-1608 - Polishing.
Throw an IllegalArgumentException when trying to create a query using 'null' as an argument for queries resulting in a $regex query operator.

Original Pull Request: #439
2017-02-13 08:20:02 +01:00
Edward Prentice
eacfd2c172 DATAMONGO-1608 - Add guard against NPE in MongoQueryCreator when using IgnoreCase.
Original Pull Request: #439
2017-02-13 08:15:21 +01:00
Christoph Strobl
439616c788 DATAMONGO-1607 - Polishing.
Move coordinate conversion to dedicated method. Additionally fix issue with assertions applied to late in the chain and added some tests.

Original Pull Request: #438
2017-02-10 14:11:47 +01:00
Thiago Diniz da Silveira
40c3204fb8 DATAMONGO-1607 - Fix ClassCastException in Circle, Point and Sphere when coordinates are not Double.
Original Pull Request: #438
2017-02-10 14:11:41 +01:00
Mark Paluch
6ad5f62d66 DATAMONGO-1602 - Remove references to Assert single-arg methods.
Replace references to Assert single-arg methods with references to methods accepting the test object and message.

Related ticket: SPR-15196.
2017-02-01 11:31:29 +01:00
Oliver Gierke
1585cc420d DATAMONGO-1573 - After release cleanups. 2017-01-26 12:04:57 +01:00
Oliver Gierke
856f156318 DATAMONGO-1573 - Prepare next development iteration. 2017-01-26 12:04:54 +01:00
Oliver Gierke
00d9da3027 DATAMONGO-1573 - Release version 1.9.7 (Hopper SR7). 2017-01-26 11:33:51 +01:00
Oliver Gierke
53eaaa02a0 DATAMONGO-1573 - Prepare 1.9.7 (Hopper SR7). 2017-01-26 11:33:07 +01:00
Oliver Gierke
315b642b3b DATAMONGO-1573 - Updated changelog. 2017-01-26 11:33:00 +01:00
Oliver Gierke
958752e72f DATAMONGO-1574 - Updated changelog. 2017-01-26 11:23:29 +01:00
Christoph Strobl
6b1dbe372e DATAMONGO-1517 - Polishing.
Remove ReflectiveSimpleTypes in favor of MongoSimpleTypes.
Add add integration test.
2017-01-25 17:00:15 +01:00
Mark Paluch
a644187131 DATAMONGO-1517 - Add support for Decimal128 BSON type.
Support Decimal128 as Mongo simple type if present. Decimal128 is stored as NumberDecimal.

class Person {

  String id;
  Decimal128 decimal128;

  Person(String id, Decimal128 decimal128) {
    this.id = id;
    this.decimal128 = decimal128;
  }
}

mongoTemplate.save(new Person("foo", new Decimal128(new BigDecimal("123.456"))));

is represented as:

{ "_id" : "foo", "decimal128" : NumberDecimal("123.456") }
2017-01-25 16:58:02 +01:00
Mark Paluch
681a4f9855 DATAMONGO-1596 - Fix typo in JavaDoc.
Use correct @RelatedDocument annotation in MongoDB cross store reference documentation.
2017-01-25 16:52:54 +01:00
Oliver Gierke
f2be1b2ca9 DATAMONGO-1592 - Adapt AuditingEventListenerUnitTests to changes in core auditing.
The core auditing implementation now skips the invocation of auditing in case the candidate aggregate doesn't need any auditing in the first place. We needed to adapt the sample class we use to actually carry the necessary auditing annotations.

Related ticket: DATACMNS-957.
2017-01-20 16:36:09 +01:00
Oliver Gierke
3c203eba8e DATAMONGO-1590 - Polishing.
Removed some compiler warnings. Hide newly introduced class in package scope and made use of Lombok annotations to avoid boilerplate code.

Original pull request: #436.
2017-01-18 19:41:53 +01:00
Christoph Strobl
f3b0665d94 DATAMONGO-1590 - EntityInformation selected now correctly considers Persistable.
We now wrap the MappingMongoEntityInformation into one that delegates the methods implemented by Persistable to the actual entity in case it implements said interface.

Original pull request: #436.
2017-01-18 19:41:53 +01:00
Mark Paluch
9f43d3fc5a DATAMONGO-1588 - Polishing.
Remove unused fields. Fix typo in method name. Reformat inner class to align formatting.

Original pull request: #435.
2017-01-16 09:16:04 +01:00
Christoph Strobl
95985fffc8 DATAMONGO-1588 - Fix derived finder not accepting subclass of parameter type.
We now allow using sub types as arguments for derived queries. This makes it possible to use eg. a GeoJsonPoint for querying while the declared property type in the domain object remains a regular (legacy) Point.

Original pull request: #435.
2017-01-16 09:15:29 +01:00
Mark Paluch
6c6ac6da5b DATAMONGO-1586 - Consider field name in TypeBasedAggregationOperationContext.getReferenceFor(…).
We now consider the provided field name (alias) in mapped fields with which it is exposed. The field name applies to the exposed field after property path resolution in TypeBasedAggregationOperationContext. Previously, the field reference used the property name which caused fields to be considered non-aliased, so aggregation projection operations dropped the alias and exposed the field with its leaf property name.

Original Pull Request: #434
2017-01-12 10:09:33 +01:00
Christoph Strobl
c1ac8767b7 DATAMONGO-1585 - Polishing.
Update documentation for better readability in html and pdf format.

Original Pull Request: #433
2017-01-12 10:05:11 +01:00
Mark Paluch
96068eb0e2 DATAMONGO-1585 - Expose synthetic fields in $project aggregation stage.
Field projections now expose their fields as synthetic simple fields. Projection aggregation stage redefines the available field set available for later aggregation stages entirely so projected fields are considered synthetic. A simple synthetic field has no target field which causes later aggregation stages to not pick up the underlying target but the exposed field name when rendering aggregation operations to Mongo documents.

The change is motivated by a bug where previously an aggregation consisting of projection of an aliased field and sort caused the sort projection stage to render with the original field name instead of the aliased field. The sort did not apply any sorting since projection redefines the available field set entirely and the original field is no longer accessible.

Original Pull Request: #433
2017-01-12 10:04:19 +01:00
Christoph Strobl
36d2e0942b DATAMONGO-1576 - Update lifecycle event documentation.
Add note on lifecycle event handling for property types.
2017-01-11 13:11:02 +01:00
Mark Paluch
b585783b75 DATAMONGO-1578 - Polishing.
Add ticket references to test methods. Extend license years in copyright header.

Original pull request: #398.
2017-01-02 11:40:47 +01:00
Martin Macko
3924b6f12a DATAMONGO-1578 - Add missing @Test annotation to ProjectionOperationUnitTests.
Original pull request: #398.
2017-01-02 11:39:36 +01:00
Mark Paluch
2674880946 DATAMONGO-1508 - Improve reference documentation.
Replace Spring Data Document with Spring Data MongoDB. Extend copyright year range. Replace static Spring version leftover with variable. Fix typos.
2017-01-02 11:19:44 +01:00
Lukasz Kryger
6c6f953a42 DATAMONGO-1577 - Fix wording repetition in MongoRepository JavaDoc.
Original pull request: #407.
2017-01-02 11:19:44 +01:00
Ken Dombeck
772e8ac85e DATAMONGO-1577 - Fix Reference and JavaDoc spelling issues.
Replaced invalid class name MongoMappingConverter with actual class name of MappingMongoConverter. Fix typos.

Original pull request: #425.
2017-01-02 11:19:41 +01:00
Mark Paluch
2bbffed62b DATAMONGO-1508 - Polishing.
Highlight attribute name. Replace tabs with spaces.

Original pull request: #399.
2017-01-02 11:12:31 +01:00
John Lilley
685990bdd6 DATAMONGO-1508 - Document authentication-dbname attribute in db-factory.
Original pull request: #399.
2017-01-02 11:12:31 +01:00
Oliver Gierke
ff83ac3fb4 DATAMONGO-1522 - After release cleanups. 2016-12-21 19:33:44 +01:00
Oliver Gierke
6827a09f26 DATAMONGO-1522 - Prepare next development iteration. 2016-12-21 19:33:40 +01:00
Oliver Gierke
a5148f89c1 DATAMONGO-1522 - Release version 1.9.6 (Hopper SR6). 2016-12-21 19:04:46 +01:00
Oliver Gierke
995a680823 DATAMONGO-1522 - Prepare 1.9.6 (Hopper SR6). 2016-12-21 19:03:39 +01:00
Oliver Gierke
9f0abb69fd DATAMONGO-1522 - Updated changelog. 2016-12-21 19:03:32 +01:00
Oliver Gierke
d65eebe9c3 DATAMONGO-1469 - Updated changelog. 2016-12-21 18:42:59 +01:00
Oliver Gierke
ca4f1f1b7c DATAMONGO-1467 - Polishing.
Original pull request: #431.
2016-12-19 19:45:17 +01:00
Christoph Strobl
46b119ce71 DATAMONGO-1467 - Add support for MongoDB 3.2 partialFilterExpression for index creation.
We now support partial filter expression on indexes via Index.partial(…). This allows to create partial indexes that only index the documents in a collection that meet a specified filter expression. 

new Index().named("idx").on("k3y", ASC).partial(filter(where("age").gte(10)))

The filter expression can be set via a plain DBObject or a CriteriaDefinition and is mapped against the associated domain type.

Original pull request: #431.
2016-12-19 19:45:14 +01:00
Oliver Gierke
fc0dd7d094 DATAMONGO-1565 - Polishing.
Formatting in ExpressionEvaluatingParameterBinder and StringBasedMongoQueryUnitTests. Turned Placeholder into value object.
2016-12-19 18:08:36 +01:00
Mark Paluch
712d8be7bb DATAMONGO-1565 - Polishing.
Consider quoted/unquoted parameter use with the same parameter reference. Extend date range in license headers.
2016-12-19 18:03:41 +01:00
Christoph Strobl
536dcc14ca DATAMONGO-1565 - Ignore placeholder pattern in replacement values for annotated queries.
We now make sure to quote single and double ticks in the replacement values before actually appending them to the query. We also replace single ticks around parameters in the actual raw annotated query by double quotes to make sure they are treated as a single string parameter.
2016-12-19 14:41:26 +01:00
Oliver Gierke
dc44c3a455 DATAMONGO-1525 - Improved creation of empty collections, esp. EnumSet.
We now use more type information to create a better empty collection in the first place. The previous algorithm always used an empty HashSet plus a subsequent conversion using the raw collection type. Especially the latter caused problems for EnumSets as the conversion into one requires the presence of component type information.

We now use Spring's collection factory and more available type information to create a proper collection in the first place and only rely on a subsequent conversion for arrays.
2016-12-01 20:18:06 +01:00
Christoph Strobl
8e90366712 DATAMONGO-1534 - Fix bulk operations missing to write type info.
We now correctly convert entities into their MongoDB representation including type information via _class property.

Original pull request: #415.
2016-11-28 09:05:46 +01:00
Oliver Gierke
ed36fd7260 DATAMONGO-1527 - Updated changelog. 2016-11-23 13:52:45 +01:00
Oliver Gierke
31a6a74743 DATAMONGO-1502 - After release cleanups. 2016-11-03 18:20:12 +01:00
Oliver Gierke
001ff508b5 DATAMONGO-1502 - Prepare next development iteration. 2016-11-03 18:20:10 +01:00
Oliver Gierke
6882fa9d10 DATAMONGO-1502 - Release version 1.9.5 (Hopper SR5). 2016-11-03 17:56:32 +01:00
Oliver Gierke
91eaae0ef6 DATAMONGO-1502 - Prepare 1.9.5 (Hopper SR5). 2016-11-03 17:56:00 +01:00
Oliver Gierke
785dc6ab78 DATAMONGO-1502 - Updated changelog. 2016-11-03 17:55:53 +01:00
Oliver Gierke
f011a9a4ee DATAMONGO-1521 - Added Aggregation.skip(…) overload to support longs.
Deprecated the one taking an int.
2016-11-03 15:04:07 +01:00
Christoph Strobl
c6c58050e7 DATAMONGO-1500 - Fix JSON serialization error in derived queries with field spec.
We now make sure not to eagerly attempt to convert given query parameters into a mongo specific format by calling toString() the query object, but rather delegate this to another step later in the chain.

Original pull request: #404.
2016-11-03 09:36:50 +01:00
Christoph Strobl
5fce8bcac6 DATAMONGO-1504 - Assert compatibility with MongoDB 3.4.
We now make sure to comply to the API requirements of mongo-java-driver 3.4 (in current beta1) by using empty DBObjects instead of null, ignoring non appropriate replication settings and cleaning up tests after execution.

Original pull request: #394.
2016-11-03 09:33:11 +01:00
Oliver Gierke
2f522bae5c DATAMONGO-1513 - Fixed identifier population for event listener generated, non-ObjectId on batch inserts.
The methods in MongoTemplate inserting a batch of documents previously only returned database generated identifiers, more especially ObjectId ones. This caused non-ObjectId identifiers potentially generated by other parties — i.e. an event listener reacting to a BeforeSaveEvent — not being considered for source object identifier population.

This commit adds a workaround augmenting the list of database generated identifiers with the ones actually present in the documents to be inserted. A follow-up ticket DATAMONGO-1519 was created to track the removal of the workaround in favor of a proper fix unfortunately requiring a change in public API (so a 2.0 candidate only).

Related tickets: DATAMONGO-1519.
2016-11-02 09:44:36 +01:00
Oliver Gierke
2e6f91924d DATAMONGO-1514 - Polishing.
Extended license years in copyright header.

Original pull request: #401.
2016-10-27 14:32:55 +02:00
Martin Macko
15f7a9c74a DATAMONGO-1514 - SpringDataMongodbQuery needs to be public.
SpringDataMongodbQuery is exposed publicly in QuerydslRepositorySupport, that's we've got to make it public to make sure class to the exposed methods from outside the package actually compile.

Original pull request: #401.
2016-10-27 14:32:55 +02:00
Oliver Gierke
49f52f0258 DATAMONGO-1495 - After release cleanups. 2016-09-29 14:20:12 +02:00
Oliver Gierke
396ea471fb DATAMONGO-1495 - Prepare next development iteration. 2016-09-29 14:20:08 +02:00
Oliver Gierke
eef17dd000 DATAMONGO-1495 - Release version 1.9.4 (Hopper SR4). 2016-09-29 13:54:03 +02:00
Oliver Gierke
84dc03b9d1 DATAMONGO-1495 - Prepare 1.9.4 (Hopper SR4). 2016-09-29 13:53:26 +02:00
Oliver Gierke
0ce220d54f DATAMONGO-1495 - Updated changelog. 2016-09-29 13:53:19 +02:00
Oliver Gierke
b693136396 DATAMONGO-1499 - Updated changelog. 2016-09-29 11:42:09 +02:00
Oliver Gierke
3c117db43b DATAMONGO-1498 - Removed defaulting of MongoMappingContext for repositories and auditing.
Previously we created a default bean definition for MongoMappingContext if none was present in the application context. That lookup for an existing one unfortunately comes too early, especially with Spring Boot in place. This then caused the MappingContext not being aware of the custom conversions and simply types registered by Boot.

We now removed the defaulting relying on a MappingMongoConverter being present in the Application context (which usually is the case for the usage with AbstractMongoConfiguration or the XML <mongo:mapping-converter /> alternative. We use that bean to lookup the MappingContext.
2016-09-28 16:25:52 +02:00
Oliver Gierke
075ccb1d00 DATAMONGO-1497 - MappingMongoConverter now consistently uses DbObjectAccessor.
We now use DbObjectAccessor also for preliminary inspections of the source DBObject (e.g. whether a value is present at all). Previously we operated on the DBObject directly which caused issues with properties mapped to nested fields as the keys weren't exploded correctly and thus the check always failed.
2016-09-22 17:56:09 +02:00
Oliver Gierke
e3bddd1c19 DATAMONGO-1494 - Updated changelog. 2016-09-21 07:28:56 +02:00
Oliver Gierke
22b113ce64 DATAMONGO-1450 - After release cleanups. 2016-09-20 11:19:04 +02:00
Oliver Gierke
0f5e91b091 DATAMONGO-1450 - Prepare next development iteration. 2016-09-20 11:18:59 +02:00
Oliver Gierke
557a528690 DATAMONGO-1450 - Release version 1.9.3 (Hopper SR3). 2016-09-20 10:54:50 +02:00
Oliver Gierke
762569c826 DATAMONGO-1450 - Prepare 1.9.3 (Hopper SR3). 2016-09-20 10:54:16 +02:00
Oliver Gierke
ad7d82f521 DATAMONGO-1450 - Updated changelog. 2016-09-20 10:54:11 +02:00
Mark Paluch
04deaacbec DATAMONGO-1493 - Fix minor typo in reference documentation.
Related pull request: #391.
2016-09-19 17:02:56 +02:00
Jordan Jennings
ec443f2b5e DATAMONGO-1493 - Fix minor typo in docs.
Original pull request: #391.
2016-09-19 17:01:01 +02:00
Christoph Strobl
f1b04ff354 DATAMONGO-1492 - Make o.s.d.m.core.aggregation.AggregationExpression public.
By turning `AggregationExpression` public we allow adding custom expressions without workarounds. It is now possible to create eg. `ProjectionOperation` like:

ProjectionOperation agg = Aggregation.project()
      .and(new AggregationExpression() {

        @Override
        public DBObject toDbObject(AggregationOperationContext context) {

          DBObject filterExpression = new BasicDBObject();
          filterExpression.put("input", "$x");
          filterExpression.put("as", "y");
          filterExpression.put("cond", new BasicDBObject("$eq", Arrays.<Object> asList("$$y.z", 2)));

          return new BasicDBObject("$filter", filterExpression);
        }
      }).as("profile");

Original pull request: #392.
2016-09-19 16:04:33 +02:00
Christoph Strobl
62dd7d070a DATAMONGO-1485 - Consider potential custom conversion for enums in Querydsl paths.
We now take potential registered converters for enums into account when serializing path expressions via SpringDataMongodbSerializer.

Original pull request: #388.
2016-09-19 07:12:35 +02:00
Oliver Gierke
5df92a86a3 DATAMONGO-1468 - Polishing.
Slightly touched test case.

Original pull request: #387
2016-09-08 10:30:21 +02:00
Christoph Strobl
2ca3df1ff4 DATAMONGO-1486 - Fix ClassCastException when mapping non-String Map key for updates.
We now make sure to convert Map keys into Strings when mapping update values for Map properties.

Original pull request: #387.
2016-09-08 10:30:21 +02:00
Mark Paluch
a8751249fd DATAMONGO-1406 - Propagate PersistentEntity when mapping query criteria for nested keywords.
We now propagate the PersistentEntity when mapping nested keywords so that the criteria mapping chain for nested keywords and properties has now access to the PersistentEntity and can use configured field names.

Previously the plain property names have been used as field names and potential customizations via @Field have been ignored.

Original Pull Request: #384
2016-08-25 13:29:06 +02:00
Mark Paluch
19abff826e DATAMONGO-1465 - Polishing.
Replace boolean flag in convertAndJoinScriptArgs with literal. Joined args are rendered to JavaScript and require always string quotation.

Original pull request: #383.
2016-08-23 15:07:31 +02:00
Christoph Strobl
5f199cf81f DATAMONGO-1465 - Fix String quotation in DefaultScriptOperations.execute().
This change prevents Strings from being quoted prior to sending them as args of a script.

Original pull request: #383.
2016-08-23 15:07:31 +02:00
Oliver Gierke
eb26b78a19 DATAMONGO-1471 - Converter only applies identifier values if actually available.
Setting the value for the identifier property is an explicit step in MappingMongoConverter and always executed if the type to be created has an identifier property. If the source document doesn't contain an _id field (e.g. because it has been excluded explicitly) that previously caused null to be set on the identifier. This caused an exception if the identifier property is a primitive type.

We now explicitly check whether the field backing the identifier property is actually present in the source document and only explicitly set the value if so.
2016-08-17 16:53:52 +02:00
Oliver Gierke
3d0053c61a DATAMONGO-1409 - Updated changelog. 2016-07-28 08:57:30 +02:00
Mark Paluch
7b15d246e8 DATAMONGO-1463 - Upgrade to mongo-java-driver 2.14.3.
Upgrade mongo-java-driver 2.14.3 and upgrade the mongo33 profile to use 3.3.0 (release).
2016-07-20 11:19:46 +02:00
Christoph Strobl
b75f4a2834 DATAMONGO-1453 - Fix GeoJson conversion when coordinates are Integers.
We now use Number instead of Double for reading "coordinates" from GeoJSON representations.

Original pull request: #369.
2016-06-24 14:05:33 +02:00
Oliver Gierke
d6ac4c6df5 DATAMONGO-1410 - After release cleanups. 2016-06-15 14:17:54 +02:00
Oliver Gierke
02f56c88f5 DATAMONGO-1410 - Prepare next development iteration. 2016-06-15 14:17:51 +02:00
Oliver Gierke
cd35b9ed2a DATAMONGO-1410 - Release version 1.9.2 (Hopper SR2). 2016-06-15 13:45:52 +02:00
Oliver Gierke
e8944a6c3a DATAMONGO-1410 - Prepare 1.9.2 (Hopper SR2). 2016-06-15 13:44:53 +02:00
Oliver Gierke
6fcbc225eb DATAMONGO-1410 - Updated changelog. 2016-06-15 13:44:46 +02:00
Kevin Dosey
f06eda488c DATAMONGO-1449 - Switched to foreach loop in collection handling of MappingMongoConverter.
This should result in minor to moderate performance improvement for iteration on Collections/Arrays during DBObject to object mapping.

Original pull request: #368.
2016-06-11 18:33:16 +02:00
Mark Paluch
0ef910445d DATAMONGO-1437 - Polishing.
Renamed test. Added JavaDoc. Simplify throws declaration.

Original pull request: #367.
2016-06-02 14:15:08 +02:00
Christoph Strobl
b22ee9d27c DATAMONGO-1437 - Preserve non translatable Exception cause when lazily resolving DBRef.
We now preserve the cause of Exceptions that cannot be translated into DataAccessExceptions when an error occurs during lazily loading DBRefs.

Original pull request: #367.
2016-06-02 14:14:55 +02:00
Oliver Gierke
859a0e83c8 DATAMONGO-1423 - Polishing.
Orignal pull request: #365.
2016-05-25 17:33:05 +02:00
Christoph Strobl
b35f151b80 DATAMONGO-1423 - Map keys now get registered conversions applied for Updates.
We now pipe map keys through the potentially registered conversions when mapping Updates.

Orignal pull request: #365.
2016-05-25 17:33:05 +02:00
Oliver Gierke
17afb07e45 DATAMONGO-1416 - Polishing.
Just use instanceOf(…) from Hamcrest's Matchers class instead of dedicated class.

Original pull request: #362.
2016-05-24 16:01:12 +02:00
Christoph Strobl
b407963344 DATAMONGO-1416 - Get rid of the warnings for Atomic… type conversions.
We now use explicit converters instead of a ConverterFactory. This reduces noise in log when registering converters.

Original pull request: #362.
2016-05-24 16:00:54 +02:00
Mark Paluch
8b31ba1836 DATAMONGO-1425 - Polishing.
Add NotContaining to documentation. Add integration test for Containing/NotContaining on collection properties.

Original pull request: #363.
2016-05-09 11:14:03 +02:00
Christoph Strobl
0cf6edae43 DATAMONGO-1425 - Fix query derivation for notContaining on String properties.
We now correctly build up the criteria for derived queries using notContaining keyword on String properties.

Original pull request: #363.
2016-05-09 11:13:58 +02:00
Mark Paluch
0824105377 DATAMONGO-1412 - Fix backticks and code element highlighting.
Fixed broken highlighting using backticks followed by chars/single quotes. Convert single quote emphasis of id to backtick code fences. Add missing spaces between words and backticks.

Original Pull Request: #359
2016-05-02 14:18:13 +02:00
Mark Paluch
dc936a5b7b DATAMONGO-1412 - Document mapping rules for Java types to MongoDB representation.
Original Pull Request: #359
Related pull request: #353
Related ticket: DATAMONGO-1404
2016-05-02 14:18:07 +02:00
Mark Paluch
c8fe02e48e DATAMONGO-1411 - Enable build on TravisCI.
We now start MongoDB server via apt-get instead of relying on the TravisCI managed 2.4.2 installation.
Doing this we altered tests to just check on the port and not the host part of the URIs.

Additionally we upgraded build profiles, removed promoted snapshot-versions, renamed mongo32-next to mongo32  and added mongo33-next build profile.

Original pull request: #358
2016-04-26 11:17:00 +02:00
Oliver Gierke
32547db306 DATAMONGO-1408 - After release cleanups. 2016-04-06 22:51:44 +02:00
Oliver Gierke
41902154ca DATAMONGO-1408 - Prepare next development iteration. 2016-04-06 22:51:43 +02:00
Oliver Gierke
2354ced1bf DATAMONGO-1408 - Release version 1.9.1 (Hopper SR1). 2016-04-06 22:32:15 +02:00
Oliver Gierke
791cc3a1b8 DATAMONGO-1408 - Prepare 1.9.1 (Hopper SR1). 2016-04-06 22:31:40 +02:00
Oliver Gierke
021c03fbbf DATAMONGO-1408 - Updated changelog. 2016-04-06 22:31:35 +02:00
Oliver Gierke
e4a59f29d0 DATAMONGO-1405 - Prepare next development iteration. 2016-04-06 16:38:18 +02:00
104 changed files with 3463 additions and 773 deletions

View File

@@ -3,13 +3,29 @@ language: java
jdk:
- oraclejdk8
services:
- mongodb
before_script:
- mongod --version
env:
matrix:
- PROFILE=ci
- PROFILE=mongo-next
- PROFILE=mongo3
- PROFILE=mongo3-next
- PROFILE=mongo31
- PROFILE=mongo32
- PROFILE=mongo33
- PROFILE=mongo34-next
# Current MongoDB version is 2.4.2 as of 2016-04, see https://github.com/travis-ci/travis-ci/issues/3694
# apt-get starts a MongoDB instance so it's not started using before_script
addons:
apt:
sources:
- mongodb-3.2-precise
packages:
- mongodb-org-server
- mongodb-org-shell
sudo: false

41
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.8.RELEASE</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>1.8.0.RELEASE</version>
<version>1.8.8.RELEASE</version>
</parent>
<modules>
@@ -28,8 +28,8 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>1.12.0.RELEASE</springdata.commons>
<mongo>2.14.0</mongo>
<springdata.commons>1.12.8.RELEASE</springdata.commons>
<mongo>2.14.3</mongo>
<mongo.osgi>2.13.0</mongo.osgi>
</properties>
@@ -107,7 +107,7 @@
<id>mongo-next</id>
<properties>
<mongo>2.14.0-SNAPSHOT</mongo>
<mongo>2.15.0-SNAPSHOT</mongo>
</properties>
<repositories>
@@ -148,16 +148,41 @@
<id>mongo31</id>
<properties>
<mongo>3.1.0</mongo>
<mongo>3.1.1</mongo>
</properties>
</profile>
<profile>
<id>mongo32-next</id>
<id>mongo32</id>
<properties>
<mongo>3.2.0-SNAPSHOT</mongo>
<mongo>3.2.2</mongo>
</properties>
</profile>
<profile>
<id>mongo33</id>
<properties>
<mongo>3.3.0</mongo>
</properties>
<repositories>
<repository>
<id>mongo-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
</profile>
<profile>
<id>mongo34-next</id>
<properties>
<mongo>3.4.0-SNAPSHOT</mongo>
</properties>
<repositories>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.8.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -48,7 +48,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.8.RELEASE</version>
</dependency>
<dependency>

View File

@@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.8.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.8.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
/**
@@ -37,6 +38,7 @@ import com.mongodb.WriteConcern;
*
* @author Jon Brisbin
* @author Oliver Gierke
* @auhtor Christoph Strobl
*/
public class MongoLog4jAppender extends AppenderSkeleton {
@@ -58,8 +60,8 @@ public class MongoLog4jAppender extends AppenderSkeleton {
protected String collectionPattern = "%c";
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
protected String applicationId = System.getProperty("APPLICATION_ID", null);
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.SAFE;
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL;
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.ACKNOWLEDGED;
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.UNACKNOWLEDGED;
protected Mongo mongo;
protected DB db;
@@ -128,7 +130,7 @@ public class MongoLog4jAppender extends AppenderSkeleton {
}
protected void connectToMongo() throws UnknownHostException {
this.mongo = new Mongo(host, port);
this.mongo = new MongoClient(host, port);
this.db = mongo.getDB(database);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,37 +22,44 @@ import java.util.Calendar;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
/**
* Integration tests for {@link MongoLog4jAppender}.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class MongoLog4jAppenderIntegrationTests {
static final String NAME = MongoLog4jAppenderIntegrationTests.class.getName();
private static final Logger log = Logger.getLogger(NAME);
Mongo mongo;
MongoClient mongo;
DB db;
String collection;
@Before
public void setUp() throws Exception {
mongo = new Mongo("localhost", 27017);
mongo = new MongoClient("localhost", 27017);
db = mongo.getDB("logs");
Calendar now = Calendar.getInstance();
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1);
db.getCollection(collection).drop();
}
@After
public void tearDown() {
db.getCollection(collection).remove(new BasicDBObject());
}
@Test
@@ -64,7 +71,6 @@ public class MongoLog4jAppenderIntegrationTests {
log.error("ERROR message");
DBCursor msgs = db.getCollection(collection).find();
assertThat(msgs.count(), is(4));
}

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.9.0.RELEASE</version>
<version>1.9.8.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -361,7 +361,9 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
* @param filters
*/
public NegatingFilter(TypeFilter... filters) {
Assert.notNull(filters);
Assert.notNull(filters, "TypeFilters must not be null");
this.delegates = new HashSet<TypeFilter>(Arrays.asList(filters));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,24 +15,24 @@
*/
package org.springframework.data.mongodb.config;
import static org.springframework.beans.factory.config.BeanDefinition.*;
import static org.springframework.data.mongodb.config.BeanNames.*;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.Assert;
/**
@@ -71,7 +71,6 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
defaultDependenciesIfNecessary(registry, annotationMetadata);
super.registerBeanDefinitions(annotationMetadata, registry);
}
@@ -85,7 +84,11 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
builder.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContextLookup.class);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
builder.addConstructorArgValue(definition.getBeanDefinition());
return configureDefaultAuditHandlerAttributes(configuration, builder);
}
@@ -102,29 +105,58 @@ class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(AuditingEventListener.class);
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(
getAuditingHandlerBeanName(), registry));
listenerBeanDefinitionBuilder
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
AuditingEventListener.class.getName(), registry);
}
/**
* Register default bean definitions for a {@link MongoMappingContext} and an {@link IsNewStrategyFactory} in case we
* don't find beans with the assumed names in the registry.
*
* @param registry the {@link BeanDefinitionRegistry} to use to register the components into.
* @param source the source which the registered components shall be registered with
* Simple helper to be able to wire the {@link MappingContext} from a {@link MappingMongoConverter} bean available in
* the application context.
*
* @author Oliver Gierke
*/
private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
static class MongoMappingContextLookup
implements FactoryBean<MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>> {
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
private final MappingMongoConverter converter;
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
definition.setRole(ROLE_INFRASTRUCTURE);
definition.setSource(source);
/**
* Creates a new {@link MongoMappingContextLookup} for the given {@link MappingMongoConverter}.
*
* @param converter must not be {@literal null}.
*/
public MongoMappingContextLookup(MappingMongoConverter converter) {
this.converter = converter;
}
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getObject() throws Exception {
return converter.getMappingContext();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return MappingContext.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
}
}

View File

@@ -25,6 +25,7 @@ import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.BulkWriteException;
import com.mongodb.BulkWriteOperation;
import com.mongodb.BulkWriteRequestBuilder;
@@ -38,6 +39,7 @@ import com.mongodb.WriteConcern;
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.9
*/
class DefaultBulkOperations implements BulkOperations {
@@ -117,7 +119,15 @@ class DefaultBulkOperations implements BulkOperations {
Assert.notNull(document, "Document must not be null!");
bulk.insert((DBObject) mongoOperations.getConverter().convertToMongoType(document));
if (document instanceof DBObject) {
bulk.insert((DBObject) document);
return this;
}
DBObject sink = new BasicDBObject();
mongoOperations.getConverter().write(document, sink);
bulk.insert(sink);
return this;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,17 +15,15 @@
*/
package org.springframework.data.mongodb.core;
import static org.springframework.data.domain.Sort.Direction.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert;
import com.mongodb.DBCollection;
@@ -42,12 +40,12 @@ import com.mongodb.MongoException;
*/
public class DefaultIndexOperations implements IndexOperations {
private static final Double ONE = Double.valueOf(1);
private static final Double MINUS_ONE = Double.valueOf(-1);
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
private static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression";
private final MongoOperations mongoOperations;
private final String collectionName;
private final QueryMapper mapper;
private final Class<?> type;
/**
* Creates a new {@link DefaultIndexOperations}.
@@ -56,12 +54,26 @@ public class DefaultIndexOperations implements IndexOperations {
* @param collectionName must not be {@literal null}.
*/
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
this(mongoOperations, collectionName, null);
}
/**
* Creates a new {@link DefaultIndexOperations}.
*
* @param mongoOperations must not be {@literal null}.
* @param collectionName must not be {@literal null}.
* @param type Type used for mapping potential partial index filter expression. Can be {@literal null}.
* @since 1.10
*/
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName, Class<?> type) {
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
Assert.notNull(collectionName, "Collection name can not be null!");
this.mongoOperations = mongoOperations;
this.collectionName = collectionName;
this.mapper = new QueryMapper(mongoOperations.getConverter());
this.type = type;
}
/*
@@ -69,9 +81,20 @@ public class DefaultIndexOperations implements IndexOperations {
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
*/
public void ensureIndex(final IndexDefinition indexDefinition) {
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject indexOptions = indexDefinition.getIndexOptions();
if (indexOptions != null && indexOptions.containsField(PARTIAL_FILTER_EXPRESSION_KEY)) {
Assert.isInstanceOf(DBObject.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
indexOptions.put(PARTIAL_FILTER_EXPRESSION_KEY,
mapper.getMappedObject((DBObject) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
lookupPersistentEntity(type, collectionName)));
}
if (indexOptions != null) {
collection.createIndex(indexDefinition.getIndexKeys(), indexOptions);
} else {
@@ -79,6 +102,24 @@ public class DefaultIndexOperations implements IndexOperations {
}
return null;
}
private MongoPersistentEntity<?> lookupPersistentEntity(Class<?> entityType, String collection) {
if (entityType != null) {
return mongoOperations.getConverter().getMappingContext().getPersistentEntity(entityType);
}
Collection<? extends MongoPersistentEntity<?>> entities = mongoOperations.getConverter().getMappingContext()
.getPersistentEntities();
for (MongoPersistentEntity<?> entity : entities) {
if (entity.getCollection().equals(collection)) {
return entity;
}
}
return null;
}
});
}
@@ -126,7 +167,9 @@ public class DefaultIndexOperations implements IndexOperations {
public List<IndexInfo> getIndexInfo() {
return mongoOperations.execute(collectionName, new CollectionCallback<List<IndexInfo>>() {
public List<IndexInfo> doInCollection(DBCollection collection) throws MongoException, DataAccessException {
List<DBObject> dbObjectList = collection.getIndexInfo();
return getIndexData(dbObjectList);
}
@@ -136,44 +179,7 @@ public class DefaultIndexOperations implements IndexOperations {
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
for (DBObject ix : dbObjectList) {
DBObject keyDbObject = (DBObject) ix.get("key");
int numberOfElements = keyDbObject.keySet().size();
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
for (String key : keyDbObject.keySet()) {
Object value = keyDbObject.get(key);
if (TWO_D_IDENTIFIERS.contains(value)) {
indexFields.add(IndexField.geo(key));
} else if ("text".equals(value)) {
DBObject weights = (DBObject) ix.get("weights");
for (String fieldName : weights.keySet()) {
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
}
} else {
Double keyValue = new Double(value.toString());
if (ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, ASC));
} else if (MINUS_ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, DESC));
}
}
}
String name = ix.get("name").toString();
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
indexInfoList.add(IndexInfo.indexInfoOf(ix));
}
return indexInfoList;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@ class DefaultScriptOperations implements ScriptOperations {
@Override
public Object doInDB(DB db) throws MongoException, DataAccessException {
return db.eval(script.getCode(), convertScriptArgs(args));
return db.eval(script.getCode(), convertScriptArgs(false, args));
}
});
}
@@ -155,7 +155,7 @@ class DefaultScriptOperations implements ScriptOperations {
return scriptNames;
}
private Object[] convertScriptArgs(Object... args) {
private Object[] convertScriptArgs(boolean quote, Object... args) {
if (ObjectUtils.isEmpty(args)) {
return args;
@@ -164,15 +164,15 @@ class DefaultScriptOperations implements ScriptOperations {
List<Object> convertedValues = new ArrayList<Object>(args.length);
for (Object arg : args) {
convertedValues.add(arg instanceof String ? String.format("'%s'", arg) : this.mongoOperations.getConverter()
.convertToMongoType(arg));
convertedValues.add(arg instanceof String && quote ? String.format("'%s'", arg)
: this.mongoOperations.getConverter().convertToMongoType(arg));
}
return convertedValues.toArray();
}
private String convertAndJoinScriptArgs(Object... args) {
return ObjectUtils.isEmpty(args) ? "" : StringUtils.arrayToCommaDelimitedString(convertScriptArgs(args));
return ObjectUtils.isEmpty(args) ? "" : StringUtils.arrayToCommaDelimitedString(convertScriptArgs(true, args));
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,8 @@ import com.mongodb.Mongo;
* Mongo server administration exposed via JMX annotations
*
* @author Mark Pollack
* @author Thomas Darimont
* @author Thomas Darimont
* @author Mark Paluch
*/
@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
@@ -35,10 +36,11 @@ public class MongoAdmin implements MongoAdminOperations {
private final Mongo mongo;
private String username;
private String password;
private String authenticationDatabaseName;
private String authenticationDatabaseName;
public MongoAdmin(Mongo mongo) {
Assert.notNull(mongo);
Assert.notNull(mongo, "Mongo must not be null!");
this.mongo = mongo;
}
@@ -84,16 +86,16 @@ public class MongoAdmin implements MongoAdminOperations {
this.password = password;
}
/**
* Sets the authenticationDatabaseName to use to authenticate with the Mongo database.
*
* @param authenticationDatabaseName The authenticationDatabaseName to use.
*/
public void setAuthenticationDatabaseName(String authenticationDatabaseName) {
this.authenticationDatabaseName = authenticationDatabaseName;
}
/**
* Sets the authenticationDatabaseName to use to authenticate with the Mongo database.
*
* @param authenticationDatabaseName The authenticationDatabaseName to use.
*/
public void setAuthenticationDatabaseName(String authenticationDatabaseName) {
this.authenticationDatabaseName = authenticationDatabaseName;
}
DB getDB(String databaseName) {
return MongoDbUtils.getDB(mongo, databaseName, new UserCredentials(username, password), authenticationDatabaseName);
return MongoDbUtils.getDB(mongo, databaseName, new UserCredentials(username, password), authenticationDatabaseName);
}
}

View File

@@ -125,7 +125,7 @@ import com.mongodb.util.JSONParseException;
/**
* Primary implementation of {@link MongoOperations}.
*
*
* @author Thomas Risberg
* @author Graeme Rocher
* @author Mark Pollack
@@ -138,6 +138,7 @@ import com.mongodb.util.JSONParseException;
* @author Chuong Ngo
* @author Christoph Strobl
* @author Doménique Tilleuil
* @author Laszlo Csontos
*/
@SuppressWarnings("deprecation")
public class MongoTemplate implements MongoOperations, ApplicationContextAware {
@@ -174,7 +175,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Constructor used for a basic template configuration
*
*
* @param mongo must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
*/
@@ -185,7 +186,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Constructor used for a template configuration with user credentials in the form of
* {@link org.springframework.data.authentication.UserCredentials}
*
*
* @param mongo must not be {@literal null}.
* @param databaseName must not be {@literal null} or empty.
* @param userCredentials
@@ -196,7 +197,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Constructor used for a basic template configuration.
*
*
* @param mongoDbFactory must not be {@literal null}.
*/
public MongoTemplate(MongoDbFactory mongoDbFactory) {
@@ -205,13 +206,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Constructor used for a basic template configuration.
*
*
* @param mongoDbFactory must not be {@literal null}.
* @param mongoConverter
*/
public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {
Assert.notNull(mongoDbFactory);
Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!");
this.mongoDbFactory = mongoDbFactory;
this.exceptionTranslator = mongoDbFactory.getExceptionTranslator();
@@ -234,7 +235,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Configures the {@link WriteResultChecking} to be used with the template. Setting {@literal null} will reset the
* default of {@value #DEFAULT_WRITE_RESULT_CHECKING}.
*
*
* @param resultChecking
*/
public void setWriteResultChecking(WriteResultChecking resultChecking) {
@@ -245,7 +246,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* Configures the {@link WriteConcern} to be used with the template. If none is configured the {@link WriteConcern}
* configured on the {@link MongoDbFactory} will apply. If you configured a {@link Mongo} instance no
* {@link WriteConcern} will be used.
*
*
* @param writeConcern
*/
public void setWriteConcern(WriteConcern writeConcern) {
@@ -254,7 +255,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Configures the {@link WriteConcernResolver} to be used with the template.
*
*
* @param writeConcernResolver
*/
public void setWriteConcernResolver(WriteConcernResolver writeConcernResolver) {
@@ -264,7 +265,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Used by @{link {@link #prepareCollection(DBCollection)} to set the {@link ReadPreference} before any operations are
* performed.
*
*
* @param readPreference
*/
public void setReadPreference(ReadPreference readPreference) {
@@ -291,7 +292,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* they were registered for the current {@link MappingContext}. If no creator for the current {@link MappingContext}
* can be found we manually add the internally created one as {@link ApplicationListener} to make sure indexes get
* created appropriately for entity types persisted through this {@link MongoTemplate} instance.
*
*
* @param context must not be {@literal null}.
*/
private void prepareIndexCreator(ApplicationContext context) {
@@ -311,15 +312,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
/**
* Returns the default {@link org.springframework.data.mongodb.core.core.convert.MongoConverter}.
*
* Returns the default {@link org.springframework.data.mongodb.core.convert.MongoConverter}.
*
* @return
*/
public MongoConverter getConverter() {
return this.mongoConverter;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoOperations#executeAsStream(org.springframework.data.mongodb.core.query.Query, java.lang.Class)
*/
@@ -412,7 +413,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Execute a MongoDB query and iterate over the query results on a per-document basis with a
* {@link DocumentCallbackHandler} using the provided CursorPreparer.
*
*
* @param query the query class that specifies the criteria used to find a record and also an optional fields
* specification, must not be {@literal null}.
* @param collectionName name of the collection to retrieve the objects from
@@ -423,7 +424,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
protected void executeQuery(Query query, String collectionName, DocumentCallbackHandler dch,
CursorPreparer preparer) {
Assert.notNull(query);
Assert.notNull(query, "Query must not be null!");
DBObject queryObject = queryMapper.getMappedObject(query.getQueryObject(), null);
DBObject sortObject = query.getSortObject();
@@ -439,7 +440,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public <T> T execute(DbCallback<T> action) {
Assert.notNull(action);
Assert.notNull(action, "DbCallbackmust not be null!");
try {
DB db = this.getDb();
@@ -455,7 +456,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public <T> T execute(String collectionName, CollectionCallback<T> callback) {
Assert.notNull(callback);
Assert.notNull(callback, "CollectionCallback must not be null!");
try {
DBCollection collection = getAndPrepareCollection(getDb(), collectionName);
@@ -541,7 +542,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public IndexOperations indexOps(Class<?> entityClass) {
return new DefaultIndexOperations(this, determineCollectionName(entityClass));
return new DefaultIndexOperations(this, determineCollectionName(entityClass), entityClass);
}
public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) {
@@ -683,7 +684,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/*
* As MongoDB currently (2.4.4) doesn't support the skipping of elements in near queries
* we skip the elements ourselves to avoid at least the document 2 object mapping overhead.
*
*
* @see https://jira.mongodb.org/browse/SERVER-3925
*/
if (index >= elementsToSkip) {
@@ -733,7 +734,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public long count(Query query, Class<?> entityClass) {
Assert.notNull(entityClass);
Assert.notNull(entityClass, "Entity class must not be null!");
return count(query, entityClass, determineCollectionName(entityClass));
}
@@ -747,7 +749,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
*/
public long count(Query query, Class<?> entityClass, String collectionName) {
Assert.hasText(collectionName);
Assert.hasText(collectionName, "Collection name must not be null or empty!");
final DBObject dbObject = query == null ? null
: queryMapper.getMappedObject(query.getQueryObject(),
entityClass == null ? null : mappingContext.getPersistentEntity(entityClass));
@@ -788,7 +791,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Prepare the collection before any processing is done using it. This allows a convenient way to apply settings like
* slaveOk() etc. Can be overridden in sub-classes.
*
*
* @param collection
*/
protected void prepareCollection(DBCollection collection) {
@@ -802,7 +805,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* settings in sub-classes. <br />
* In case of using MongoDB Java driver version 3 the returned {@link WriteConcern} will be defaulted to
* {@link WriteConcern#ACKNOWLEDGED} when {@link WriteResultChecking} is set to {@link WriteResultChecking#EXCEPTION}.
*
*
* @param writeConcern any WriteConcern already configured or null
* @return The prepared WriteConcern or null
*/
@@ -826,11 +829,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
protected <T> void doInsert(String collectionName, T objectToSave, MongoWriter<T> writer) {
assertUpdateableIdIfNotSet(objectToSave);
initializeVersionProperty(objectToSave);
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
assertUpdateableIdIfNotSet(objectToSave);
DBObject dbDoc = toDbObject(objectToSave, writer);
@@ -918,21 +919,24 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
protected <T> void doInsertBatch(String collectionName, Collection<? extends T> batchToSave, MongoWriter<T> writer) {
Assert.notNull(writer);
Assert.notNull(writer, "MongoWriter must not be null!");
List<DBObject> dbObjectList = new ArrayList<DBObject>();
for (T o : batchToSave) {
initializeVersionProperty(o);
BasicDBObject dbDoc = new BasicDBObject();
maybeEmitEvent(new BeforeConvertEvent<T>(o, collectionName));
BasicDBObject dbDoc = new BasicDBObject();
writer.write(o, dbDoc);
maybeEmitEvent(new BeforeSaveEvent<T>(o, dbDoc, collectionName));
dbObjectList.add(dbDoc);
}
List<ObjectId> ids = insertDBObjectList(collectionName, dbObjectList);
List<Object> ids = consolidateIdentifiers(insertDBObjectList(collectionName, dbObjectList), dbObjectList);
int i = 0;
for (T obj : batchToSave) {
if (i < ids.size()) {
@@ -945,14 +949,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public void save(Object objectToSave) {
Assert.notNull(objectToSave);
Assert.notNull(objectToSave, "Object to save must not be null!");
save(objectToSave, determineEntityCollectionName(objectToSave));
}
public void save(Object objectToSave, String collectionName) {
Assert.notNull(objectToSave);
Assert.hasText(collectionName);
Assert.notNull(objectToSave, "Object to save must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(objectToSave.getClass());
@@ -981,6 +985,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
doInsert(collectionName, objectToSave, this.mongoConverter);
} else {
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
assertUpdateableIdIfNotSet(objectToSave);
// Create query for entity with the id and old version
@@ -992,7 +997,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
BasicDBObject dbObject = new BasicDBObject();
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
this.mongoConverter.write(objectToSave, dbObject);
maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbObject, collectionName));
@@ -1005,9 +1009,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
protected <T> void doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
assertUpdateableIdIfNotSet(objectToSave);
maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
assertUpdateableIdIfNotSet(objectToSave);
DBObject dbDoc = toDbObject(objectToSave, writer);
@@ -1037,6 +1040,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
});
}
// TODO: 2.0 - Change method signature to return List<Object> and return all identifiers (DATAMONGO-1513,
// DATAMONGO-1519)
protected List<ObjectId> insertDBObjectList(final String collectionName, final List<DBObject> dbDocList) {
if (dbDocList.isEmpty()) {
return Collections.emptyList();
@@ -1196,7 +1201,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public WriteResult remove(Object object, String collection) {
Assert.hasText(collection);
Assert.hasText(collection, "Collection name must not be null or empty!");
if (object == null) {
return null;
@@ -1208,7 +1213,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Returns {@link Entry} containing the field name of the id property as {@link Entry#getKey()} and the {@link Id}s
* property value as its {@link Entry#getValue()}.
*
*
* @param object
* @return
*/
@@ -1235,7 +1240,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Returns a {@link Query} for the given entity by its id.
*
*
* @param object must not be {@literal null}.
* @return
*/
@@ -1247,7 +1252,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Returns a {@link Query} for the given entities by their ids.
*
*
* @param objects must not be {@literal null} or {@literal empty}.
* @return
*/
@@ -1518,7 +1523,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* Retrieve and remove all documents matching the given {@code query} by calling {@link #find(Query, Class, String)}
* and {@link #remove(Query, Class, String)}, whereas the {@link Query} for {@link #remove(Query, Class, String)} is
* constructed out of the find result.
*
*
* @param collectionName
* @param query
* @param entityClass
@@ -1558,7 +1563,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Returns the potentially mapped results of the given {@commandResult} contained some.
*
*
* @param outputType
* @param commandResult
* @return
@@ -1670,7 +1675,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Create the specified collection using the provided options
*
*
* @param collectionName
* @param collectionOptions
* @return the collection that was created
@@ -1691,7 +1696,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter.
* The query document is specified as a standard {@link DBObject} and so is the fields specification.
*
*
* @param collectionName name of the collection to retrieve the objects from.
* @param query the query document that specifies the criteria used to find a record.
* @param fields the document that specifies the fields to be returned.
@@ -1716,7 +1721,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Map the results of an ad-hoc query on the default MongoDB collection to a List using the template's converter. The
* query document is specified as a standard DBObject and so is the fields specification.
*
*
* @param collectionName name of the collection to retrieve the objects from
* @param query the query document that specifies the criteria used to find a record
* @param fields the document that specifies the fields to be returned
@@ -1732,7 +1737,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* Map the results of an ad-hoc query on the default MongoDB collection to a List of the specified type. The object is
* converted from the MongoDB native representation using an instance of {@see MongoConverter}. The query document is
* specified as a standard DBObject and so is the fields specification.
*
*
* @param collectionName name of the collection to retrieve the objects from.
* @param query the query document that specifies the criteria used to find a record.
* @param fields the document that specifies the fields to be returned.
@@ -1785,7 +1790,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* The first document that matches the query is returned and also removed from the collection in the database.
* <p/>
* The query document is specified as a standard DBObject and so is the fields specification.
*
*
* @param collectionName name of the collection to retrieve the objects from
* @param query the query document that specifies the criteria used to find a record
* @param entityClass the parameterized type of the returned list.
@@ -1836,7 +1841,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Populates the id property of the saved object, if it's not set already.
*
*
* @param savedObject
* @param id
*/
@@ -1886,7 +1891,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* <li>Execute the given {@link ConnectionCallback} for a {@link DBObject}.</li>
* <li>Apply the given {@link DbObjectCallback} to each of the {@link DBObject}s to obtain the result.</li>
* <ol>
*
*
* @param <T>
* @param collectionCallback the callback to retrieve the {@link DBObject} with
* @param objectCallback the {@link DbObjectCallback} to transform {@link DBObject}s into the actual domain type
@@ -1915,7 +1920,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
* <li>Iterate over the {@link DBCursor} and applies the given {@link DbObjectCallback} to each of the
* {@link DBObject}s collecting the actual result {@link List}.</li>
* <ol>
*
*
* @param <T>
* @param collectionCallback the callback to retrieve the {@link DBCursor} with
* @param preparer the {@link CursorPreparer} to potentially modify the {@link DBCursor} before ireating over it
@@ -2022,7 +2027,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Handles {@link WriteResult} errors based on the configured {@link WriteResultChecking}.
*
*
* @param writeResult
* @param query
* @param operation
@@ -2066,7 +2071,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Inspects the given {@link CommandResult} for erros and potentially throws an
* {@link InvalidDataAccessApiUsageException} for that error.
*
*
* @param result must not be {@literal null}.
* @param source must not be {@literal null}.
*/
@@ -2104,7 +2109,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Tries to convert the given {@link RuntimeException} into a {@link DataAccessException} but returns the original
* exception if the conversation failed. Thus allows safe re-throwing of the return value.
*
*
* @param ex the exception to translate
* @param exceptionTranslator the {@link PersistenceExceptionTranslator} to be used for translation
* @return
@@ -2115,12 +2120,34 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
return resolved == null ? ex : resolved;
}
/**
* Returns all identifiers for the given documents. Will augment the given identifiers and fill in only the ones that
* are {@literal null} currently. This would've been better solved in {@link #insertDBObjectList(String, List)}
* directly but would require a signature change of that method.
*
* @param ids
* @param documents
* @return TODO: Remove for 2.0 and change method signature of {@link #insertDBObjectList(String, List)}.
*/
private static List<Object> consolidateIdentifiers(List<ObjectId> ids, List<DBObject> documents) {
List<Object> result = new ArrayList<Object>(ids.size());
for (int i = 0; i < ids.size(); i++) {
ObjectId objectId = ids.get(i);
result.add(objectId == null ? documents.get(i).get(ID_FIELD) : objectId);
}
return result;
}
// Callback implementations
/**
* Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification
* {@link DBObject} and executes that against the {@link DBCollection}.
*
*
* @author Oliver Gierke
* @author Thomas Risberg
*/
@@ -2154,7 +2181,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification
* {@link DBObject} and executes that against the {@link DBCollection}.
*
*
* @author Oliver Gierke
* @author Thomas Risberg
*/
@@ -2168,7 +2195,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
}
public FindCallback(DBObject query, DBObject fields) {
this.query = query;
this.query = query == null ? new BasicDBObject() : query;
this.fields = fields;
}
@@ -2185,7 +2212,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Simple {@link CollectionCallback} that takes a query {@link DBObject} plus an optional fields specification
* {@link DBObject} and executes that against the {@link DBCollection}.
*
*
* @author Thomas Risberg
*/
private static class FindAndRemoveCallback implements CollectionCallback<DBObject> {
@@ -2230,7 +2257,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Simple internal callback to allow operations on a {@link DBObject}.
*
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
@@ -2243,7 +2270,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Simple {@link DbObjectCallback} that will transform {@link DBObject} into the given target type using the given
* {@link MongoReader}.
*
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
@@ -2255,8 +2282,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
public ReadDbObjectCallback(EntityReader<? super T, DBObject> reader, Class<T> type, String collectionName) {
Assert.notNull(reader);
Assert.notNull(type);
Assert.notNull(reader, "EntityReader must not be null!");
Assert.notNull(type, "Entity type must not be null!");
this.reader = reader;
this.type = type;
this.collectionName = collectionName;
@@ -2363,7 +2391,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* {@link DbObjectCallback} that assumes a {@link GeoResult} to be created, delegates actual content unmarshalling to
* a delegate and creates a {@link GeoResult} from the result.
*
*
* @author Oliver Gierke
*/
static class GeoNearResultDbObjectCallback<T> implements DbObjectCallback<GeoResult<T>> {
@@ -2374,11 +2402,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Creates a new {@link GeoNearResultDbObjectCallback} using the given {@link DbObjectCallback} delegate for
* {@link GeoResult} content unmarshalling.
*
*
* @param delegate must not be {@literal null}.
*/
public GeoNearResultDbObjectCallback(DbObjectCallback<T> delegate, Metric metric) {
Assert.notNull(delegate);
Assert.notNull(delegate, "DocumentCallback must not be null!");
this.delegate = delegate;
this.metric = metric;
}
@@ -2396,7 +2426,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* A {@link CloseableIterator} that is backed by a MongoDB {@link Cursor}.
*
*
* @since 1.7
* @author Thomas Darimont
*/
@@ -2408,7 +2438,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
/**
* Creates a new {@link CloseableIterableCursorAdapter} backed by the given {@link Cursor}.
*
*
* @param cursor
* @param exceptionTranslator
* @param objectReadCallback

View File

@@ -248,11 +248,22 @@ public class Aggregation {
*
* @param elementsToSkip must not be less than zero.
* @return
* @deprecated prepare to get this one removed in favor of {@link #skip(long)}.
*/
public static SkipOperation skip(int elementsToSkip) {
return new SkipOperation(elementsToSkip);
}
/**
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param elementsToSkip must not be less than zero.
* @return
*/
public static SkipOperation skip(long elementsToSkip) {
return new SkipOperation(elementsToSkip);
}
/**
* Creates a new {@link LimitOperation} limiting the result to the given number of elements.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,16 +20,17 @@ import com.mongodb.DBObject;
/**
* An {@link AggregationExpression} can be used with field expressions in aggregation pipeline stages like
* {@code project} and {@code group}.
*
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
*/
interface AggregationExpression {
public interface AggregationExpression {
/**
* Turns the {@link AggregationExpression} into a {@link DBObject} within the given
* {@link AggregationOperationContext}.
*
*
* @param context
* @return
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import com.mongodb.DBObject;
* @author Tobias Trelle
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @param <T> The class in which the results are mapped onto.
* @since 1.3
*/
@@ -46,8 +47,8 @@ public class AggregationResults<T> implements Iterable<T> {
*/
public AggregationResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
Assert.notNull(mappedResults, "List of mapped results must not be null!");
Assert.notNull(rawResults, "Raw results must not be null!");
this.mappedResults = Collections.unmodifiableList(mappedResults);
this.rawResults = rawResults;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
@@ -552,7 +553,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
/**
* Generates an {@code $mod} expression that divides the value of the given field by the previously mentioned field
* and returns the remainder.
*
*
* @param fieldReference
* @return
*/
@@ -566,7 +567,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
return project("size");
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@@ -622,6 +623,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
*/
static class FieldProjection extends Projection {
@@ -640,7 +642,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
private FieldProjection(Field field, Object value) {
super(field);
super(new ExposedField(field.getName(), true));
this.field = field;
this.value = value;
@@ -732,7 +734,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
this.values = Arrays.asList(values);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public class SkipOperation implements AggregationOperation {
/**
* Creates a new {@link SkipOperation} skipping the given number of elements.
*
* @param skipCount number of documents to skip.
* @param skipCount number of documents to skip, must not be less than zero.
*/
public SkipOperation(long skipCount) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import com.mongodb.DBObject;
* property references into document field names.
*
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.3
*/
public class TypeBasedAggregationOperationContext implements AggregationOperationContext {
@@ -95,7 +96,7 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(
field.getTarget(), type);
Field mappedField = field(propertyPath.getLeafProperty().getName(),
Field mappedField = field(field.getName(),
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE));
return new FieldReference(new ExposedField(mappedField, true));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.springframework.util.Assert;
* Conversion registration information.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
class ConverterRegistration {
@@ -39,7 +40,7 @@ class ConverterRegistration {
*/
public ConverterRegistration(ConvertiblePair convertiblePair, boolean isReading, boolean isWriting) {
Assert.notNull(convertiblePair);
Assert.notNull(convertiblePair, "ConvertiblePair must not be null!");
this.convertiblePair = convertiblePair;
this.reading = isReading;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,6 +56,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class CustomConversions {
@@ -88,7 +89,7 @@ public class CustomConversions {
*/
public CustomConversions(List<?> converters) {
Assert.notNull(converters);
Assert.notNull(converters, "List of converters must not be null!");
this.readingPairs = new LinkedHashSet<ConvertiblePair>();
this.writingPairs = new LinkedHashSet<ConvertiblePair>();
@@ -345,8 +346,8 @@ public class CustomConversions {
private static Class<?> getCustomTarget(Class<?> sourceType, Class<?> requestedTargetType,
Collection<ConvertiblePair> pairs) {
Assert.notNull(sourceType);
Assert.notNull(pairs);
Assert.notNull(sourceType, "Source Class must not be null!");
Assert.notNull(pairs, "Collection of ConvertiblePair must not be null!");
if (requestedTargetType != null && pairs.contains(new ConvertiblePair(sourceType, requestedTargetType))) {
return requestedTargetType;

View File

@@ -114,6 +114,40 @@ class DBObjectAccessor {
return result;
}
/**
* Returns whether the underlying {@link DBObject} has a value ({@literal null} or non-{@literal null}) for the given
* {@link MongoPersistentProperty}.
*
* @param property must not be {@literal null}.
* @return
*/
public boolean hasValue(MongoPersistentProperty property) {
Assert.notNull(property, "Property must not be null!");
String fieldName = property.getFieldName();
if (!fieldName.contains(".")) {
return this.dbObject.containsField(fieldName);
}
String[] parts = fieldName.split("\\.");
Map<String, Object> source = this.dbObject;
Object result = null;
for (int i = 1; i < parts.length; i++) {
result = source.get(parts[i - 1]);
source = getAsMap(result);
if (source == null) {
return false;
}
}
return source.containsKey(parts[parts.length - 1]);
}
/**
* Returns the given source object as map, i.e. {@link BasicDBObject}s and maps as is or {@literal null} otherwise.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -180,8 +180,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
* @author Oliver Gierke
* @author Christoph Strobl
*/
static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor,
Serializable {
static class LazyLoadingInterceptor
implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, Serializable {
private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD, FINALIZE_METHOD;
@@ -387,7 +387,8 @@ public class DefaultDbRefResolver implements DbRefResolver {
} catch (RuntimeException ex) {
DataAccessException translatedException = this.exceptionTranslator.translateExceptionIfPossible(ex);
throw new LazyLoadingException("Unable to lazily resolve DBRef!", translatedException);
throw new LazyLoadingException("Unable to lazily resolve DBRef!",
translatedException != null ? translatedException : ex);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@ import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.query.GeoCommand;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBList;
@@ -53,6 +54,7 @@ import com.mongodb.DBObject;
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
* @author Thiago Diniz da Silveira
* @since 1.5
*/
abstract class GeoConverters {
@@ -121,7 +123,7 @@ abstract class GeoConverters {
return DbObjectToGeoJsonPointConverter.INSTANCE.convert(source);
}
return new Point((Double) source.get("x"), (Double) source.get("y"));
return new Point(toPrimitiveDoubleValue(source.get("x")), toPrimitiveDoubleValue(source.get("y")));
}
}
@@ -255,9 +257,12 @@ abstract class GeoConverters {
}
DBObject center = (DBObject) source.get("center");
Double radius = (Double) source.get("radius");
Number radius = (Number) source.get("radius");
Distance distance = new Distance(radius);
Assert.notNull(center, "Center must not be null!");
Assert.notNull(radius, "Radius must not be null!");
Distance distance = new Distance(toPrimitiveDoubleValue(radius));
if (source.containsField("metric")) {
@@ -267,9 +272,6 @@ abstract class GeoConverters {
distance = distance.in(Metrics.valueOf(metricString));
}
Assert.notNull(center, "Center must not be null!");
Assert.notNull(radius, "Radius must not be null!");
return new Circle(DbObjectToPointConverter.INSTANCE.convert(center), distance);
}
}
@@ -326,9 +328,12 @@ abstract class GeoConverters {
}
DBObject center = (DBObject) source.get("center");
Double radius = (Double) source.get("radius");
Number radius = (Number) source.get("radius");
Distance distance = new Distance(radius);
Assert.notNull(center, "Center must not be null!");
Assert.notNull(radius, "Radius must not be null!");
Distance distance = new Distance(toPrimitiveDoubleValue(radius));
if (source.containsField("metric")) {
@@ -338,9 +343,6 @@ abstract class GeoConverters {
distance = distance.in(Metrics.valueOf(metricString));
}
Assert.notNull(center, "Center must not be null!");
Assert.notNull(radius, "Radius must not be null!");
return new Sphere(DbObjectToPointConverter.INSTANCE.convert(center), distance);
}
}
@@ -599,8 +601,8 @@ abstract class GeoConverters {
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Point"),
String.format("Cannot convert type '%s' to Point.", source.get("type")));
List<Double> dbl = (List<Double>) source.get("coordinates");
return new GeoJsonPoint(dbl.get(0).doubleValue(), dbl.get(1).doubleValue());
List<Number> dbl = (List<Number>) source.get("coordinates");
return new GeoJsonPoint(toPrimitiveDoubleValue(dbl.get(0)), toPrimitiveDoubleValue(dbl.get(1)));
}
}
@@ -832,9 +834,10 @@ abstract class GeoConverters {
Assert.isInstanceOf(List.class, point);
List<Double> coordinatesList = (List<Double>) point;
List<Number> coordinatesList = (List<Number>) point;
points.add(new GeoJsonPoint(coordinatesList.get(0).doubleValue(), coordinatesList.get(1).doubleValue()));
points.add(new GeoJsonPoint(toPrimitiveDoubleValue(coordinatesList.get(0)),
toPrimitiveDoubleValue(coordinatesList.get(1))));
}
return points;
}
@@ -849,4 +852,10 @@ abstract class GeoConverters {
static GeoJsonPolygon toGeoJsonPolygon(BasicDBList dbList) {
return new GeoJsonPolygon(toListOfPoint((BasicDBList) dbList.get(0)));
}
private static double toPrimitiveDoubleValue(Object value) {
Assert.isInstanceOf(Number.class, value, "Argument must be a Number.");
return NumberUtils.convertNumberToTargetClass((Number) value, Double.class).doubleValue();
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011-2015 by the original author(s).
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -95,7 +96,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
/**
* Creates a new {@link MappingMongoConverter} given the new {@link DbRefResolver} and {@link MappingContext}.
*
* @param mongoDbFactory must not be {@literal null}.
* @param dbRefResolver must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
public MappingMongoConverter(DbRefResolver dbRefResolver,
@@ -258,14 +259,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
// make sure id property is set before all other properties
Object idValue = null;
final DBObjectAccessor dbObjectAccessor = new DBObjectAccessor(dbo);
if (idProperty != null) {
if (idProperty != null && dbObjectAccessor.hasValue(idProperty)) {
idValue = getValueInternal(idProperty, dbo, evaluator, path);
accessor.setProperty(idProperty, idValue);
}
final ObjectPath currentPath = path.push(result, entity,
idValue != null ? dbo.get(idProperty.getFieldName()) : null);
final ObjectPath currentPath = path.push(result, entity, idValue != null ? dbObjectAccessor.get(idProperty) : null);
// Set properties not already set in the constructor
entity.doWithProperties(new PropertyHandler<MongoPersistentProperty>() {
@@ -276,7 +277,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return;
}
if (!dbo.containsField(prop.getFieldName()) || entity.isConstructorArgument(prop)) {
if (entity.isConstructorArgument(prop) || !dbObjectAccessor.hasValue(prop)) {
return;
}
@@ -289,7 +290,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
public void doWithAssociation(Association<MongoPersistentProperty> association) {
final MongoPersistentProperty property = association.getInverse();
Object value = dbo.get(property.getFieldName());
Object value = dbObjectAccessor.get(property);
if (value == null || entity.isConstructorArgument(property)) {
return;
@@ -309,16 +310,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return result;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoWriter#toDBRef(java.lang.Object, org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
*/
public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) {
public DBRef toDBRef(Object object, MongoPersistentProperty referringProperty) {
org.springframework.data.mongodb.core.mapping.DBRef annotation = null;
if (referingProperty != null) {
annotation = referingProperty.getDBRef();
if (referringProperty != null) {
annotation = referringProperty.getDBRef();
Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef!");
}
@@ -327,14 +328,14 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return ((LazyLoadingProxy) object).toDBRef();
}
return createDBRef(object, referingProperty);
return createDBRef(object, referringProperty);
}
/**
* Root entry method into write conversion. Adds a type discriminator to the {@link DBObject}. Shouldn't be called for
* nested conversions.
*
* @see org.springframework.data.mongodb.core.core.convert.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
* @see org.springframework.data.mongodb.core.convert.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
*/
public void write(final Object obj, final DBObject dbo) {
@@ -477,7 +478,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
DBRef dbRefObj = null;
/*
* If we already have a LazyLoadingProxy, we use it's cached DBRef value instead of
* If we already have a LazyLoadingProxy, we use it's cached DBRef value instead of
* unnecessarily initializing it only to convert it to a DBRef a few instructions later.
*/
if (obj instanceof LazyLoadingProxy) {
@@ -824,7 +825,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
protected DBRef createDBRef(Object target, MongoPersistentProperty property) {
Assert.notNull(target);
Assert.notNull(target, "Target object must not be null!");
if (target instanceof DBRef) {
return (DBRef) target;
@@ -885,10 +886,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Class<?> collectionType = targetType.getType();
if (sourceValue.isEmpty()) {
return getPotentiallyConvertedSimpleRead(new HashSet<Object>(), collectionType);
}
TypeInformation<?> componentType = targetType.getComponentType();
Class<?> rawComponentType = componentType == null ? null : componentType.getType();
@@ -896,9 +893,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Collection<Object> items = targetType.getType().isArray() ? new ArrayList<Object>()
: CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size());
for (int i = 0; i < sourceValue.size(); i++) {
if (sourceValue.isEmpty()) {
return getPotentiallyConvertedSimpleRead(items, collectionType);
}
Object dbObjItem = sourceValue.get(i);
for (Object dbObjItem : sourceValue) {
if (dbObjItem instanceof DBRef) {
items.add(
@@ -992,20 +991,32 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
if (obj instanceof DBObject) {
DBObject newValueDbo = new BasicDBObject();
for (String vk : ((DBObject) obj).keySet()) {
Object o = ((DBObject) obj).get(vk);
newValueDbo.put(vk, convertToMongoType(o, typeHint));
}
return newValueDbo;
}
if (obj instanceof Map) {
DBObject result = new BasicDBObject();
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
result.put(entry.getKey().toString(), convertToMongoType(entry.getValue(), typeHint));
Map<Object, Object> converted = new LinkedHashMap<Object, Object>();
for (Entry<Object, Object> entry : ((Map<Object, Object>) obj).entrySet()) {
TypeInformation<? extends Object> valueTypeHint = typeHint != null && typeHint.getMapValueType() != null
? typeHint.getMapValueType() : typeHint;
converted.put(getPotentiallyConvertedSimpleWrite(entry.getKey()).toString(),
convertToMongoType(entry.getValue(), valueTypeHint));
}
return result;
return new BasicDBObject(converted);
}
if (obj.getClass().isArray()) {
@@ -1042,7 +1053,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
/**
* Removes the type information from the entire conversion result.
*
*
* @param object
* @param recursively whether to apply the removal recursively
* @return
@@ -1103,22 +1114,22 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
/**
* Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link SpELExpressionEvaluator} and
* {@link ObjectPath}.
*
*
* @param source must not be {@literal null}.
* @param evaluator must not be {@literal null}.
* @param path can be {@literal null}.
*/
public MongoDbPropertyValueProvider(DBObject source, SpELExpressionEvaluator evaluator, ObjectPath path) {
Assert.notNull(source);
Assert.notNull(evaluator);
Assert.notNull(source, "DBObject must not be null!");
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null!");
this.source = new DBObjectAccessor(source);
this.evaluator = evaluator;
this.path = path;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@@ -1161,7 +1172,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.path = path;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELExpressionParameterValueProvider#potentiallyConvertSpelValue(java.lang.Object, org.springframework.data.mapping.PreferredConstructor.Parameter)
*/

View File

@@ -81,7 +81,10 @@ abstract class MongoConverters {
converters.add(DBObjectToNamedMongoScriptCoverter.INSTANCE);
converters.add(CurrencyToStringConverter.INSTANCE);
converters.add(StringToCurrencyConverter.INSTANCE);
converters.add(NumberToNumberConverterFactory.INSTANCE);
converters.add(AtomicIntegerToIntegerConverter.INSTANCE);
converters.add(AtomicLongToLongConverter.INSTANCE);
converters.add(LongToAtomicLongConverter.INSTANCE);
converters.add(IntegerToAtomicIntegerConverter.INSTANCE);
return converters;
}
@@ -374,4 +377,68 @@ abstract class MongoConverters {
}
}
}
/**
* {@link ConverterFactory} implementation converting {@link AtomicLong} into {@link Long}.
*
* @author Christoph Strobl
* @since 1.10
*/
@WritingConverter
public static enum AtomicLongToLongConverter implements Converter<AtomicLong, Long> {
INSTANCE;
@Override
public Long convert(AtomicLong source) {
return NumberUtils.convertNumberToTargetClass(source, Long.class);
}
}
/**
* {@link ConverterFactory} implementation converting {@link AtomicInteger} into {@link Integer}.
*
* @author Christoph Strobl
* @since 1.10
*/
@WritingConverter
public static enum AtomicIntegerToIntegerConverter implements Converter<AtomicInteger, Integer> {
INSTANCE;
@Override
public Integer convert(AtomicInteger source) {
return NumberUtils.convertNumberToTargetClass(source, Integer.class);
}
}
/**
* {@link ConverterFactory} implementation converting {@link Long} into {@link AtomicLong}.
*
* @author Christoph Strobl
* @since 1.10
*/
@ReadingConverter
public static enum LongToAtomicLongConverter implements Converter<Long, AtomicLong> {
INSTANCE;
@Override
public AtomicLong convert(Long source) {
return source != null ? new AtomicLong(source) : null;
}
}
/**
* {@link ConverterFactory} implementation converting {@link Integer} into {@link AtomicInteger}.
*
* @author Christoph Strobl
* @since 1.10
*/
@ReadingConverter
public static enum IntegerToAtomicIntegerConverter implements Converter<Integer, AtomicInteger> {
INSTANCE;
@Override
public AtomicInteger convert(Integer source) {
return source != null ? new AtomicInteger(source) : null;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,6 +58,7 @@ import com.mongodb.DBRef;
* @author Patryk Wasik
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class QueryMapper {
@@ -66,7 +67,7 @@ public class QueryMapper {
static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
private enum MetaMapping {
FORCE, WHEN_PRESENT, IGNORE;
FORCE, WHEN_PRESENT, IGNORE
}
private final ConversionService conversionService;
@@ -81,7 +82,7 @@ public class QueryMapper {
*/
public QueryMapper(MongoConverter converter) {
Assert.notNull(converter);
Assert.notNull(converter, "MongoConverter must not be null!");
this.conversionService = converter.getConversionService();
this.converter = converter;
@@ -316,7 +317,7 @@ public class QueryMapper {
}
if (isNestedKeyword(value)) {
return getMappedKeyword(new Keyword((DBObject) value), null);
return getMappedKeyword(new Keyword((DBObject) value), documentField.getPropertyEntity());
}
if (isAssociationConversionNecessary(documentField, value)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.springframework.util.Assert;
* Represents a geospatial sphere value.
*
* @author Thomas Darimont
* @author Mark Paluch
* @since 1.5
*/
public class Sphere implements Shape {
@@ -46,8 +47,8 @@ public class Sphere implements Shape {
@PersistenceConstructor
public Sphere(Point center, Distance radius) {
Assert.notNull(center);
Assert.notNull(radius);
Assert.notNull(center, "Center point must not be null!");
Assert.notNull(radius, "Radius must not be null!");
Assert.isTrue(radius.getValue() >= 0, "Radius must not be negative!");
this.center = center;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ public class GeospatialIndex implements IndexDefinition {
private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D;
private Double bucketSize = 1.0;
private String additionalField;
private IndexFilter filter;
/**
* Creates a new {@link GeospatialIndex} for the given field.
@@ -119,6 +120,22 @@ public class GeospatialIndex implements IndexDefinition {
return this;
}
/**
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
*
* @param filter can be {@literal null}.
* @return
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 1.10
*/
public GeospatialIndex partial(IndexFilter filter) {
this.filter = filter;
return this;
}
public DBObject getIndexKeys() {
DBObject dbo = new BasicDBObject();
@@ -186,6 +203,10 @@ public class GeospatialIndex implements IndexDefinition {
break;
}
if (filter != null) {
dbo.put("partialFilterExpression", filter.getFilterObject());
}
return dbo;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 the original author or authors.
* Copyright 2010-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,6 +63,8 @@ public class Index implements IndexDefinition {
private long expire = -1;
private IndexFilter filter;
public Index() {}
public Index(String key, Direction direction) {
@@ -176,6 +178,21 @@ public class Index implements IndexDefinition {
return unique();
}
/**
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
*
* @param filter can be {@literal null}.
* @return
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 1.10
*/
public Index partial(IndexFilter filter) {
this.filter = filter;
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexKeys()
@@ -213,6 +230,9 @@ public class Index implements IndexDefinition {
dbo.put("expireAfterSeconds", expire);
}
if (filter != null) {
dbo.put("partialFilterExpression", filter.getFilterObject());
}
return dbo;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,8 +44,13 @@ public final class IndexField {
private IndexField(String key, Direction direction, Type type, Float weight) {
Assert.hasText(key);
Assert.isTrue(direction != null ^ (Type.GEO.equals(type) || Type.TEXT.equals(type)));
Assert.hasText(key, "Key must not be null or empty");
if (Type.GEO.equals(type) || Type.TEXT.equals(type)) {
Assert.isTrue(direction == null, "Geo/Text indexes must not have a direction!");
} else {
Assert.notNull(direction, "Default indexes require a direction");
}
this.key = key;
this.direction = direction;
@@ -58,17 +63,21 @@ public final class IndexField {
*
* @deprecated use {@link #create(String, Direction)}.
* @param key must not be {@literal null} or emtpy.
* @param direction must not be {@literal null}.
* @param order must not be {@literal null}.
* @return
*/
@Deprecated
public static IndexField create(String key, Order order) {
Assert.notNull(order);
Assert.notNull(order, "Order must not be null!");
return new IndexField(key, order.toDirection(), Type.DEFAULT);
}
public static IndexField create(String key, Direction order) {
Assert.notNull(order);
Assert.notNull(order, "Direction must not be null!");
return new IndexField(key, order, Type.DEFAULT);
}
@@ -128,7 +137,7 @@ public final class IndexField {
}
/**
* Returns wheter the {@link IndexField} is a text index field.
* Returns whether the {@link IndexField} is a text index field.
*
* @return true if type is {@link Type#TEXT}
* @since 1.6
@@ -158,7 +167,7 @@ public final class IndexField {
&& this.type == that.type;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@@ -173,7 +182,7 @@ public final class IndexField {
return result;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.index;
import com.mongodb.DBObject;
/**
* Use {@link IndexFilter} to create the partial filter expression used when creating
* <a href="https://docs.mongodb.com/manual/core/index-partial/">Partial Indexes</a>.
*
* @author Christoph Strobl
* @since 1.10
*/
public interface IndexFilter {
/**
* Get the raw (unmapped) filter expression.
*
* @return
*/
DBObject getFilterObject();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,10 @@
*/
package org.springframework.data.mongodb.core.index;
import static org.springframework.data.domain.Sort.Direction.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -23,13 +26,20 @@ import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.DBObject;
/**
* @author Mark Pollack
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public class IndexInfo {
private static final Double ONE = Double.valueOf(1);
private static final Double MINUS_ONE = Double.valueOf(-1);
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
private final List<IndexField> indexFields;
private final String name;
@@ -37,6 +47,7 @@ public class IndexInfo {
private final boolean dropDuplicates;
private final boolean sparse;
private final String language;
private String partialFilterExpression;
/**
* @deprecated Will be removed in 1.7. Please use {@link #IndexInfo(List, String, boolean, boolean, boolean, String)}
@@ -62,6 +73,64 @@ public class IndexInfo {
this.language = language;
}
/**
* Creates new {@link IndexInfo} parsing required properties from the given {@literal sourceDocument}.
*
* @param sourceDocument
* @return
* @since 1.10
*/
public static IndexInfo indexInfoOf(DBObject sourceDocument) {
DBObject keyDbObject = (DBObject) sourceDocument.get("key");
int numberOfElements = keyDbObject.keySet().size();
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
for (String key : keyDbObject.keySet()) {
Object value = keyDbObject.get(key);
if (TWO_D_IDENTIFIERS.contains(value)) {
indexFields.add(IndexField.geo(key));
} else if ("text".equals(value)) {
DBObject weights = (DBObject) sourceDocument.get("weights");
for (String fieldName : weights.keySet()) {
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
}
} else {
Double keyValue = new Double(value.toString());
if (ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, ASC));
} else if (MINUS_ONE.equals(keyValue)) {
indexFields.add(IndexField.create(key, DESC));
}
}
}
String name = sourceDocument.get("name").toString();
boolean unique = sourceDocument.containsField("unique") ? (Boolean) sourceDocument.get("unique") : false;
boolean dropDuplicates = sourceDocument.containsField("dropDups") ? (Boolean) sourceDocument.get("dropDups")
: false;
boolean sparse = sourceDocument.containsField("sparse") ? (Boolean) sourceDocument.get("sparse") : false;
String language = sourceDocument.containsField("default_language") ? (String) sourceDocument.get("default_language")
: "";
String partialFilter = sourceDocument.containsField("partialFilterExpression")
? sourceDocument.get("partialFilterExpression").toString() : "";
IndexInfo info = new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language);
info.partialFilterExpression = partialFilter;
return info;
}
/**
* Returns the individual index fields of the index.
*
@@ -79,7 +148,8 @@ public class IndexInfo {
*/
public boolean isIndexForFields(Collection<String> keys) {
Assert.notNull(keys);
Assert.notNull(keys, "Collection of keys must not be null!");
List<String> indexKeys = new ArrayList<String>(indexFields.size());
for (IndexField field : indexFields) {
@@ -113,10 +183,19 @@ public class IndexInfo {
return language;
}
/**
* @return
* @since 1.0
*/
public String getPartialFilterExpression() {
return partialFilterExpression;
}
@Override
public String toString() {
return "IndexInfo [indexFields=" + indexFields + ", name=" + name + ", unique=" + unique + ", dropDuplicates="
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + "]";
+ dropDuplicates + ", sparse=" + sparse + ", language=" + language + ", partialFilterExpression="
+ partialFilterExpression + "]";
}
@Override
@@ -130,6 +209,7 @@ public class IndexInfo {
result = prime * result + (sparse ? 1231 : 1237);
result = prime * result + (unique ? 1231 : 1237);
result = prime * result + ObjectUtils.nullSafeHashCode(language);
result = prime * result + ObjectUtils.nullSafeHashCode(partialFilterExpression);
return result;
}
@@ -171,6 +251,9 @@ public class IndexInfo {
if (!ObjectUtils.nullSafeEquals(language, other.language)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(partialFilterExpression, other.partialFilterExpression)) {
return false;
}
return true;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import org.springframework.util.Assert;
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Mark Paluch
*/
public class MongoMappingEventPublisher implements ApplicationEventPublisher {
@@ -46,7 +47,7 @@ public class MongoMappingEventPublisher implements ApplicationEventPublisher {
*/
public MongoMappingEventPublisher(MongoPersistentEntityIndexCreator indexCreator) {
Assert.notNull(indexCreator);
Assert.notNull(indexCreator, "MongoPersistentEntityIndexCreator must not be null!");
this.indexCreator = indexCreator;
}
@@ -61,7 +62,7 @@ public class MongoMappingEventPublisher implements ApplicationEventPublisher {
}
}
/*
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -79,9 +79,9 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener<Ma
public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory,
IndexResolver indexResolver) {
Assert.notNull(mongoDbFactory);
Assert.notNull(mappingContext);
Assert.notNull(indexResolver);
Assert.notNull(mappingContext, "MongoMappingContext must not be null!");
Assert.notNull(mongoDbFactory, "MongoDbFactory must not be null!");
Assert.notNull(indexResolver, "IndexResolver must not be null!");
this.mongoDbFactory = mongoDbFactory;
this.mappingContext = mappingContext;

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2016. the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.index;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import com.mongodb.DBObject;
/**
* {@link IndexFilter} implementation for usage with plain {@link DBObject} as well as {@link CriteriaDefinition} filter
* expressions.
*
* @author Christoph Strobl
* @since 1.10
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class PartialIndexFilter implements IndexFilter {
private final @NonNull Object filterExpression;
/**
* Create new {@link PartialIndexFilter} for given {@link DBObject filter expression}.
*
* @param where must not be {@literal null}.
* @return
*/
public static PartialIndexFilter of(DBObject where) {
return new PartialIndexFilter(where);
}
/**
* Create new {@link PartialIndexFilter} for given {@link CriteriaDefinition filter expression}.
*
* @param where must not be {@literal null}.
* @return
*/
public static PartialIndexFilter of(CriteriaDefinition where) {
return new PartialIndexFilter(where);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.index.IndexFilter#getFilterObject()
*/
public DBObject getFilterObject() {
if (filterExpression instanceof DBObject) {
return (DBObject) filterExpression;
}
if (filterExpression instanceof CriteriaDefinition) {
return ((CriteriaDefinition) filterExpression).getCriteriaObject();
}
throw new IllegalArgumentException(
String.format("Unknown type %s used as filter expression.", filterExpression.getClass()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ public class TextIndexDefinition implements IndexDefinition {
private Set<TextIndexedFieldSpec> fieldSpecs;
private String defaultLanguage;
private String languageOverride;
private IndexFilter filter;
TextIndexDefinition() {
fieldSpecs = new LinkedHashSet<TextIndexedFieldSpec>();
@@ -129,6 +130,10 @@ public class TextIndexDefinition implements IndexDefinition {
options.put("language_override", languageOverride);
}
if (filter != null) {
options.put("partialFilterExpression", filter.getFilterObject());
}
return options;
}
@@ -288,8 +293,8 @@ public class TextIndexDefinition implements IndexDefinition {
public TextIndexDefinitionBuilder onField(String fieldname, Float weight) {
if (this.instance.fieldSpecs.contains(ALL_FIELDS)) {
throw new InvalidDataAccessApiUsageException(String.format("Cannot add %s to field spec for all fields.",
fieldname));
throw new InvalidDataAccessApiUsageException(
String.format("Cannot add %s to field spec for all fields.", fieldname));
}
this.instance.fieldSpecs.add(new TextIndexedFieldSpec(fieldname, weight));
@@ -318,15 +323,30 @@ public class TextIndexDefinition implements IndexDefinition {
public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) {
if (StringUtils.hasText(this.instance.languageOverride)) {
throw new InvalidDataAccessApiUsageException(String.format(
"Cannot set language override on %s as it is already defined on %s.", fieldname,
this.instance.languageOverride));
throw new InvalidDataAccessApiUsageException(
String.format("Cannot set language override on %s as it is already defined on %s.", fieldname,
this.instance.languageOverride));
}
this.instance.languageOverride = fieldname;
return this;
}
/**
* Only index the documents that meet the specified {@link IndexFilter filter expression}.
*
* @param filter can be {@literal null}.
* @return
* @see <a href=
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
* @since 1.10
*/
public TextIndexDefinitionBuilder partial(IndexFilter filter) {
this.instance.filter = filter;
return this;
}
public TextIndexDefinition build() {
return this.instance;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,6 +51,7 @@ import org.springframework.util.StringUtils;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, MongoPersistentProperty> implements
MongoPersistentEntity<T>, ApplicationContextAware {
@@ -138,7 +139,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
return getTextScoreProperty() != null;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.BasicPersistentEntity#verify()
*/
@@ -200,7 +201,7 @@ public class BasicMongoPersistentEntity<T> extends BasicPersistentEntity<T, Mong
@Override
protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(MongoPersistentProperty property) {
Assert.notNull(property);
Assert.notNull(property, "MongoPersistentProperty must not be null!");
if (!property.isIdProperty()) {
return null;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 by the original author(s).
* Copyright (c) 2011-2017 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ import org.bson.types.Binary;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.util.MongoClientVersion;
import org.springframework.util.ClassUtils;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
@@ -34,6 +36,7 @@ import com.mongodb.DBRef;
* Simple constant holder for a {@link SimpleTypeHolder} enriched with Mongo specific simple types.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public abstract class MongoSimpleTypes {
@@ -54,12 +57,17 @@ public abstract class MongoSimpleTypes {
simpleTypes.add(Pattern.class);
simpleTypes.add(Binary.class);
simpleTypes.add(UUID.class);
if (MongoClientVersion.isMongo34Driver()) {
simpleTypes
.add(ClassUtils.resolveClassName("org.bson.types.Decimal128", MongoSimpleTypes.class.getClassLoader()));
}
MONGO_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}
private static final Set<Class<?>> MONGO_SIMPLE_TYPES;
public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(MONGO_SIMPLE_TYPES, true);
private MongoSimpleTypes() {
}
private MongoSimpleTypes() {}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011 - 2014 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,6 +27,7 @@ import com.mongodb.DBObject;
*
* @author Mark Pollack
* @author Christoph Strobl
* @author Mark Paluch
* @param <T> The class in which the results are mapped onto, accessible via an {@link Iterator}.
*/
public class GroupByResults<T> implements Iterable<T> {
@@ -40,10 +41,12 @@ public class GroupByResults<T> implements Iterable<T> {
public GroupByResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
Assert.notNull(mappedResults, "List of mapped results must not be null!");
Assert.notNull(rawResults, "Raw results must not be null!");
this.mappedResults = mappedResults;
this.rawResults = rawResults;
parseKeys();
parseCount();
parseServerUsed();

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -29,6 +29,7 @@ import com.mongodb.MapReduceOutput;
* @author Mark Pollack
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @param <T> The class in which the results are mapped onto, accessible via an iterator.
*/
public class MapReduceResults<T> implements Iterable<T> {
@@ -49,8 +50,8 @@ public class MapReduceResults<T> implements Iterable<T> {
@Deprecated
public MapReduceResults(List<T> mappedResults, DBObject rawResults) {
Assert.notNull(mappedResults);
Assert.notNull(rawResults);
Assert.notNull(mappedResults, "List of mapped results must not be null!");
Assert.notNull(rawResults, "Raw results must not be null!");
this.mappedResults = mappedResults;
this.rawResults = rawResults;

View File

@@ -50,6 +50,7 @@ import com.mongodb.DBObject;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class Criteria implements CriteriaDefinition {
@@ -387,7 +388,7 @@ public class Criteria implements CriteriaDefinition {
*/
public Criteria regex(Pattern pattern) {
Assert.notNull(pattern);
Assert.notNull(pattern, "Pattern must not be null!");
if (lastOperatorWasNot()) {
return not(pattern);
@@ -398,7 +399,9 @@ public class Criteria implements CriteriaDefinition {
}
private Pattern toPattern(String regex, String options) {
Assert.notNull(regex);
Assert.notNull(regex, "Regex string must not be null!");
return Pattern.compile(regex, options == null ? 0 : BSON.regexFlags(options));
}
@@ -412,7 +415,9 @@ public class Criteria implements CriteriaDefinition {
* @return
*/
public Criteria withinSphere(Circle circle) {
Assert.notNull(circle);
Assert.notNull(circle, "Circle must not be null!");
criteria.put("$geoWithin", new GeoCommand(new Sphere(circle)));
return this;
}
@@ -426,7 +431,8 @@ public class Criteria implements CriteriaDefinition {
*/
public Criteria within(Shape shape) {
Assert.notNull(shape);
Assert.notNull(shape, "Shape must not be null!");
criteria.put("$geoWithin", new GeoCommand(shape));
return this;
}
@@ -439,7 +445,9 @@ public class Criteria implements CriteriaDefinition {
* @return
*/
public Criteria near(Point point) {
Assert.notNull(point);
Assert.notNull(point, "Point must not be null!");
criteria.put("$near", point);
return this;
}
@@ -453,7 +461,9 @@ public class Criteria implements CriteriaDefinition {
* @return
*/
public Criteria nearSphere(Point point) {
Assert.notNull(point);
Assert.notNull(point, "Point must not be null!");
criteria.put("$nearSphere", point);
return this;
}
@@ -707,7 +717,7 @@ public class Criteria implements CriteriaDefinition {
return false;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@@ -770,7 +780,7 @@ public class Criteria implements CriteriaDefinition {
return ObjectUtils.nullSafeEquals(left, right);
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -49,11 +49,11 @@ public final class NearQuery {
/**
* Creates a new {@link NearQuery}.
*
* @param point
* @param point must not be {@literal null}.
*/
private NearQuery(Point point, Metric metric) {
Assert.notNull(point);
Assert.notNull(point, "Point must not be null!");
this.point = point;
this.spherical = false;
@@ -108,7 +108,6 @@ public final class NearQuery {
* @return
*/
public static NearQuery near(Point point, Metric metric) {
Assert.notNull(point);
return new NearQuery(point, metric);
}
@@ -185,7 +184,8 @@ public final class NearQuery {
*/
public NearQuery maxDistance(double maxDistance, Metric metric) {
Assert.notNull(metric);
Assert.notNull(metric, "Metric must not be null!");
return maxDistance(new Distance(maxDistance, metric));
}
@@ -198,7 +198,7 @@ public final class NearQuery {
*/
public NearQuery maxDistance(Distance distance) {
Assert.notNull(distance);
Assert.notNull(distance, "Distance must not be null!");
if (distance.getMetric() != Metrics.NEUTRAL) {
this.spherical(true);
@@ -241,7 +241,8 @@ public final class NearQuery {
*/
public NearQuery minDistance(double minDistance, Metric metric) {
Assert.notNull(metric);
Assert.notNull(metric, "Metric must not be null!");
return minDistance(new Distance(minDistance, metric));
}
@@ -255,7 +256,7 @@ public final class NearQuery {
*/
public NearQuery minDistance(Distance distance) {
Assert.notNull(distance);
Assert.notNull(distance, "Distance must not be null!");
if (distance.getMetric() != Metrics.NEUTRAL) {
this.spherical(true);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,8 @@ import org.springframework.util.Assert;
* A value object for nodes in an expression. Allows iterating ove potentially available child {@link ExpressionNode}s.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public class ExpressionNode implements Iterable<ExpressionNode> {
@@ -157,7 +159,7 @@ public class ExpressionNode implements Iterable<ExpressionNode> {
*/
public ExpressionNode getChild(int index) {
Assert.isTrue(index >= 0);
Assert.isTrue(index >= 0, "Index must be greater or equal to zero!");
return from(node.getChild(index), state);
}
@@ -183,7 +185,7 @@ public class ExpressionNode implements Iterable<ExpressionNode> {
return from(node, state);
}
/*
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.springframework.util.Assert;
* Value object to abstract Ant paths.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
class AntPath {
@@ -38,7 +39,9 @@ class AntPath {
* @param path must not be {@literal null}.
*/
public AntPath(String path) {
Assert.notNull(path);
Assert.notNull(path, "Path must not be null!");
this.path = path;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,6 +45,8 @@ import com.mongodb.gridfs.GridFSInputFile;
* @author Philipp Schneider
* @author Thomas Darimont
* @author Martin Baumgartner
* @author Christoph Strobl
* @author Mark Paluch
*/
public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver {
@@ -72,8 +74,8 @@ public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver
*/
public GridFsTemplate(MongoDbFactory dbFactory, MongoConverter converter, String bucket) {
Assert.notNull(dbFactory);
Assert.notNull(converter);
Assert.notNull(dbFactory, "MongoDbFactory must not be null!");
Assert.notNull(converter, "MongoConverter must not be null!");
this.dbFactory = dbFactory;
this.converter = converter;
@@ -155,7 +157,7 @@ public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver
*/
public GridFSFile store(InputStream content, String filename, String contentType, DBObject metadata) {
Assert.notNull(content);
Assert.notNull(content, "InputStream must not be null!");
GridFSInputFile file = getGridFs().createFile(content);
@@ -182,7 +184,7 @@ public class GridFsTemplate implements GridFsOperations, ResourcePatternResolver
public List<GridFSDBFile> find(Query query) {
if (query == null) {
return getGridFs().find((DBObject) null);
return getGridFs().find(new BasicDBObject());
}
DBObject queryObject = getMappedQuery(query.getQueryObject());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 the original author or authors.
* Copyright 2010-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,7 +55,7 @@ public interface MongoRepository<T, ID extends Serializable>
List<T> findAll(Sort sort);
/**
* Inserts the given a given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use
* Inserts the given entity. Assumes the instance to be new to be able to apply insertion optimizations. Use
* the returned instance for further operations as the save operation might have changed the entity instance
* completely. Prefer using {@link #save(Object)} instead to avoid the usage of store-specific API.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,11 +53,11 @@ public class MongoRepositoryBean<T> extends CdiRepositoryBean<T> {
super(qualifiers, repositoryType, beanManager, detector);
Assert.notNull(operations);
Assert.notNull(operations, "MongoOperations bean must not be null!");
this.operations = operations;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class)
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,21 +19,15 @@ import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.mongodb.config.BeanNames;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
import org.springframework.data.repository.config.RepositoryConfigurationSource;
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
import org.w3c.dom.Element;
@@ -47,8 +41,6 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
private static final String MONGO_TEMPLATE_REF = "mongo-template-ref";
private static final String CREATE_QUERY_INDEXES = "create-query-indexes";
private boolean fallbackMappingContextCreated = false;
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModuleName()
@@ -81,7 +73,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
*/
@Override
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
return Collections.<Class<? extends Annotation>> singleton(Document.class);
return Collections.<Class<? extends Annotation>>singleton(Document.class);
}
/*
@@ -90,19 +82,7 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
*/
@Override
protected Collection<Class<?>> getIdentifyingTypes() {
return Collections.<Class<?>> singleton(MongoRepository.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource)
*/
@Override
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
if (fallbackMappingContextCreated) {
builder.addPropertyReference("mappingContext", BeanNames.MAPPING_CONTEXT_BEAN_NAME);
}
return Collections.<Class<?>>singleton(MongoRepository.class);
}
/*
@@ -130,23 +110,4 @@ public class MongoRepositoryConfigurationExtension extends RepositoryConfigurati
builder.addPropertyReference("mongoOperations", attributes.getString("mongoTemplateRef"));
builder.addPropertyValue("createIndexesForQueryMethods", attributes.getBoolean("createIndexesForQueryMethods"));
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource)
*/
@Override
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) {
super.registerBeansForRoot(registry, configurationSource);
if (!registry.containsBeanDefinition(BeanNames.MAPPING_CONTEXT_BEAN_NAME)) {
RootBeanDefinition definition = new RootBeanDefinition(MongoMappingContext.class);
definition.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE);
definition.setSource(configurationSource.getSource());
registry.registerBeanDefinition(BeanNames.MAPPING_CONTEXT_BEAN_NAME, definition);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,6 +42,7 @@ import com.mongodb.DBRef;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Thomas Darimont
* @author Mark Paluch
*/
public class ConvertingParameterAccessor implements MongoParameterAccessor {
@@ -56,41 +57,41 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
*/
public ConvertingParameterAccessor(MongoWriter<?> writer, MongoParameterAccessor delegate) {
Assert.notNull(writer);
Assert.notNull(delegate);
Assert.notNull(writer, "MongoWriter must not be null!");
Assert.notNull(delegate, "MongoParameterAccessor must not be null!");
this.writer = writer;
this.delegate = delegate;
}
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
public PotentiallyConvertingIterator iterator() {
return new ConvertingIterator(delegate.iterator());
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
*/
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getPageable()
*/
public Pageable getPageable() {
return delegate.getPageable();
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getSort()
*/
* (non-Javadoc)
*
* @see org.springframework.data.repository.query.ParameterAccessor#getSort()
*/
public Sort getSort() {
return delegate.getSort();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#getDynamicProjection()
*/
@@ -107,7 +108,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return getConvertedValue(delegate.getBindableValue(index), null);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getDistanceRange()
*/
@@ -116,7 +117,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return delegate.getDistanceRange();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoParameterAccessor#getGeoNearLocation()
*/
@@ -143,7 +144,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return writer.convertToMongoType(value, typeInformation == null ? null : typeInformation.getActualType());
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ParameterAccessor#hasBindableNullValue()
*/
@@ -185,7 +186,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return delegate.next();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.ConvertingParameterAccessor.PotentiallConvertingIterator#nextConverted()
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,17 @@
*/
package org.springframework.data.mongodb.repository.query;
import java.util.Collections;
import lombok.EqualsAndHashCode;
import lombok.Value;
import lombok.experimental.UtilityClass;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;
@@ -30,15 +39,17 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* {@link ExpressionEvaluatingParameterBinder} allows to evaluate, convert and bind parameters to placholders within a
* {@link ExpressionEvaluatingParameterBinder} allows to evaluate, convert and bind parameters to placeholders within a
* {@link String}.
*
*
* @author Christoph Strobl
* @author Thomas Darimont
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.9
*/
class ExpressionEvaluatingParameterBinder {
@@ -48,7 +59,7 @@ class ExpressionEvaluatingParameterBinder {
/**
* Creates new {@link ExpressionEvaluatingParameterBinder}
*
*
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
*/
@@ -65,7 +76,7 @@ class ExpressionEvaluatingParameterBinder {
/**
* Bind values provided by {@link MongoParameterAccessor} to placeholders in {@literal raw} while considering
* potential conversions and parameter types.
*
*
* @param raw can be {@literal null} or empty.
* @param accessor must not be {@literal null}.
* @param bindingContext must not be {@literal null}.
@@ -82,10 +93,10 @@ class ExpressionEvaluatingParameterBinder {
/**
* Replaced the parameter placeholders with the actual parameter values from the given {@link ParameterBinding}s.
*
*
* @param input must not be {@literal null} or empty.
* @param accessor must not be {@literal null}.
* @param bindings must not be {@literal null}.
* @param bindingContext must not be {@literal null}.
* @return
*/
private String replacePlaceholders(String input, MongoParameterAccessor accessor, BindingContext bindingContext) {
@@ -94,47 +105,88 @@ class ExpressionEvaluatingParameterBinder {
return input;
}
boolean isCompletlyParameterizedQuery = input.matches("^\\?\\d+$");
StringBuilder result = new StringBuilder(input);
for (ParameterBinding binding : bindingContext.getBindings()) {
String parameter = binding.getParameter();
int idx = result.indexOf(parameter);
if (idx == -1) {
continue;
}
String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding);
int start = idx;
int end = idx + parameter.length();
// If the value to bind is an object literal we need to remove the quoting around the expression insertion point.
if (valueForBinding.startsWith("{") && !isCompletlyParameterizedQuery) {
// Is the insertion point actually surrounded by quotes?
char beforeStart = result.charAt(start - 1);
char afterEnd = result.charAt(end);
if ((beforeStart == '\'' || beforeStart == '"') && (afterEnd == '\'' || afterEnd == '"')) {
// Skip preceding and following quote
start -= 1;
end += 1;
}
}
result.replace(start, end, valueForBinding);
if (input.matches("^\\?\\d+$")) {
return getParameterValueForBinding(accessor, bindingContext.getParameters(),
bindingContext.getBindings().iterator().next());
}
return result.toString();
Matcher matcher = createReplacementPattern(bindingContext.getBindings()).matcher(input);
StringBuffer buffer = new StringBuffer();
int parameterIndex = 0;
while (matcher.find()) {
Placeholder placeholder = extractPlaceholder(parameterIndex++, matcher);
ParameterBinding binding = bindingContext.getBindingFor(placeholder);
String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding);
// appendReplacement does not like unescaped $ sign and others, so we need to quote that stuff first
matcher.appendReplacement(buffer, Matcher.quoteReplacement(valueForBinding));
if (StringUtils.hasText(placeholder.getSuffix())) {
buffer.append(placeholder.getSuffix());
}
if (placeholder.isQuoted()) {
postProcessQuotedBinding(buffer, valueForBinding,
!binding.isExpression() ? accessor.getBindableValue(binding.getParameterIndex()) : null,
binding.isExpression());
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
/**
* Sanitize String binding by replacing single quoted values with double quotes which prevents potential single quotes
* contained in replacement to interfere with the Json parsing. Also take care of complex objects by removing the
* quotation entirely.
*
* @param buffer the {@link StringBuffer} to operate upon.
* @param valueForBinding the actual binding value.
* @param raw the raw binding value
* @param isExpression {@literal true} if the binding value results from a SpEL expression.
*/
private void postProcessQuotedBinding(StringBuffer buffer, String valueForBinding, Object raw, boolean isExpression) {
int quotationMarkIndex = buffer.length() - valueForBinding.length() - 1;
char quotationMark = buffer.charAt(quotationMarkIndex);
while (quotationMark != '\'' && quotationMark != '"') {
quotationMarkIndex--;
if (quotationMarkIndex < 0) {
throw new IllegalArgumentException("Could not find opening quotes for quoted parameter");
}
quotationMark = buffer.charAt(quotationMarkIndex);
}
// remove quotation char before the complex object string
if (valueForBinding.startsWith("{") && (raw instanceof DBObject || isExpression)) {
buffer.deleteCharAt(quotationMarkIndex);
} else {
if (isExpression) {
buffer.deleteCharAt(quotationMarkIndex);
return;
}
if (quotationMark == '\'') {
buffer.replace(quotationMarkIndex, quotationMarkIndex + 1, "\"");
}
buffer.append("\"");
}
}
/**
* Returns the serialized value to be used for the given {@link ParameterBinding}.
*
*
* @param accessor must not be {@literal null}.
* @param parameters
* @param binding must not be {@literal null}.
@@ -148,7 +200,12 @@ class ExpressionEvaluatingParameterBinder {
: accessor.getBindableValue(binding.getParameterIndex());
if (value instanceof String && binding.isQuoted()) {
return (String) value;
if (binding.isExpression() && ((String) value).startsWith("{")) {
return (String) value;
}
return binding.isExpression() ? JSON.serialize(value) : QuotedString.unquote(JSON.serialize(value));
}
if (value instanceof byte[]) {
@@ -167,7 +224,7 @@ class ExpressionEvaluatingParameterBinder {
/**
* Evaluates the given {@code expressionString}.
*
*
* @param expressionString must not be {@literal null} or empty.
* @param parameters must not be {@literal null}.
* @param parameterValues must not be {@literal null}.
@@ -181,25 +238,88 @@ class ExpressionEvaluatingParameterBinder {
return expression.getValue(evaluationContext, Object.class);
}
/**
* Creates a replacement {@link Pattern} for all {@link ParameterBinding#getParameter() binding parameters} including
* a potentially trailing quotation mark.
*
* @param bindings
* @return
*/
private Pattern createReplacementPattern(List<ParameterBinding> bindings) {
StringBuilder regex = new StringBuilder();
for (ParameterBinding binding : bindings) {
regex.append("|");
regex.append("(" + Pattern.quote(binding.getParameter()) + ")");
regex.append("([\\w.]*");
regex.append("(\\W?['\"]|\\w*')?)");
}
return Pattern.compile(regex.substring(1));
}
/**
* Extract the placeholder stripping any trailing trailing quotation mark that might have resulted from the
* {@link #createReplacementPattern(List) pattern} used.
*
* @param parameterIndex The actual parameter index.
* @param matcher The actual {@link Matcher}.
* @return
*/
private Placeholder extractPlaceholder(int parameterIndex, Matcher matcher) {
String rawPlaceholder = matcher.group(parameterIndex * 3 + 1);
String suffix = matcher.group(parameterIndex * 3 + 2);
if (!StringUtils.hasText(rawPlaceholder)) {
rawPlaceholder = matcher.group();
if (rawPlaceholder.matches(".*\\d$")) {
suffix = "";
} else {
int index = rawPlaceholder.replaceAll("[^\\?0-9]*$", "").length() - 1;
if (index > 0 && rawPlaceholder.length() > index) {
suffix = rawPlaceholder.substring(index + 1);
}
}
if (QuotedString.endsWithQuote(rawPlaceholder)) {
rawPlaceholder = rawPlaceholder.substring(0,
rawPlaceholder.length() - (StringUtils.hasText(suffix) ? suffix.length() : 1));
}
}
if (StringUtils.hasText(suffix)) {
boolean quoted = QuotedString.endsWithQuote(suffix);
return Placeholder.of(parameterIndex, rawPlaceholder, quoted,
quoted ? QuotedString.unquoteSuffix(suffix) : suffix);
}
return Placeholder.of(parameterIndex, rawPlaceholder, false, null);
}
/**
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.9
*/
static class BindingContext {
final MongoParameters parameters;
final List<ParameterBinding> bindings;
final Map<Placeholder, ParameterBinding> bindings;
/**
* Creates new {@link BindingContext}.
*
*
* @param parameters
* @param bindings
*/
public BindingContext(MongoParameters parameters, List<ParameterBinding> bindings) {
this.parameters = parameters;
this.bindings = bindings;
this.bindings = mapBindings(bindings);
}
/**
@@ -211,21 +331,113 @@ class ExpressionEvaluatingParameterBinder {
/**
* Get unmodifiable list of {@link ParameterBinding}s.
*
*
* @return never {@literal null}.
*/
public List<ParameterBinding> getBindings() {
return Collections.unmodifiableList(bindings);
return new ArrayList<ParameterBinding>(bindings.values());
}
/**
* Get the concrete {@link ParameterBinding} for a given {@literal placeholder}.
*
* @param placeholder must not be {@literal null}.
* @return
* @throws java.util.NoSuchElementException
* @since 1.10
*/
ParameterBinding getBindingFor(Placeholder placeholder) {
if (!bindings.containsKey(placeholder)) {
throw new NoSuchElementException(String.format("Could not to find binding for placeholder '%s'.", placeholder));
}
return bindings.get(placeholder);
}
/**
* Get the associated {@link MongoParameters}.
*
*
* @return
*/
public MongoParameters getParameters() {
return parameters;
}
private static Map<Placeholder, ParameterBinding> mapBindings(List<ParameterBinding> bindings) {
Map<Placeholder, ParameterBinding> map = new LinkedHashMap<Placeholder, ParameterBinding>(bindings.size(), 1);
int parameterIndex = 0;
for (ParameterBinding binding : bindings) {
map.put(Placeholder.of(parameterIndex++, binding.getParameter(), binding.isQuoted(), null), binding);
}
return map;
}
}
/**
* Encapsulates a quoted/unquoted parameter placeholder.
*
* @author Mark Paluch
* @since 1.9
*/
@Value(staticConstructor = "of")
@EqualsAndHashCode(exclude = { "quoted", "suffix" })
static class Placeholder {
private int parameterIndex;
private final String parameter;
private final boolean quoted;
private final String suffix;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return quoted ? String.format("'%s'", parameter + (suffix != null ? suffix : ""))
: parameter + (suffix != null ? suffix : "");
}
}
/**
* Utility to handle quoted strings using single/double quotes.
*
* @author Mark Paluch
*/
@UtilityClass
static class QuotedString {
/**
* @param string
* @return {@literal true} if {@literal string} ends with a single/double quote.
*/
static boolean endsWithQuote(String string) {
return string.endsWith("'") || string.endsWith("\"");
}
/**
* Remove trailing quoting from {@literal quoted}.
*
* @param quoted
* @return {@literal quoted} with removed quotes.
*/
public static String unquoteSuffix(String quoted) {
return quoted.substring(0, quoted.length() - 1);
}
/**
* Remove leading and trailing quoting from {@literal quoted}.
*
* @param quoted
* @return {@literal quoted} with removed quotes.
*/
public static String unquote(String quoted) {
return quoted.substring(1, quoted.length() - 1);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 the original author or authors.
* Copyright 2010-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Custom query creator to create Mongo criterias.
@@ -54,6 +55,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Edward Prentice
*/
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
@@ -91,7 +93,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
super(tree, accessor);
Assert.notNull(context);
Assert.notNull(context, "MappingContext must not be null!");
this.accessor = accessor;
this.isGeoNearQuery = isGeoNearQuery;
@@ -200,7 +202,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
case CONTAINING:
return createContainingCriteria(part, property, criteria, parameters);
case NOT_CONTAINING:
return createContainingCriteria(part, property, criteria, parameters).not();
return createContainingCriteria(part, property, criteria.not(), parameters);
case REGEX:
return criteria.regex(parameters.next().toString());
case EXISTS:
@@ -296,7 +298,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
criteria = criteria.not();
}
return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString());
return addAppropriateLikeRegexTo(criteria, part, parameters.next());
case NEVER:
// intentional no-op
@@ -324,7 +326,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return criteria.in(nextAsArray(parameters));
}
return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString());
return addAppropriateLikeRegexTo(criteria, part, parameters.next());
}
/**
@@ -335,9 +337,15 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
* @param value
* @return the criteria extended with the regex.
*/
private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, String value) {
private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, Object value) {
return criteria.regex(toLikeRegex(value, part), toRegexOptions(part));
if (value == null) {
throw new IllegalArgumentException(String.format(
"Argument for creating $regex pattern for property '%s' must not be null!", part.getProperty().getSegment()));
}
return criteria.regex(toLikeRegex(value.toString(), part), toRegexOptions(part));
}
/**
@@ -367,8 +375,10 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
*/
@SuppressWarnings("unchecked")
private <T> T nextAs(Iterator<Object> iterator, Class<T> type) {
Object parameter = iterator.next();
if (parameter.getClass().isAssignableFrom(type)) {
if (ClassUtils.isAssignable(type, parameter.getClass())) {
return (T) parameter;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.repository.query;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -110,7 +112,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
try {
BasicQuery result = new BasicQuery(query.getQueryObject().toString(), fieldSpec);
BasicQuery result = new BasicQuery(query.getQueryObject(), (DBObject) JSON.parse(fieldSpec));
result.setSortObject(query.getSortObject());
return result;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,10 +38,11 @@ import com.mongodb.util.JSON;
/**
* Query to use a plain JSON String to create the {@link Query} to actually execute.
*
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Thomas Darimont
* @author Mark Paluch
*/
public class StringBasedMongoQuery extends AbstractMongoQuery {
@@ -59,7 +60,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
/**
* Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod} and {@link MongoOperations}.
*
*
* @param method must not be {@literal null}.
* @param mongoOperations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
@@ -112,10 +113,10 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
String queryString = parameterBinder.bind(this.query, accessor, new BindingContext(getQueryMethod()
.getParameters(), queryParameterBindings));
String fieldsString = parameterBinder.bind(this.fieldSpec, accessor, new BindingContext(getQueryMethod()
.getParameters(), fieldSpecParameterBindings));
String queryString = parameterBinder.bind(this.query, accessor,
new BindingContext(getQueryMethod().getParameters(), queryParameterBindings));
String fieldsString = parameterBinder.bind(this.fieldSpec, accessor,
new BindingContext(getQueryMethod().getParameters(), fieldSpecParameterBindings));
Query query = new BasicQuery(queryString, fieldsString).with(accessor.getSort());
@@ -126,7 +127,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
return query;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isCountQuery()
*/
@@ -146,10 +147,10 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
/**
* A parser that extracts the parameter bindings from a given query string.
*
*
* @author Thomas Darimont
*/
private static enum ParameterBindingParser {
private enum ParameterBindingParser {
INSTANCE;
@@ -169,7 +170,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
/**
* Returns a list of {@link ParameterBinding}s found in the given {@code input} or an
* {@link Collections#emptyList()}.
*
*
* @param input can be {@literal null} or empty.
* @param bindings must not be {@literal null}.
* @return
@@ -256,7 +257,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
} else if (value instanceof Pattern) {
String string = ((Pattern) value).toString().trim();
String string = value.toString().trim();
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(string);
while (valueMatcher.find()) {
@@ -264,7 +265,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
/*
* The pattern is used as a direct parameter replacement, e.g. 'field': ?1,
* The pattern is used as a direct parameter replacement, e.g. 'field': ?1,
* therefore we treat it as not quoted to remain backwards compatible.
*/
boolean quoted = !string.equals(PARAMETER_PREFIX + paramIndex);
@@ -297,8 +298,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
while (valueMatcher.find()) {
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
boolean quoted = (source.startsWith("'") && source.endsWith("'"))
|| (source.startsWith("\"") && source.endsWith("\""));
boolean quoted = source.startsWith("'") || source.startsWith("\"");
bindings.add(new ParameterBinding(paramIndex, quoted));
}
@@ -315,7 +315,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
/**
* A generic parameter binding with name or position information.
*
*
* @author Thomas Darimont
*/
static class ParameterBinding {
@@ -326,7 +326,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
/**
* Creates a new {@link ParameterBinding} with the given {@code parameterIndex} and {@code quoted} information.
*
*
* @param parameterIndex
* @param quoted whether or not the parameter is already quoted.
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2013 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ class IndexEnsuringQueryCreationListener implements QueryCreationListener<PartTr
*/
public IndexEnsuringQueryCreationListener(MongoOperations operations) {
Assert.notNull(operations);
Assert.notNull(operations, "MongoOperations must not be null!");
this.operations = operations;
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.repository.support;
import java.io.Serializable;
import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Support class responsible for creating {@link MongoEntityInformation} instances for a given
* {@link MongoPersistentEntity}.
*
* @author Christoph Strobl
* @since 1.10
*/
final class MongoEntityInformationSupport {
private MongoEntityInformationSupport() {}
/**
* Factory method for creating {@link MongoEntityInformation}.
*
* @param entity must not be {@literal null}.
* @param idType can be {@literal null}.
* @return never {@literal null}.
*/
@SuppressWarnings("unchecked")
static <T, ID extends Serializable> MongoEntityInformation<T, ID> entityInformationFor(
MongoPersistentEntity<?> entity, Class<?> idType) {
Assert.notNull(entity, "Entity must not be null!");
MappingMongoEntityInformation<T, ID> entityInformation = new MappingMongoEntityInformation<T, ID>(
(MongoPersistentEntity<T>) entity, (Class<ID>) idType);
return ClassUtils.isAssignable(Persistable.class, entity.getType())
? new PersistableMongoEntityInformation<T, ID>(entityInformation) : entityInformation;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 the original author or authors.
* Copyright 2010-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import static org.springframework.data.querydsl.QueryDslUtils.*;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.data.domain.Persistable;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.MongoOperations;
@@ -42,6 +43,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Factory to create {@link MongoRepository} instances.
@@ -49,6 +51,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class MongoRepositoryFactory extends RepositoryFactorySupport {
@@ -64,7 +67,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
*/
public MongoRepositoryFactory(MongoOperations mongoOperations) {
Assert.notNull(mongoOperations);
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
this.operations = mongoOperations;
this.mappingContext = mongoOperations.getConverter().getMappingContext();
@@ -95,7 +98,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
return getTargetRepositoryViaReflection(information, entityInformation, operations);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
*/
@@ -123,8 +126,8 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
String.format("Could not lookup mapping metadata for domain class %s!", domainClass.getName()));
}
return new MappingMongoEntityInformation<T, ID>((MongoPersistentEntity<T>) entity,
information != null ? (Class<ID>) information.getIdType() : null);
return MongoEntityInformationSupport.<T, ID> entityInformationFor(entity,
information != null ? information.getIdType() : null);
}
/**
@@ -147,7 +150,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
this.mappingContext = mappingContext;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
*/

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.repository.support;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.io.Serializable;
import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
/**
* {@link MongoEntityInformation} implementation wrapping an existing {@link MongoEntityInformation} considering
* {@link Persistable} types by delegating {@link #isNew(Object)} and {@link #getId(Object)} to the corresponding
* {@link Persistable#isNew()} and {@link Persistable#getId()} implementations.
*
* @author Christoph Strobl
* @author Oliver Gierke
* @since 1.10
*/
@RequiredArgsConstructor
class PersistableMongoEntityInformation<T, ID extends Serializable> implements MongoEntityInformation<T, ID> {
private final @NonNull MongoEntityInformation<T, ID> delegate;
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getCollectionName()
*/
@Override
public String getCollectionName() {
return delegate.getCollectionName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoEntityInformation#getIdAttribute()
*/
@Override
public String getIdAttribute() {
return delegate.getIdAttribute();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.EntityInformation#isNew(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public boolean isNew(T t) {
if (t instanceof Persistable) {
return ((Persistable<ID>) t).isNew();
}
return delegate.isNew(t);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public ID getId(T t) {
if (t instanceof Persistable) {
return (ID) ((Persistable<ID>) t).getId();
}
return delegate.getId(t);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.PersistentEntityInformation#getIdType()
*/
@Override
public Class<ID> getIdType() {
return delegate.getIdType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.EntityMetadata#getJavaType()
*/
@Override
public Class<T> getJavaType() {
return delegate.getJavaType();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,7 +78,8 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
super(entityInformation, mongoOperations);
Assert.notNull(resolver);
Assert.notNull(resolver, "EntityPathResolver must not be null!");
EntityPath<T> path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
@@ -122,7 +123,7 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
return applySorting(createQueryFor(predicate), sort).fetchResults().getResults();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.OrderSpecifier[])
*/
@@ -177,7 +178,7 @@ public class QueryDslMongoRepository<T, ID extends Serializable> extends SimpleM
return createQueryFor(predicate).fetchCount();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#exists(com.mysema.query.types.Predicate)
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import com.querydsl.mongodb.AbstractMongodbQuery;
* Base class to create repository implementations based on Querydsl.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
public abstract class QuerydslRepositorySupport {
@@ -40,7 +41,7 @@ public abstract class QuerydslRepositorySupport {
*/
public QuerydslRepositorySupport(MongoOperations operations) {
Assert.notNull(operations);
Assert.notNull(operations, "MongoOperations must not be null!");
this.template = operations;
this.context = operations.getConverter().getMappingContext();
@@ -54,7 +55,9 @@ public abstract class QuerydslRepositorySupport {
* @return
*/
protected <T> AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> from(final EntityPath<T> path) {
Assert.notNull(path);
Assert.notNull(path, "EntityPath must not be null!");
MongoPersistentEntity<?> entity = context.getPersistentEntity(path.getType());
return from(path, entity.getCollection());
}
@@ -68,8 +71,8 @@ public abstract class QuerydslRepositorySupport {
*/
protected <T> AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> from(final EntityPath<T> path, String collection) {
Assert.notNull(path);
Assert.hasText(collection);
Assert.notNull(path, "EntityPath must not be null!");
Assert.hasText(collection, "Collection name must not be null or empty!");
return new SpringDataMongodbQuery<T>(template, path.getType(), collection);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 the original author or authors.
* Copyright 2010-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,8 +59,8 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
*/
public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
Assert.notNull(mongoOperations);
Assert.notNull(metadata);
Assert.notNull(metadata, "MongoEntityInformation must not be null!");
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
this.entityInformation = metadata;
this.mongoOperations = mongoOperations;
@@ -195,7 +195,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
return findAll(new Query());
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
*/
@@ -229,7 +229,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
return findAll(new Query().with(sort));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoRepository#insert(java.lang.Object)
*/
@@ -242,7 +242,7 @@ public class SimpleMongoRepository<T, ID extends Serializable> implements MongoR
return entity;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.repository.MongoRepository#insert(java.lang.Iterable)
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import com.querydsl.mongodb.AbstractMongodbQuery;
*
* @author Oliver Gierke
*/
class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
public class SpringDataMongodbQuery<T> extends AbstractMongodbQuery<T, SpringDataMongodbQuery<T>> {
private final MongoOperations operations;

View File

@@ -26,9 +26,11 @@ import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.querydsl.core.types.Constant;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathType;
@@ -72,6 +74,20 @@ class SpringDataMongodbSerializer extends MongodbSerializer {
this.mapper = new QueryMapper(converter);
}
/*
* (non-Javadoc)
* @see com.querydsl.mongodb.MongodbSerializer#visit(com.querydsl.core.types.Constant, java.lang.Void)
*/
@Override
public Object visit(Constant<?> expr, Void context) {
if (!ClassUtils.isAssignable(Enum.class, expr.getType())) {
return super.visit(expr, context);
}
return converter.convertToMongoType(expr.getConstant());
}
/*
* (non-Javadoc)
* @see com.querydsl.mongodb.MongodbSerializer#getKeyForPath(com.querydsl.core.types.Path, com.querydsl.core.types.PathMetadata)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,16 +28,28 @@ public class MongoClientVersion {
private static final boolean IS_MONGO_30 = ClassUtils.isPresent("com.mongodb.binding.SingleServerBinding",
MongoClientVersion.class.getClassLoader());
private static final boolean IS_MONGO_34 = ClassUtils.isPresent("org.bson.types.Decimal128",
MongoClientVersion.class.getClassLoader());
private static final boolean IS_ASYNC_CLIENT = ClassUtils.isPresent("com.mongodb.async.client.MongoClient",
MongoClientVersion.class.getClassLoader());
/**
* @return |literal true} if MongoDB Java driver version 3.0 or later is on classpath.
* @return {@literal true} if MongoDB Java driver version 3.0 or later is on classpath.
*/
public static boolean isMongo3Driver() {
return IS_MONGO_30;
}
/**
* @return {@literal true} if MongoDB Java driver version 3.4 or later is on classpath.
* @since 1.10
*/
public static boolean isMongo34Driver() {
return IS_MONGO_34;
}
/**
* @return {lliteral true} if MongoDB Java driver is on classpath.
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,8 +19,6 @@ import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.net.UnknownHostException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,8 +28,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.mongodb.core.AuditablePerson;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.stereotype.Repository;
@@ -123,22 +119,20 @@ public class AuditingViaJavaConfigRepositoriesTests {
@Configuration
@EnableMongoRepositories
@EnableMongoAuditing
static class SimpleConfigWithRepositories {
@Bean
public MongoTemplate mongoTemplate() throws UnknownHostException {
return new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "database"));
}
}
static class SimpleConfigWithRepositories extends SimpleConfig {}
@Configuration
@EnableMongoAuditing
static class SimpleConfig {
static class SimpleConfig extends AbstractMongoConfiguration {
@Bean
public MongoTemplate mongoTemplate() throws UnknownHostException {
return new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "database"));
@Override
public Mongo mongo() throws Exception {
return new MongoClient();
}
@Override
protected String getDatabaseName() {
return "database";
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2012 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import org.junit.Ignore;
@@ -37,6 +38,13 @@ import com.mongodb.CommandResult;
import com.mongodb.Mongo;
import com.mongodb.ServerAddress;
/**
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MongoNamespaceReplicaSetTests {
@@ -70,10 +78,13 @@ public class MongoNamespaceReplicaSetTests {
assertThat(replicaSetSeeds, is(notNullValue()));
assertThat(replicaSetSeeds, hasSize(3));
assertThat(
replicaSetSeeds,
hasItems(new ServerAddress("192.168.174.130", 27017), new ServerAddress("192.168.174.130", 27018),
new ServerAddress("192.168.174.130", 27019)));
List<Integer> ports = new ArrayList<Integer>();
for (ServerAddress replicaSetSeed : replicaSetSeeds) {
ports.add(replicaSetSeed.getPort());
}
assertThat(ports, hasItems(27017, 27018, 27019));
}
@Test

View File

@@ -46,6 +46,7 @@ import com.mongodb.WriteConcern;
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -270,6 +271,25 @@ public class DefaultBulkOperationsIntegrationTests {
assertThat(result.getRemovedCount(), is(1));
}
/**
* @see DATAMONGO-1534
*/
@Test
public void insertShouldConsiderInheritance() {
SpecialDoc specialDoc = new SpecialDoc();
specialDoc.id = "id-special";
specialDoc.value = "normal-value";
specialDoc.specialValue = "special-value";
createBulkOps(BulkMode.ORDERED).insert(Arrays.asList(specialDoc)).execute();
BaseDoc doc = operations.findOne(where("_id", specialDoc.id), BaseDoc.class, COLLECTION_NAME);
assertThat(doc, notNullValue());
assertThat(doc, instanceOf(SpecialDoc.class));
}
private void testUpdate(BulkMode mode, boolean multi, int expectedUpdates) {
BulkOperations bulkOps = createBulkOps(mode);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,18 +17,28 @@ package org.springframework.data.mongodb.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.data.mongodb.core.ReflectiveDBCollectionInvoker.*;
import static org.springframework.data.mongodb.core.index.PartialIndexFilter.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.util.Version;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
@@ -42,6 +52,9 @@ import com.mongodb.DBObject;
@ContextConfiguration("classpath:infrastructure.xml")
public class DefaultIndexOperationsIntegrationTests {
private static final Version THREE_DOT_TWO = new Version(3, 2);
private static Version mongoVersion;
static final DBObject GEO_SPHERE_2D = new BasicDBObject("loaction", "2dsphere");
@Autowired MongoTemplate template;
@@ -51,6 +64,7 @@ public class DefaultIndexOperationsIntegrationTests {
@Before
public void setUp() {
queryMongoVersionIfNecessary();
String collectionName = this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class);
this.collection = this.template.getDb().getCollection(collectionName);
@@ -59,6 +73,14 @@ public class DefaultIndexOperationsIntegrationTests {
this.indexOps = new DefaultIndexOperations(template, collectionName);
}
private void queryMongoVersionIfNecessary() {
if (mongoVersion == null) {
CommandResult result = template.executeCommand("{ buildInfo: 1 }");
mongoVersion = Version.parse(result.get("version").toString());
}
}
/**
* @see DATAMONGO-1008
*/
@@ -71,6 +93,78 @@ public class DefaultIndexOperationsIntegrationTests {
assertThat(info.getIndexFields().get(0).isGeo(), is(true));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldApplyPartialFilterCorrectly() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-criteria").on("k3y", Direction.ASC)
.partial(of(where("q-t-y").gte(10)));
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-criteria");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"q-t-y\" : { \"$gte\" : 10}}")));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldApplyPartialFilterWithMappedPropertyCorrectly() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-mapped-criteria").on("k3y", Direction.ASC)
.partial(of(where("quantity").gte(10)));
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-mapped-criteria");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"qty\" : { \"$gte\" : 10}}")));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldApplyPartialDBOFilterCorrectly() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-dbo").on("k3y", Direction.ASC)
.partial(of(new BasicDBObject("qty", new BasicDBObject("$gte", 10))));
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-dbo");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"qty\" : { \"$gte\" : 10}}")));
}
/**
* @see DATAMONGO-1467
*/
@Test
public void shouldFavorExplicitMappingHintViaClass() {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO), is(true));
IndexDefinition id = new Index().named("partial-with-inheritance").on("k3y", Direction.ASC)
.partial(of(where("age").gte(10)));
indexOps = new DefaultIndexOperations(template,
this.template.getCollectionName(DefaultIndexOperationsIntegrationTestsSample.class),
MappingToSameCollection.class);
indexOps.ensureIndex(id);
IndexInfo info = findAndReturnIndexInfo(indexOps.getIndexInfo(), "partial-with-inheritance");
assertThat(info.getPartialFilterExpression(), is(equalTo("{ \"a_g_e\" : { \"$gte\" : 10}}")));
}
private IndexInfo findAndReturnIndexInfo(DBObject keys) {
return findAndReturnIndexInfo(indexOps.getIndexInfo(), keys);
}
@@ -89,5 +183,15 @@ public class DefaultIndexOperationsIntegrationTests {
throw new AssertionError(String.format("Index with %s was not found", name));
}
static class DefaultIndexOperationsIntegrationTestsSample {}
@Document(collection = "default-index-operations-tests")
static class DefaultIndexOperationsIntegrationTestsSample {
@Field("qty") Integer quantity;
}
@Document(collection = "default-index-operations-tests")
static class MappingToSameCollection extends DefaultIndexOperationsIntegrationTestsSample {
@Field("a_g_e") Integer age;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -190,4 +190,12 @@ public class DefaultScriptOperationsTests {
public void scriptNamesShouldReturnEmptySetWhenNoScriptRegistered() {
assertThat(scriptOps.getScriptNames(), is(empty()));
}
/**
* @see DATAMONGO-1465
*/
@Test
public void executeShouldNotQuoteStrings() {
assertThat(scriptOps.execute(EXECUTABLE_SCRIPT, "spring-data"), is((Object) "spring-data"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,10 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.core.query.Update.*;
import lombok.Data;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,6 +37,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
@@ -43,6 +48,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
@@ -70,13 +76,19 @@ import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.util.MongoClientVersion;
import org.springframework.data.util.CloseableIterator;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -102,6 +114,7 @@ import com.mongodb.WriteResult;
* @author Thomas Darimont
* @author Komi Innocent
* @author Christoph Strobl
* @author Laszlo Csontos
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@@ -111,27 +124,39 @@ public class MongoTemplateTests {
.parse("2.4");
private static final org.springframework.data.util.Version TWO_DOT_EIGHT = org.springframework.data.util.Version
.parse("2.8");
private static final org.springframework.data.util.Version THREE_DOT_FOUR = org.springframework.data.util.Version
.parse("3.4");
@Autowired MongoTemplate template;
@Autowired MongoDbFactory factory;
ConfigurableApplicationContext context;
MongoTemplate mappingTemplate;
org.springframework.data.util.Version mongoVersion;
@Rule public ExpectedException thrown = ExpectedException.none();
@Autowired
public void setApplicationContext(ConfigurableApplicationContext context) {
this.context = context;
context.addApplicationListener(new PersonWithIdPropertyOfTypeUUIDListener());
}
@Autowired
public void setMongo(Mongo mongo) throws Exception {
CustomConversions conversions = new CustomConversions(Arrays.asList(DateToDateTimeConverter.INSTANCE,
DateTimeToDateConverter.INSTANCE));
CustomConversions conversions = new CustomConversions(
Arrays.asList(DateToDateTimeConverter.INSTANCE, DateTimeToDateConverter.INSTANCE));
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setInitialEntitySet(new HashSet<Class<?>>(Arrays.asList(PersonWith_idPropertyOfTypeObjectId.class,
PersonWith_idPropertyOfTypeString.class, PersonWithIdPropertyOfTypeObjectId.class,
PersonWithIdPropertyOfTypeString.class, PersonWithIdPropertyOfTypeInteger.class,
PersonWithIdPropertyOfTypeBigInteger.class, PersonWithIdPropertyOfPrimitiveInt.class,
PersonWithIdPropertyOfTypeLong.class, PersonWithIdPropertyOfPrimitiveLong.class)));
mappingContext.setInitialEntitySet(new HashSet<Class<?>>(
Arrays.asList(PersonWith_idPropertyOfTypeObjectId.class, PersonWith_idPropertyOfTypeString.class,
PersonWithIdPropertyOfTypeObjectId.class, PersonWithIdPropertyOfTypeString.class,
PersonWithIdPropertyOfTypeInteger.class, PersonWithIdPropertyOfTypeBigInteger.class,
PersonWithIdPropertyOfPrimitiveInt.class, PersonWithIdPropertyOfTypeLong.class,
PersonWithIdPropertyOfPrimitiveLong.class, PersonWithIdPropertyOfTypeUUID.class)));
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
mappingContext.initialize();
@@ -145,8 +170,11 @@ public class MongoTemplateTests {
@Before
public void setUp() {
cleanDb();
queryMongoVersionIfNecessary();
this.mappingTemplate.setApplicationContext(context);
}
@After
@@ -174,6 +202,7 @@ public class MongoTemplateTests {
template.dropCollection(PersonWithIdPropertyOfPrimitiveInt.class);
template.dropCollection(PersonWithIdPropertyOfTypeLong.class);
template.dropCollection(PersonWithIdPropertyOfPrimitiveLong.class);
template.dropCollection(PersonWithIdPropertyOfTypeUUID.class);
template.dropCollection(PersonWithVersionPropertyOfTypeInteger.class);
template.dropCollection(TestClass.class);
template.dropCollection(Sample.class);
@@ -629,6 +658,22 @@ public class MongoTemplateTests {
assertThat(p12q, notNullValue());
assertThat(p12q.getId(), is(p12.getId()));
checkCollectionContents(PersonWithIdPropertyOfPrimitiveLong.class, 1);
// DATAMONGO-1617
// UUID id - provided
PersonWithIdPropertyOfTypeUUID p13 = new PersonWithIdPropertyOfTypeUUID();
p13.setFirstName("Sven_10");
p13.setAge(22);
// insert
mongoTemplate.insert(p13);
// also try save
mongoTemplate.save(p13);
assertThat(p13.getId(), notNullValue());
PersonWithIdPropertyOfTypeUUID p13q = mongoTemplate.findOne(new Query(where("id").in(p13.getId())),
PersonWithIdPropertyOfTypeUUID.class);
assertThat(p13q, notNullValue());
assertThat(p13q.getId(), is(p13.getId()));
checkCollectionContents(PersonWithIdPropertyOfTypeUUID.class, 1);
}
private void checkCollectionContents(Class<?> entityClass, int count) {
@@ -1483,6 +1528,17 @@ public class MongoTemplateTests {
template.insert(dbObject, template.determineCollectionName(PersonWithVersionPropertyOfTypeInteger.class));
}
@Test // DATAMONGO-1617
public void doesNotFailOnInsertForEntityWithNonAutogeneratableId() {
PersonWithIdPropertyOfTypeUUID person = new PersonWithIdPropertyOfTypeUUID();
person.setFirstName("Laszlo");
person.setAge(33);
template.insert(person);
assertThat(person.getId(), is(notNullValue()));
}
/**
* @see DATAMONGO-539
*/
@@ -2571,7 +2627,7 @@ public class MongoTemplateTests {
doc.dbRefAnnotatedList = Arrays.asList( //
sample1, //
sample2 //
);
);
template.save(doc);
Update update = new Update().pull("dbRefAnnotatedList", doc.dbRefAnnotatedList.get(1));
@@ -2603,7 +2659,7 @@ public class MongoTemplateTests {
doc.dbRefAnnotatedList = Arrays.asList( //
sample1, //
sample2 //
);
);
template.save(doc);
Update update = new Update().pull("dbRefAnnotatedList.id", "2");
@@ -2677,8 +2733,8 @@ public class MongoTemplateTests {
@Test
public void testUpdateShouldWorkForPathsOnInterfaceMethods() {
DocumentWithCollection document = new DocumentWithCollection(Arrays.<Model> asList(new ModelA("spring"),
new ModelA("data")));
DocumentWithCollection document = new DocumentWithCollection(
Arrays.<Model> asList(new ModelA("spring"), new ModelA("data")));
template.save(document);
@@ -3164,6 +3220,62 @@ public class MongoTemplateTests {
assertThat(template.findOne(query(where("id").is(wgj.id)), WithGeoJson.class).point, is(equalTo(wgj.point)));
}
/**
* @see DATAMONGO-1513
*/
@Test
@DirtiesContext
public void populatesIdsAddedByEventListener() {
context.addApplicationListener(new AbstractMongoEventListener<Document>() {
@Override
public void onBeforeSave(BeforeSaveEvent<Document> event) {
event.getDBObject().put("_id", UUID.randomUUID().toString());
}
});
Document document = new Document();
template.insertAll(Arrays.asList(document));
assertThat(document.id, is(notNullValue()));
}
/**
* @see DATAMONGO-1517
*/
@Test
public void decimal128TypeShouldBeSavedAndLoadedCorrectly()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
assumeThat(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR), is(true));
assumeThat(MongoClientVersion.isMongo34Driver(), is(true));
Class<?> decimal128Type = ClassUtils.resolveClassName("org.bson.types.Decimal128", null);
WithObjectTypeProperty source = new WithObjectTypeProperty();
source.id = "decimal128-property-value";
source.value = decimal128Type.getConstructor(BigDecimal.class).newInstance(new BigDecimal(100));
template.save(source);
WithObjectTypeProperty loaded = template.findOne(query(where("id").is(source.id)), WithObjectTypeProperty.class);
assertThat(loaded.getValue(), instanceOf(decimal128Type));
}
static class TypeWithNumbers {
@Id String id;
Integer intVal;
Float floatVal;
Long longVal;
Double doubleVal;
BigDecimal bigDeciamVal;
BigInteger bigIntegerVal;
Byte byteVal;
}
static class DoucmentWithNamedIdField {
@Id String someIdKey;
@@ -3215,11 +3327,11 @@ public class MongoTemplateTests {
@Id public String id;
@Field("db_ref_list")/** @see DATAMONGO-1058 */
@org.springframework.data.mongodb.core.mapping.DBRef//
@Field("db_ref_list") /** @see DATAMONGO-1058 */
@org.springframework.data.mongodb.core.mapping.DBRef //
public List<Sample> dbRefAnnotatedList;
@org.springframework.data.mongodb.core.mapping.DBRef//
@org.springframework.data.mongodb.core.mapping.DBRef //
public Sample dbRefProperty;
}
@@ -3514,4 +3626,26 @@ public class MongoTemplateTests {
GeoJsonPoint point;
}
@Data
static class WithObjectTypeProperty {
@Id String id;
Object value;
}
static class PersonWithIdPropertyOfTypeUUIDListener
extends AbstractMongoEventListener<PersonWithIdPropertyOfTypeUUID> {
@Override
public void onBeforeConvert(BeforeConvertEvent<PersonWithIdPropertyOfTypeUUID> event) {
PersonWithIdPropertyOfTypeUUID person = event.getSource();
if (person.getId() != null) {
return;
}
person.setId(UUID.randomUUID());
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import lombok.Data;
import java.util.UUID;
@Data
public class PersonWithIdPropertyOfTypeUUID {
private UUID id;
private String firstName;
private int age;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import java.util.ArrayList;
import java.util.List;
@@ -32,6 +33,7 @@ import org.springframework.data.domain.Sort.Direction;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
/**
* Unit tests for {@link Aggregation}.
@@ -204,6 +206,47 @@ public class AggregationUnitTests {
assertThat(id.get("ruleType"), is((Object) "$rules.ruleType"));
}
/**
* @see DATAMONGO-1585
*/
@Test
public void shouldSupportSortingBySyntheticAndExposedGroupFields() {
DBObject agg = newAggregation( //
group("cmsParameterId").addToSet("title").as("titles"), //
sort(Direction.ASC, "cmsParameterId", "titles") //
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
assertThat(agg, is(notNullValue()));
DBObject sort = ((List<DBObject>) agg.get("pipeline")).get(1);
assertThat(getAsDBObject(sort, "$sort"), is(JSON.parse("{ \"_id.cmsParameterId\" : 1 , \"titles\" : 1}")));
}
/**
* @see DATAMONGO-1585
*/
@Test
public void shouldSupportSortingByProjectedFields() {
DBObject agg = newAggregation( //
project("cmsParameterId") //
.and(SystemVariable.CURRENT + ".titles").as("titles") //
.and("field").as("alias"), //
sort(Direction.ASC, "cmsParameterId", "titles", "alias") //
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
assertThat(agg, is(notNullValue()));
DBObject sort = ((List<DBObject>) agg.get("pipeline")).get(1);
assertThat(getAsDBObject(sort, "$sort"),
isBsonObject().containing("cmsParameterId", 1) //
.containing("titles", 1) //
.containing("alias", 1));
}
/**
* @see DATAMONGO-924
*/
@@ -248,19 +291,20 @@ public class AggregationUnitTests {
DBObject agg = newAggregation( //
project().and("a").as("aa") //
) //
.withOptions(aggregationOptions) //
.withOptions(aggregationOptions) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
assertThat(agg.toString(), is("{ \"aggregate\" : \"foo\" , " //
+ "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " //
+ "\"allowDiskUse\" : true , " //
+ "\"explain\" : true , " //
+ "\"cursor\" : { \"foo\" : 1}}" //
));
assertThat(agg.toString(),
is("{ \"aggregate\" : \"foo\" , " //
+ "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " //
+ "\"allowDiskUse\" : true , " //
+ "\"explain\" : true , " //
+ "\"cursor\" : { \"foo\" : 1}}" //
));
}
/**
* @see DATAMONGO-954
* @see DATAMONGO-954, DATAMONGO-1585
*/
@Test
public void shouldSupportReferencingSystemVariables() {
@@ -269,16 +313,16 @@ public class AggregationUnitTests {
project("someKey") //
.and("a").as("a1") //
.and(Aggregation.CURRENT + ".a").as("a2") //
, sort(Direction.DESC, "a") //
, sort(Direction.DESC, "a1") //
, group("someKey").first(Aggregation.ROOT).as("doc") //
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject projection0 = extractPipelineElement(agg, 0, "$project");
assertThat(projection0, is((DBObject) new BasicDBObject("someKey", 1).append("a1", "$a")
.append("a2", "$$CURRENT.a")));
assertThat(projection0,
is((DBObject) new BasicDBObject("someKey", 1).append("a1", "$a").append("a2", "$$CURRENT.a")));
DBObject sort = extractPipelineElement(agg, 1, "$sort");
assertThat(sort, is((DBObject) new BasicDBObject("a", -1)));
assertThat(sort, is((DBObject) new BasicDBObject("a1", -1)));
DBObject group = extractPipelineElement(agg, 2, "$group");
assertThat(group,
@@ -296,7 +340,7 @@ public class AggregationUnitTests {
.and("tags").minus(10).as("tags_count")//
, group("date")//
.sum("tags_count").as("count")//
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject group = extractPipelineElement(agg, 1, "$group");
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
@@ -313,7 +357,7 @@ public class AggregationUnitTests {
.andExpression("tags-10")//
, group("date")//
.sum("tags_count").as("count")//
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject group = extractPipelineElement(agg, 1, "$group");
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,11 +46,17 @@ public class ProjectionOperationUnitTests {
static final String DIVIDE = "$divide";
static final String PROJECT = "$project";
/**
* @see DATAMONGO-586
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullFields() {
new ProjectionOperation(null);
}
/**
* @see DATAMONGO-586
*/
@Test
public void declaresBackReferenceCorrectly() {
@@ -62,6 +68,9 @@ public class ProjectionOperationUnitTests {
assertThat(projectClause.get("prop"), is((Object) Fields.UNDERSCORE_ID_REF));
}
/**
* @see DATAMONGO-586
*/
@Test
public void alwaysUsesExplicitReference() {
@@ -74,6 +83,9 @@ public class ProjectionOperationUnitTests {
assertThat(projectClause.get("bar"), is((Object) "$foobar"));
}
/**
* @see DATAMONGO-586
*/
@Test
public void aliasesSimpleFieldProjection() {
@@ -85,6 +97,9 @@ public class ProjectionOperationUnitTests {
assertThat(projectClause.get("bar"), is((Object) "$foo"));
}
/**
* @see DATAMONGO-586
*/
@Test
public void aliasesArithmeticProjection() {
@@ -100,6 +115,10 @@ public class ProjectionOperationUnitTests {
assertThat(addClause.get(1), is((Object) 41));
}
/**
* @see DATAMONGO-586
*/
@Test
public void arithmenticProjectionOperationWithoutAlias() {
String fieldName = "a";
@@ -112,6 +131,9 @@ public class ProjectionOperationUnitTests {
assertThat(oper.get(ADD), is((Object) Arrays.<Object> asList("$a", 1)));
}
/**
* @see DATAMONGO-586
*/
@Test
public void arithmenticProjectionOperationPlus() {
@@ -126,6 +148,9 @@ public class ProjectionOperationUnitTests {
assertThat(oper.get(ADD), is((Object) Arrays.<Object> asList("$a", 1)));
}
/**
* @see DATAMONGO-586
*/
@Test
public void arithmenticProjectionOperationMinus() {
@@ -140,6 +165,9 @@ public class ProjectionOperationUnitTests {
assertThat(oper.get(SUBTRACT), is((Object) Arrays.<Object> asList("$a", 1)));
}
/**
* @see DATAMONGO-586
*/
@Test
public void arithmenticProjectionOperationMultiply() {
@@ -154,6 +182,9 @@ public class ProjectionOperationUnitTests {
assertThat(oper.get(MULTIPLY), is((Object) Arrays.<Object> asList("$a", 1)));
}
/**
* @see DATAMONGO-586
*/
@Test
public void arithmenticProjectionOperationDivide() {
@@ -168,12 +199,18 @@ public class ProjectionOperationUnitTests {
assertThat(oper.get(DIVIDE), is((Object) Arrays.<Object> asList("$a", 1)));
}
/**
* @see DATAMONGO-586
*/
@Test(expected = IllegalArgumentException.class)
public void arithmenticProjectionOperationDivideByZeroException() {
new ProjectionOperation().and("a").divide(0);
}
/**
* @see DATAMONGO-586
*/
@Test
public void arithmenticProjectionOperationMod() {
@@ -273,9 +310,8 @@ public class ProjectionOperationUnitTests {
.and("foo").as("bar"); //
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
assertThat(
dbObject.toString(),
is("{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"));
assertThat(dbObject.toString(), is(
"{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"));
}
/**
@@ -330,10 +366,8 @@ public class ProjectionOperationUnitTests {
assertThat(dbObject, is(notNullValue()));
DBObject projected = exctractOperation("$project", dbObject);
assertThat(
projected.get("dayOfYearPlus1Day"),
is((Object) new BasicDBObject("$dayOfYear", Arrays.asList(new BasicDBObject("$add", Arrays.<Object> asList(
"$date", 86400000))))));
assertThat(projected.get("dayOfYearPlus1Day"), is((Object) new BasicDBObject("$dayOfYear",
Arrays.asList(new BasicDBObject("$add", Arrays.<Object> asList("$date", 86400000))))));
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,9 @@ package org.springframework.data.mongodb.core.aggregation;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import java.util.Arrays;
import java.util.List;
@@ -171,6 +173,44 @@ public class TypeBasedAggregationOperationContextUnitTests {
assertThat(dbo.get("cursor"), is((Object) new BasicDBObject("foo", 1)));
}
/**
* @see DATAMONGO-1585
*/
@Test
public void rendersSortOfProjectedFieldCorrectly() {
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
TypedAggregation<MeterData> agg = newAggregation(MeterData.class, project().and("counterName").as("counter"), //
sort(Direction.ASC, "counter"));
DBObject dbo = agg.toDbObject("meterData", context);
DBObject sort = getPipelineElementFromAggregationAt(dbo, 1);
DBObject definition = (DBObject) sort.get("$sort");
assertThat(definition.get("counter"), is(equalTo((Object) 1)));
}
/**
* @see DATAMONGO-1586
*/
@Test
public void rendersFieldAliasingProjectionCorrectly() {
AggregationOperationContext context = getContext(FooPerson.class);
TypedAggregation<FooPerson> agg = newAggregation(FooPerson.class,
project() //
.and("name").as("person_name") //
.and("age.value").as("age"));
DBObject dbo = agg.toDbObject("person", context);
DBObject projection = getPipelineElementFromAggregationAt(dbo, 0);
assertThat(getAsDBObject(projection, "$project"),
isBsonObject() //
.containing("person_name", "$name") //
.containing("age", "$age.value"));
}
/**
* @see DATAMONGO-1133
*/
@@ -190,14 +230,15 @@ public class TypeBasedAggregationOperationContextUnitTests {
}
/**
* @see DATAMONGO-1326
* @see DATAMONGO-1326, DATAMONGO-1585
*/
@Test
public void lookupShouldInheritFieldsFromInheritingAggregationOperation() {
TypeBasedAggregationOperationContext context = getContext(MeterData.class);
TypedAggregation<MeterData> agg = newAggregation(MeterData.class,
lookup("OtherCollection", "resourceId", "otherId", "lookup"), sort(Direction.ASC, "resourceId"));
lookup("OtherCollection", "resourceId", "otherId", "lookup"), //
sort(Direction.ASC, "resourceId", "counterName"));
DBObject dbo = agg.toDbObject("meterData", context);
DBObject sort = getPipelineElementFromAggregationAt(dbo, 1);
@@ -205,6 +246,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
DBObject definition = (DBObject) sort.get("$sort");
assertThat(definition.get("resourceId"), is(equalTo((Object) 1)));
assertThat(definition.get("counter_name"), is(equalTo((Object) 1)));
}
/**

View File

@@ -101,6 +101,20 @@ public class DBObjectAccessorUnitTests {
assertThat(nestedA.get("c"), is((Object) "c"));
}
/**
* @see DATAMONGO-1471
*/
@Test
public void exposesAvailabilityOfFields() {
DBObjectAccessor accessor = new DBObjectAccessor(new BasicDBObject("a", new BasicDBObject("c", "d")));
MongoPersistentEntity<?> entity = context.getPersistentEntity(ProjectingType.class);
assertThat(accessor.hasValue(entity.getPersistentProperty("foo")), is(false));
assertThat(accessor.hasValue(entity.getPersistentProperty("a")), is(true));
assertThat(accessor.hasValue(entity.getPersistentProperty("name")), is(false));
}
static class ProjectingType {
String name;

View File

@@ -41,6 +41,7 @@ import org.springframework.data.mongodb.core.convert.GeoConverters.SphereToDbObj
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.query.GeoCommand;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
@@ -48,6 +49,7 @@ import com.mongodb.DBObject;
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.5
*/
public class GeoConvertersUnitTests {
@@ -177,4 +179,32 @@ public class GeoConvertersUnitTests {
assertThat(boxObject,
is((Object) Arrays.asList(GeoConverters.toList(box.getFirst()), GeoConverters.toList(box.getSecond()))));
}
@Test // DATAMONGO-1607
public void convertsPointCorrectlyWhenUsingNonDoubleForCoordinates() {
assertThat(DbObjectToPointConverter.INSTANCE.convert(new BasicDBObject().append("x", 1L).append("y", 2L)),
is(new Point(1, 2)));
}
@Test // DATAMONGO-1607
public void convertsCircleCorrectlyWhenUsingNonDoubleForCoordinates() {
DBObject circle = new BasicDBObject();
circle.put("center", new BasicDBObject().append("x", 1).append("y", 2));
circle.put("radius", 3L);
assertThat(DbObjectToCircleConverter.INSTANCE.convert(circle), is(new Circle(new Point(1, 2), new Distance(3))));
}
@Test // DATAMONGO-1607
public void convertsSphereCorrectlyWhenUsingNonDoubleForCoordinates() {
DBObject sphere = new BasicDBObject();
sphere.put("center", new BasicDBObject().append("x", 1).append("y", 2));
sphere.put("radius", 3L);
assertThat(DbObjectToSphereConverter.INSTANCE.convert(sphere), is(new Sphere(new Point(1, 2), new Distance(3))));
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsEqual.*;
import static org.mockito.Mockito.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.LazyLoadingException;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import com.mongodb.DBRef;
/**
* Unit tests for {@link LazyLoadingInterceptor}.
*
* @author Christoph Strobl
*/
@RunWith(MockitoJUnitRunner.class)
public class LazyLoadingInterceptorUnitTests {
public @Rule ExpectedException exception = ExpectedException.none();
@Mock MongoPersistentProperty propertyMock;
@Mock DBRef dbrefMock;
@Mock DbRefResolverCallback callbackMock;
/**
* @see DATAMONGO-1437
*/
@Test
public void shouldPreserveCauseForNonTranslatableExceptions() throws Throwable {
NullPointerException npe = new NullPointerException("Some Exception we did not think about.");
when(callbackMock.resolve(propertyMock)).thenThrow(npe);
exception.expect(LazyLoadingException.class);
exception.expectCause(is(equalTo(npe)));
new LazyLoadingInterceptor(propertyMock, dbrefMock, new NullExceptionTranslator(), callbackMock).intercept(null,
LazyLoadingProxy.class.getMethod("getTarget"), null, null);
}
static class NullExceptionTranslator implements PersistenceExceptionTranslator {
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return null;
}
}
}

View File

@@ -2066,6 +2066,37 @@ public class MappingMongoConverterUnitTests {
assertThat(target.map.get(FooBarEnum.FOO), is("spring"));
}
/**
* @see DATAMONGO-1471
*/
@Test
public void readsDocumentWithPrimitiveIdButNoValue() {
assertThat(converter.read(ClassWithIntId.class, new BasicDBObject()), is(notNullValue()));
}
/**
* @see DATAMONGO-1497
*/
@Test
public void readsPropertyFromNestedFieldCorrectly() {
DBObject source = new BasicDBObject("nested", new BasicDBObject("sample", "value"));
TypeWithPropertyInNestedField result = converter.read(TypeWithPropertyInNestedField.class, source);
assertThat(result.sample, is("value"));
}
/**
* @see DATAMONGO-1525
*/
@Test
public void readsEmptyEnumSet() {
DBObject source = new BasicDBObject("enumSet", new BasicDBList());
assertThat(converter.read(ClassWithEnumProperty.class, source).enumSet, is(EnumSet.noneOf(SampleEnum.class)));
}
static class GenericType<T> {
T content;
}
@@ -2412,4 +2443,8 @@ public class MappingMongoConverterUnitTests {
throw new ConversionNotSupportedException(source, String.class, null);
}
}
static class TypeWithPropertyInNestedField {
@Field("nested.sample") String sample;
}
}

View File

@@ -15,11 +15,13 @@
*/
package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Test;
import org.springframework.data.geo.Box;
@@ -27,8 +29,12 @@ import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.geo.Shape;
import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicIntegerToIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.AtomicLongToLongConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.BigDecimalToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.CurrencyToStringConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.IntegerToAtomicIntegerConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.LongToAtomicLongConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigDecimalConverter;
import org.springframework.data.mongodb.core.convert.MongoConverters.StringToCurrencyConverter;
import org.springframework.data.mongodb.core.geo.Sphere;
@@ -140,4 +146,36 @@ public class MongoConvertersUnitTests {
public void convertsStringToCurrencyCorrectly() {
assertThat(StringToCurrencyConverter.INSTANCE.convert("USD"), is(Currency.getInstance("USD")));
}
/**
* @see DATAMONGO-1416
*/
@Test
public void convertsAtomicLongToLongCorrectly() {
assertThat(AtomicLongToLongConverter.INSTANCE.convert(new AtomicLong(100L)), is(100L));
}
/**
* @see DATAMONGO-1416
*/
@Test
public void convertsAtomicIntegerToIntegerCorrectly() {
assertThat(AtomicIntegerToIntegerConverter.INSTANCE.convert(new AtomicInteger(100)), is(100));
}
/**
* @see DATAMONGO-1416
*/
@Test
public void convertsLongToAtomicLongCorrectly() {
assertThat(LongToAtomicLongConverter.INSTANCE.convert(100L), is(instanceOf(AtomicLong.class)));
}
/**
* @see DATAMONGO-1416
*/
@Test
public void convertsIntegerToAtomicIntegerCorrectly() {
assertThat(IntegerToAtomicIntegerConverter.INSTANCE.convert(100), is(instanceOf(AtomicInteger.class)));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
@@ -54,6 +55,7 @@ import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -68,6 +70,7 @@ import com.mongodb.QueryBuilder;
* @author Patryk Wasik
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class QueryMapperUnitTests {
@@ -595,6 +598,28 @@ public class QueryMapperUnitTests {
assertThat(dbo.toString(), equalTo("{ \"embedded\" : { \"$in\" : [ { \"_id\" : \"1\"} , { \"_id\" : \"2\"}]}}"));
}
/**
* @see DATAMONGO-1406
*/
@Test
public void shouldMapQueryForNestedCustomizedPropertiesUsingConfiguredFieldNames() {
EmbeddedClass embeddedClass = new EmbeddedClass();
embeddedClass.customizedField = "hello";
Foo foo = new Foo();
foo.listOfItems = Arrays.asList(embeddedClass);
Query query = new Query(Criteria.where("listOfItems") //
.elemMatch(new Criteria(). //
andOperator(Criteria.where("customizedField").is(embeddedClass.customizedField))));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class));
assertThat(dbo, isBsonObject().containing("my_items.$elemMatch.$and",
new BasicDbListBuilder().add(new BasicDBObject("fancy_custom_name", embeddedClass.customizedField)).get()));
}
/**
* @see DATAMONGO-647
*/
@@ -792,8 +817,7 @@ public class QueryMapperUnitTests {
}
/**
* <<<<<<< HEAD
*
*
* @see DATAMONGO-1269
*/
@Test
@@ -859,10 +883,15 @@ public class QueryMapperUnitTests {
public class Foo {
@Id private ObjectId id;
EmbeddedClass embedded;
@Field("my_items")
List<EmbeddedClass> listOfItems;
}
public class EmbeddedClass {
public String id;
@Field("fancy_custom_name") public String customizedField;
}
class IdWrapper {

View File

@@ -887,6 +887,50 @@ public class UpdateMapperUnitTests {
assertThat($set.get("primIntValue"), Is.<Object> is(10));
}
/**
* @see DATAMONGO-1423
*/
@Test
@SuppressWarnings("unchecked")
public void mappingShouldConsiderCustomConvertersForEnumMapKeys() {
CustomConversions conversions = new CustomConversions(
Arrays.asList(AllocationToStringConverter.INSTANCE, StringToAllocationConverter.INSTANCE));
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
mappingContext.afterPropertiesSet();
MappingMongoConverter converter = new MappingMongoConverter(mock(DbRefResolver.class), mappingContext);
converter.setCustomConversions(conversions);
converter.afterPropertiesSet();
UpdateMapper mapper = new UpdateMapper(converter);
Update update = new Update().set("enumAsMapKey", Collections.singletonMap(Allocation.AVAILABLE, 100));
DBObject result = mapper.getMappedObject(update.getUpdateObject(),
mappingContext.getPersistentEntity(ClassWithEnum.class));
assertThat(result, isBsonObject().containing("$set.enumAsMapKey.V", 100));
}
/**
* @see DATAMONGO-1486
*/
@Test
public void mappingShouldConvertMapKeysToString() {
Update update = new Update().set("map", Collections.singletonMap(25, "#StarTrek50"));
DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
context.getPersistentEntity(EntityWithObjectMap.class));
DBObject mapToSet = getAsDBObject(getAsDBObject(mappedUpdate, "$set"), "map");
for (Object key : mapToSet.keySet()) {
assertThat(key, is(instanceOf(String.class)));
}
}
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
}
@@ -1113,6 +1157,7 @@ public class UpdateMapperUnitTests {
static class ClassWithEnum {
Allocation allocation;
Map<Allocation, String> enumAsMapKey;
static enum Allocation {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.geo.GeoResults;
@@ -36,6 +37,7 @@ import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
@@ -43,11 +45,15 @@ import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
/**
@@ -317,6 +323,66 @@ public class GeoJsonTests {
assertThat(venues.size(), is(2));
}
/**
* @see DATAMONGO-1453
*/
@Test
public void shouldConvertPointRepresentationCorrectlyWhenSourceCoordinatesUsesInteger() {
this.template.execute(template.getCollectionName(DocumentWithPropertyUsingGeoJsonType.class),
new CollectionCallback<Object>() {
@Override
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
BasicDBObject pointRepresentation = new BasicDBObject();
pointRepresentation.put("type", "Point");
pointRepresentation.put("coordinates", new BasicDbListBuilder().add(0).add(0).get());
BasicDBObject document = new BasicDBObject();
document.append("_id", "datamongo-1453");
document.append("geoJsonPoint", pointRepresentation);
return collection.save(document);
}
});
assertThat(template.findOne(query(where("id").is("datamongo-1453")),
DocumentWithPropertyUsingGeoJsonType.class).geoJsonPoint, is(equalTo(new GeoJsonPoint(0D, 0D))));
}
/**
* @see DATAMONGO-1453
*/
@Test
public void shouldConvertLineStringRepresentationCorrectlyWhenSourceCoordinatesUsesInteger() {
this.template.execute(template.getCollectionName(DocumentWithPropertyUsingGeoJsonType.class),
new CollectionCallback<Object>() {
@Override
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
BasicDBObject lineStringRepresentation = new BasicDBObject();
lineStringRepresentation.put("type", "LineString");
lineStringRepresentation.put("coordinates",
new BasicDbListBuilder().add(new BasicDbListBuilder().add(0).add(0).get())
.add(new BasicDbListBuilder().add(1).add(1).get()).get());
BasicDBObject document = new BasicDBObject();
document.append("_id", "datamongo-1453");
document.append("geoJsonLineString", lineStringRepresentation);
return collection.save(document);
}
});
assertThat(
template.findOne(query(where("id").is("datamongo-1453")),
DocumentWithPropertyUsingGeoJsonType.class).geoJsonLineString,
is(equalTo(new GeoJsonLineString(new Point(0D, 0D), new Point(1, 1)))));
}
private void addVenues() {
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));

View File

@@ -19,6 +19,7 @@ import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
@@ -27,7 +28,9 @@ import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
@@ -101,5 +104,7 @@ public class AuditingEventListenerUnitTests {
static class Sample {
@Id String id;
@CreatedDate Date created;
@LastModifiedDate Date modified;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,63 +15,61 @@
*/
package org.springframework.data.mongodb.monitor;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.UnknownHostException;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.UnknownHostException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.mongodb.Mongo;
import com.mongodb.Mongo;
/**
* This test class assumes that you are already running the MongoDB server.
*
* @author Mark Pollack
* @author Thomas Darimont
* @author Thomas Darimont
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
@ContextConfiguration("classpath:infrastructure.xml")
public class MongoMonitorIntegrationTests {
@Autowired Mongo mongo;
@Autowired Mongo mongo;
@Test
public void serverInfo() {
ServerInfo serverInfo = new ServerInfo(mongo);
serverInfo.getVersion();
Assert.isTrue(StringUtils.hasText("1."));
}
/**
* @throws UnknownHostException
* @see DATAMONGO-685
*/
@Test
public void getHostNameShouldReturnServerNameReportedByMongo() throws UnknownHostException {
ServerInfo serverInfo = new ServerInfo(mongo);
String hostName = null;
try {
hostName = serverInfo.getHostName();
} catch (UnknownHostException e) {
throw e;
}
assertThat(hostName, is(notNullValue()));
assertThat(hostName, is("127.0.0.1"));
}
/**
* @throws UnknownHostException
* @see DATAMONGO-685
*/
@Test
public void getHostNameShouldReturnServerNameReportedByMongo() throws UnknownHostException {
ServerInfo serverInfo = new ServerInfo(mongo);
String hostName = null;
try {
hostName = serverInfo.getHostName();
} catch (UnknownHostException e) {
throw e;
}
assertThat(hostName, is(notNullValue()));
assertThat(hostName, is("127.0.0.1"));
}
@Test
public void operationCounters() {
OperationCounters operationCounters = new OperationCounters(mongo);
operationCounters.getInsertCount();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@ package org.springframework.data.mongodb.performance;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.util.Assert.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -47,6 +46,7 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
@@ -66,6 +66,7 @@ import com.mongodb.WriteConcern;
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
public class PerformanceTests {
@@ -74,7 +75,7 @@ public class PerformanceTests {
private static final int ITERATIONS = 50;
private static final StopWatch watch = new StopWatch();
private static final Collection<String> IGNORED_WRITE_CONCERNS = Arrays.asList("MAJORITY", "REPLICAS_SAFE",
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED");
"FSYNC_SAFE", "FSYNCED", "JOURNAL_SAFE", "JOURNALED", "REPLICA_ACKNOWLEDGED", "W2", "W3");
private static final int COLLECTION_SIZE = 1024 * 1024 * 256; // 256 MB
private static final Collection<String> COLLECTION_NAMES = Arrays.asList("template", "driver", "person");
@@ -622,7 +623,7 @@ public class PerformanceTests {
private static <T> List<T> pickRandomNumerOfItemsFrom(List<T> source) {
isTrue(!source.isEmpty());
Assert.isTrue(!source.isEmpty(), "Source must not be empty!");
Random random = new Random();
int numberOfItems = random.nextInt(source.size());
@@ -836,7 +837,7 @@ public class PerformanceTests {
String.format(" %s%%", DEVIATION_FORMAT.format(getMediaDeviationFrom(referenceMedian)))) + '\n';
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@@ -895,7 +896,7 @@ public class PerformanceTests {
return builder.toString();
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -30,7 +30,9 @@ import java.util.stream.Stream;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
@@ -51,6 +53,7 @@ import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.repository.Person.Sex;
import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension.SampleSecurityContextHolder;
@@ -65,10 +68,14 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Fırat KÜÇÜK
* @author Edward Prentice
*/
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractPersonRepositoryIntegrationTests {
public @Rule ExpectedException expectedException = ExpectedException.none();
@Autowired protected PersonRepository repository;
@Autowired MongoOperations operations;
@@ -86,8 +93,10 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
dave = new Person("Dave", "Matthews", 42);
oliver = new Person("Oliver August", "Matthews", 4);
carter = new Person("Carter", "Beauford", 49);
carter.setSkills(Arrays.asList("Drums", "percussion", "vocals"));
Thread.sleep(10);
boyd = new Person("Boyd", "Tinsley", 45);
boyd.setSkills(Arrays.asList("Violin", "Electric Violin", "Viola", "Mandolin", "Vocals", "Guitar"));
stefan = new Person("Stefan", "Lessard", 34);
leroi = new Person("Leroi", "Moore", 41);
@@ -166,6 +175,18 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result, hasItem(boyd));
}
/**
* @see DATAMONGO-1608
*/
@Test
public void findByFirstnameLikeWithNull() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("property 'firstname'");
repository.findByFirstnameLike(null);
}
@Test
public void findsPagedPersons() throws Exception {
@@ -270,6 +291,18 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result, hasItem(dave));
}
@Test // DATAMONGO-1588
public void findsPeopleByLocationNearUsingGeoJsonType() {
GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868);
dave.setLocation(point);
repository.save(dave);
List<Person> result = repository.findByLocationNear(point);
assertThat(result.size(), is(1));
assertThat(result, hasItem(dave));
}
@Test
public void findsPeopleByLocationWithinCircle() {
Point point = new Point(-73.99171, 40.738868);
@@ -697,6 +730,18 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(results.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
}
/***
* @see DATAMONGO-1608
*/
@Test
public void findByFirstNameIgnoreCaseWithNull() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("property 'firstname'");
repository.findByFirstnameIgnoreCase(null);
}
/**
* @see DATAMONGO-770
*/
@@ -995,7 +1040,7 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
/**
* Ignored for now as this requires Querydsl 3.4.1 to succeed.
*
*
* @see DATAMONGO-972
*/
@Test
@@ -1261,4 +1306,37 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
assertThat(result.size(), is(2));
}
/**
* @see DATAMONGO-1425
*/
@Test
public void findsPersonsByFirstnameNotContains() throws Exception {
List<Person> result = repository.findByFirstnameNotContains("Boyd");
assertThat(result.size(), is((int) (repository.count() - 1)));
assertThat(result, not(hasItem(boyd)));
}
/**
* @see DATAMONGO-1425
*/
@Test
public void findBySkillsContains() throws Exception {
List<Person> result = repository.findBySkillsContains(Arrays.asList("Drums"));
assertThat(result.size(), is(1));
assertThat(result, hasItem(carter));
}
/**
* @see DATAMONGO-1425
*/
@Test
public void findBySkillsNotContains() throws Exception {
List<Person> result = repository.findBySkillsNotContains(Arrays.asList("Drums"));
assertThat(result.size(), is((int) (repository.count() - 1)));
assertThat(result, not(hasItem(carter)));
}
}

View File

@@ -89,8 +89,14 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
*/
List<Person> findByFirstnameLike(String firstname);
List<Person> findByFirstnameNotContains(String firstname);
List<Person> findByFirstnameLikeOrderByLastnameAsc(String firstname, Sort sort);
List<Person> findBySkillsContains(List<String> skills);
List<Person> findBySkillsNotContains(List<String> skills);
@Query("{'age' : { '$lt' : ?0 } }")
List<Person> findByAgeLessThan(int age, Sort sort);
@@ -309,7 +315,8 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
* @see DATAMONGO-745
*/
@Query("{lastname:?0, address.street:{$in:?1}}")
Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, List<String> streetNames, Pageable page);
Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, List<String> streetNames,
Pageable page);
/**
* @see DATAMONGO-950
@@ -334,19 +341,19 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
*/
@Query("{ firstname : { $in : ?0 }}")
Stream<Person> findByCustomQueryWithStreamingCursorByFirstnames(List<String> firstnames);
/**
* @see DATAMONGO-990
*/
@Query("{ firstname : ?#{[0]}}")
List<Person> findWithSpelByFirstnameForSpELExpressionWithParameterIndexOnly(String firstname);
/**
* @see DATAMONGO-990
*/
@Query("{ firstname : ?#{[0]}, email: ?#{principal.email} }")
List<Person> findWithSpelByFirstnameAndCurrentUserWithCustomQuery(String firstname);
/**
* @see DATAMONGO-990
*/

View File

@@ -44,6 +44,8 @@ import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
@@ -69,8 +71,6 @@ import com.mongodb.DBObject;
*/
public class MongoQueryCreatorUnitTests {
Method findByFirstname, findByFirstnameAndFriend, findByFirstnameNotNull;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
MongoConverter converter;
@@ -454,6 +454,7 @@ public class MongoQueryCreatorUnitTests {
/**
* @see DATAMONGO-1075
* @see DATAMONGO-1425
*/
@Test
public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() {
@@ -462,14 +463,14 @@ public class MongoQueryCreatorUnitTests {
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("username").regex(".*thew.*").not())));
assertThat(query.getQueryObject(), is(query(where("username").not().regex(".*thew.*")).getQueryObject()));
}
/**
* @see DATAMONGO-1139
*/
@Test
public void createsNonShericalNearForDistanceWithDefaultMetric() {
public void createsNonSphericalNearForDistanceWithDefaultMetric() {
Point point = new Point(1.0, 1.0);
Distance distance = new Distance(1.0);
@@ -668,6 +669,35 @@ public class MongoQueryCreatorUnitTests {
assertThat(query, is(query(where("emailAddresses").in((Object) null))));
}
/**
* @see DATAMONGO-1588
*/
@Test // DATAMONGO-1588
public void queryShouldAcceptSubclassOfDeclaredArgument() {
PartTree tree = new PartTree("findByLocationNear", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, new GeoJsonPoint(-74.044502D, 40.689247D));
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query.getQueryObject().containsField("location"), is(true));
}
/**
* @see DATAMONGO-1588
*/
@Test
public void queryShouldThrowExceptionWhenArgumentDoesNotMatchDeclaration() {
expection.expect(IllegalArgumentException.class);
expection.expectMessage("Expected parameter type of " + Point.class);
PartTree tree = new PartTree("findByLocationNear", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter,
new GeoJsonLineString(new Point(-74.044502D, 40.689247D), new Point(-73.997330D, 40.730824D)));
new MongoQueryCreator(tree, accessor, context).createQuery();
}
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
@@ -686,16 +716,21 @@ public class MongoQueryCreatorUnitTests {
Address address;
Address2dSphere address2dSphere;
Point location;
}
static class Address {
String street;
Point geo;
}
static class Address2dSphere {
String street;
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.query.IsTextQuery.*;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
@@ -40,6 +41,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.TextCriteria;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Person;
import org.springframework.data.mongodb.repository.Person.Sex;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -192,6 +194,18 @@ public class PartTreeMongoQueryUnitTests {
assertThat(fields.get("age"), is((Object) 1));
}
/**
* @see DATAMONGO-1500
*/
@Test
public void shouldLeaveParameterConversionToQueryMapper() {
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findBySex", Sex.FEMALE);
assertThat(query.getQueryObject().get("sex"), is((Object) Sex.FEMALE));
assertThat(query.getFieldsObject().get("firstname"), is((Object) 1));
}
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object... args) {
Class<?>[] types = new Class<?>[args.length];
@@ -249,6 +263,9 @@ public class PartTreeMongoQueryUnitTests {
PersonDto findPersonDtoByAge(Integer age);
<T> T findDynamicallyProjectedBy(Class<T> type);
@Query(fields = "{ 'firstname' : 1 }")
List<Person> findBySex(Sex sex);
}
interface PersonProjection {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;
@@ -50,14 +51,16 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
import org.springframework.data.repository.query.DefaultEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.mongodb.util.JSON;
/**
* Unit tests for {@link StringBasedMongoQuery}.
*
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Thomas Darimont
@@ -84,9 +87,9 @@ public class StringBasedMongoQueryUnitTests {
public void bindsSimplePropertyCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -98,13 +101,13 @@ public class StringBasedMongoQueryUnitTests {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAddress", Address.class);
Address address = new Address("Foo", "0123", "Bar");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, address);
DBObject dbObject = new BasicDBObject();
converter.write(address, dbObject);
dbObject.removeField(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
BasicDBObject queryObject = new BasicDBObject("address", dbObject);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
@@ -117,7 +120,7 @@ public class StringBasedMongoQueryUnitTests {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAndAddress", String.class, Address.class);
Address address = new Address("Foo", "0123", "Bar");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews", address);
DBObject addressDbObject = new BasicDBObject();
converter.write(address, addressDbObject);
@@ -126,7 +129,7 @@ public class StringBasedMongoQueryUnitTests {
DBObject reference = new BasicDBObject("address", addressDbObject);
reference.put("lastname", "Matthews");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is(reference));
}
@@ -148,7 +151,7 @@ public class StringBasedMongoQueryUnitTests {
public void bindsDbrefCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByHavingSizeFansNotZero");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {});
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is(new BasicQuery("{ fans : { $not : { $size : 0 } } }").getQueryObject()));
@@ -178,8 +181,8 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportFindByParameterizedCriteriaAndFields() throws Exception {
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] {
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1) });
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
new BasicDBObject("firstname", "first").append("lastname", "last"), Collections.singletonMap("lastname", 1));
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields", DBObject.class,
Map.class);
@@ -196,7 +199,7 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportRespectExistingQuotingInFindByTitleBeginsWithExplicitQuoting() throws Exception {
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { "fun" });
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "fun");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByTitleBeginsWithExplicitQuoting", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
@@ -210,7 +213,7 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldParseQueryWithParametersInExpression() throws Exception {
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { 1, 2, 3, 4 });
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 1, 2, 3, 4);
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithParametersInExpression", int.class,
int.class, int.class, int.class);
@@ -227,10 +230,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void bindsSimplePropertyAlreadyQuotedCorrectly() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -242,10 +245,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void bindsSimplePropertyAlreadyQuotedWithRegexCorrectly() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -258,9 +261,9 @@ public class StringBasedMongoQueryUnitTests {
public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "^Mat.*");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : '^Mat.*'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -303,10 +306,10 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueries() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -318,11 +321,11 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueriesWithNestedObject() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject", boolean.class,
String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
@@ -334,11 +337,11 @@ public class StringBasedMongoQueryUnitTests {
@Test
public void shouldSupportExpressionsInCustomQueriesWithMultipleNestedObjects() throws Exception {
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, true, "param1", "param2");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
boolean.class, String.class, String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
"{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}");
@@ -352,16 +355,252 @@ public class StringBasedMongoQueryUnitTests {
public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
byte[] binaryData = "Matthews".getBytes("UTF-8");
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, binaryData);
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
+ DatatypeConverter.printBase64Binary(binaryData) + "', '$type' : " + BSON.B_GENERAL + "}}");
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void bindsPropertyReferenceMultipleTimesCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByAgeQuotedAndUnquoted", Integer.TYPE);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 3);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
BasicDBList or = new BasicDBList();
or.add(new BasicDBObject("age", 3));
or.add(new BasicDBObject("displayAge", "3"));
BasicDBObject queryObject = new BasicDBObject("$or", or);
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldIgnorePlaceholderPatternInReplacementValue() throws Exception {
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "argWith?1andText",
"nothing-special");
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByStringWithWildcardChar", String.class, String.class);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is(JSON.parse("{ \"arg0\" : \"argWith?1andText\" , \"arg1\" : \"nothing-special\"}")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteStringReplacementCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews', password: 'foo");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews', password: 'foo")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteStringReplacementContainingQuotesCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews\", password: \"foo");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews\", password: \"foo")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteStringReplacementWithQuotationsCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
"\"Dave Matthews\", password: 'foo");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is((DBObject) new BasicDBObject("lastname", "\"Dave Matthews\", password: 'foo")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuoteComplexQueryStringCorreclty() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "{ $ne : \"calamity\" }");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "{ $ne : \"calamity\" }")));
}
/**
* @see DATAMONGO-1565
*/
@Test
public void shouldQuotationInQuotedComplexQueryString() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
"{ $ne : \"\\\"calamity\\\"\" }");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "{ $ne : \"\\\"calamity\\\"\" }")));
}
/**
* @see DATAMONGO-1575
*/
@Test
public void shouldTakeBsonParameterAsIs() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWithBsonArgument", DBObject.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
new BasicDBObject("$regex", "^calamity$"));
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("arg0", Pattern.compile("^calamity$"))));
}
/**
* @see DATAMONGO-1575
*/
@Test
public void shouldReplaceParametersInInQuotedExpressionOfNestedQueryOperator() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", Pattern.compile("^(calamity)"))));
}
/**
* @see DATAMONGO-1603
*/
@Test
public void shouldAllowReuseOfPlaceholderWithinQuery() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByReusingPlaceholdersMultipleTimes", String.class,
String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject().append("arg0", "calamity")
.append("arg1", "regalia").append("arg2", "calamity")));
}
/**
* @see DATAMONGO-1575
*/
@Test
public void shouldAllowReuseOfQuotedPlaceholderWithinQuery() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByReusingPlaceholdersMultipleTimesWhenQuoted",
String.class, String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject().append("arg0", "calamity")
.append("arg1", "regalia").append("arg2", "calamity")));
}
/**
* @see DATAMONGO-1575
*/
@Test
public void shouldAllowReuseOfQuotedPlaceholderWithinQueryAndIncludeSuffixCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod(
"findByReusingPlaceholdersMultipleTimesWhenQuotedAndSomeStuffAppended", String.class, String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject().append("arg0", "calamity")
.append("arg1", "regalia").append("arg2", "calamitys")));
}
@Test // DATAMONGO-1603
public void shouldAllowQuotedParameterWithSuffixAppended() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWhenQuotedAndSomeStuffAppended", String.class,
String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is((DBObject) new BasicDBObject().append("arg0", "calamity").append("arg1", "regalias")));
}
@Test // DATAMONGO-1603
public void shouldCaptureReplacementWithComplexSuffixCorrectly() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByMultiRegex", String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) JSON.parse(
"{ \"$or\" : [ { \"firstname\" : { \"$regex\" : \".*calamity.*\" , \"$options\" : \"i\"}} , { \"lastname\" : { \"$regex\" : \".*calamityxyz.*\" , \"$options\" : \"i\"}}]}")));
}
@Test // DATAMONGO-1603
public void shouldAllowPlaceholderReuseInQuotedValue() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class, String.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "calamity", "regalia");
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(),
is((DBObject) JSON.parse("{ 'lastname' : { '$regex' : '^(calamity|John regalia|regalia)'} }")));
}
@Test // DATAMONGO-1605
public void findUsingSpelShouldRetainParameterType() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByUsingSpel", Object.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, 100.01D);
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject().append("arg0", 100.01D)));
}
@Test // DATAMONGO-1605
public void findUsingSpelShouldRetainNullValues() throws Exception {
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByUsingSpel", Object.class);
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new Object[] { null });
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject().append("arg0", null)));
}
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
Method method = SampleRepository.class.getMethod(name, parameters);
@@ -382,6 +621,12 @@ public class StringBasedMongoQueryUnitTests {
@Query("{ 'lastname' : '?0' }")
Person findByLastnameQuoted(String lastname);
@Query("{ 'lastname' : { '$regex' : '^(?0)'} }")
Person findByLastnameRegex(String lastname);
@Query("{'$or' : [{'firstname': {'$regex': '.*?0.*', '$options': 'i'}}, {'lastname' : {'$regex': '.*?0xyz.*', '$options': 'i'}} ]}")
Person findByMultiRegex(String arg0);
@Query("{ 'address' : ?0 }")
Person findByAddress(Address address);
@@ -420,5 +665,32 @@ public class StringBasedMongoQueryUnitTests {
@Query("{'id':?#{ [0] ? { $exists :true} : [1] }, 'foo':42, 'bar': ?#{ [0] ? { $exists :false} : [1] }}")
List<Person> findByQueryWithExpressionAndMultipleNestedObjects(boolean param0, String param1, String param2);
@Query(value = "{ $or : [{'age' : ?0 }, {'displayAge' : '?0'}] }")
boolean findByAgeQuotedAndUnquoted(int age);
@Query("{ 'arg0' : ?0, 'arg1' : ?1 }")
List<Person> findByStringWithWildcardChar(String arg0, String arg1);
@Query("{ 'arg0' : ?0 }")
List<Person> findByWithBsonArgument(DBObject arg0);
@Query("{ 'arg0' : ?0, 'arg1' : ?1, 'arg2' : ?0 }")
List<Person> findByReusingPlaceholdersMultipleTimes(String arg0, String arg1);
@Query("{ 'arg0' : ?0, 'arg1' : ?1, 'arg2' : '?0' }")
List<Person> findByReusingPlaceholdersMultipleTimesWhenQuoted(String arg0, String arg1);
@Query("{ 'arg0' : '?0', 'arg1' : ?1, 'arg2' : '?0s' }")
List<Person> findByReusingPlaceholdersMultipleTimesWhenQuotedAndSomeStuffAppended(String arg0, String arg1);
@Query("{ 'arg0' : '?0', 'arg1' : '?1s' }")
List<Person> findByWhenQuotedAndSomeStuffAppended(String arg0, String arg1);
@Query("{ 'lastname' : { '$regex' : '^(?0|John ?1|?1)'} }") // use spel or some regex string this is fucking bad
Person findByLastnameRegex(String lastname, String alternative);
@Query("{ arg0 : ?#{[0]} }")
List<Person> findByUsingSpel(Object arg0);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.repository.support;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import lombok.Value;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.domain.Persistable;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
/**
* Tests for {@link PersistableMongoEntityInformation}.
*
* @author Christoph Strobl
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class PersistableMappingMongoEntityInformationUnitTests {
@Mock MongoPersistentEntity<TypeImplementingPersistable> persistableImplementingEntityTypeInfo;
@Before
public void setUp() {
when(persistableImplementingEntityTypeInfo.getType()).thenReturn(TypeImplementingPersistable.class);
}
@Test // DATAMONGO-1590
public void considersPersistableIsNew() {
PersistableMongoEntityInformation<TypeImplementingPersistable, Long> information = new PersistableMongoEntityInformation<TypeImplementingPersistable, Long>(
new MappingMongoEntityInformation<TypeImplementingPersistable, Long>(persistableImplementingEntityTypeInfo));
assertThat(information.isNew(new TypeImplementingPersistable(100L, false)), is(false));
}
@Value
static class TypeImplementingPersistable implements Persistable<Long> {
private static final long serialVersionUID = -1619090149320971099L;
Long id;
boolean isNew;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import java.util.Collections;
import org.bson.types.ObjectId;
import org.hamcrest.Matchers;
import org.junit.Before;
@@ -26,11 +28,15 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.repository.Person.Sex;
import org.springframework.data.mongodb.repository.QAddress;
import org.springframework.data.mongodb.repository.QPerson;
@@ -172,10 +178,52 @@ public class SpringDataMongodbSerializerUnitTests {
assertThat($in, Matchers.<Object> arrayContaining(firstId, secondId));
}
/**
* @see DATAMONGO-1485
*/
@Test
public void takesCustomConversionForEnumsIntoAccount() {
MongoMappingContext context = new MongoMappingContext();
MappingMongoConverter converter = new MappingMongoConverter(dbFactory, context);
converter.setCustomConversions(new CustomConversions(Collections.singletonList(new SexTypeWriteConverter())));
converter.afterPropertiesSet();
this.converter = converter;
this.serializer = new SpringDataMongodbSerializer(this.converter);
Object mappedPredicate = this.serializer.handle(QPerson.person.sex.eq(Sex.FEMALE));
assertThat(mappedPredicate, is(instanceOf(DBObject.class)));
assertThat(((DBObject) mappedPredicate).get("sex"), is((Object) "f"));
}
class Address {
String id;
String street;
@Field("zip_code") String zipCode;
@Field("bar") String[] foo;
}
@WritingConverter
public class SexTypeWriteConverter implements Converter<Sex, String> {
@Override
public String convert(Sex source) {
if (source == null) {
return null;
}
switch (source) {
case MALE:
return "m";
case FEMALE:
return "f";
default:
throw new IllegalArgumentException("o_O");
}
}
}
}

View File

@@ -6,7 +6,7 @@ Mark Pollack; Thomas Risberg; Oliver Gierke; Costin Leau; Jon Brisbin; Thomas Da
:toc-placement!:
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
(C) 2008-2015 The original authors.
(C) 2008-2017 The original authors.
NOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._

View File

@@ -3,21 +3,21 @@
The Spring Data MongoDB project applies core Spring concepts to the development of solutions using the MongoDB document style data store. We provide a "template" as a high-level abstraction for storing and querying documents. You will notice similarities to the JDBC support in the Spring Framework.
This document is the reference guide for Spring Data - Document Support. It explains Document module concepts and semantics and the syntax for various stores namespaces.
This document is the reference guide for Spring Data - Document Support. It explains Document module concepts and semantics and the syntax for various store namespaces.
This section provides some basic introduction to Spring and Document database. The rest of the document refers only to Spring Data Document features and assumes the user is familiar with document databases such as MongoDB and CouchDB as well as Spring concepts.
This section provides some basic introduction to Spring and Document databases. The rest of the document refers only to Spring Data MongoDB features and assumes the user is familiar with MongoDB and Spring concepts.
[[get-started:first-steps:spring]]
== Knowing Spring
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
The core functionality of the MongoDB and CouchDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data document, such as the repository support, you will need to configure some parts of the library using Spring.
The core functionality of the MongoDB support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `JdbcTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Data MongoDB, such as the repository support, you will need to configure some parts of the library using Spring.
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page ] for more information.
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page] for more information.
[[get-started:first-steps:nosql]]
== Knowing NoSQL and Document databases
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms and patterns (to make things worth even the term itself has multiple http://www.google.com/search?q=nosoql+acronym[meanings]). While some of the principles are common, it is crucial that the user is familiar to some degree with the stores supported by DATADOC. The best way to get acquainted to this solutions is to read their documentation and follow their examples - it usually doesn't take more then 5-10 minutes to go through them and if you are coming from an RDMBS-only background many times these exercises can be an eye opener.
NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms and patterns (to make things worse even the term itself has multiple http://www.google.com/search?q=nosoql+acronym[meanings]). While some of the principles are common, it is crucial that the user is familiar to some degree with MongoDB. The best way to get acquainted to this solutions is to read their documentation and follow their examples - it usually doesn't take more then 5-10 minutes to go through them and if you are coming from an RDMBS-only background many times these exercises can be an eye opener.
The jumping off ground for learning about MongoDB is http://www.mongodb.org/[www.mongodb.org]. Here is a list of other useful resources:
@@ -36,7 +36,7 @@ In terms of document stores, http://www.mongodb.org/[MongoDB] at least 2.6.
== Additional Help Resources
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Data Document module. However, if you encounter issues or you are just looking for an advice, feel free to use one of the links below:
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Data MongoDB module. However, if you encounter issues or you are just looking for an advice, feel free to use one of the links below:
[[get-started:help]]
=== Support
@@ -56,4 +56,4 @@ Professional, from-the-source support, with guaranteed response time, is availab
[[get-started:up-to-date]]
=== Following Development
For information on the Spring Data Mongo source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-data-mongodb/[Spring Data Mongo homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow]. To follow developer activity look for the mailing list information on the Spring Data Mongo homepage. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data issue https://jira.spring.io/browse/DATAMONGO[tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the SpringSource Data http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/SpringData[SpringData]).
For information on the Spring Data Mongo source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-data-mongodb/[Spring Data Mongo homepage]. You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stackoverflow]. To follow developer activity look for the mailing list information on the Spring Data Mongo homepage. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data issue https://jira.spring.io/browse/DATAMONGO[tracker]. To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the Spring http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/SpringData[SpringData]).

View File

@@ -157,7 +157,7 @@ Finally, you need to configure your project to use MongoDB and also configure th
[[mongodb_cross-store-application]]
== Writing the Cross Store Application
We are assuming that you have a working JPA application so we will only cover the additional steps needed to persist part of your Entity in your Mongo database. First you need to identify the field you want persisted. It should be a domain class and follow the general rules for the Mongo mapping support covered in previous chapters. The field you want persisted in MongoDB should be annotated using the `@RelatedDocument` annotation. That is really all you need to do!. The cross-store aspects take care of the rest. This includes marking the field with `@Transient` so it won't be persisted using JPA, keeping track of any changes made to the field value and writing them to the database on successful transaction completion, loading the document from MongoDB the first time the value is used in your application. Here is an example of a simple Entity that has a field annotated with `@RelatedEntity`.
We are assuming that you have a working JPA application so we will only cover the additional steps needed to persist part of your Entity in your Mongo database. First you need to identify the field you want persisted. It should be a domain class and follow the general rules for the Mongo mapping support covered in previous chapters. The field you want persisted in MongoDB should be annotated using the `@RelatedDocument` annotation. That is really all you need to do!. The cross-store aspects take care of the rest. This includes marking the field with `@Transient` so it won't be persisted using JPA, keeping track of any changes made to the field value and writing them to the database on successful transaction completion, loading the document from MongoDB the first time the value is used in your application. Here is an example of a simple Entity that has a field annotated with `@RelatedDocument`.
.Example of Entity with @RelatedDocument
====

View File

@@ -3,7 +3,7 @@
== Document Structure
This part of the reference documentation explains the core functionality offered by Spring Data Document.
This part of the reference documentation explains the core functionality offered by Spring Data MongoDB.
<<mongo.core>> introduces the MongoDB module feature set.

View File

@@ -1,36 +1,36 @@
[[mapping-chapter]]
= Mapping
Rich mapping support is provided by the `MongoMappingConverter`. `MongoMappingConverter` has a rich metadata model that provides a full feature set of functionality to map domain objects to MongoDB documents.The mapping metadata model is populated using annotations on your domain objects. However, the infrastructure is not limited to using annotations as the only source of metadata information. The `MongoMappingConverter` also allows you to map objects to documents without providing any additional metadata, by following a set of conventions.
Rich mapping support is provided by the `MappingMongoConverter`. `MappingMongoConverter` has a rich metadata model that provides a full feature set of functionality to map domain objects to MongoDB documents.The mapping metadata model is populated using annotations on your domain objects. However, the infrastructure is not limited to using annotations as the only source of metadata information. The `MappingMongoConverter` also allows you to map objects to documents without providing any additional metadata, by following a set of conventions.
In this section we will describe the features of the `MongoMappingConverter`. How to use conventions for mapping objects to documents and how to override those conventions with annotation based mapping metadata.
In this section we will describe the features of the `MappingMongoConverter`. How to use conventions for mapping objects to documents and how to override those conventions with annotation based mapping metadata.
NOTE: `SimpleMongoConverter` has been deprecated in Spring Data MongoDB M3 as all of its functionality has been subsumed into `MappingMongoConverter`.
[[mapping-conventions]]
== Convention based Mapping
`MongoMappingConverter` has a few conventions for mapping objects to documents when no additional mapping metadata is provided. The conventions are:
`MappingMongoConverter` has a few conventions for mapping objects to documents when no additional mapping metadata is provided. The conventions are:
* The short Java class name is mapped to the collection name in the following manner. The class '`com.bigbank.SavingsAccount`' maps to '`savingsAccount`' collection name.
* The short Java class name is mapped to the collection name in the following manner. The class `com.bigbank.SavingsAccount` maps to `savingsAccount` collection name.
* All nested objects are stored as nested objects in the document and *not* as DBRefs
* The converter will use any Spring Converters registered with it to override the default mapping of object properties to document field/values.
* The fields of an object are used to convert to and from fields in the document. Public JavaBean properties are not used.
* You can have a single non-zero argument constructor whose constructor argument names match top level field names of document, that constructor will be used. Otherwise the zero arg constructor will be used. if there is more than one non-zero argument constructor an exception will be thrown.
[[mapping.conventions.id-field]]
=== How the '_id' field is handled in the mapping layer
=== How the `_id` field is handled in the mapping layer
MongoDB requires that you have an '_id' field for all documents. If you don't provide one the driver will assign a ObjectId with a generated value. The "_id" field can be of any type the, other than arrays, so long as it is unique. The driver naturally supports all primitive types and Dates. When using the `MongoMappingConverter` there are certain rules that govern how properties from the Java class is mapped to this '_id' field.
MongoDB requires that you have an `_id` field for all documents. If you don't provide one the driver will assign a ObjectId with a generated value. The "_id" field can be of any type the, other than arrays, so long as it is unique. The driver naturally supports all primitive types and Dates. When using the `MappingMongoConverter` there are certain rules that govern how properties from the Java class is mapped to this `_id` field.
The following outlines what field will be mapped to the '_id' document field:
The following outlines what field will be mapped to the `_id` document field:
* A field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the '_id' field.
* A field without an annotation but named 'id' will be mapped to the '_id' field.
* The default field name for identifiers is '_id' and can be customized via the `@Field` annotation.
* A field annotated with `@Id` (`org.springframework.data.annotation.Id`) will be mapped to the `_id` field.
* A field without an annotation but named `id` will be mapped to the `_id` field.
* The default field name for identifiers is `_id` and can be customized via the `@Field` annotation.
[cols="1,2", options="header"]
.Examples for the translation of '_id'-field definitions
.Examples for the translation of `_id` field definitions
|===
| Field definition
| Resulting Id-Fieldname in MongoDB
@@ -41,30 +41,218 @@ The following outlines what field will be mapped to the '_id' document field:
| `@Field` `String` id
| `_id`
| `@Field('x')` `String` id
| `@Field("x")` `String` id
| `x`
| `@Id` `String` x
| `_id`
| `@Field('x')` `@Id` `String` x
| `@Field("x")` `@Id` `String` x
| `_id`
|===
The following outlines what type conversion, if any, will be done on the property mapped to the _id document field.
* If a field named 'id' is declared as a String or BigInteger in the Java class it will be converted to and stored as an ObjectId if possible. ObjectId as a field type is also valid. If you specify a value for 'id' in your application, the conversion to an ObjectId is detected to the MongoDBdriver. If the specified 'id' value cannot be converted to an ObjectId, then the value will be stored as is in the document's _id field.
* If a field named ' id' id field is not declared as a String, BigInteger, or ObjectID in the Java class then you should assign it a value in your application so it can be stored 'as-is' in the document's _id field.
* If no field named 'id' is present in the Java class then an implicit '_id' file will be generated by the driver but not mapped to a property or field of the Java class.
* If a field named `id` is declared as a String or BigInteger in the Java class it will be converted to and stored as an ObjectId if possible. ObjectId as a field type is also valid. If you specify a value for `id` in your application, the conversion to an ObjectId is detected to the MongoDBdriver. If the specified `id` value cannot be converted to an ObjectId, then the value will be stored as is in the document's _id field.
* If a field named `id` id field is not declared as a String, BigInteger, or ObjectID in the Java class then you should assign it a value in your application so it can be stored 'as-is' in the document's _id field.
* If no field named `id` is present in the Java class then an implicit `_id` file will be generated by the driver but not mapped to a property or field of the Java class.
When querying and updating `MongoTemplate` will use the converter to handle conversions of the `Query` and `Update` objects that correspond to the above rules for saving documents so field names and types used in your queries will be able to match what is in your domain classes.
[[mapping-conversion]]
== Data mapping and type conversion
This section explain how types are mapped to a MongoDB representation and vice versa. Spring Data MongoDB supports all types that can be represented as BSON, MongoDB's internal document format.
In addition to these types, Spring Data MongoDB provides a set of built-in converters to map additional types. You can provide your own converters to adjust type conversion, see <<mapping-explicit-converters>> for further details.
[cols="3,1,6", options="header"]
.Type
|===
| Type
| Type conversion
| Sample
| `String`
| native
| `{"firstname" : "Dave"}`
| `double`, `Double`, `float`, `Float`
| native
| `{"weight" : 42.5}`
| `int`, `Integer`, `short`, `Short`
| native +
32-bit integer
| `{"height" : 42}`
| `long`, `Long`
| native +
64-bit integer
| `{"height" : 42}`
| `Date`, `Timestamp`
| native
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
| `byte[]`
| native
| `{"bin" : { "$binary" : "AQIDBA==", "$type" : "00" }}`
| `java.util.UUID` (Legacy UUID)
| native
| `{"uuid" : { "$binary" : "MEaf1CFQ6lSphaa3b9AtlA==", "$type" : "03" }}`
| `Date`
| native
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
| `ObjectId`
| native
| `{"_id" : ObjectId("5707a2690364aba3136ab870")}`
| Array, `List`, `BasicDBList`
| native
| `{"cookies" : [ … ]}`
| `boolean`, `Boolean`
| native
| `{"active" : true}`
| `null`
| native
| `{"value" : null}`
| `DBObject`
| native
| `{"value" : { … }}`
| `Decimal128`
| native
| `{"value" : NumberDecimal(…)}`
| `AtomicInteger` +
calling `get()` before the actual conversion
| converter +
32-bit integer
| `{"value" : "741" }`
| `AtomicLong` +
calling `get()` before the actual conversion
| converter +
64-bit integer
| `{"value" : "741" }`
| `BigInteger`
| converter +
`String`
| `{"value" : "741" }`
| `BigDecimal`
| converter +
`String`
| `{"value" : "741.99" }`
| `URL`
| converter
| `{"website" : "http://projects.spring.io/spring-data-mongodb/" }`
| `Locale`
| converter
| `{"locale : "en_US" }`
| `char`, `Character`
| converter
| `{"char" : "a" }`
| `NamedMongoScript`
| converter +
`Code`
| `{"_id" : "script name", value: (some javascript code)`}
| `java.util.Currency`
| converter
| `{"currencyCode" : "EUR"}`
| `LocalDate` +
(Joda, Java 8, JSR310-BackPort)
| converter
| `{"date" : ISODate("2019-11-12T00:00:00.000Z")}`
| `LocalDateTime`, `LocalTime`, `Instant` +
(Joda, Java 8, JSR310-BackPort)
| converter
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
| `DateTime` (Joda)
| converter
| `{"date" : ISODate("2019-11-12T23:00:00.809Z")}`
| `DateMidnight` (Joda)
| converter
| `{"date" : ISODate("2019-11-12T00:00:00.000Z")}`
| `ZoneId` (Java 8, JSR310-BackPort)
| converter
| `{"zoneId" : "ECT - Europe/Paris"}`
| `Box`
| converter
| `{"box" : { "first" : { "x" : 1.0 , "y" : 2.0} , "second" : { "x" : 3.0 , "y" : 4.0}}`
| `Polygon`
| converter
| `{"polygon" : { "points" : [ { "x" : 1.0 , "y" : 2.0} , { "x" : 3.0 , "y" : 4.0} , { "x" : 4.0 , "y" : 5.0}]}}`
| `Circle`
| converter
| `{"circle" : { "center" : { "x" : 1.0 , "y" : 2.0} , "radius" : 3.0 , "metric" : "NEUTRAL"}}`
| `Point`
| converter
| `{"point" : { "x" : 1.0 , "y" : 2.0}}`
| `GeoJsonPoint`
| converter
| `{"point" : { "type" : "Point" , "coordinates" : [3.0 , 4.0] }}`
| `GeoJsonMultiPoint`
| converter
| `{"geoJsonLineString" : {"type":"MultiPoint", "coordinates": [ [ 0 , 0 ], [ 0 , 1 ], [ 1 , 1 ] ] }}`
| `Sphere`
| converter
| `{"sphere" : { "center" : { "x" : 1.0 , "y" : 2.0} , "radius" : 3.0 , "metric" : "NEUTRAL"}}`
| `GeoJsonPolygon`
| converter
| `{"polygon" : { "type" : "Polygon", "coordinates" : [[ [ 0 , 0 ], [ 3 , 6 ], [ 6 , 1 ], [ 0 , 0 ] ]] }}`
| `GeoJsonMultiPolygon`
| converter
| `{"geoJsonMultiPolygon" : { "type" : "MultiPolygon", "coordinates" : [
[ [ [ -73.958 , 40.8003 ] , [ -73.9498 , 40.7968 ] ] ],
[ [ [ -73.973 , 40.7648 ] , [ -73.9588 , 40.8003 ] ] ]
] }}`
| `GeoJsonLineString`
| converter
| `{ "geoJsonLineString" : { "type" : "LineString", "coordinates" : [ [ 40 , 5 ], [ 41 , 6 ] ] }}`
| `GeoJsonMultiLineString`
| converter
| `{"geoJsonLineString" : { "type" : "MultiLineString", coordinates: [
[ [ -73.97162 , 40.78205 ], [ -73.96374 , 40.77715 ] ],
[ [ -73.97880 , 40.77247 ], [ -73.97036 , 40.76811 ] ]
] }}`
|===
[[mapping-configuration]]
== Mapping Configuration
Unless explicitly configured, an instance of `MongoMappingConverter` is created by default when creating a `MongoTemplate`. You can create your own instance of the `MappingMongoConverter` so as to tell it where to scan the classpath at startup your domain classes in order to extract metadata and construct indexes. Also, by creating your own instance you can register Spring converters to use for mapping specific classes to and from the database.
Unless explicitly configured, an instance of `MappingMongoConverter` is created by default when creating a `MongoTemplate`. You can create your own instance of the `MappingMongoConverter` so as to tell it where to scan the classpath at startup your domain classes in order to extract metadata and construct indexes. Also, by creating your own instance you can register Spring converters to use for mapping specific classes to and from the database.
You can configure the `MongoMappingConverter` as well as `com.mongodb.Mongo` and MongoTemplate either using Java or XML based metadata. Here is an example using Spring's Java based configuration
You can configure the `MappingMongoConverter` as well as `com.mongodb.Mongo` and MongoTemplate either using Java or XML based metadata. Here is an example using Spring's Java based configuration
.@Configuration class to configure MongoDB mapping support
====
@@ -108,11 +296,11 @@ public class GeoSpatialAppConfig extends AbstractMongoConfiguration {
----
====
`AbstractMongoConfiguration` requires you to implement methods that define a `com.mongodb.Mongo` as well as provide a database name. `AbstractMongoConfiguration` also has a method you can override named '`getMappingBasePackage`' which tells the converter where to scan for classes annotated with the `@org.springframework.data.mongodb.core.mapping.Document` annotation.
`AbstractMongoConfiguration` requires you to implement methods that define a `com.mongodb.Mongo` as well as provide a database name. `AbstractMongoConfiguration` also has a method you can override named `getMappingBasePackage(…)` which tells the converter where to scan for classes annotated with the `@Document` annotation.
You can add additional converters to the converter by overriding the method afterMappingMongoConverterCreation. Also shown in the above example is a `LoggingEventListener` which logs `MongoMappingEvent`s that are posted onto Spring's `ApplicationContextEvent` infrastructure.
You can add additional converters to the converter by overriding the method afterMappingMongoConverterCreation. Also shown in the above example is a `LoggingEventListener` which logs `MongoMappingEvent` s that are posted onto Spring's `ApplicationContextEvent` infrastructure.
NOTE: AbstractMongoConfiguration will create a MongoTemplate instance and registered with the container under the name 'mongoTemplate'.
NOTE: AbstractMongoConfiguration will create a MongoTemplate instance and registered with the container under the name `mongoTemplate`.
You can also override the method `UserCredentials getUserCredentials()` to provide the username and password information to connect to the database.
@@ -165,7 +353,7 @@ The `base-package` property tells it where to scan for classes annotated with th
[[mapping-usage]]
== Metadata based Mapping
To take full advantage of the object mapping functionality inside the Spring Data/MongoDB support, you should annotate your mapped objects with the `@org.springframework.data.mongodb.core.mapping.Document` annotation. Although it is not necessary for the mapping framework to have this annotation (your POJOs will be mapped correctly, even without any annotations), it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. If you don't use this annotation, your application will take a slight performance hit the first time you store a domain object because the mapping framework needs to build up its internal metadata model so it knows about the properties of your domain object and how to persist them.
To take full advantage of the object mapping functionality inside the Spring Data/MongoDB support, you should annotate your mapped objects with the `@Document` annotation. Although it is not necessary for the mapping framework to have this annotation (your POJOs will be mapped correctly, even without any annotations), it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. If you don't use this annotation, your application will take a slight performance hit the first time you store a domain object because the mapping framework needs to build up its internal metadata model so it knows about the properties of your domain object and how to persist them.
.Example domain object
====
@@ -271,7 +459,7 @@ public class Person<T extends Address> {
return ssn;
}
// other getters/setters ommitted
// other getters/setters omitted
----
[[mapping-custom-object-construction]]
@@ -281,7 +469,7 @@ The mapping subsystem allows the customization of the object construction by ann
* If a parameter is annotated with the `@Value` annotation, the given expression is evaluated and the result is used as the parameter value.
* If the Java type has a property whose name matches the given field of the input document, then it's property information is used to select the appropriate constructor parameter to pass the input field value to. This works only if the parameter name information is present in the java `.class` files which can be achieved by compiling the source with debug information or using the new `-parameters` command-line switch for javac in Java 8.
* Otherwise an `MappingException` will be thrown indicating that the given constructor parameter could not be bound.
* Otherwise a `MappingException` will be thrown indicating that the given constructor parameter could not be bound.
[source,java]
----
@@ -313,7 +501,7 @@ Additional examples for using the `@PersistenceConstructor` annotation can be fo
[[mapping-usage-indexes.compound-index]]
=== Compound Indexes
Compound indexes are also supported. They are defined at the class level, rather than on indidividual properties.
Compound indexes are also supported. They are defined at the class level, rather than on individual properties.
NOTE: Compound indexes are very important to improve the performance of queries that involve criteria on multiple fields
@@ -346,7 +534,7 @@ public class Person {
NOTE: The text index feature is disabled by default for mongodb v.2.4.
Creating a text index allows to accumulate several fields into a searchable full text index. It is only possible to have one text index per collection so all fields marked with `@TextIndexed` are combined into this index. Properties can be weighted to influence document score for ranking results. The default language for the text index is english, to change the default language set `@Document(language="spanish")` to any language you want. Using a property called `language` or `@Language` allows to define a language override on a per document base.
Creating a text index allows accumulating several fields into a searchable full text index. It is only possible to have one text index per collection so all fields marked with `@TextIndexed` are combined into this index. Properties can be weighted to influence document score for ranking results. The default language for the text index is english, to change the default language set `@Document(language="spanish")` to any language you want. Using a property called `language` or `@Language` allows to define a language override on a per document base.
.Example Text Index Usage
====
@@ -401,7 +589,7 @@ public class Person {
----
====
There's no need to use something like `@OneToMany` because the mapping framework sees that you're wanting a one-to-many relationship because there is a List of objects. When the object is stored in MongoDB, there will be a list of DBRefs rather than the `Account` objects themselves.
There's no need to use something like `@OneToMany` because the mapping framework sees that you want a one-to-many relationship because there is a List of objects. When the object is stored in MongoDB, there will be a list of DBRefs rather than the `Account` objects themselves.
IMPORTANT: The mapping framework does not handle cascading saves. If you change an `Account` object that is referenced by a `Person` object, you must save the Account object separately. Calling `save` on the `Person` object will not automatically save the `Account` objects in the property `accounts`.
@@ -415,7 +603,7 @@ Simply declaring these beans in your Spring ApplicationContext will cause them t
[[mapping-explicit-converters]]
=== Overriding Mapping with explicit Converters
When storing and querying your objects it is convenient to have a `MongoConverter` instance handle the mapping of all Java types to DBObjects. However, sometimes you may want the `MongoConverter`'s do most of the work but allow you to selectively handle the conversion for a particular type or to optimize performance.
When storing and querying your objects it is convenient to have a `MongoConverter` instance handle the mapping of all Java types to DBObjects. However, sometimes you may want the `MongoConverter` s do most of the work but allow you to selectively handle the conversion for a particular type or to optimize performance.
To selectively handle the conversion yourself, register one or more one or more `org.springframework.core.convert.converter.Converter` instances with the MongoConverter.

View File

@@ -88,7 +88,7 @@ This section covers additional things to keep in mind when using the 3.0 driver.
* `IndexOperations.resetIndexCache()` is no longer supported.
* Any `MapReduceOptions.extraOption` is silently ignored.
* `WriteResult` does not longer hold error informations but throws an Exception.
* `WriteResult` does not longer hold error information but throws an Exception.
* `MongoOperations.executeInSession(…)` no longer calls `requestStart` / `requestDone`.
* Index name generation has become a driver internal operations, still we use the 2.x schema to generate names.
* Some Exception messages differ between the generation 2 and 3 servers as well as between _MMap.v1_ and _WiredTiger_ storage engine.

Some files were not shown because too many files have changed in this diff Show More