使用mongodb和mongoose(nodejs)的不同字段名称映射多个数据源

问题描述 投票:1回答:3

我正在开发一个网站,它将收集来自不同来源的大量数据。源可能有很多相同的数据(有一些冗余),但json中的字段可能会有所不同。这些来源来自REST API,所有json。

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

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

例如。

第一来源:

{
    "address":"123 abc st"
}

第二个来源:

{
    "addr":"123 abc st"
}

我的架构:

{
    address:{
        type: String,
        required: true
}
node.js mongodb express mongoose
3个回答
1
投票

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

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

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

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

let mappings = require('mappings.json');
let MyModel = requirse('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"
  }
}

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


0
投票

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


0
投票

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

作为完整的演示:

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()时,address函数被用来读取addr,并且set()函数当然适用于任何试图为addr提供值然后将该值放入adddress的情况。

唯一的问题是,这些“虚拟”不会自动应用于您可能尝试使用它们的“无处不在”。他们使用new Model()工作,但他们不会自动适用于Model.create()Model.insertMany()

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

// 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文档中

© www.soinside.com 2019 - 2024. All rights reserved.