Возврат документа с двумя полями из одного массива в MongoDB - PullRequest
0 голосов
/ 21 ноября 2018

С учетом документов, таких как

{
  _id: 'abcd',
  userId: '12345',
  activities: [
    { status: 'login', timestamp: '10000001' },
    { status: 'logout', timestamp: '10000002' },
    { status: 'login', timestamp: '10000003' },
    { status: 'logout', timestamp: '10000004' },
  ]
}

Я пытаюсь создать конвейер, так как будут возвращены все пользователи, чьи последние действия по входу / выходу из системы зарегистрированы между двумя временными метками.Например, если значения двух отметок времени находятся в диапазоне от 10000002 до 10000003, ожидаемый документ должен иметь значение

{
  _id: 'abcd',
  userId: '12345',
  login: '10000003',
  logout: '10000002'
}

. Если значения отметок времени находятся в диапазоне от -1 до 10000001, ожидаемое значениедокумент должен быть:

{
  _id: 'abcd',
  userId: '12345',
  login: '10000001',
  logout: null
}

и т. д.

Я знаю, что это связано с агрегациями, мне нужно $unwind и т. д., но я не уверен насчет остальныха именно вычисление двух полей из одного массива документов

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Можно использовать ниже агрегации

Вместо $unwind использовать $lte и $gte с агрегацией $fitler.

db.collection.aggregate([
  { "$project": {
    "userId": 1,
    "login": {
      "$max": {
        "$filter": {
          "input": "$activities",
          "cond": {
            "$and": [
              { "$gte": ["$$this.timestamp", "10000001"] },
              { "$lte": ["$$this.timestamp", "10000004"] },
              { "$lte": ["$$this.status", "login"] }
            ]
          }
        }
      }
    },
    "logout": {
      "$max": {
        "$filter": {
          "input": "$activities",
          "cond": {
            "$and": [
              { "$gte": ["$$this.timestamp", "10000001"] },
              { "$lte": ["$$this.timestamp", "10000004"] },
              { "$lte": ["$$this.status", "logout"] }
            ]
          }
        }
      }
    }
  }}
])
0 голосов
/ 21 ноября 2018

Вы можете попробовать агрегацию ниже:

db.col.aggregate([
    { 
        $unwind: "$activities" 
    },
    {
        $match: {
            $and: [
                { "activities.timestamp": { $gte: "10000001" } },
                { "activities.timestamp": { $lte: "10000002" } }
            ]
        }
    },
    {
        $sort: {
            "activities.timestamp": -1
        }
    },
    {
        $group: {
            _id: "$_id",
            userId: { $first: "$userId" },
            activities: { $push: "$activities" }
        }
    },
    {
        $addFields: {
            login: { $arrayElemAt: [ { $filter: { input: "$activities", as: "a", cond: { $eq: [ "$$a.status", "login" ] } } } , 0 ] },
            logout: { $arrayElemAt: [ { $filter: { input: "$activities", as: "a", cond: { $eq: [ "$$a.status", "logout" ] } } } , 0 ] }
        }
    },
    {
        $project: {
            _id: 1,
            userId: 1,
            login: { $ifNull: [ "$login.timestamp", null ] },
            logout: { $ifNull: [ "$logout.timestamp", null ] }
        }
    }
])

Нам нужно использовать $ размотку + $ sort + $ group , чтобы сделатьуверен, что наша деятельность будет отсортирована по отметке времени.После $unwind вы можете использовать $ match , чтобы применить условие фильтрации.Затем вы можете использовать $ filter с $ arrayElemAt , чтобы получить первое (последнее) значение отфильтрованного массива.В последнем $project вы можете явно использовать $ ifNull (в противном случае ключ JSON будет пропущен при отсутствии значения)

...