/* * 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. * 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.aggregation; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.springframework.data.domain.Sort.Direction.*; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; import static org.springframework.data.mongodb.core.aggregation.Fields.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.test.util.IsBsonObject.*; import lombok.Builder; import java.io.BufferedInputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Scanner; import org.bson.Document; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDateTime; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.dao.DataAccessException; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.geo.Metrics; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mongodb.core.CollectionCallback; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.Venue; import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry; import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.Granularities; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; import org.springframework.data.mongodb.core.index.GeospatialIndex; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.Person; import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.Version; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.mongodb.MongoException; import com.mongodb.client.MongoCollection; /** * Tests for {@link MongoTemplate#aggregate(Aggregation, Class, Class)}. * * @author Tobias Trelle * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl * @author Mark Paluch * @author Nikolay Bogdanov * @author Maninder Singh */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:infrastructure.xml") public class AggregationTests { private static final String INPUT_COLLECTION = "aggregation_test_collection"; private static final Logger LOGGER = LoggerFactory.getLogger(AggregationTests.class); private static final Version TWO_DOT_FOUR = new Version(2, 4); private static final Version TWO_DOT_SIX = new Version(2, 6); private static final Version THREE_DOT_TWO = new Version(3, 2); private static final Version THREE_DOT_FOUR = new Version(3, 4); private static boolean initialized = false; @Autowired MongoTemplate mongoTemplate; @Rule public ExpectedException exception = ExpectedException.none(); private static Version mongoVersion; @Before public void setUp() { queryMongoVersionIfNecessary(); cleanDb(); initSampleDataIfNecessary(); } private void queryMongoVersionIfNecessary() { if (mongoVersion == null) { org.bson.Document result = mongoTemplate.executeCommand("{ buildInfo: 1 }"); mongoVersion = Version.parse(result.get("version").toString()); } } @After public void cleanUp() { cleanDb(); } private void cleanDb() { mongoTemplate.dropCollection(INPUT_COLLECTION); mongoTemplate.dropCollection(Product.class); mongoTemplate.dropCollection(UserWithLikes.class); mongoTemplate.dropCollection(DATAMONGO753.class); mongoTemplate.dropCollection(Data.class); mongoTemplate.dropCollection(DATAMONGO788.class); mongoTemplate.dropCollection(User.class); mongoTemplate.dropCollection(Person.class); mongoTemplate.dropCollection(Reservation.class); mongoTemplate.dropCollection(Venue.class); mongoTemplate.dropCollection(MeterData.class); mongoTemplate.dropCollection(LineItem.class); mongoTemplate.dropCollection(InventoryItem.class); mongoTemplate.dropCollection(Sales.class); mongoTemplate.dropCollection(Sales2.class); mongoTemplate.dropCollection(Employee.class); mongoTemplate.dropCollection(Art.class); } /** * Imports the sample dataset (zips.json) if necessary (e.g. if it doesn't exist yet). The dataset can originally be * found on the mongodb aggregation framework example website: * * @see <a href="https://docs.mongodb.org/manual/tutorial/aggregation-examples/">MongoDB Aggregation Examples</a> */ private void initSampleDataIfNecessary() { if (!initialized) { LOGGER.debug("Server uses MongoDB Version: {}", mongoVersion); mongoTemplate.dropCollection(ZipInfo.class); mongoTemplate.execute(ZipInfo.class, new CollectionCallback<Void>() { @Override public Void doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException { Scanner scanner = null; try { scanner = new Scanner(new BufferedInputStream(new ClassPathResource("zips.json").getInputStream())); while (scanner.hasNextLine()) { String zipInfoRecord = scanner.nextLine(); collection.insertOne(Document.parse(zipInfoRecord)); } } catch (Exception e) { if (scanner != null) { scanner.close(); } throw new RuntimeException("Could not load mongodb sample dataset!", e); } return null; } }); long count = mongoTemplate.count(new Query(), ZipInfo.class); assertThat(count, is(29467L)); initialized = true; } } @Test(expected = IllegalArgumentException.class) // DATAMONGO-586 public void shouldHandleMissingInputCollection() { mongoTemplate.aggregate(newAggregation(), (String) null, TagCount.class); } @Test(expected = IllegalArgumentException.class) // DATAMONGO-586 public void shouldHandleMissingAggregationPipeline() { mongoTemplate.aggregate(null, INPUT_COLLECTION, TagCount.class); } @Test(expected = IllegalArgumentException.class) // DATAMONGO-586 public void shouldHandleMissingEntityClass() { mongoTemplate.aggregate(newAggregation(), INPUT_COLLECTION, null); } @Test // DATAMONGO-586 public void shouldAggregate() { createTagDocuments(); Aggregation agg = newAggregation( // project("tags"), // unwind("tags"), // group("tags") // .count().as("n"), // project("n") // .and("tag").previousOperation(), // sort(DESC, "n") // ); AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); List<TagCount> tagCount = results.getMappedResults(); assertThat(tagCount, is(notNullValue())); assertThat(tagCount.size(), is(3)); assertTagCount("spring", 3, tagCount.get(0)); assertTagCount("mongodb", 2, tagCount.get(1)); assertTagCount("nosql", 1, tagCount.get(2)); } @Test // DATAMONGO-1637 public void shouldAggregateAndStream() { createTagDocuments(); Aggregation agg = newAggregation( // project("tags"), // unwind("tags"), // group("tags") // .count().as("n"), // project("n") // .and("tag").previousOperation(), // sort(DESC, "n") // ).withOptions(new AggregationOptions(true, false, 1)); CloseableIterator<TagCount> iterator = mongoTemplate.aggregateStream(agg, INPUT_COLLECTION, TagCount.class); assertThat(iterator, is(notNullValue())); List<TagCount> tagCount = toList(iterator); iterator.close(); assertThat(tagCount, is(notNullValue())); assertThat(tagCount.size(), is(3)); assertTagCount("spring", 3, tagCount.get(0)); assertTagCount("mongodb", 2, tagCount.get(1)); assertTagCount("nosql", 1, tagCount.get(2)); } @Test // DATAMONGO-586 public void shouldAggregateEmptyCollection() { Aggregation aggregation = newAggregation(// project("tags"), // unwind("tags"), // group("tags") // .count().as("n"), // project("n") // .and("tag").previousOperation(), // sort(DESC, "n") // ); AggregationResults<TagCount> results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); List<TagCount> tagCount = results.getMappedResults(); assertThat(tagCount, is(notNullValue())); assertThat(tagCount.size(), is(0)); } @Test // DATAMONGO-1637 public void shouldAggregateEmptyCollectionAndStream() { Aggregation aggregation = newAggregation(// project("tags"), // unwind("tags"), // group("tags") // .count().as("n"), // project("n") // .and("tag").previousOperation(), // sort(DESC, "n") // ); CloseableIterator<TagCount> results = mongoTemplate.aggregateStream(aggregation, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); List<TagCount> tagCount = toList(results); results.close(); assertThat(tagCount.size(), is(0)); } @Test // DATAMONGO-1391 public void shouldUnwindWithIndex() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); MongoCollection<Document> coll = mongoTemplate.getCollection(INPUT_COLLECTION); coll.insertOne(createDocument("Doc1", "spring", "mongodb", "nosql")); coll.insertOne(createDocument("Doc2")); Aggregation agg = newAggregation( // project("tags"), // unwind("tags", "n"), // project("n") // .and("tag").previousOperation(), // sort(DESC, "n") // ); AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); List<TagCount> tagCount = results.getMappedResults(); assertThat(tagCount, is(notNullValue())); assertThat(tagCount.size(), is(3)); } @Test // DATAMONGO-1391 public void shouldUnwindPreserveEmpty() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); MongoCollection<Document> coll = mongoTemplate.getCollection(INPUT_COLLECTION); coll.insertOne(createDocument("Doc1", "spring", "mongodb", "nosql")); coll.insertOne(createDocument("Doc2")); Aggregation agg = newAggregation( // project("tags"), // unwind("tags", "n", true), // sort(DESC, "n") // ); AggregationResults<Document> results = mongoTemplate.aggregate(agg, INPUT_COLLECTION, Document.class); assertThat(results, is(notNullValue())); List<Document> tagCount = results.getMappedResults(); assertThat(tagCount, is(notNullValue())); assertThat(tagCount.size(), is(4)); assertThat(tagCount.get(0), isBsonObject().containing("n", 2L)); assertThat(tagCount.get(3), isBsonObject().notContaining("n")); } @Test // DATAMONGO-586 public void shouldDetectResultMismatch() { createTagDocuments(); Aggregation aggregation = newAggregation( // project("tags"), // unwind("tags"), // group("tags") // .count().as("count"), // count field not present limit(2) // ); AggregationResults<TagCount> results = mongoTemplate.aggregate(aggregation, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); List<TagCount> tagCount = results.getMappedResults(); assertThat(tagCount, is(notNullValue())); assertThat(tagCount.size(), is(2)); assertTagCount(null, 0, tagCount.get(0)); assertTagCount(null, 0, tagCount.get(1)); } @Test // DATAMONGO-1637 public void shouldDetectResultMismatchWhileStreaming() { createTagDocuments(); Aggregation aggregation = newAggregation( // project("tags"), // unwind("tags"), // group("tags") // .count().as("count"), // count field not present limit(2) // ); CloseableIterator<TagCount> results = mongoTemplate.aggregateStream(aggregation, INPUT_COLLECTION, TagCount.class); assertThat(results, is(notNullValue())); List<TagCount> tagCount = toList(results); results.close(); assertThat(tagCount.size(), is(2)); assertTagCount(null, 0, tagCount.get(0)); assertTagCount(null, 0, tagCount.get(1)); } @Test // DATAMONGO-586 public void complexAggregationFrameworkUsageLargestAndSmallestCitiesByState() { /* //complex mongodb aggregation framework example from https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state db.zipInfo.aggregate( { $group: { _id: { state: '$state', city: '$city' }, pop: { $sum: '$pop' } } }, { $sort: { pop: 1, '_id.state': 1, '_id.city': 1 } }, { $group: { _id: '$_id.state', biggestCity: { $last: '$_id.city' }, biggestPop: { $last: '$pop' }, smallestCity: { $first: '$_id.city' }, smallestPop: { $first: '$pop' } } }, { $project: { _id: 0, state: '$_id', biggestCity: { name: '$biggestCity', pop: '$biggestPop' }, smallestCity: { name: '$smallestCity', pop: '$smallestPop' } } }, { $sort: { state: 1 } } ) */ TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, // group("state", "city").sum("population").as("pop"), // sort(ASC, "pop", "state", "city"), // group("state") // .last("city").as("biggestCity") // .last("pop").as("biggestPop") // .first("city").as("smallestCity") // .first("pop").as("smallestPop"), // project() // .and("state").previousOperation() // .and("biggestCity").nested(bind("name", "biggestCity").and("population", "biggestPop")) // .and("smallestCity").nested(bind("name", "smallestCity").and("population", "smallestPop")), // sort(ASC, "state") // ); assertThat(aggregation, is(notNullValue())); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<ZipInfoStats> result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class); assertThat(result, is(notNullValue())); assertThat(result.getMappedResults(), is(notNullValue())); assertThat(result.getMappedResults().size(), is(51)); ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0); assertThat(firstZipInfoStats, is(notNullValue())); assertThat(firstZipInfoStats.id, is(nullValue())); assertThat(firstZipInfoStats.state, is("AK")); assertThat(firstZipInfoStats.smallestCity, is(notNullValue())); assertThat(firstZipInfoStats.smallestCity.name, is("CHEVAK")); assertThat(firstZipInfoStats.smallestCity.population, is(0)); assertThat(firstZipInfoStats.biggestCity, is(notNullValue())); assertThat(firstZipInfoStats.biggestCity.name, is("ANCHORAGE")); assertThat(firstZipInfoStats.biggestCity.population, is(183987)); ZipInfoStats lastZipInfoStats = result.getMappedResults().get(50); assertThat(lastZipInfoStats, is(notNullValue())); assertThat(lastZipInfoStats.id, is(nullValue())); assertThat(lastZipInfoStats.state, is("WY")); assertThat(lastZipInfoStats.smallestCity, is(notNullValue())); assertThat(lastZipInfoStats.smallestCity.name, is("LOST SPRINGS")); assertThat(lastZipInfoStats.smallestCity.population, is(6)); assertThat(lastZipInfoStats.biggestCity, is(notNullValue())); assertThat(lastZipInfoStats.biggestCity.name, is("CHEYENNE")); assertThat(lastZipInfoStats.biggestCity.population, is(70185)); } @Test // DATAMONGO-586 public void findStatesWithPopulationOver10MillionAggregationExample() { /* //complex mongodb aggregation framework example from https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state db.zipcodes.aggregate( { $group: { _id:"$state", totalPop:{ $sum:"$pop"} } }, { $sort: { _id: 1, "totalPop": 1 } }, { $match: { totalPop: { $gte:10*1000*1000 } } } ) */ TypedAggregation<ZipInfo> agg = newAggregation(ZipInfo.class, // group("state") // .sum("population").as("totalPop"), // sort(ASC, previousOperation(), "totalPop"), // match(where("totalPop").gte(10 * 1000 * 1000)) // ); assertThat(agg, is(notNullValue())); assertThat(agg.toString(), is(notNullValue())); AggregationResults<StateStats> result = mongoTemplate.aggregate(agg, StateStats.class); assertThat(result, is(notNullValue())); assertThat(result.getMappedResults(), is(notNullValue())); assertThat(result.getMappedResults().size(), is(7)); StateStats stateStats = result.getMappedResults().get(0); assertThat(stateStats, is(notNullValue())); assertThat(stateStats.id, is("CA")); assertThat(stateStats.state, is(nullValue())); assertThat(stateStats.totalPopulation, is(29760021)); } /** * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/cond/#example">MongoDB Aggregation * Framework: $cond</a> */ @Test // DATAMONGO-861 public void aggregationUsingConditionalProjectionToCalculateDiscount() { /* db.inventory.aggregate( [ { $project: { item: 1, discount: { $cond: { if: { $gte: [ "$qty", 250 ] }, then: 30, else: 20 } } } } ] ) */ mongoTemplate.insert(new InventoryItem(1, "abc1", 300)); mongoTemplate.insert(new InventoryItem(2, "abc2", 200)); mongoTemplate.insert(new InventoryItem(3, "xyz1", 250)); TypedAggregation<InventoryItem> aggregation = newAggregation(InventoryItem.class, // project("item") // .and("discount")// .applyCondition(ConditionalOperators.Cond.newBuilder().when(Criteria.where("qty").gte(250)) // .then(30) // .otherwise(20))); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(3)); Document first = result.getMappedResults().get(0); assertThat(first.get("_id"), is((Object) 1)); assertThat(first.get("discount"), is((Object) 30)); Document second = result.getMappedResults().get(1); assertThat(second.get("_id"), is((Object) 2)); assertThat(second.get("discount"), is((Object) 20)); Document third = result.getMappedResults().get(2); assertThat(third.get("_id"), is((Object) 3)); assertThat(third.get("discount"), is((Object) 30)); } /** * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/#example">MongoDB Aggregation * Framework: $ifNull</a> */ @Test // DATAMONGO-861 public void aggregationUsingIfNullToProjectSaneDefaults() { /* db.inventory.aggregate( [ { $project: { item: 1, description: { $ifNull: [ "$description", "Unspecified" ] } } } ] ) */ mongoTemplate.insert(new InventoryItem(1, "abc1", "product 1", 300)); mongoTemplate.insert(new InventoryItem(2, "abc2", 200)); mongoTemplate.insert(new InventoryItem(3, "xyz1", 250)); TypedAggregation<InventoryItem> aggregation = newAggregation(InventoryItem.class, // project("item") // .and(ConditionalOperators.ifNull("description").then("Unspecified")) // .as("description")// ); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(3)); Document first = result.getMappedResults().get(0); assertThat(first.get("_id"), is((Object) 1)); assertThat(first.get("description"), is((Object) "product 1")); Document second = result.getMappedResults().get(1); assertThat(second.get("_id"), is((Object) 2)); assertThat(second.get("description"), is((Object) "Unspecified")); } @Test // DATAMONGO-861 public void aggregationUsingConditionalProjection() { TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, // project() // .and("largePopulation")// .applyCondition(ConditionalOperators.when(Criteria.where("population").gte(20000)) // .then(true) // .otherwise(false)) // .and("population").as("population")); assertThat(aggregation, is(notNullValue())); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(29467)); Document firstZipInfoStats = result.getMappedResults().get(0); assertThat(firstZipInfoStats.get("largePopulation"), is((Object) false)); assertThat(firstZipInfoStats.get("population"), is((Object) 6055)); } @Test // DATAMONGO-861 public void aggregationUsingNestedConditionalProjection() { TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, // project() // .and("size")// .applyCondition(ConditionalOperators.when(Criteria.where("population").gte(20000)) // .then( ConditionalOperators.when(Criteria.where("population").gte(200000)).then("huge").otherwise("small")) // .otherwise("small")) // .and("population").as("population")); assertThat(aggregation, is(notNullValue())); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(29467)); Document firstZipInfoStats = result.getMappedResults().get(0); assertThat(firstZipInfoStats.get("size"), is((Object) "small")); assertThat(firstZipInfoStats.get("population"), is((Object) 6055)); } @Test // DATAMONGO-861 public void aggregationUsingIfNullProjection() { mongoTemplate.insert(new LineItem("id", "caption", 0)); mongoTemplate.insert(new LineItem("idonly", null, 0)); TypedAggregation<LineItem> aggregation = newAggregation(LineItem.class, // project("id") // .and("caption")// .applyCondition(ConditionalOperators.ifNull("caption").then("unknown")), sort(ASC, "id")); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(2)); Document id = result.getMappedResults().get(0); assertThat((String) id.get("caption"), is(equalTo("caption"))); Document idonly = result.getMappedResults().get(1); assertThat((String) idonly.get("caption"), is(equalTo("unknown"))); } @Test // DATAMONGO-861 public void aggregationUsingIfNullReplaceWithFieldReferenceProjection() { mongoTemplate.insert(new LineItem("id", "caption", 0)); mongoTemplate.insert(new LineItem("idonly", null, 0)); TypedAggregation<LineItem> aggregation = newAggregation(LineItem.class, // project("id") // .and("caption")// .applyCondition(ConditionalOperators.ifNull("caption").thenValueOf("id")), sort(ASC, "id")); assertThat(aggregation.toString(), is(notNullValue())); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(2)); Document id = result.getMappedResults().get(0); assertThat((String) id.get("caption"), is(equalTo("caption"))); Document idonly = result.getMappedResults().get(1); assertThat((String) idonly.get("caption"), is(equalTo("idonly"))); } @Test // DATAMONGO-861 public void shouldAllowGroupingUsingConditionalExpressions() { mongoTemplate.dropCollection(CarPerson.class); CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000), new CarDescriptor.Entry("MAKE1", "MODEL2", 2001)); CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2015)); mongoTemplate.save(person1); mongoTemplate.save(person2); mongoTemplate.save(person3); TypedAggregation<CarPerson> agg = Aggregation.newAggregation(CarPerson.class, unwind("descriptors.carDescriptor.entries"), // project() // .and(ConditionalOperators // .when(Criteria.where("descriptors.carDescriptor.entries.make").is("MAKE1")).then("good") .otherwise("meh")) .as("make") // .and("descriptors.carDescriptor.entries.model").as("model") // .and("descriptors.carDescriptor.entries.year").as("year"), // group("make").avg(ConditionalOperators // .when(Criteria.where("year").gte(2012)) // .then(1) // .otherwise(9000)).as("score"), sort(ASC, "make")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); assertThat(result.getMappedResults(), hasSize(2)); Document meh = result.getMappedResults().get(0); assertThat((String) meh.get("_id"), is(equalTo("meh"))); assertThat(((Number) meh.get("score")).longValue(), is(equalTo(1L))); Document good = result.getMappedResults().get(1); assertThat((String) good.get("_id"), is(equalTo("good"))); assertThat(((Number) good.get("score")).longValue(), is(equalTo(9000L))); } /** * @see <a href= * "https://docs.mongodb.com/manual/tutorial/aggregation-with-user-preference-data/#return-the-five-most-common-likes">Return * the Five Most Common “Likes”</a> */ @Test // DATAMONGO-586 public void returnFiveMostCommonLikesAggregationFrameworkExample() { createUserWithLikesDocuments(); TypedAggregation<UserWithLikes> agg = createUsersWithCommonLikesAggregation(); assertThat(agg, is(notNullValue())); assertThat(agg.toString(), is(notNullValue())); AggregationResults<LikeStats> result = mongoTemplate.aggregate(agg, LikeStats.class); assertThat(result, is(notNullValue())); assertThat(result.getMappedResults(), is(notNullValue())); assertThat(result.getMappedResults().size(), is(5)); assertLikeStats(result.getMappedResults().get(0), "a", 4); assertLikeStats(result.getMappedResults().get(1), "b", 2); assertLikeStats(result.getMappedResults().get(2), "c", 4); assertLikeStats(result.getMappedResults().get(3), "d", 2); assertLikeStats(result.getMappedResults().get(4), "e", 3); } protected TypedAggregation<UserWithLikes> createUsersWithCommonLikesAggregation() { return newAggregation(UserWithLikes.class, // unwind("likes"), // group("likes").count().as("number"), // sort(DESC, "number"), // limit(5), // sort(ASC, previousOperation()) // ); } @Test // DATAMONGO-586 public void arithmenticOperatorsInProjectionExample() { Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); mongoTemplate.insert(product); TypedAggregation<Product> agg = newAggregation(Product.class, // project("name", "netPrice") // .and("netPrice").plus(1).as("netPricePlus1") // .and("netPrice").minus(1).as("netPriceMinus1") // .and("netPrice").multiply(2).as("netPriceMul2") // .and("netPrice").divide(1.19).as("netPriceDiv119") // .and("spaceUnits").mod(2).as("spaceUnitsMod2") // .and("spaceUnits").plus("spaceUnits").as("spaceUnitsPlusSpaceUnits") // .and("spaceUnits").minus("spaceUnits").as("spaceUnitsMinusSpaceUnits") // .and("spaceUnits").multiply("spaceUnits").as("spaceUnitsMultiplySpaceUnits") // .and("spaceUnits").divide("spaceUnits").as("spaceUnitsDivideSpaceUnits") // .and("spaceUnits").mod("spaceUnits").as("spaceUnitsModSpaceUnits") // ); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); List<Document> resultList = result.getMappedResults(); assertThat(resultList, is(notNullValue())); assertThat((String) resultList.get(0).get("_id"), is(product.id)); assertThat((String) resultList.get(0).get("name"), is(product.name)); assertThat((Double) resultList.get(0).get("netPricePlus1"), is(product.netPrice + 1)); assertThat((Double) resultList.get(0).get("netPriceMinus1"), is(product.netPrice - 1)); assertThat((Double) resultList.get(0).get("netPriceMul2"), is(product.netPrice * 2)); assertThat((Double) resultList.get(0).get("netPriceDiv119"), is(product.netPrice / 1.19)); assertThat((Integer) resultList.get(0).get("spaceUnitsMod2"), is(product.spaceUnits % 2)); assertThat((Integer) resultList.get(0).get("spaceUnitsPlusSpaceUnits"), is(product.spaceUnits + product.spaceUnits)); assertThat((Integer) resultList.get(0).get("spaceUnitsMinusSpaceUnits"), is(product.spaceUnits - product.spaceUnits)); assertThat((Integer) resultList.get(0).get("spaceUnitsMultiplySpaceUnits"), is(product.spaceUnits * product.spaceUnits)); assertThat((Double) resultList.get(0).get("spaceUnitsDivideSpaceUnits"), is((double) (product.spaceUnits / product.spaceUnits))); assertThat((Integer) resultList.get(0).get("spaceUnitsModSpaceUnits"), is(product.spaceUnits % product.spaceUnits)); } @Test // DATAMONGO-774 public void expressionsInProjectionExample() { Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); mongoTemplate.insert(product); TypedAggregation<Product> agg = newAggregation(Product.class, // project("name", "netPrice") // .andExpression("netPrice + 1").as("netPricePlus1") // .andExpression("netPrice - 1").as("netPriceMinus1") // .andExpression("netPrice / 2").as("netPriceDiv2") // .andExpression("netPrice * 1.19").as("grossPrice") // .andExpression("spaceUnits % 2").as("spaceUnitsMod2") // .andExpression("(netPrice * 0.8 + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge") // ); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); List<Document> resultList = result.getMappedResults(); assertThat(resultList, is(notNullValue())); assertThat((String) resultList.get(0).get("_id"), is(product.id)); assertThat((String) resultList.get(0).get("name"), is(product.name)); assertThat((Double) resultList.get(0).get("netPricePlus1"), is(product.netPrice + 1)); assertThat((Double) resultList.get(0).get("netPriceMinus1"), is(product.netPrice - 1)); assertThat((Double) resultList.get(0).get("netPriceDiv2"), is(product.netPrice / 2)); assertThat((Double) resultList.get(0).get("grossPrice"), is(product.netPrice * 1.19)); assertThat((Integer) resultList.get(0).get("spaceUnitsMod2"), is(product.spaceUnits % 2)); assertThat((Double) resultList.get(0).get("grossPriceIncludingDiscountAndCharge"), is((product.netPrice * 0.8 + 1.2) * 1.19)); } @Test // DATAMONGO-774 public void stringExpressionsInProjectionExample() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_FOUR)); Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); mongoTemplate.insert(product); TypedAggregation<Product> agg = newAggregation(Product.class, // project("name", "netPrice") // .andExpression("concat(name, '_bubu')").as("name_bubu") // ); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); List<Document> resultList = result.getMappedResults(); assertThat(resultList, is(notNullValue())); assertThat((String) resultList.get(0).get("_id"), is(product.id)); assertThat((String) resultList.get(0).get("name"), is(product.name)); assertThat((String) resultList.get(0).get("name_bubu"), is(product.name + "_bubu")); } @Test // DATAMONGO-774 public void expressionsInProjectionExampleShowcase() { Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); mongoTemplate.insert(product); double shippingCosts = 1.2; TypedAggregation<Product> agg = newAggregation(Product.class, // project("name", "netPrice") // .andExpression("(netPrice * (1-discountRate) + [0]) * (1+taxRate)", shippingCosts).as("salesPrice") // ); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); List<Document> resultList = result.getMappedResults(); assertThat(resultList, is(notNullValue())); Document firstItem = resultList.get(0); assertThat((String) firstItem.get("_id"), is(product.id)); assertThat((String) firstItem.get("name"), is(product.name)); assertThat((Double) firstItem.get("salesPrice"), is((product.netPrice * (1 - product.discountRate) + shippingCosts) * (1 + product.taxRate))); } @Test public void shouldThrowExceptionIfUnknownFieldIsReferencedInArithmenticExpressionsInProjection() { exception.expect(MappingException.class); exception.expectMessage("unknown"); Product product = new Product("P1", "A", 1.99, 3, 0.05, 0.19); mongoTemplate.insert(product); TypedAggregation<Product> agg = newAggregation(Product.class, // project("name", "netPrice") // .andExpression("unknown + 1").as("netPricePlus1") // ); mongoTemplate.aggregate(agg, Document.class); } /** * @see <a href= * "http://stackoverflow.com/questions/18653574/spring-data-mongodb-aggregation-framework-invalid-reference-in-group-operati">Spring * Data MongoDB - Aggregation Framework - invalid reference in group Operation</a> */ @Test // DATAMONGO-753 public void allowsNestedFieldReferencesAsGroupIdsInGroupExpressions() { mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("A", 1), new PD("B", 1), new PD("C", 1))); mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("B", 1), new PD("B", 1), new PD("C", 1))); TypedAggregation<DATAMONGO753> agg = newAggregation(DATAMONGO753.class, // unwind("pd"), // group("pd.pDch") // the nested field expression .sum("pd.up").as("uplift"), // project("_id", "uplift")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); List<Document> stats = result.getMappedResults(); assertThat(stats.size(), is(3)); assertThat(stats.get(0).get("_id").toString(), is("C")); assertThat((Integer) stats.get(0).get("uplift"), is(2)); assertThat(stats.get(1).get("_id").toString(), is("B")); assertThat((Integer) stats.get(1).get("uplift"), is(3)); assertThat(stats.get(2).get("_id").toString(), is("A")); assertThat((Integer) stats.get(2).get("uplift"), is(1)); } /** * @see <a href= * "http://stackoverflow.com/questions/18653574/spring-data-mongodb-aggregation-framework-invalid-reference-in-group-operati">Spring * Data MongoDB - Aggregation Framework - invalid reference in group Operation</a> */ @Test // DATAMONGO-753 public void aliasesNestedFieldInProjectionImmediately() { mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("A", 1), new PD("B", 1), new PD("C", 1))); mongoTemplate.insert(new DATAMONGO753().withPDs(new PD("B", 1), new PD("B", 1), new PD("C", 1))); TypedAggregation<DATAMONGO753> agg = newAggregation(DATAMONGO753.class, // unwind("pd"), // project().and("pd.up").as("up")); AggregationResults<Document> results = mongoTemplate.aggregate(agg, Document.class); List<Document> mappedResults = results.getMappedResults(); assertThat(mappedResults, hasSize(6)); for (Document element : mappedResults) { assertThat(element.get("up"), is((Object) 1)); } } @Test // DATAMONGO-774 public void shouldPerformDateProjectionOperatorsCorrectly() throws ParseException { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_FOUR)); Data data = new Data(); data.stringValue = "ABC"; mongoTemplate.insert(data); TypedAggregation<Data> agg = newAggregation(Data.class, project() // .andExpression("concat(stringValue, 'DE')").as("concat") // .andExpression("strcasecmp(stringValue,'XYZ')").as("strcasecmp") // .andExpression("substr(stringValue,1,1)").as("substr") // .andExpression("toLower(stringValue)").as("toLower") // .andExpression("toUpper(toLower(stringValue))").as("toUpper") // ); AggregationResults<Document> results = mongoTemplate.aggregate(agg, Document.class); Document document = results.getUniqueMappedResult(); assertThat(document, is(notNullValue())); assertThat((String) document.get("concat"), is("ABCDE")); assertThat((Integer) document.get("strcasecmp"), is(-1)); assertThat((String) document.get("substr"), is("B")); assertThat((String) document.get("toLower"), is("abc")); assertThat((String) document.get("toUpper"), is("ABC")); } @Test // DATAMONGO-774 public void shouldPerformStringProjectionOperatorsCorrectly() throws ParseException { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_FOUR)); Data data = new Data(); data.dateValue = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSSZ").parse("29.08.1983 12:34:56.789+0000"); mongoTemplate.insert(data); TypedAggregation<Data> agg = newAggregation(Data.class, project() // .andExpression("dayOfYear(dateValue)").as("dayOfYear") // .andExpression("dayOfMonth(dateValue)").as("dayOfMonth") // .andExpression("dayOfWeek(dateValue)").as("dayOfWeek") // .andExpression("year(dateValue)").as("year") // .andExpression("month(dateValue)").as("month") // .andExpression("week(dateValue)").as("week") // .andExpression("hour(dateValue)").as("hour") // .andExpression("minute(dateValue)").as("minute") // .andExpression("second(dateValue)").as("second") // .andExpression("millisecond(dateValue)").as("millisecond") // ); AggregationResults<Document> results = mongoTemplate.aggregate(agg, Document.class); Document document = results.getUniqueMappedResult(); assertThat(document, is(notNullValue())); assertThat((Integer) document.get("dayOfYear"), is(241)); assertThat((Integer) document.get("dayOfMonth"), is(29)); assertThat((Integer) document.get("dayOfWeek"), is(2)); assertThat((Integer) document.get("year"), is(1983)); assertThat((Integer) document.get("month"), is(8)); assertThat((Integer) document.get("week"), is(35)); assertThat((Integer) document.get("hour"), is(12)); assertThat((Integer) document.get("minute"), is(34)); assertThat((Integer) document.get("second"), is(56)); assertThat((Integer) document.get("millisecond"), is(789)); } @Test // DATAMONGO-1550 public void shouldPerformReplaceRootOperatorCorrectly() throws ParseException { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); Data data = new Data(); DataItem dataItem = new DataItem(); dataItem.primitiveIntValue = 42; data.item = dataItem; mongoTemplate.insert(data); TypedAggregation<Data> agg = newAggregation(Data.class, project("item"), // replaceRoot("item"), // project().and("primitiveIntValue").as("my_primitiveIntValue")); AggregationResults<Document> results = mongoTemplate.aggregate(agg, Document.class); Document resultDocument = results.getUniqueMappedResult(); assertThat(resultDocument, is(notNullValue())); assertThat((Integer) resultDocument.get("my_primitiveIntValue"), is(42)); assertThat((Integer) resultDocument.keySet().size(), is(1)); } @Test // DATAMONGO-788 public void referencesToGroupIdsShouldBeRenderedProperly() { mongoTemplate.insert(new DATAMONGO788(1, 1)); mongoTemplate.insert(new DATAMONGO788(1, 1)); mongoTemplate.insert(new DATAMONGO788(1, 1)); mongoTemplate.insert(new DATAMONGO788(2, 1)); mongoTemplate.insert(new DATAMONGO788(2, 1)); AggregationOperation projectFirst = Aggregation.project("x", "y").and("xField").as("x").and("yField").as("y"); AggregationOperation group = Aggregation.group("x", "y").count().as("xPerY"); AggregationOperation project = Aggregation.project("xPerY", "x", "y").andExclude("_id"); TypedAggregation<DATAMONGO788> aggregation = Aggregation.newAggregation(DATAMONGO788.class, projectFirst, group, project); AggregationResults<Document> aggResults = mongoTemplate.aggregate(aggregation, Document.class); List<Document> items = aggResults.getMappedResults(); assertThat(items.size(), is(2)); assertThat((Integer) items.get(0).get("xPerY"), is(2)); assertThat((Integer) items.get(0).get("x"), is(2)); assertThat((Integer) items.get(0).get("y"), is(1)); assertThat((Integer) items.get(1).get("xPerY"), is(3)); assertThat((Integer) items.get(1).get("x"), is(1)); assertThat((Integer) items.get(1).get("y"), is(1)); } @Test // DATAMONGO-806 public void shouldAllowGroupByIdFields() { mongoTemplate.dropCollection(User.class); LocalDateTime now = new LocalDateTime(); User user1 = new User("u1", new PushMessage("1", "aaa", now.toDate())); User user2 = new User("u2", new PushMessage("2", "bbb", now.minusDays(2).toDate())); User user3 = new User("u3", new PushMessage("3", "ccc", now.minusDays(1).toDate())); mongoTemplate.save(user1); mongoTemplate.save(user2); mongoTemplate.save(user3); Aggregation agg = newAggregation( // project("id", "msgs"), // unwind("msgs"), // match(where("msgs.createDate").gt(now.minusDays(1).toDate())), // group("id").push("msgs").as("msgs") // ); AggregationResults<Document> results = mongoTemplate.aggregate(agg, User.class, Document.class); List<Document> mappedResults = results.getMappedResults(); Document firstItem = mappedResults.get(0); assertThat(firstItem.get("_id"), is(notNullValue())); assertThat(String.valueOf(firstItem.get("_id")), is("u1")); } @Test // DATAMONGO-840 public void shouldAggregateOrderDataToAnInvoice() { mongoTemplate.dropCollection(Order.class); double taxRate = 0.19; LineItem product1 = new LineItem("1", "p1", 1.23); LineItem product2 = new LineItem("2", "p2", 0.87, 2); LineItem product3 = new LineItem("3", "p3", 5.33); Order order = new Order("o4711", "c42", new Date()).addItem(product1).addItem(product2).addItem(product3); mongoTemplate.save(order); AggregationResults<Invoice> results = mongoTemplate.aggregate(newAggregation(Order.class, // match(where("id").is(order.getId())), unwind("items"), // project("id", "customerId", "items") // .andExpression("items.price * items.quantity").as("lineTotal"), // group("id") // .sum("lineTotal").as("netAmount") // .addToSet("items").as("items"), // project("id", "items", "netAmount") // .and("orderId").previousOperation() // .andExpression("netAmount * [0]", taxRate).as("taxAmount") // .andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount") // ), Invoice.class); Invoice invoice = results.getUniqueMappedResult(); assertThat(invoice, is(notNullValue())); assertThat(invoice.getOrderId(), is(order.getId())); assertThat(invoice.getNetAmount(), is(closeTo(8.3, 000001))); assertThat(invoice.getTaxAmount(), is(closeTo(1.577, 000001))); assertThat(invoice.getTotalAmount(), is(closeTo(9.877, 000001))); } @Test // DATAMONGO-924 public void shouldAllowGroupingByAliasedFieldDefinedInFormerAggregationStage() { mongoTemplate.dropCollection(CarPerson.class); CarPerson person1 = new CarPerson("first1", "last1", new CarDescriptor.Entry("MAKE1", "MODEL1", 2000), new CarDescriptor.Entry("MAKE1", "MODEL2", 2001), new CarDescriptor.Entry("MAKE2", "MODEL3", 2010), new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); CarPerson person2 = new CarPerson("first2", "last2", new CarDescriptor.Entry("MAKE3", "MODEL4", 2014)); CarPerson person3 = new CarPerson("first3", "last3", new CarDescriptor.Entry("MAKE2", "MODEL5", 2011)); mongoTemplate.save(person1); mongoTemplate.save(person2); mongoTemplate.save(person3); TypedAggregation<CarPerson> agg = Aggregation.newAggregation(CarPerson.class, unwind("descriptors.carDescriptor.entries"), // project() // .and("descriptors.carDescriptor.entries.make").as("make") // .and("descriptors.carDescriptor.entries.model").as("model") // .and("firstName").as("firstName") // .and("lastName").as("lastName"), // group("make")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); assertThat(result.getMappedResults(), hasSize(3)); } @Test // DATAMONGO-960 public void returnFiveMostCommonLikesAggregationFrameworkExampleWithSortOnDiskOptionEnabled() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); createUserWithLikesDocuments(); TypedAggregation<UserWithLikes> agg = createUsersWithCommonLikesAggregation() // .withOptions(newAggregationOptions().allowDiskUse(true).build()); assertThat(agg, is(notNullValue())); assertThat(agg.toString(), is(notNullValue())); AggregationResults<LikeStats> result = mongoTemplate.aggregate(agg, LikeStats.class); assertThat(result, is(notNullValue())); assertThat(result.getMappedResults(), is(notNullValue())); assertThat(result.getMappedResults().size(), is(5)); assertLikeStats(result.getMappedResults().get(0), "a", 4); assertLikeStats(result.getMappedResults().get(1), "b", 2); assertLikeStats(result.getMappedResults().get(2), "c", 4); assertLikeStats(result.getMappedResults().get(3), "d", 2); assertLikeStats(result.getMappedResults().get(4), "e", 3); } @Test // DATAMONGO-1637 public void returnFiveMostCommonLikesAggregationFrameworkExampleWithSortOnDiskOptionEnabledWhileStreaming() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); createUserWithLikesDocuments(); TypedAggregation<UserWithLikes> agg = createUsersWithCommonLikesAggregation() // .withOptions(newAggregationOptions().allowDiskUse(true).build()); assertThat(agg, is(notNullValue())); assertThat(agg.toString(), is(notNullValue())); CloseableIterator<LikeStats> iterator = mongoTemplate.aggregateStream(agg, LikeStats.class); List<LikeStats> result = toList(iterator); iterator.close(); assertThat(result, is(notNullValue())); assertThat(result, is(notNullValue())); assertThat(result.size(), is(5)); assertLikeStats(result.get(0), "a", 4); assertLikeStats(result.get(1), "b", 2); assertLikeStats(result.get(2), "c", 4); assertLikeStats(result.get(3), "d", 2); assertLikeStats(result.get(4), "e", 3); } @Test // DATAMONGO-960 public void returnFiveMostCommonLikesShouldReturnStageExecutionInformationWithExplainOptionEnabled() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); createUserWithLikesDocuments(); TypedAggregation<UserWithLikes> agg = createUsersWithCommonLikesAggregation() // .withOptions(newAggregationOptions().explain(true).build()); AggregationResults<LikeStats> result = mongoTemplate.aggregate(agg, LikeStats.class); assertThat(result.getMappedResults(), is(empty())); Document rawResult = result.getRawResults(); assertThat(rawResult, is(notNullValue())); assertThat(rawResult.containsKey("stages"), is(true)); } @Test // DATAMONGO-954 public void shouldSupportReturningCurrentAggregationRoot() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); mongoTemplate.save(new Person("p1_first", "p1_last", 25)); mongoTemplate.save(new Person("p2_first", "p2_last", 32)); mongoTemplate.save(new Person("p3_first", "p3_last", 25)); mongoTemplate.save(new Person("p4_first", "p4_last", 15)); List<Document> personsWithAge25 = mongoTemplate.find(Query.query(where("age").is(25)), Document.class, mongoTemplate.getCollectionName(Person.class)); Aggregation agg = newAggregation(group("age").push(Aggregation.ROOT).as("users")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Person.class, Document.class); assertThat(result.getMappedResults(), hasSize(3)); Document o = (Document) result.getMappedResults().get(2); assertThat(o.get("_id"), is((Object) 25)); assertThat((List<?>) o.get("users"), hasSize(2)); assertThat((List<?>) o.get("users"), is(contains(personsWithAge25.toArray()))); } /** * {@link http://stackoverflow.com/questions/24185987/using-root-inside-spring-data-mongodb-for-retrieving-whole-document} */ @Test // DATAMONGO-954 public void shouldSupportReturningCurrentAggregationRootInReference() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); mongoTemplate.save(new Reservation("0123", "42", 100)); mongoTemplate.save(new Reservation("0360", "43", 200)); mongoTemplate.save(new Reservation("0360", "44", 300)); Aggregation agg = newAggregation( // match(where("hotelCode").is("0360")), // sort(Direction.DESC, "confirmationNumber", "timestamp"), // group("confirmationNumber") // .first("timestamp").as("timestamp") // .first(Aggregation.ROOT).as("reservationImage") // ); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Reservation.class, Document.class); assertThat(result.getMappedResults(), hasSize(2)); } @Test // DATAMONGO-1549 public void shouldApplyCountCorrectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); mongoTemplate.save(new Reservation("0123", "42", 100)); mongoTemplate.save(new Reservation("0360", "43", 200)); mongoTemplate.save(new Reservation("0360", "44", 300)); Aggregation agg = newAggregation( // count().as("documents"), // project("documents") // .andExpression("documents * 2").as("twice")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Reservation.class, Document.class); assertThat(result.getMappedResults(), hasSize(1)); Document document = result.getMappedResults().get(0); assertThat(document, isBsonObject().containing("documents", 3).containing("twice", 6)); } @Test // DATAMONGO-975 public void shouldRetrieveDateTimeFragementsCorrectly() throws Exception { mongoTemplate.dropCollection(ObjectWithDate.class); DateTime dateTime = new DateTime() // .withYear(2014) // .withMonthOfYear(2) // .withDayOfMonth(7) // .withTime(3, 4, 5, 6).toDateTime(DateTimeZone.UTC).toDateTimeISO(); ObjectWithDate owd = new ObjectWithDate(dateTime.toDate()); mongoTemplate.insert(owd); ProjectionOperation dateProjection = Aggregation.project() // .and("dateValue").extractHour().as("hour") // .and("dateValue").extractMinute().as("min") // .and("dateValue").extractSecond().as("second") // .and("dateValue").extractMillisecond().as("millis") // .and("dateValue").extractYear().as("year") // .and("dateValue").extractMonth().as("month") // .and("dateValue").extractWeek().as("week") // .and("dateValue").extractDayOfYear().as("dayOfYear") // .and("dateValue").extractDayOfMonth().as("dayOfMonth") // .and("dateValue").extractDayOfWeek().as("dayOfWeek") // .andExpression("dateValue + 86400000").extractDayOfYear().as("dayOfYearPlus1Day") // .andExpression("dateValue + 86400000").project("dayOfYear").as("dayOfYearPlus1DayManually") // ; Aggregation agg = newAggregation(dateProjection); AggregationResults<Document> result = mongoTemplate.aggregate(agg, ObjectWithDate.class, Document.class); assertThat(result.getMappedResults(), hasSize(1)); Document document = result.getMappedResults().get(0); assertThat(document.get("hour"), is((Object) dateTime.getHourOfDay())); assertThat(document.get("min"), is((Object) dateTime.getMinuteOfHour())); assertThat(document.get("second"), is((Object) dateTime.getSecondOfMinute())); assertThat(document.get("millis"), is((Object) dateTime.getMillisOfSecond())); assertThat(document.get("year"), is((Object) dateTime.getYear())); assertThat(document.get("month"), is((Object) dateTime.getMonthOfYear())); // dateTime.getWeekOfWeekyear()) returns 6 since for MongoDB the week starts on sunday and not on monday. assertThat(document.get("week"), is((Object) 5)); assertThat(document.get("dayOfYear"), is((Object) dateTime.getDayOfYear())); assertThat(document.get("dayOfMonth"), is((Object) dateTime.getDayOfMonth())); // dateTime.getDayOfWeek() assertThat(document.get("dayOfWeek"), is((Object) 6)); assertThat(document.get("dayOfYearPlus1Day"), is((Object) dateTime.plusDays(1).getDayOfYear())); assertThat(document.get("dayOfYearPlus1DayManually"), is((Object) dateTime.plusDays(1).getDayOfYear())); } @Test // DATAMONGO-1127 public void shouldSupportGeoNearQueriesForAggregationWithDistanceField() { mongoTemplate.insert(new Venue("Penn Station", -73.99408, 40.75057)); mongoTemplate.insert(new Venue("10gen Office", -73.99171, 40.738868)); mongoTemplate.insert(new Venue("Flatiron Building", -73.988135, 40.741404)); mongoTemplate.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location")); NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150); Aggregation agg = newAggregation(Aggregation.geoNear(geoNear, "distance")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Venue.class, Document.class); assertThat(result.getMappedResults(), hasSize(3)); Document firstResult = result.getMappedResults().get(0); assertThat(firstResult.containsKey("distance"), is(true)); assertThat((Double) firstResult.get("distance"), closeTo(117.620092203928, 0.00001)); } @Test // DATAMONGO-1133 public void shouldHonorFieldAliasesForFieldReferences() { mongoTemplate.insert(new MeterData("m1", "counter1", 42)); mongoTemplate.insert(new MeterData("m1", "counter1", 13)); mongoTemplate.insert(new MeterData("m1", "counter1", 45)); TypedAggregation<MeterData> agg = newAggregation(MeterData.class, // match(where("resourceId").is("m1")), // group("counterName").sum("counterVolume").as("totalValue")); AggregationResults<Document> results = mongoTemplate.aggregate(agg, Document.class); assertThat(results.getMappedResults(), hasSize(1)); Document result = results.getMappedResults().get(0); assertThat(result.get("_id"), is(equalTo((Object) "counter1"))); assertThat(result.get("totalValue"), is(equalTo((Object) 100.0))); } @Test // DATAMONGO-1326 public void shouldLookupPeopleCorectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); createUsersWithReferencedPersons(); TypedAggregation<User> agg = newAggregation(User.class, // lookup("person", "_id", "firstname", "linkedPerson"), // sort(ASC, "id")); AggregationResults<Document> results = mongoTemplate.aggregate(agg, User.class, Document.class); List<Document> mappedResults = results.getMappedResults(); Document firstItem = mappedResults.get(0); assertThat(firstItem, isBsonObject().containing("_id", "u1")); assertThat(firstItem, isBsonObject().containing("linkedPerson.[0].firstname", "u1")); } @Test // DATAMONGO-1326 public void shouldGroupByAndLookupPeopleCorectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); createUsersWithReferencedPersons(); TypedAggregation<User> agg = newAggregation(User.class, // group().min("id").as("foreignKey"), // lookup("person", "foreignKey", "firstname", "linkedPerson"), // sort(ASC, "foreignKey", "linkedPerson.firstname")); AggregationResults<Document> results = mongoTemplate.aggregate(agg, User.class, Document.class); List<Document> mappedResults = results.getMappedResults(); Document firstItem = mappedResults.get(0); assertThat(firstItem, isBsonObject().containing("foreignKey", "u1")); assertThat(firstItem, isBsonObject().containing("linkedPerson.[0].firstname", "u1")); } @Test // DATAMONGO-1418 public void shouldCreateOutputCollection() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); createPersonDocuments(); String tempOutCollection = "personQueryTemp"; TypedAggregation<Person> agg = newAggregation(Person.class, // group("sex").count().as("count"), // sort(DESC, "count"), // out(tempOutCollection)); AggregationResults<Document> results = mongoTemplate.aggregate(agg, Document.class); assertThat(results.getMappedResults(), is(empty())); List<Document> list = mongoTemplate.findAll(Document.class, tempOutCollection); assertThat(list, hasSize(2)); assertThat(list.get(0), isBsonObject().containing("_id", "MALE").containing("count", 3)); assertThat(list.get(1), isBsonObject().containing("_id", "FEMALE").containing("count", 2)); mongoTemplate.dropCollection(tempOutCollection); } @Test // DATAMONGO-1637 public void shouldCreateOutputCollectionWhileStreaming() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); createPersonDocuments(); String tempOutCollection = "personQueryTemp"; TypedAggregation<Person> agg = newAggregation(Person.class, // group("sex").count().as("count"), // sort(DESC, "count"), // out(tempOutCollection)); mongoTemplate.aggregateStream(agg, Document.class).close(); List<Document> list = mongoTemplate.findAll(Document.class, tempOutCollection); assertThat(list, hasSize(2)); assertThat(list.get(0), isBsonObject().containing("_id", "MALE").containing("count", 3)); assertThat(list.get(1), isBsonObject().containing("_id", "FEMALE").containing("count", 2)); mongoTemplate.dropCollection(tempOutCollection); } @Test // DATAMONGO-1637 public void shouldReturnDocumentsWithOutputCollectionWhileStreaming() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(TWO_DOT_SIX)); createPersonDocuments(); String tempOutCollection = "personQueryTemp"; TypedAggregation<Person> agg = newAggregation(Person.class, // group("sex").count().as("count"), // sort(DESC, "count"), // out(tempOutCollection)); CloseableIterator<Document> iterator = mongoTemplate.aggregateStream(agg, Document.class); List<Document> result = toList(iterator); assertThat(result, hasSize(2)); assertThat(result.get(0), isBsonObject().containing("_id", "MALE").containing("count", 3)); assertThat(result.get(1), isBsonObject().containing("_id", "FEMALE").containing("count", 2)); mongoTemplate.dropCollection(tempOutCollection); } private void createPersonDocuments() { mongoTemplate.save(new Person("Anna", "Ivanova", 21, Person.Sex.FEMALE)); mongoTemplate.save(new Person("Pavel", "Sidorov", 36, Person.Sex.MALE)); mongoTemplate.save(new Person("Anastasia", "Volochkova", 29, Person.Sex.FEMALE)); mongoTemplate.save(new Person("Igor", "Stepanov", 31, Person.Sex.MALE)); mongoTemplate.save(new Person("Leoniv", "Yakubov", 55, Person.Sex.MALE)); } @Test(expected = IllegalArgumentException.class) // DATAMONGO-1418 public void outShouldOutBeTheLastOperation() { newAggregation(match(new Criteria()), // group("field1").count().as("totalCount"), // out("collection1"), // skip(100L)); } @Test // DATAMONGO-1325 public void shouldApplySampleCorrectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); createUserWithLikesDocuments(); TypedAggregation<UserWithLikes> agg = newAggregation(UserWithLikes.class, // unwind("likes"), // sample(3) // ); assertThat(agg.toString(), is(notNullValue())); AggregationResults<LikeStats> result = mongoTemplate.aggregate(agg, LikeStats.class); assertThat(result.getMappedResults().size(), is(3)); } @Test // DATAMONGO-1457 public void sliceShouldBeAppliedCorrectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); createUserWithLikesDocuments(); TypedAggregation<UserWithLikes> agg = newAggregation(UserWithLikes.class, match(new Criteria()), project().and("likes").slice(2)); AggregationResults<UserWithLikes> result = mongoTemplate.aggregate(agg, UserWithLikes.class); assertThat(result.getMappedResults(), hasSize(9)); for (UserWithLikes user : result) { assertThat(user.likes.size() <= 2, is(true)); } } @Test // DATAMONGO-1491 public void filterShouldBeAppliedCorrectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); Item item43 = Item.builder().itemId("43").quantity(2).price(2L).build(); Item item2 = Item.builder().itemId("2").quantity(1).price(240L).build(); Sales sales1 = Sales.builder().id("0") .items(Arrays.asList( // item43, item2)) // .build(); Item item23 = Item.builder().itemId("23").quantity(3).price(110L).build(); Item item103 = Item.builder().itemId("103").quantity(4).price(5L).build(); Item item38 = Item.builder().itemId("38").quantity(1).price(300L).build(); Sales sales2 = Sales.builder().id("1").items(Arrays.asList( // item23, item103, item38)).build(); Item item4 = Item.builder().itemId("4").quantity(1).price(23L).build(); Sales sales3 = Sales.builder().id("2").items(Arrays.asList( // item4)).build(); mongoTemplate.insert(Arrays.asList(sales1, sales2, sales3), Sales.class); TypedAggregation<Sales> agg = newAggregation(Sales.class, project().and("items") .filter("item", AggregationFunctionExpressions.GTE.of(field("item.price"), 100)).as("items")); assertThat(mongoTemplate.aggregate(agg, Sales.class).getMappedResults(), contains(Sales.builder().id("0").items(Collections.singletonList(item2)).build(), Sales.builder().id("1").items(Arrays.asList(item23, item38)).build(), Sales.builder().id("2").items(Collections.<Item> emptyList()).build())); } @Test // DATAMONGO-1538 public void letShouldBeAppliedCorrectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_TWO)); Sales2 sales1 = Sales2.builder().id("1").price(10).tax(0.5F).applyDiscount(true).build(); Sales2 sales2 = Sales2.builder().id("2").price(10).tax(0.25F).applyDiscount(false).build(); mongoTemplate.insert(Arrays.asList(sales1, sales2), Sales2.class); ExpressionVariable total = ExpressionVariable.newVariable("total") .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))); ExpressionVariable discounted = ExpressionVariable.newVariable("discounted") .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); TypedAggregation<Sales2> agg = Aggregation.newAggregation(Sales2.class, Aggregation.project() .and(VariableOperators.Let.define(total, discounted).andApply( AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted")))) .as("finalTotal")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); assertThat(result.getMappedResults(), contains(new Document("_id", "1").append("finalTotal", 9.450000000000001D), new Document("_id", "2").append("finalTotal", 10.25D))); } @Test // DATAMONGO-1551 public void graphLookupShouldBeAppliedCorrectly() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); Employee em1 = Employee.builder().id(1).name("Dev").build(); Employee em2 = Employee.builder().id(2).name("Eliot").reportsTo("Dev").build(); Employee em4 = Employee.builder().id(4).name("Andrew").reportsTo("Eliot").build(); mongoTemplate.insert(Arrays.asList(em1, em2, em4), Employee.class); TypedAggregation<Employee> agg = Aggregation.newAggregation(Employee.class, match(Criteria.where("name").is("Andrew")), // Aggregation.graphLookup("employee") // .startWith("reportsTo") // .connectFrom("reportsTo") // .connectTo("name") // .depthField("depth") // .maxDepth(5) // .as("reportingHierarchy")); AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class); Document object = result.getUniqueMappedResult(); List<Object> list = (List<Object>) object.get("reportingHierarchy"); assertThat(object, isBsonObject().containing("reportingHierarchy", List.class)); assertThat((Document) list.get(0), isBsonObject().containing("name", "Dev").containing("depth", 1L)); assertThat((Document) list.get(1), isBsonObject().containing("name", "Eliot").containing("depth", 0L)); } @Test // DATAMONGO-1552 public void bucketShouldCollectDocumentsIntoABucket() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); Art a1 = Art.builder().id(1).title("The Pillars of Society").artist("Grosz").year(1926).price(199.99).build(); Art a2 = Art.builder().id(2).title("Melancholy III").artist("Munch").year(1902).price(280.00).build(); Art a3 = Art.builder().id(3).title("Dancer").artist("Miro").year(1925).price(76.04).build(); Art a4 = Art.builder().id(4).title("The Great Wave off Kanagawa").artist("Hokusai").price(167.30).build(); mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); TypedAggregation<Art> aggregation = newAggregation(Art.class, // bucket("price") // .withBoundaries(0, 100, 200) // .withDefaultBucket("other") // .andOutputCount().as("count") // .andOutput("title").push().as("titles") // .andOutputExpression("price * 10").sum().as("sum")); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(3)); // { "_id" : 0 , "count" : 1 , "titles" : [ "Dancer"] , "sum" : 760.4000000000001} Document bound0 = result.getMappedResults().get(0); assertThat(bound0, isBsonObject().containing("count", 1).containing("titles.[0]", "Dancer")); assertThat((Double) bound0.get("sum"), is(closeTo(760.40, 0.1))); // { "_id" : 100 , "count" : 2 , "titles" : [ "The Pillars of Society" , "The Great Wave off Kanagawa"] , "sum" : // 3672.9} Document bound100 = result.getMappedResults().get(1); assertThat(bound100, isBsonObject().containing("count", 2).containing("_id", 100)); assertThat((List<String>) bound100.get("titles"), hasItems("The Pillars of Society", "The Great Wave off Kanagawa")); assertThat((Double) bound100.get("sum"), is(closeTo(3672.9, 0.1))); } @Test // DATAMONGO-1552 public void bucketAutoShouldCollectDocumentsIntoABucket() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); Art a1 = Art.builder().id(1).title("The Pillars of Society").artist("Grosz").year(1926).price(199.99).build(); Art a2 = Art.builder().id(2).title("Melancholy III").artist("Munch").year(1902).price(280.00).build(); Art a3 = Art.builder().id(3).title("Dancer").artist("Miro").year(1925).price(76.04).build(); Art a4 = Art.builder().id(4).title("The Great Wave off Kanagawa").artist("Hokusai").price(167.30).build(); mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); TypedAggregation<Art> aggregation = newAggregation(Art.class, // bucketAuto(ArithmeticOperators.Multiply.valueOf("price").multiplyBy(10), 3) // .withGranularity(Granularities.E12) // .andOutputCount().as("count") // .andOutput("title").push().as("titles") // .andOutputExpression("price * 10").sum().as("sum")); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(3)); // { "min" : 680.0 , "max" : 820.0 , "count" : 1 , "titles" : [ "Dancer"] , "sum" : 760.4000000000001} Document bound0 = result.getMappedResults().get(0); assertThat(bound0, isBsonObject().containing("count", 1).containing("titles.[0]", "Dancer").containing("min", 680.0) .containing("max")); // { "min" : 820.0 , "max" : 1800.0 , "count" : 1 , "titles" : [ "The Great Wave off Kanagawa"] , "sum" : 1673.0} Document bound1 = result.getMappedResults().get(1); assertThat(bound1, isBsonObject().containing("count", 1).containing("min", 820.0)); assertThat((List<String>) bound1.get("titles"), hasItems("The Great Wave off Kanagawa")); assertThat((Double) bound1.get("sum"), is(closeTo(1673.0, 0.1))); } @Test // DATAMONGO-1552 public void facetShouldCreateFacets() { assumeTrue(mongoVersion.isGreaterThanOrEqualTo(THREE_DOT_FOUR)); Art a1 = Art.builder().id(1).title("The Pillars of Society").artist("Grosz").year(1926).price(199.99).build(); Art a2 = Art.builder().id(2).title("Melancholy III").artist("Munch").year(1902).price(280.00).build(); Art a3 = Art.builder().id(3).title("Dancer").artist("Miro").year(1925).price(76.04).build(); Art a4 = Art.builder().id(4).title("The Great Wave off Kanagawa").artist("Hokusai").price(167.30).build(); mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); BucketAutoOperation bucketPrice = bucketAuto(ArithmeticOperators.Multiply.valueOf("price").multiplyBy(10), 3) // .withGranularity(Granularities.E12) // .andOutputCount().as("count") // .andOutput("title").push().as("titles") // .andOutputExpression("price * 10") // .sum().as("sum"); TypedAggregation<Art> aggregation = newAggregation(Art.class, // project("title", "artist", "year", "price"), // facet(bucketPrice).as("categorizeByPrice") // .and(bucketAuto("year", 3)).as("categorizeByYear")); AggregationResults<Document> result = mongoTemplate.aggregate(aggregation, Document.class); assertThat(result.getMappedResults().size(), is(1)); Document mappedResult = result.getUniqueMappedResult(); // [ { "_id" : { "min" : 680.0 , "max" : 820.0} , "count" : 1 , "titles" : [ "Dancer"] , "sum" : 760.4000000000001} // , // { "_id" : { "min" : 820.0 , "max" : 1800.0} , "count" : 1 , "titles" : [ "The Great Wave off Kanagawa"] , "sum" : // 1673.0} , // { "_id" : { "min" : 1800.0 , "max" : 3300.0} , "count" : 2 , "titles" : [ "The Pillars of Society" , "Melancholy // III"] , "sum" : 4799.9}] List<Object> categorizeByPrice = (List<Object>) mappedResult.get("categorizeByPrice"); assertThat(categorizeByPrice, hasSize(3)); // [ { "_id" : { "min" : null , "max" : 1902} , "count" : 1} , // { "_id" : { "min" : 1902 , "max" : 1925} , "count" : 1} , // { "_id" : { "min" : 1925 , "max" : 1926} , "count" : 2}] List<Object> categorizeByYear = (List<Object>) mappedResult.get("categorizeByYear"); assertThat(categorizeByYear, hasSize(3)); } private void createUsersWithReferencedPersons() { mongoTemplate.dropCollection(User.class); mongoTemplate.dropCollection(Person.class); User user1 = new User("u1"); User user2 = new User("u2"); User user3 = new User("u3"); mongoTemplate.save(user1); mongoTemplate.save(user2); mongoTemplate.save(user3); Person person1 = new Person("u1", "User 1"); Person person2 = new Person("u2", "User 2"); mongoTemplate.save(person1); mongoTemplate.save(person2); mongoTemplate.save(user3); } private void assertLikeStats(LikeStats like, String id, long count) { assertThat(like, is(notNullValue())); assertThat(like.id, is(id)); assertThat(like.count, is(count)); } private void createUserWithLikesDocuments() { mongoTemplate.insert(new UserWithLikes("u1", new Date(), "a", "b", "c")); mongoTemplate.insert(new UserWithLikes("u2", new Date(), "a")); mongoTemplate.insert(new UserWithLikes("u3", new Date(), "b", "c")); mongoTemplate.insert(new UserWithLikes("u4", new Date(), "c", "d", "e")); mongoTemplate.insert(new UserWithLikes("u5", new Date(), "a", "e", "c")); mongoTemplate.insert(new UserWithLikes("u6", new Date())); mongoTemplate.insert(new UserWithLikes("u7", new Date(), "a")); mongoTemplate.insert(new UserWithLikes("u8", new Date(), "x", "e")); mongoTemplate.insert(new UserWithLikes("u9", new Date(), "y", "d")); } private void createTagDocuments() { MongoCollection<Document> coll = mongoTemplate.getCollection(INPUT_COLLECTION); coll.insertOne(createDocument("Doc1", "spring", "mongodb", "nosql")); coll.insertOne(createDocument("Doc2", "spring", "mongodb")); coll.insertOne(createDocument("Doc3", "spring")); } private static Document createDocument(String title, String... tags) { Document doc = new Document("title", title); List<String> tagList = new ArrayList<String>(); for (String tag : tags) { tagList.add(tag); } doc.put("tags", tagList); return doc; } private static void assertTagCount(String tag, int n, TagCount tagCount) { assertThat(tagCount.getTag(), is(tag)); assertThat(tagCount.getN(), is(n)); } private static <T> List<T> toList(CloseableIterator<? extends T> results) { List<T> result = new ArrayList<T>(); while (results.hasNext()) { result.add(results.next()); } return result; } static class DATAMONGO753 { PD[] pd; DATAMONGO753 withPDs(PD... pds) { this.pd = pds; return this; } } static class PD { String pDch; @org.springframework.data.mongodb.core.mapping.Field("alias") int up; public PD(String pDch, int up) { this.pDch = pDch; this.up = up; } } static class DATAMONGO788 { int x; int y; int xField; int yField; public DATAMONGO788() {} public DATAMONGO788(int x, int y) { this.x = x; this.xField = x; this.y = y; this.yField = y; } } // DATAMONGO-806 static class User { @Id String id; List<PushMessage> msgs; public User() {} public User(String id, PushMessage... msgs) { this.id = id; this.msgs = Arrays.asList(msgs); } } // DATAMONGO-806 static class PushMessage { @Id String id; String content; Date createDate; public PushMessage() {} public PushMessage(String id, String content, Date createDate) { this.id = id; this.content = content; this.createDate = createDate; } } @org.springframework.data.mongodb.core.mapping.Document static class CarPerson { @Id private String id; private String firstName; private String lastName; private Descriptors descriptors; public CarPerson(String firstname, String lastname, Entry... entries) { this.firstName = firstname; this.lastName = lastname; this.descriptors = new Descriptors(); this.descriptors.carDescriptor = new CarDescriptor(entries); } } @SuppressWarnings("unused") static class Descriptors { private CarDescriptor carDescriptor; } static class CarDescriptor { private List<Entry> entries = new ArrayList<AggregationTests.CarDescriptor.Entry>(); public CarDescriptor(Entry... entries) { for (Entry entry : entries) { this.entries.add(entry); } } @SuppressWarnings("unused") static class Entry { private String make; private String model; private int year; public Entry() {} public Entry(String make, String model, int year) { this.make = make; this.model = model; this.year = year; } } } static class Reservation { String hotelCode; String confirmationNumber; int timestamp; public Reservation() {} public Reservation(String hotelCode, String confirmationNumber, int timestamp) { this.hotelCode = hotelCode; this.confirmationNumber = confirmationNumber; this.timestamp = timestamp; } } static class ObjectWithDate { Date dateValue; public ObjectWithDate(Date dateValue) { this.dateValue = dateValue; } } // DATAMONGO-861 @org.springframework.data.mongodb.core.mapping.Document(collection = "inventory") static class InventoryItem { int id; String item; String description; int qty; public InventoryItem() {} public InventoryItem(int id, String item, int qty) { this.id = id; this.item = item; this.qty = qty; } public InventoryItem(int id, String item, String description, int qty) { this.id = id; this.item = item; this.description = description; this.qty = qty; } } // DATAMONGO-1491 @lombok.Data @Builder static class Sales { @Id String id; List<Item> items; } // DATAMONGO-1491 @lombok.Data @Builder static class Item { @org.springframework.data.mongodb.core.mapping.Field("item_id") // String itemId; Integer quantity; Long price; } // DATAMONGO-1538 @lombok.Data @Builder static class Sales2 { String id; Integer price; Float tax; boolean applyDiscount; } // DATAMONGO-1551 @lombok.Data @Builder static class Employee { int id; String name; String reportsTo; } // DATAMONGO-1552 @lombok.Data @Builder static class Art { int id; String title; String artist; Integer year; double price; } }