Docs 菜单

迁移未定义的数据和查询

从 MongoDB 8.0开始,等值匹配表达式中与null的比较与undefined值不匹配。

示例,考虑以下文档和查询:

// create the people collection
db.people.insertMany( [
{ _id: 1, name: null },
{ _id: 2, name: undefined },
{ _id: 3, name: [ "Gabriel", undefined ] },
{ _id: 4, names: [ "Alice", "Charu" ] }
] )
db.people.find( { name: null } )

在 MongoDB 8.0之前,前面的查询将匹配符合以下条件的文档:

  • name字段为null ( _id: 1 )

  • name字段为 undefined 或包含 undefined大量元素(_id: 2_id: 3

  • name字段不存在 ( _id: 4 )

从MongoDB 8.0 开始,前面的查询不匹配 name字段为 undefined 或包含 undefined大量元素的文档。 该查询仅匹配符合以下条件的文档:

  • name字段为 null 或包含 null大量元素 (_id: 1)

  • name字段不存在 ( _id: 4 )

此查询行为更改还会影响以下操作:

  • $eq

  • $in

  • $lookup ,因为null本地字段不再与undefined外部字段匹配。

To account for this behavior change, you can:

注意

undefined is a deprecated BSON type. Recent versions of the MongoDB Shell and drivers automatically convert undefined values to null when performing inserts and updates. The guidance on this page applies to deployments that have undefined values from older driver versions or legacy mongo shell.

If you don't need to keep fields with undefined values in your documents, you can remove those fields. MongoDB's flexible data model means your collection's document fields do not need to be consistent, so you can remove a particular field from a subset of documents.

How to remove undefined fields from your documents depends on whether you know the field name to remove. If you know the field name, the operation is more performant because it can use an index.

See either:

If you know the name of the field that contains undefined values that you want to remove, use the following example. The example updates the people collection to remove:

  • The name field if its value is the scalar value undefined.

  • undefined array elements in the name field.

db.people.updateMany(
{ name: { $type: "undefined" } },
[ {
$set: {
"name": {
$cond: {
// When "name" is an array, convert { name: [ "Alice", undefined ] }
// to { name: [ "Alice" ] }
if: {
$eq: [ { $type: "$name" }, "array" ]
},
then: {
$filter: {
input: "$name",
cond: {
$not: { $eq: [ { $type: "$$this" }, "undefined" ] }
}
},
},
// When "name" is scalar undefined, remove it
else: "$$REMOVE"
}
}
}
} ]
)

After you run the operation, the people collection contains these documents:

[
{ _id: 1, name: null },
{ _id: 2 },
{ _id: 3, name: [ "Gabriel" ] }
{ _id: 4, names: [ "Alice", "Charu" ] }
]

If you don't know which fields contain undefined values, use the following example to remove all undefined top-level fields.

注意

When you don't specify a field name for the update, the operation is not performant because the query can't use an index. If you run the following example on a large collection, the query might be slow and resource-intensive.

The following example removes top-level document fields from the people collection where the value is undefined:

db.people.updateMany(
{ },
[ {
$replaceWith: {
// Detect undefined top-level fields under the root and remove them
$arrayToObject: {
$filter: {
input: { $objectToArray: "$$ROOT" },
cond: {
$not: { $eq: [ { $type: "$$this.v" }, "undefined" ] }
}
}
}
}
} ]
)

After you run the operation, the people collection contains these documents:

[
{ _id: 1, name: null },
{ _id: 2 },
{ _id: 3, name: [ "Gabriel", undefined ] }
{ _id: 4, names: [ "Alice", "Charu" ] }
]

注意

The preceding approach only modifies top-level fields. The document with _id: 3 still contains an undefined value because the value appears in an array.

You can update undefined data values to the null data type. Use this approach to migrate your data off of the deprecated undefined data type while retaining your document fields.

How to update undefined fields depends on whether you know the field name to update. If you know the field name, the operation is more performant because it can use an index.

See either:

If you know the name of the field that contains undefined values that you want to set to null, use the following example. The example updates the people collection to set the following values to null:

  • The name field if its value is the scalar value undefined.

  • undefined array elements that appear in the name field.

db.people.updateMany(
{ name: { $type: "undefined" } },
[ {
$set: {
"name": {
$cond: {
// When "name" is an array, convert { name: [ "Alice", undefined ] }
// to { name: [ "Alice", null ] }
if: {
$eq: [ { $type: "$name" }, "array" ]
},
then: {
$map: {
input: "$name",
in: {
$cond: {
if: { $eq: [ { $type: "$$this" }, "undefined" ] },
then: null,
else: "$$this"
}
}
},
},
// When "name" is the scalar undefined, convert to null
else: null
}
}
}
} ]
)

After you run the operation, the people collection contains these documents:

[
{ _id: 1, name: null },
{ _id: 2, name: null },
{ _id: 3, name: [ "Gabriel", null ] }
{ _id: 4, names: [ "Alice", "Charu" ] }
]

If you don't know which fields contain undefined values, use the following example to set all undefined top-level fields to null.

注意

When you don't specify a field name for the update, the operation is not performant because the query can't use an index. If you run the following example on a large collection, the query might be slow and resource-intensive.

The following example updates the people collection to set undefined top-level document fields to null:

db.people.updateMany(
{ },
[ {
$replaceWith: {
// Detect undefined top-level fields under the root and replace them with null
$arrayToObject: {
$map: {
input: { $objectToArray: "$$ROOT" },
in: {
$cond: {
if: { $eq: [ { $type: "$$this.v" }, "undefined" ] },
then: { k: "$$this.k", v: null },
else: "$$this"
}
}
}
}
}
} ]
)

After you run the operation, the people collection contains these documents:

[
{ _id: 1, name: null },
{ _id: 2, name: null },
{ _id: 3, name: [ "Gabriel", undefined ] }
{ _id: 4, names: [ "Alice", "Charu" ] }
]

注意

The preceding approach only modifies top-level fields. The document with _id: 3 still contains an undefined value because the value appears in an array.

If you can't migrate your data types from null to undefined, you can rewrite your queries to match undefined values. If you use this approach, your data will still contain the deprecated undefined BSON type.

To have queries for null match undefined values, add a query predicate that explicitly matches the undefined type. For example, the following query matches documents where name is undefined, null, or missing:

db.people.find( {
$or: [
{ name: null },
{ name: { $type: "undefined" } }
]
} )

The query returns all documents in the people collection:

[
{ _id: 1, name: null },
{ _id: 2, name: undefined },
{ _id: 3, name: [ "Gabriel", undefined ],
{ _id: 4, names: [ "Alice", "Charu" ] }
]