Mam taką strukturę danych dokumentów w MongoDB i jest ona przeznaczona do kompleksowej analizy danych z dowolnego punktu widzenia w odniesieniu do szeregów czasowych różnych działań (płaski log danych). Trudno mi było wyodrębnić czas między określonymi typami zmian w dokumencie za pomocą zapytań mongo, a następnie zastosowania funkcji $graphLookup (pokazanej poniżej). Jestem początkującym użytkownikiem MongoDB i potrzebuję pomocy z zapytaniem, aby pobrać wymagane dane.

Struktura danych pojedynczego dokumentu (przykład):

{  
    "_id":NumberInt(1),
    "Creation":     ISODate("2018-11-19T06:30:42Z"),
    "Creator":      NumberInt(1),
    "Replies":      NumberInt(10),
    //... other aggregated properties 
    "CurrentProperties":{  // a copy of the last update signifying the current state
        "StatusId":         NumberInt(8),
        "PriorityId":       NumberInt(6),
        "DepartmentId":     NumberInt(5),
        "TypeId":           NumberInt(4),
        "CategoryId":       NumberInt(2),
        "SubcategoryId":    NumberInt(333),
        "ChangeTime":       ISODate("2018-11-19T10:17:20Z"),
        "TimeDelta":        NumberLong(3600000), //timespan from last change in MS 
        "ChangeType":       NumberInt(4),
        "UserId":           NumberInt(1)
    },
    "ChangeHistory":[  // time series changes
        {  
            "StatusId":         NumberInt(8),
            "PriorityId":       NumberInt(6),
            "DepartmentId":     NumberInt(1),
            "TypeId":           NumberInt(4),
            "CategoryId":       NumberInt(2),
            "SubcategoryId":    NumberInt(333),
            "ChangeTime":       ISODate("2018-11-19T10:14:20Z"),
            "TimeDelta":        NumberLong(0), //timespan from last change in MS 
            "ChangeType":       NumberInt(0), // the changed property identifier (0= creation)
            "UserId":           NumberInt(1)
        },
        {  
            "StatusId":         NumberInt(8),
            "PriorityId":       NumberInt(6),
            "DepartmentId":     NumberInt(2),
            "TypeId":           NumberInt(4),
            "CategoryId":       NumberInt(2),
            "SubcategoryId":    NumberInt(333),
            "ChangeTime":       ISODate("2018-11-19T10:15:50Z"),
            "TimeDelta":        NumberLong(90000), //timespan from last change in MS 
            "ChangeType":       NumberInt(4), // the changed property identifier (4= department)
            "UserId":           NumberInt(1)
        },
        {  
            "StatusId":         NumberInt(2),
            "PriorityId":       NumberInt(6),
            "DepartmentId":     NumberInt(2),
            "TypeId":           NumberInt(4),
            "CategoryId":       NumberInt(2),
            "SubcategoryId":    NumberInt(333),
            "ChangeTime":       ISODate("2018-11-19T10:16:20Z"),
            "TimeDelta":        NumberLong(30000), //timespan from last change in MS 
            "ChangeType":       NumberInt(2), // the changed property identifier (2= status)
            "UserId":           NumberInt(1)
        },
        {  
            "StatusId":         NumberInt(2),
            "PriorityId":       NumberInt(6),
            "DepartmentId":     NumberInt(5),
            "TypeId":           NumberInt(4),
            "CategoryId":       NumberInt(2),
            "SubcategoryId":    NumberInt(333),
            "ChangeTime":       ISODate("2018-11-19T10:17:20Z"),
            "TimeDelta":        NumberLong(60000), //timespan from last change in MS 
            "ChangeType":       NumberInt(4), // the changed property identifier (4= department)
            "UserId":           NumberInt(1)
        }
    ]
}

Oczekiwany wynik dla zmian działu w czasie:

[{
    RecordID:       1,
    Department:     1,
    ChangeTime:     ISODate("2018-11-19T10:15:50Z"),
    TimeSpent:      90000
},
{
    RecordID:       1,
    Department:     2,
    ChangeTime:     ISODate("2018-11-19T10:17:20Z")
    TimeSpent:      90000
},
{
    RecordID:       1,
    Department:     5,
    ChangeTime:     ISODate("2018-11-21T09:47:47Z") // Current Time
    TimeSpent:      171027000 //difference between now and last change in departments
}]

A dla statusu:

[{
    RecordID:       1,
    Status:         8,
    ChangeTime:     ISODate("2018-11-19T10:16:20Z"),
    TimeDelta:      120000
},
{
    RecordID:       1,
    Status:         2,
    ChangeTime:     ISODate("2018-11-21T09:47:47Z"), // Current Time
    TimeDelta:      171087000 //difference between now and last change in status
}]

Czego próbowałem do tej pory

Najlepszym wynikiem, jaki uzyskałem do tej pory, było użycie następującej agregacji do utworzenia widoku, a następnie zastosowanie funkcji $GraphLookup na widoku:

db.test.aggregate([
    {$project: {
      _id:0,
      RecordID: "$_id",
      history: {
        $filter: {
          input: "$ChangeHistory",
          as: "changeHistory",
          cond: {$or:[
            {$eq:["$$changeHistory.ChangeType",0]},
            {$eq:["$$changeHistory.ChangeType",4]}
            ]}

                }
      }
    }}, 
    {$unwind: {
      path: "$history",
      includeArrayIndex:"order"
    }}, {$project: {
      _id:"$RecordID",
      "RecordID": "$RecordID",
      "departmentID": "$history.DepartmentId",
      "actionOrder":"$order",
      "nextAction":{$add:["$order",1]},
      "time":"$history.ChangeTime"
    }}
])

Następnie zastosował następujące elementy:

db.TestView.aggregate([{
        $graphLookup: {
            from: 'TestView',
            startWith: "$nextAction",
            connectFromField: 'nextAction',
            connectToField: 'actionOrder',
            as: 'pair',
        }
    }, {
        $unwind: {
            path: "$pair"
        }
    }, {
        $project: {
            _id: 0,
            RecordID: "$_id",
            Department: "$departmentID",
            ChangeTime: "$pair.time",
            TimeSpent: {
                $subtract: ["$pair.time", "$time"]
            }
        }
    }
])

Problem z tym polega na tym, że miesza parowanie akcji w różnych dokumentach, nie uwzględnia czasu spędzonego do bieżącego czasu i ma tak wiele propagacji oprócz używania widoku pośrodku.

W razie potrzeby strukturę danych można nieco zmodyfikować.

0
CME64 21 listopad 2018, 13:21

1 odpowiedź

Najlepsza odpowiedź

Właściwie zajęło mi 2 dni, próbując znaleźć rozwiązanie tego problemu przed wysłaniem pytania, i rozwiązałem je kilka godzin później.

Chciałem tylko podzielić się moim rozwiązaniem i jeśli ktoś mógłby zoptymalizować je pod kątem wydajności lub czegokolwiek, proszę o opublikowanie swoich odpowiedzi

Rozwiązanie

Wykorzystuje funkcję $zip w celu utworzenia par akcji po zastosowaniu filtra poprzez przekazanie oryginalnej tablicy zdarzeń i innej kopii tej samej tablicy z wyłączeniem pierwszego elementu, tak aby pierwszy element został dopasowany drugi i drugi z trzecim i tak dalej. Dodałem również domyślny czas bieżący, aby obliczyć deltę ostatniego elementu od czasu bieżącego.

db.test.aggregate([{
    $project: {
      RecordID: "$_id",
      history: {
        $filter: {
          input: "$ChangeHistory",
          as: "changeHistory",
          cond: {
            $or: [{
                $eq: ["$$changeHistory.ChangeType", 0]
              },
              {
                $eq: ["$$changeHistory.ChangeType", 2]
              }
            ]
          }

        }
      }
    }
  },
  {
    $addFields: {
      pairs: {
        $zip: { // here is the trick
          inputs: ["$history", {
            $slice: ["$history", 1, {
              $size: "$history"
            }]
          }],
          useLongestLength: true,
          defaults: [0, {
            ChangeTime: new Date()
          }]
        }
      }
    }
  },
  {
    $unwind: {
      path: "$pairs"
    }
  },
  {
    $project: {
      id: "$_id",
      old: {
        $arrayElemAt: ["$pairs", 0]
      },
      new: {
        $arrayElemAt: ["$pairs", 1]
      }
    }
  },
  {
    $project: {
      RecordID: "$id",
      Status: "$old.StatusId",
      TimeDeltaMS: {
        $subtract: ["$new.ChangeTime", "$old.ChangeTime"]
      },
      ChangeTime: "$new.ChangeTime"
    }
  },
])
0
CME64 22 listopad 2018, 09:53