Как проверить, содержит ли массив последовательные элементы в MongoDB? - PullRequest
0 голосов
/ 15 апреля 2020

Если у меня есть документ в понедельник go со следующей структурой, как проверить, содержит ли массив data два или более свойств с одинаковым значением, одно за другим?

В этом В этом случае мои цели заключаются в том, чтобы указать, что среди всех документов в коллекции у этого есть два статуса с одинаковым значением ("status_B").

Я знаю, как подсчитать, сколько раз статус возникает в data массив, но мне нужно найти, в каких документах два или более статуса появляются последовательно.

{
    "_id": ObjectId("XYZ"),
    "data": [
        {
            "status": "status_A",
            "other": "data"
        },
        {
            "status": "status_B",
            "other": "data"
        },
        {
            "status": "status_B",
            "other": "data"
        }
    ]
}

Ответы [ 2 ]

1 голос
/ 16 апреля 2020

Агрегация проверяет, имеют ли два последовательных элемента массива data одинаковое значение status, и печатает эти документы.

Используя следующие два образца документов :

{
        "_id" : 1,
        "data" : [
                {
                        "status" : "status_A",
                        "other" : "data"
                },
                {
                        "status" : "status_B",
                        "other" : "data"
                },
                {
                        "status" : "status_B",
                        "other" : "data"
                }
        ]
},
{
        "_id" : 2,
        "data" : [
                {
                        "status" : "status_B",
                        "other" : "data"
                },
                {
                        "status" : "status_A",
                        "other" : "data"
                },
                {
                        "status" : "status_B",
                        "other" : "data"
                }
        ]
}

агрегация запрос:

db.collection.aggregate( [
  { 
      $addFields: {
           matches: { 
               $reduce: {
                   input: "$data", 
                   initialValue: {  prev_status: "",  has_seq: false  },
                   in: {
                       $cond: [ { $eq: [ "$$value.prev_status", "$$this.status" ] },
                                { has_seq: true, prev_status: "$$this.status" },
                                { has_seq: "$$value.is_seq", prev_status: "$$this.status" }
                       ]
                   }
               }
           }
      }
  },
  { 
      $match: { "matches.has_seq": true } 
  },
  { 
      $project: { matches: 0 } 
  }
] )

Результат равен документ с _id: 1, который имеет последовательные элементы массива с status: "status_B".

1 голос
/ 15 апреля 2020

$map и $range на помощь. Вместе они позволяют создавать «петли» для массивов. Учитывая входной набор, подобный этому:

[
 {_id:0,
  "data": [
{ "status": "status_A", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_C", "other": "data" },
{ "status": "status_D", "other": "data" }
    ]
 }
 ,{_id:1,
  "data": [
{ "status": "status_A", "other": "data" },
{ "status": "status_X", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_X", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_B", "other": "data" },
{ "status": "status_D", "other": "data" },
{ "status": "status_D", "other": "data" }
    ]
 }
]

, тогда этот конвейер будет определять, где в массиве data поле status дублируется в элементах n и n+1:

db.foo.aggregate([
{$project: {dupe: {$map: {
                input: {$range:[0, {$add:[{$size:"$data"},-1]} ]},
                as: "z",
                in: {$cond: [ {$eq: [
// Important trick: $arrayElemAt[array,n] will give you the whole object at offset n.
// $arrayElemAt[arr plus dotpath into object, n] will give you just the field at the
// dotpath; in our case here, status is a single scalar string:
{$arrayElemAt: ["$data.status", "$$z"]},
{$arrayElemAt: ["$data.status", {$add:["$$z",1]} ]}
                                     ]},
{$arrayElemAt: ["$data.status", "$$z"]},null]}
        }}
    }}
]);

, чтобы получить:

{ "_id" : 0, "dupe" : [ null, null, null ] }
{
    "_id" : 1,
    "dupe" : [
        null,
        null,
        "status_B",
        "status_B",
        null,
        null,
        "status_B",
        null,
        "status_D"
    ]
}

Некоторые могут найти конвейер проще, устанавливая переменные с помощью $let:

db.foo.aggregate([
{$project: {dupe: {$map: {
                input: {$range:[0, {$add:[{$size:"$data"},-1]} ]},
                as: "z",
                in: {$let: {
                  vars: { n0: {$arrayElemAt: ["$data.status", "$$z"]},
                          n1: {$arrayElemAt: ["$data.status", {$add:["$$z",1]} ]}
                  },
                  in: {$cond: [ {$eq: [ "$$n0", "$$n1" ]}, "$$n0", null ]}
            }}
        }}
    }}
]);

Если вы хотите более простой результат, который говорит, есть ли статус n / n + 1 дублируется для любого количества раз, используйте $anyElementTrue, чтобы получить простой логический вывод:

db.foo.aggregate([
{$project: {dupe: {$anyElementTrue: { $map: {
                input: {$range:[0, {$add:[{$size:"$data"},-1]} ]},
                as: "z",
                in: {$cond: [ {$eq: [
{$arrayElemAt: ["$data.status", "$$z"]},
{$arrayElemAt: ["$data.status", {$add:["$$z",1]} ]}
                                     ]},
{$arrayElemAt: ["$data.status", "$$z"]},null]}
                    }}
        }
    }}
]);

, чтобы получить:

{ "_id" : 0, "dupe" : false }
{ "_id" : 1, "dupe" : true }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...