首页 > 解决方案 > 将数据类型更新为 mongoDB 中的对象

问题描述

我已将 mongoDB 中集合的一个字段从字符串数组更改为包含 2 个字符串的对象数组。插入新文档没有任何问题,但是当调用 get 方法获取时,查询所有文档时出现此错误:

未能解码“学生”。解码“photoAddresses”时出错:readStartDocument 只能在 CurrentBSONType 为 DOCUMENT 时调用,而 CurrentBSONType 为 STRING 时不能调用。

photoAddresses 是学生中更改的字段。

我想知道有没有办法更新所有记录,使它们都具有相同的数据类型,而不会丢失任何数据。

旧版照片地址:

"photoAddresses" : ["something","something else"]

这应该像这样更新到新版本:

"photoAddresses" : [{photoAddresses:"something"},{photoAddresses:"something else"}]

标签: databasemongodbtypes

解决方案


以下聚合查询将字符串数组更新为对象数组,前提是数组具有字符串元素。聚合运算符$map用于将字符串数组元素映射到objects。您可以使用这两个查询中的任何一个。

db.test.aggregate( [
  { 
      $match: { 
          $expr: { $and: [ { $isArray: "$photo" }, 
                           { $gt: [ { $size: "$photo" }, 0 ] }
                         ] 
          }, 
          "photo.0": { $type: "string" }
      } 
  },
  {
      $project: {
          photo: { 
              $map: {
                   input: "$photo", 
                      as: "ph", 
                      in: { addr: "$$ph" } 
               } 
          }
      } 
  },
] ).forEach( doc => db.test.updateOne( { _id: doc._id }, { $set: { photo: doc.photo } } ) )


以下查询仅适用于 MongoDB 版本 4.2+。请注意,更新操作是聚合而不是更新。请参阅updateMany

db.test.updateMany(
  { 
          $expr: { $and: [ { $isArray: "$photo" }, 
                           { $gt: [ { $size: "$photo" }, 0 ] }
                         ] 
          }, 
          "photo.0": { $type: "string" }
  },
  [
    {
      $set: {
          photo: { 
              $map: {
                   input: "$photo", 
                      as: "ph", 
                      in: { addr: "$$ph" } 
               } 
          }
      } 
    }
  ]
)



[编辑添加]:以下查询适用于MongoDB 3.4版本:

db.test.aggregate( [
  { 
      $addFields: { 
          matches: {
              $cond: {
                  if: { $and: [ 
                          { $isArray: "$photoAddresses" }, 
                          { $gt: [ { $size: "$photoAddresses" }, 0 ] },
                          { $eq: [ { $type: { $arrayElemAt: [ "$photoAddresses", 0 ] } }, "string" ] }
                       ] },
                  then: true,
                  else: false
              }
          }
      }
  },
  {
      $match: { matches: true }
  },
  {
      $project: {
          photoAddresses: { 
              $map:  {
                   input: "$photoAddresses", 
                      as: "ph", 
                      in: { photoAddresses: "$$ph" } 
               } 
          }
      } 
  },
] ).forEach( doc => db.test.updateOne( { _id: doc._id }, { $set: { photoAddresses: doc.photoAddresses } } ) )

推荐阅读