skip to Main Content

I’ve been trying to flatten a field of array that occasionally has a nested array when its size is 1 as you can see in example below. (This happened due to a previous error I made with updateMany() while trying to put every string in array but somehow, I put the string in a nested array).

This is a Japanese-English dictionary database and "re_pri" stands for reading-priority. Not every, but most documents have this field inside.

I managed to flatten the array with unwind but later I found out that unwind cannot be used with updateMany(). So, I started searching for another way to accomplish this but couldn’t find a way so far.

{
  "r_ele": [{
      "re_pri": [
        "ichi1",
        "news1",
        "nf10"
      ]}],
},
{
  "r_ele": [{
      "re_pri": [
        [
          "ichi1"
        ]
      ]
}]
}

I tried setting it to a new field called "a", assigning it to the re_pri field then unsetting the "a" field. But it didn’t work the way I expected.

This is the way I tried with aggregation pipeline

[
  {
    $match: {
      "r_ele.re_pri": {
        $exists: 1,
      },
    },
  },
  {
    $project: {
      "r_ele.re_pri": 1,
      a: {
        $reduce: {
          input: "$r_ele.re_pri",
          initialValue: [],
          in: {
            $cond: {
              if: {
                $and: [
                  {
                    $eq: [
                      {
                        $type: "$$this",
                      },
                      "array",
                    ],
                  },
                  {
                    $eq: [
                      {
                        $size: "$$this",
                      },
                      1,
                    ],
                  },
                ],
              },
              then: {
                $concatArrays: "$$this",
              },
              else: {
                $concatArrays: [
                  "$$this",
                  "$$value",
                ],
              },
            },
          },
        },
      },
    },
  },
  {
    $unwind: "$a",
  },
  {
    $set: {
      "r_ele.re_pri": "$a",
    },
  },
  {
    $unset: "a",
  }
]

this is the output:

{
  "a": [
    "ichi1",
    "news1",
    "nf10"
  ]
},
{
  "a": [
    [
      "ichi1"
    ]
  ]
}

But my expectation was:

{
  "a": [
    "ichi1",
    "news1",
    "nf10"
  ]
},
{
  "a": [
      "ichi1"
  ]
}

What am I doing wrong?

2

Answers


  1. The input $r_ele.re_pri to your $reduce operator, is an array of arrays. For your second document, the input is essentially,

    [
        [
            "ichi1"
        ]
    ]
    

    You are checking if the element is an array of size 1, and then concatenating that array.

    then: {
        $concatArrays: "$$this",
    }
        
    

    Instead, you should be concatenating just the element of that array.

    [
        "ichi1"
    ]
    

    Try this,

    then: {
        $concatArrays: {$arrayElemAt: ["$$this",0]}
    }
    

    Demo

    Login or Signup to reply.
  2. I don’t see any reason for $reduce. Try this one:

    db.collection.aggregate([
       {
          $project: {
             a: {
                $cond: {
                   if: { $eq: [{ $type: { $first: { $first: "$r_ele.re_pri" } } }, "array"] },
                   then: { $first: { $first: "$r_ele.re_pri" } },
                   else: { $first: "$r_ele.re_pri" }
                }
             }
          }
       }
    ])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search