使用mongodb和mongoose(nodejs)的不同字段名称映射多个数据源
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用mongodb和mongoose(nodejs)的不同字段名称映射多个数据源相关的知识,希望对你有一定的参考价值。
我正在开发一个网站,它将收集来自不同来源的大量数据。源可能有很多相同的数据(有一些冗余),但json中的字段可能会有所不同。这些来源来自REST API,所有json。
我的问题是,如何使用mongoose模式映射这些不同的源?我希望它尽可能简单,就像某种表格,我只需要添加新源的字段名称,以及它在我的数据库中映射到的字段。我正在使用express / node。
这是两个来源的示例,以及我的架构规范。
例如。
第一来源:
{
"address":"123 abc st"
}
第二个来源:
{
"addr":"123 abc st"
}
我的架构:
{
address:{
type: String,
required: true
}
您可以创建一个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"
}
}
只需检查输入数据来自哪个来源。
通过使用model.discriminator()方法,您可以尝试在模式之间创建继承。检查Mongoose文档here。
你在这里想要的只是设置一个带有“别名”名称的“虚拟”字段。这允许您为字段使用不同的名称,但这些名称将“重新映射”到架构中定义的正确名称。
作为完整的演示:
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文档中
以上是关于使用mongodb和mongoose(nodejs)的不同字段名称映射多个数据源的主要内容,如果未能解决你的问题,请参考以下文章
使用 Nodejs、Express、Mongoose 和 React 将图像上传到 MongoDB
如何使用 mongoose 和 NodeJs 在 Mongodb 的数组字段中插入数据
javascript 使用NodeJS和Mongoose的MongoDB CRUD操作