【问题标题】:Mapping multiple sources of data with different field names for mongodb and mongoose (nodejs)为 mongodb 和 mongoose (nodejs) 映射具有不同字段名称的多个数据源
【发布时间】:2020-04-22 16:23:28
【问题描述】:

我正在开发一个网站,该网站将包含来自不同来源的大量数据。来源可能有很多相同的数据(有一些冗余),但 json 中的字段可能会在这里和那里有所不同。这些来源来自 REST API,都是 json。

我的问题是,如何使用猫鼬模式映射这些不同的来源?我希望它尽可能简单,就像某种表,我只需要添加新源的字段名称,以及它在我的数据库中映射到的字段。我正在使用快递/节点。

这是两个来源的示例,以及我的架构规范。

例如。

第一来源:

{
    "address":"123 abc st"
}

第二来源:

{
    "addr":"123 abc st"
}

我的架构:

{
    address:{
        type: String,
        required: true
}

【问题讨论】:

  • 您认为提供的答案中是否有某些内容无法解决您的问题?如果是这样,请对答案发表评论,以澄清究竟需要解决哪些尚未解决的问题。如果它确实回答了您提出的问题,请注意Accept your Answers您提出的问题

标签: node.js mongodb express mongoose


【解决方案1】:

您可以创建一个 JSON 文件来存储映射,并在存储对象时检索它。

例如,创建一个JSON文件mappings.json

{
  "addr": "address",
  "address": "address"
}

然后在代码中检查该属性是否对应于映射的属性。

let mappings = require('mappings.json');
let MyModel = require('models/myModel');

function createNewObjectFromInput (input, callback) {
   let options = {};
   for (let property in input) {
      if (input.hasOwnProperty(property) && mappings[property]) {
        options[mappings[property]] = input[property];
      }
   }
   let newObject = new MyModel(options);
   newObject.save(callback);
}

为了在可能存在属性冲突的多个源中使事情更加健壮,您可以为每个源创建这样的 JSON 文件,或者为每个源创建单独的映射,如下所示:

{
  "firstSource": { 
    "addr": "address
  },
  "secondSource": {
    "address": "address"
  }
}

只需检查您的输入数据来自哪个来源。

【讨论】:

    【解决方案2】:

    通过使用 model.discriminator() 方法,您可以尝试在模式之间创建继承。查看 Mongoose 文档here

    【讨论】:

      【解决方案3】:

      您似乎想要在这里简单地设置一个带有“别名”名称的“虚拟”字段。这允许您为字段使用不同的名称,但这些名称将“重新映射”到架构中定义的正确名称。

      作为一个完整的演示:

      const { Schema } = mongoose = require('mongoose');
      
      const uri = 'mongodb://localhost/test';
      
      mongoose.Promise = global.Promise;
      mongoose.set('debug', true);
      
      const addressSchema = new Schema({
        address: { type: String, required: true }
      });
      
      // asssign a virtual to the "mapped" name
      addressSchema.virtual('addr')
        .get(function() { return this.address })
        .set(function(v) { this.address = v });
      
      const Address = mongoose.model('Adddress', addressSchema);
      
      const log = data => console.log(JSON.stringify(data, undefined, 2));
      
      (async function() {
      
        try {
      
          const conn = await mongoose.connect(uri);
      
          await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
      
          // Raw documents don't work as the setter is ignored
          try {
            await Address.insertMany(
              [{ adddress: "123 abc st" },{ addr: "123 abc st" }]
            );
          } catch(e) {
            console.error(e);
          }
      
          // Creating via new works as expected
          let doc = new Address({ addr: "123 abc st" });
          log(doc);
      
          // Using Array.map over the objects to cast them works fine
          await Address.insertMany(
            [{ address: "123 abc str" },{ addr: "245 abc st" }]
              .map(Address)
          );
      
          let inserted = await Address.find();
          log({ inserted });
      
          mongoose.disconnect();
      
        } catch (e) {
          console.error(e)
        } finally {
          process.exit()
        }
      
      })();
      

      这证明了预期的结果:

      Mongoose: adddresses.remove({}, {})
      { ValidationError: Adddress validation failed: address: Path `address` is required.
          at ValidationError.inspect (/home/neillunn/projects/alias/node_modules/mongoose/lib/error/validation.js:56:24)
          at formatValue (util.js:430:38)
          at inspect (util.js:324:10)
          at format (util.js:191:12)
          at Console.warn (console.js:145:21)
          at /home/neillunn/projects/alias/index.js:37:15
          at <anonymous>
          at process._tickCallback (internal/process/next_tick.js:188:7)
        errors:
         { address:
            { ValidatorError: Path `address` is required.
          at new ValidatorError (/home/neillunn/projects/alias/node_modules/mongoose/lib/error/validator.js:25:11)
          at validate (/home/neillunn/projects/alias/node_modules/mongoose/lib/schematype.js:805:13)
          at /home/neillunn/projects/alias/node_modules/mongoose/lib/schematype.js:854:11
          at Array.forEach (<anonymous>)
          at SchemaString.SchemaType.doValidate (/home/neillunn/projects/alias/node_modules/mongoose/lib/schematype.js:814:19)
          at /home/neillunn/projects/alias/node_modules/mongoose/lib/document.js:1712:9
          at _combinedTickCallback (internal/process/next_tick.js:131:7)
          at process._tickCallback (internal/process/next_tick.js:180:9)
              message: 'Path `address` is required.',
              name: 'ValidatorError',
              properties: [Object],
              kind: 'required',
              path: 'address',
              value: undefined,
              reason: undefined,
              '$isValidatorError': true } },
        _message: 'Adddress validation failed',
        name: 'ValidationError' }
      {
        "_id": "5b110aa571311d0a70cd3c88",
        "address": "123 abc st"
      }
      Mongoose: adddresses.insertMany([ { _id: 5b110aa571311d0a70cd3c89, address: '123 abc str', __v: 0 }, { _id: 5b110aa571311d0a70cd3c8a, address: '245 abc st', __v: 0 } ], {})
      Mongoose: adddresses.find({}, { fields: {} })
      {
        "inserted": [
          {
            "_id": "5b110aa571311d0a70cd3c89",
            "address": "123 abc str",
            "__v": 0
          },
          {
            "_id": "5b110aa571311d0a70cd3c8a",
            "address": "245 abc st",
            "__v": 0
          }
        ]
      }
      

      为此,除了架构中的 adddress 字段之外,定义的所有内容都是在架构上设置另一个虚拟属性:

      addressSchema.virtual('addr')
        .get(function() { return this.address })
        .set(function(v) { this.address = v });
      

      这应该很简单,因为get() 函数用于在您实际请求addr 时从address 读取,而set() 函数当然适用于任何尝试为addr 然后将该值放入 adddress 中。

      唯一的问题是这些“虚拟”不会自动应用到您可能尝试使用它们的“任何地方”。它们使用 new Model() 查找,但它们不会自动应用于 Model.create()Model.insertMany()

      对于这种情况,如图所示的常见解决方法是,当您有一个原始输入文档列表时,通过“首先”通过模型构造函数运行它们来“转换”它们,正如清单所示:

      // Using Array.map over the objects to cast them works fine
      await Address.insertMany(
        [{ address: "123 abc str" },{ addr: "245 abc st" }]
          .map(Address)
      );
      

      然后,数据将在要插入的两个文档中正确地重新写入address,这发生在required 验证器触发之前。

      另一种情况是在架构上使用alias 属性:

      const addressSchema = new Schema({
        address: { type: String, required: true, alias: 'addr' }
      });
      

      这实际上以完全相同的方式工作,无需编写完整的“虚拟”方法。您可能会认为这是执行相同操作的较短选项,但您可能应该意识到,预期目的实际上是“反向”,您应该在架构中定义“较短”名称并使用“较长”名称用于作业。

      您还可以根据需要添加任意数量的“虚拟”,但您只能在架构定义中使用 alias 赋值“一次”。因此,如果您有不同的数据源,它们都使用不同的名称来存储您想要存储的内容,那么使用较长的形式可能是更好的方法。

      两者都记录在 Mongoose Schema 文档中

      【讨论】:

      • 我正在尝试走虚拟路线,但没有任何运气。这就是我所拥有的,我尝试通过 set 传递一个参数,然后将字段设置为其值,但这似乎也不起作用。我知道我在这里做错了什么。您能否提供有关完成我想做的事情的虚拟方法的更多详细信息? globalMarketCap .virtual('market_cap') .get(function(){ return this.get('total_market_cap_usd'); }) .set(function(){ return this.set('total_market_cap_usd'); });
      • @Salx 问题中有“工作”代码示例。您需要做的就是运行代码以查看该方法是否有效。运行代码的输出是有问题的。所以你真的没有什么可以“尝试”的。只需“做”运行代码并“做”完全相同的事情,而不是你编造的事情。你的“问题”是关于addraddress,这就是你得到的有效答案。学习Accept Your Answers,他们清楚地解决了实际提出的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 2022-09-30
      • 1970-01-01
      相关资源
      最近更新 更多