У нас есть сбор "сообщение" со следующими полями
_id | messageId | chainId | createOn
1 | 1 | A | 155
2 | 2 | A | 185
3 | 3 | A | 225
4 | 4 | B | 226
5 | 5 | C | 228
6 | 6 | B | 300
Мы хотим выбрать все поля документа со следующими критериями
поэтому ожидаемый результат
_id | messageId | chainId | createOn
3 | 3 | A | 225
5 | 5 | C | 228
6 | 6 | B | 300
Мы используем spring -data в нашем java-приложении. Я пытался пойти разными подходами, пока ничего не помогло. Можно ли достичь выше с помощью одного запроса?
То, что вы хотите, - это то, что может быть достигнуто с помощью структуры агрегации. Основная форма (полезная для других):
db.collection.aggregate([
// Group by the grouping key, but keep the valid values
{ "$group": {
"_id": "$chainId",
"docId": { "$first": "$_id" },
"messageId": { "$first": "$messageId" },
"createOn": { "$first": "$createdOn" }
}},
// Then sort
{ "$sort": { "createOn": -1 } }
])
Итак, чтобы "группы" находились в разных значениях "messageId", принимая граничные значения $first
для каждого из других полей. Альтернативно, если вы хотите, чтобы наибольший использовали вместо $last
, но для самой маленькой или самой большой по строкам это, вероятно, делает сначала нужно $sort
, иначе просто используйте $min
и $max
, если вся строка не важно.
Дополнительную информацию об использовании см. в документации MongoDB aggregate()
, а также в JavaDocs и SpringData Mongo для более широкого использования агрегатного метода и возможных помощников.
вот решение с использованием драйвера Java MongoDB
final MongoClient mongoClient = new MongoClient();
final DB db = mongoClient.getDB("mstreettest");
final DBCollection collection = db.getCollection("message");
final BasicDBObject groupFields = new BasicDBObject("_id", "$chainId");
groupFields.put("docId", new BasicDBObject("$first", "$_id"));
groupFields.put("messageId", new BasicDBObject("$first", "$messageId"));
groupFields.put("createOn", new BasicDBObject("$first", "$createdOn"));
final DBObject group = new BasicDBObject("$group", groupFields);
final DBObject sortFields = new BasicDBObject("createOn", -1);
final DBObject sort = new BasicDBObject("$sort", sortFields);
final DBObject projectFields = new BasicDBObject("_id", 0);
projectFields.put("_id", "$docId");
projectFields.put("messageId", "$messageId");
projectFields.put("chainId", "$_id");
projectFields.put("createOn", "$createOn");
final DBObject project = new BasicDBObject("$project", projectFields);
final AggregationOutput aggregate = collection.aggregate(group, sort, project);
и результат будет:
{ "_id" : 5 , "messageId" : 5 , "createOn" : { "$date" : "2014-04-23T04:45:45.173Z"} , "chainId" : "C"}
{ "_id" : 4 , "messageId" : 4 , "createOn" : { "$date" : "2014-04-23T04:12:25.173Z"} , "chainId" : "B"}
{ "_id" : 1 , "messageId" : 1 , "createOn" : { "$date" : "2014-04-22T08:29:05.173Z"} , "chainId" : "A"}
Я попробовал его с SpringData Mongo, и он не работал, когда я группирую его по цепочке (java.lang.NumberFormatException: для строки ввода: "C" ) было исключением
вот решение, использующее springframework.data.mongodb:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("chainId"),
Aggregation.sort(new Sort(Sort.Direction.ASC, "createdOn"))
);
AggregationResults<XxxBean> results = mongoTemplate.aggregate(aggregation, "collection_name", XxxBean.class);
Заменить эту строку:
final DBObject group = new BasicDBObject("$group", groupFields);
с этим:
final DBObject group = new BasicDBObject("_id", groupFields);