当前位置: 首页 > 知识库问答 >
问题:

node.js mongoose返回第二层的所有非空字段

杭胜
2023-03-14
{
    "metadata": {"isActive": true, "isDeleted": false },
    "test": "123",
    "resource": {
        "id": "59e94f3f6d5789611ce9926f",
        "resourceType": "Patient",
        "active": true,
        "gender": "male",
        "birthDate": "2000-01-01T00:00:00.000Z",
        "extension": [
            {
                "url": "hxxp://example.com/fhir/StructureDefinition/patient-default-bundle",
                "valueCodeableConcept": {
                    "code": "sys",
                    "display": ""
                }
            }
        ],
        "link": [],
        "careProvider": [],
        "communication": [],
        "animal": {
            "genderStatus": {
                "coding": []
            },
            "breed": {
                "coding": []
            },
            "species": {
                "coding": []
            }
        },
        "contact": []
    }
}

我的预期结果如下,即“resource”元素下的所有非空字段。

{
  "id": "59e94f3f6d5789611ce9926f",
  "resourceType": "Patient",
  "active": true,
  "gender": "male",
  "birthDate": "2000-01-01T00:00:00.000Z",
  "extension": [
      {
          "url": "hxxp://example.com/fhir/StructureDefinition/patient-default-bundle",
          "valueCodeableConcept": {
              "code": "sys",
              "display": ""
          }
      }
  ]
}

我的当前编码:

module.exports.findById = function (req, res, next) {
    var resourceId = req.params.resourceId;
    var resourceType = req.params.resourceType;
    var thisModel = require('mongoose').model(resourceType);

    console.log("findById is being called by the API [" + resourceType + "][" + resourceId + "]");
    thisModel.findOne(
        {'resource.id': resourceId, 'metadata.isActive': true, 'metadata.isDeleted': false},
        'resource -_id',
        function(err, doc) {
            if (err) {
                globalsvc.sendOperationOutcome(res, resourceId, "Error", "findOne() Not Found", err, 404);
            }
            else {
                if (doc) {
                    sendJsonResponse(res, 200, doc);
                }  else {
                    delete doc._id;
                    globalsvc.sendOperationOutcome(res, resourceId, "Error", "Id: [" + resourceId + "] Not Found", err, 404);
                }
            }
        }
    );
}

共有1个答案

端木宏才
2023-03-14

如前所述,实际上不在MongoDB集合中存储空数组比在返回数据时处理它们要好得多。实际上,您只能通过使用最新版本中的聚合框架特性(然后仍然不是递归的),或者允许服务器返回整个对象,然后在传递这些属性之前从文档中删除这些属性,从返回的结果中省略它们。

所以我真的认为这是一个两步的过程来修复数据。

当然,您指出在模式中有更多的字段,但根据我所看到的,我可以给您举几个例子。基本上,您需要在任何具有undefined数组的东西上放置一个default值。只是列出几个部分作为您的模式:

"resource": {
  "extension": {
    "type": [{
      "url": String,
      "valueCodeableConcept": {
        "code": String,
        "display": String
      }
    ],
    "default": undefined
  },
  "link": { "type": [String], "default": undefined },
  "animal": {
    "genderStatus": { 
      "coding": { "type": [String], "default": undefined }
    },
    "breed": {
      "coding": { "type": [String], "default": undefined }
    }
  }
}

那应该能让你大致了解一下。使用这些“default”值,当没有提供其他数据时,mongoose将不会尝试写入空数组。一旦您通过这样标记每个数组定义来修复您的模式,那么就不会再创建空数组了。

这应该是一个“一次性”操作,以移除仅仅承载空数组的所有属性。这意味着您还真的希望删除那些在每个内部键下只有一个空数组的属性,例如“animals”属性。

因此,我只需要做一个简单的列表来查看数据,重写它:

const MongoClient = require('mongodb').MongoClient;

const uri = 'mongodb://localhost/test',
      collectionName = 'junk';

function returnEmpty(obj) {
  var result = {};

  Object.keys(obj).forEach(k => {
    if ( typeof(obj[k]) === "object" && obj[k].constructor === Object ) {
      let temp = returnEmpty(obj[k]);
      if (Object.keys(temp).length !== 0)
        result[k] = temp;
    } else if ( !((Array.isArray(obj[k]) && obj[k].length > 0)
      || !Array.isArray(obj[k]) ) )
    {
      result[k] = obj[k];
    }
  });

  return result;
}

function stripPaths(obj,cmp) {
  var result = {};

  Object.keys(obj).forEach( k => {
    if ( Object.keys(obj[k]).length !== Object.keys(cmp[k]).length ) {
      result[k] = stripPaths(obj[k], cmp[k]);
    } else {
      result[k] = "";
    }
  });

  return result;
}

function dotNotate(obj,target,prefix) {
  target = target || {};
  prefix = prefix || "";

  Object.keys(obj).forEach( key => {
    if ( typeof(obj[key]) === 'object' ) {
      dotNotate(obj[key], target, prefix + key + '.');
    } else {
      target[prefix + key] = obj[key];
    }
  });

  return target;
}

function log(data) {
  console.log(JSON.stringify(data, undefined, 2))
}

(async function() {

  let db;

  try {

    db = await MongoClient.connect(uri);

    let collection = db.collection(collectionName);

    let ops = [];
    let cursor = collection.find();

    while ( await cursor.hasNext() ) {
      let doc = await cursor.next();
      let stripped = returnEmpty(doc);
      let res = stripPaths(stripped, doc);
      let $unset = dotNotate(res);

      ops.push({
        updateOne: {
          filter: { _id: doc._id },
          update: { $unset }
        }
      });

      if ( ops.length > 1000 ) {
        await collection.bulkWrite(ops);
        ops = [];
      }
    }

    if ( ops.length > 0 ) {
      await collection.bulkWrite(ops);
      log(ops);
      ops = [];
    }


  } catch(e) {
    console.error(e);
  } finally {
    db.close();
  }

})();

对于您提供的文档,更新将如下所示:

[
  {
    "updateOne": {
      "filter": {
        "_id": "5a0151108204f6bce9baf86f"
      },
      "update": {
        "$unset": {
          "resource.link": "",
          "resource.careProvider": "",
          "resource.communication": "",
          "resource.animal": "",
          "resource.contact": ""
        }
      }
    }
  }
]

它基本上标识了具有空数组的所有属性,甚至删除了“animal”下的所有键,因为每个键都有一个空数组,如果我们只删除子键,那么该键将只是一个空对象。所以我们移除整个键和它的子键。

一旦运行,所有那些不需要的键都将从存储的文档中删除,然后任何查询将只返回实际定义的数据。因此,这是短期内的一点工作,以获得更长期的收益。

当然,对于懒惰者,您可以简单地应用用于返回要删除的路径的基本函数,并使用反向逻辑从返回的对象中删除路径:

function returnStripped(obj) {
  var result = {};

  Object.keys(obj).forEach(k => {
   if ( typeof(obj[k]) === "object" && obj[k].constructor === Object ) {
     var temp = returnStripped(obj[k]);
     if (Object.keys(temp).length !== 0)
       result[k] = temp;
   } else if ( ((Array.isArray(obj[k]) && obj[k].length > 0) || !Array.isArray(obj[k])) ) {
     result[k] = obj[k];
   }
  });

  return result;
}


db.collection.find().map(returnStripped)

它只是从结果中删除不需要的键。

它可以完成这项工作,但这里更大的收获是从实际修复模式和永久更新数据中获得的。

 类似资料:
  • 问题内容: 我正在尝试检索以SQlite中任何非字母字符开头的所有列,但似乎无法使其正常工作。我目前有此代码,但它返回每一行: 有没有办法检索TestNames的第一个字符不属于字母的所有行? 问题答案: 你只去第一个角色吗? substr函数(在某些SQL语言中也可以称为left())将帮助您隔离字符串中的第一个char。 编辑:也许在sqllite中的substr(TestNames,1,1)

  • 返回第一个 non-null/undefined 的参数。 使用 Array.find() 返回第一个非 null / undefined 的参数。 const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_)); coalesce(null, undefined, '', NaN, 'Waldo'); /

  • 问题内容: 遇到这个问题(有点烦人),我正在尝试查找集合中的所有记录,而不显示(或显示)特定字段(得分)。这只是一个示例,并不能反映我的实际代码,但是问题总是可以重现的。我要排除的字段非常大,我只是试图生成可用记录的菜单。 像这样的命令 始终返回每个字段,而不是触发mongodb中的排除/包含。我不需要担心什么,因为模板可以控制哪些数据呈现为html?仍然感觉无论如何数据都已传输到客户端。并显示在

  • 问题内容: 但是我 没有 关闭(展平)表,一个孩子可以有很多父母,并且ID遍历不一定是按顺序进行的。嵌套深度没有限制。 假设不可能使用循环引用…我想返回遍历层次结构所需的所有行。 假定下表​​: 给定我将如何编写单个查询以返回所有行(获取所有后代的Relationips)? 同样,假设我期望第2、3、4、7、8行 鉴于我希望第6和第10行 偶然的假阳性和结果中重复的行都是可以接受的。缺少行是不可接

  • 问题内容: 我有两个相同的电话: 在msg1中,我得到了正确的期望值,但是msg2是一个空字符串。我没有设置 Out 消息,因此exchange In 消息应该仍然完整。请解释为什么会这样。 骆驼路线: 处理器仅包含上面的2条语句。route1中的处理是正确的,但是在route2中,我得到了描述的行为:第一个调用-有效字符串,第二个调用- 空字符串。所以我认为也许与HttpMessage转换有关。

  • 这应该只得到数字和返回,但不做它作为预期的!有什么建议吗?