Использование $ last в конвейере агрегации Mongo

Я искал похожие вопросы, но не нашел. Не стесняйтесь указывать мне в их направлении.

Скажем, у меня есть эти данные:

{ "_id" : ObjectId("5694c9eed4c65e923780f28e"), "name" : "foo1", "attr" : "foo" }
{ "_id" : ObjectId("5694ca3ad4c65e923780f290"), "name" : "foo2", "attr" : "foo" }
{ "_id" : ObjectId("5694ca47d4c65e923780f294"), "name" : "bar1", "attr" : "bar" }
{ "_id" : ObjectId("5694ca53d4c65e923780f296"), "name" : "bar2", "attr" : "bar" }

Если я хочу получить последнюю запись для каждой группы атрибутов, я могу сделать это:

> db.content.aggregate({$group: {_id: '$attr', name: {$last: '$name'}}})
{ "_id" : "bar", "name" : "bar2" }
{ "_id" : "foo", "name" : "foo2" }

Я хотел бы, чтобы мои данные были сгруппированы по attr, а затем отсортированы по _id, чтобы в каждой группе оставалась только последняя запись, и вот как я могу этого добиться. НО мне нужен способ избежать именования всех полей, которые я хочу получить в результате (в этом примере «имя»), потому что в моем реальном варианте использования они заранее не известны.

Итак, есть ли способ добиться этого, но без необходимости явно указывать каждое поле с помощью $last и просто использовать вместо этого все поля? Конечно, я бы отсортировал свои данные перед группировкой, и мне просто нужно как-то сказать Монго «взять все значения из последнего».


person slouc    schedule 20.01.2016    source источник
comment
Имена полей, которые заранее не известны, являются анти-шаблоном в MongoDB, который приводит ко всем видам неразрешимых проблем, и его следует избегать, когда это возможно.   -  person Philipp    schedule 20.01.2016
comment
Они не являются полностью произвольными, просто у меня есть набор предметов, которые могут принадлежать одной из двух категорий. Как коллекция автомобилей, в которой есть как грузовики, так и автомобили. Должен ли я реорганизовать его так, чтобы все поля были одинаковыми во всех записях в коллекции?   -  person slouc    schedule 20.01.2016
comment
Бессхемный характер MongoDB позволяет вам иметь необязательные поля, которые существуют только в определенных типах документов, но когда у вас есть поля, которые означают одно и то же в разных типах, они должны иметь одно и то же имя. В противном случае вы столкнетесь с этой (и многими другими) проблемами.   -  person Philipp    schedule 20.01.2016
comment
Нет, нет полей, которые означают одно и то же в разных типах. Мои два типа элементов совпадают в 70% атрибутов, а остальные являются необязательными и специфичными для каждого типа. Но я бы хотел избежать логики и жесткого кодирования имен атрибутов в моем сервисе. Я бы хотел взять все, что есть, если возможно.   -  person slouc    schedule 20.01.2016


Ответы (1)


См. Некоторые возможные варианты здесь:

  • Выполните несколько запросов find (). Sort () для каждого значения attr, которое вы хотите найти.
  • Возьмите исходный _id из $ last doc, затем выполните findOne () для каждого из этих значений (это более расширяемый вариант).
  • Используйте системную переменную $$ ROOT, как показано здесь.

Это не самая быстрая операция, но я предполагаю, что вы используете ее больше для аналитики, а не в ответ на поведение пользователя.

Отредактировано, чтобы добавить пример slouc, размещенный в комментариях: db.content.aggregate({$group: {_id: '$attr', lastItem: { $last: "$$ROOT" }}}).

person metame    schedule 20.01.2016
comment
Я понятия не имел о $$ ROOT, это мне помогло. Пример для других: db.content.aggregate ({$ group: {_id: '$ attr', lastItem: {$ last: $$ ROOT}}}). Обратите внимание, что если вы используете ReactiveMongo (как и я), вам нужно использовать только один знак доллара. - person slouc; 20.01.2016