gpt4 book ai didi

javascript - 如何按条件从输出中删除不需要的字段

转载 作者:行者123 更新时间:2023-11-30 15:04:14 25 4
gpt4 key购买 nike

我有一个投影阶段,如下所示

{
'name': {$ifNull: [ '$invName', {} ]},,
'info.type': {$ifNull: [ '$invType', {} ]},
'info.qty': {$ifNull: [ '$invQty', {} ]},
'info.detailed.desc': {$ifNull: [ '$invDesc', {} ]}
}

如果没有字段,我将投影空对象( {}),因为如果在字段中执行排序并且该字段不存在,则该文档将以排序顺序( Sort Documents Without Existing Field to End of Results)首先出现。下一阶段是排序,希望不存在的字段按排序顺序排在最后。这正在按预期方式工作。

现在,我要删除那些具有空对象作为值的字段(如果 info.detailed.desc为空,则 info.detailed在输出中不应存在)。我可以像这样使用 lodash( https://stackoverflow.com/a/38278831/6048928)在 Node 级别执行此操作。但是我试图在mongodb级别上做到这一点。是否有可能?我尝试了 $redact,但是它过滤掉了整个文档。是否可以基于值对文档的 PRUNEDESCEND字段进行操作?

最佳答案

从文档中完全删除属性并不是一件容易的事。基本原理是,服务器本身在MongoDB 3.4和 $replaceRoot 引入之前没有任何方式可以做到这一点,从本质上讲,该表达式允许将表达式作为文档上下文返回。

即使添加了该功能,如果没有MongoDB 3.4.4中引入的 $objectToArray $arrayToObject 的其他功能,这样做还是不切实际的。但是要审理案件。

快速 sample 处理

{ "_id" : ObjectId("59adff0aad465e105d91374c"),  "a" : 1 }
{ "_id" : ObjectId("59adff0aad465e105d91374d"), "a" : {} }

有条件地返回根对象
db.junk.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$cond": {
"if": { "$ne": [ "$a", {} ] },
"then": "$$ROOT",
"else": { "_id": "$_id" }
}
}
}}
])

这是一个非常简单的原理,实际上可以应用于任何嵌套属性以删除其子键,但需要各种级别的嵌套 $cond 甚至 $switch 才能应用可能的条件。 $replaceRoot 当然是“顶级”删除所必需的,因为它是有条件表达顶级 key 返回的唯一方法。

因此,虽然理论上可以使用 $cond$switch来决定要返回的内容,但它通常很麻烦,并且您需要更灵活的方法。

过滤空对象
db.junk.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": [ "$$this.v", {} ] }
}
}
}
}}
])

这是 $objectToArray $arrayToObject 启用的地方。无需写出每个可能的键的条件,我们只是将对象内容转换为“数组”,并在数组条目上应用 $filter 来决定要保留的内容。

$objectToArray 将任何对象转换为文档数组,这些文档将每个属性表示为用于键名的 "k"和用于该属性的值的 "v"。由于这些现在可以作为“值”进行访问,因此您可以使用诸如 $filter 之类的方法来检查每个数组条目并丢弃不需要的条目。

最后, $arrayToObject 获取“已过滤”的内容,并将那些 "k""v"值转换回属性名称和值作为结果对象。这样,“过滤器”条件从结果对象中删除了不符合条件的所有属性。

返回到$ cond
db.junk.aggregate([
{ "$project": {
"a": { "$cond": [{ "$eq": [ "$a", {} ] }, "$$REMOVE", "$a" ] }
}}
])

MongoDB 3.6引入了一个带有 $$REMOVE常量的新播放器。这是可以与 $cond一起应用的新功能,以确定是否完全显示该属性。因此,这当然是发布可用时的另一种方法。

在上述所有情况下,如果该值是我们要测试以移除的空对象,则不返回 "a"属性。
{ "_id" : ObjectId("59adff0aad465e105d91374c"),  "a" : 1 }
{ "_id" : ObjectId("59adff0aad465e105d91374d") }

更复杂的结构

您在此处的特定要求是包含嵌套属性的数据。因此,从概述的方法继续,我们可以演示如何实现。

首先是一些样本数据:
{ "_id" : ObjectId("59ae03bdad465e105d913750"), "a" : 1, "info" : { "type" : 1, "qty" : 2, "detailed" : { "desc" : "this thing" } } }
{ "_id" : ObjectId("59ae03bdad465e105d913751"), "a" : 2, "info" : { "type" : 2, "qty" : 3, "detailed" : { "desc" : { } } } }
{ "_id" : ObjectId("59ae03bdad465e105d913752"), "a" : 3, "info" : { "type" : 3, "qty" : { }, "detailed" : { "desc" : { } } } }
{ "_id" : ObjectId("59ae03bdad465e105d913753"), "a" : 4, "info" : { "type" : { }, "qty" : { }, "detailed" : { "desc" : { } } } }

应用过滤方法
db.junk.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$filter": {
"input": {
"$concatArrays": [
{ "$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": [ "$$this.k", "info" ] }
}},
[
{
"k": "info",
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$info" },
"cond": {
"$not": {
"$or": [
{ "$eq": [ "$$this.v", {} ] },
{ "$eq": [ "$$this.v.desc", {} ] }
]
}
}
}
}
}
}
]
]
},
"cond": { "$ne": [ "$$this.v", {} ] }
}
}
}
}}
])

由于嵌套的级别,这需要更复杂的处理。在主要情况下,您需要独立查看此处的 "info"键,并删除所有不符合条件的子属性。由于您需要返回“something”,因此,当所有内部属性都被删除时,我们基本上就需要删除 "info" key 本身。这就是对每组结果进行嵌套过滤器操作的原因。

与$ REMOVE一起应用$ cond

首先,在可行的情况下,这似乎是更合乎逻辑的选择,因此,首先从最简化的形式看这是有帮助的:
db.junk.aggregate([
{ "$addFields": {
"info.type": {
"$cond": [
{ "$eq": [ "$info.type", {} ] },
"$$REMOVE",
"$info.type"
]
},
"info.qty": {
"$cond": [
{ "$eq": [ "$info.qty", {} ] },
"$$REMOVE",
"$info.qty"
]
},
"info.detailed.desc": {
"$cond": [
{ "$eq": [ "$info.detailed.desc", {} ] },
"$$REMOVE",
"$info.detailed.desc"
]
}
}}
])

但是然后您需要查看它实际产生的输出:
/* 1 */
{
"_id" : ObjectId("59ae03bdad465e105d913750"),
"a" : 1.0,
"info" : {
"type" : 1.0,
"qty" : 2.0,
"detailed" : {
"desc" : "this thing"
}
}
}

/* 2 */
{
"_id" : ObjectId("59ae03bdad465e105d913751"),
"a" : 2.0,
"info" : {
"type" : 2.0,
"qty" : 3.0,
"detailed" : {}
}
}

/* 3 */
{
"_id" : ObjectId("59ae03bdad465e105d913752"),
"a" : 3.0,
"info" : {
"type" : 3.0,
"detailed" : {}
}
}

/* 4 */
{
"_id" : ObjectId("59ae03bdad465e105d913753"),
"a" : 4.0,
"info" : {
"detailed" : {}
}
}

尽管其他键已删除,但 "info.detailed"仍然存在,因为在此级别上没有任何实际测试。实际上,您不能简单地用简单的术语来表达它,因此解决此问题的唯一方法是将对象作为表达式求值,然后在输出的每个级别上应用附加的过滤条件以查看空对象仍驻留的位置,然后删除他们:
db.junk.aggregate([
{ "$addFields": {
"info": {
"$let": {
"vars": {
"info": {
"$arrayToObject": {
"$filter": {
"input": {
"$objectToArray": {
"type": { "$cond": [ { "$eq": [ "$info.type", {} ] },"$$REMOVE", "$info.type" ] },
"qty": { "$cond": [ { "$eq": [ "$info.qty", {} ] },"$$REMOVE", "$info.qty" ] },
"detailed": {
"desc": { "$cond": [ { "$eq": [ "$info.detailed.desc", {} ] },"$$REMOVE", "$info.detailed.desc" ] }
}
}
},
"cond": { "$ne": [ "$$this.v", {} ] }
}
}
}
},
"in": { "$cond": [ { "$eq": [ "$$info", {} ] }, "$$REMOVE", "$$info" ] }
}
}
}}
])

与普通的 $filter方法一样,该方法实际上从结果中删除了“所有”空对象:
/* 1 */
{
"_id" : ObjectId("59ae03bdad465e105d913750"),
"a" : 1.0,
"info" : {
"type" : 1.0,
"qty" : 2.0,
"detailed" : {
"desc" : "this thing"
}
}
}

/* 2 */
{
"_id" : ObjectId("59ae03bdad465e105d913751"),
"a" : 2.0,
"info" : {
"type" : 2.0,
"qty" : 3.0
}
}

/* 3 */
{
"_id" : ObjectId("59ae03bdad465e105d913752"),
"a" : 3.0,
"info" : {
"type" : 3.0
}
}

/* 4 */
{
"_id" : ObjectId("59ae03bdad465e105d913753"),
"a" : 4.0
}

用代码做这一切

因此,这里的所有内容实际上都取决于您正在使用的MongoDB版本中可用的最新功能或“即将到来的功能”。在这些条件不可用的情况下,替代方法是简单地从游标返回的结果中删除空对象。

这通常是最理智的事情,实际上,这就是您所需要的,除非聚合管道需要继续经过要删除字段的点。即使这样,您也可能应该在逻辑上解决此问题,并将最终结果留给游标处理。

作为 shell 程序的JavaScript,您可以使用以下方法,并且无论使用哪种实际语言,其基本原理都保持不变:
db.junk.find().map( d => {
let info = Object.keys(d.info)
.map( k => ({ k, v: d.info[k] }))
.filter(e => !(
typeof e.v === 'object' &&
( Object.keys(e.v).length === 0 || Object.keys(e.v.desc).length === 0 )
))
.reduce((acc,curr) => Object.assign(acc,{ [curr.k]: curr.v }),{});
delete d.info;
return Object.assign(d,(Object.keys(info).length !== 0) ? { info } : {})
})

与上面的示例相同,这几乎是 native 语言的说明方式,其中,如果预期属性之一包含一个空对象,请从输出中完全删除该属性。

关于javascript - 如何按条件从输出中删除不需要的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46038174/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com