我是mongodb的新手,我使用mongoose来验证和订购数据(如果这不起作用,我可以将其更改为MySQL)。 该应用程序将是一个电子商店,购买与电影,游戏,分机相关的商品。
我的架构如下:
var productSchema = {
id: {
type: String,
required: true
},
name: {
type: String,
required: true
},
img: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
stock: {
type: Number,
required: true
},
category: {
object: {
type: String,
required: true
},
group: {
type: String,
required: true
},
name: {
type: String,
required: true
}
}
};
如果我在类别中有以下数据:
我希望id由类别中每个字段的首字母和数字(最后添加的项目的编号加1)组成。在这种情况下,它将是RMLOTR1。
我同时添加了大量数据,因此每次执行此操作时,我都会创建一个函数来迭代所有添加的项目并执行我想要的但是...
是否有内置的方法可以使用mongodb或mongoose执行此操作,同时添加数据并创建ID?我知道我可以做虚拟,但我希望存储数据。
附加功能
答案 0 :(得分:1)
你基本上是在"保存"上寻找一个"pre" middleware钩子。通过在集合中创建新文档来触发事件。这将检查当前文档内容并提取"字符串"从值中创建你的"前缀" _id
的值。
还有另一部分,其中"前缀"当已经存在该特定"前缀"的值时,需要添加数字计数器。使其与众不同。在用于"Generate an auto-incrementing sequence field"的MongoDB中有一种常见的技术,它基本上涉及保持"计数器"每次访问时都收集并递增值。
作为一个完整且独立的演示,您将如下技术组合在一起:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/warehouse');
var counterSchema = new Schema({
"type": { "type": String, "required": true },
"prefix": { "type": String, "required": true },
"counter": Number
});
counterSchema.index({ "type": 1, "prefix": 1 },{ "unique": true });
counterSchema.virtual('nextId').get(function() {
return this.prefix + this.counter;
});
var productSchema = new Schema({
"_id": "String",
"category": {
"object": { "type": String, "required": true },
"group": { "type": String, "required": true },
"name": { "type": String, "required": true }
}
},{ "_id": false });
productSchema.pre('save', function(next) {
var self = this;
if ( !self.hasOwnProperty("_id") ) {
var prefix = self.category.object.substr(0,1).toUpperCase()
+ self.category.group.substr(0,1).toUpperCase()
+ self.category.name.split(" ").map(function(word) {
return word.substr(0,1).toUpperCase();
}).join("");
mongoose.model('Counter').findOneAndUpdate(
{ "type": "product", "prefix": prefix },
{ "$inc": { "counter": 1 } },
{ "new": true, "upsert": true },
function(err,counter) {
self._id = counter.nextId;
next(err);
}
);
} else {
next(); // Just skip when _id is already there
}
});
var Product = mongoose.model('Product',productSchema),
Counter = mongoose.model('Counter', counterSchema);
async.series(
[
// Clean data
function(callback) {
async.each([Product,Counter],function(model,callback) {
model.remove({},callback);
},callback);
},
function(callback) {
async.each(
[
{
"category": {
"object": "ring",
"group": "movies",
"name": "lord of the rings"
}
},
{
"category": {
"object": "ring",
"group": "movies",
"name": "four weddings and a funeral"
}
},
{
"category": {
"object": "ring",
"group": "movies",
"name": "lord of the rings"
}
}
],
function(data,callback) {
Product.create(data,callback)
},
callback
)
},
function(callback) {
Product.find().exec(function(err,products) {
console.log(products);
callback(err);
});
},
function(callback) {
Counter.find().exec(function(err,counters) {
console.log(counters);
callback(err);
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
)
这为您提供如下输出:
[ { category: { name: 'lord of the rings', group: 'movies', object: 'ring' },
__v: 0,
_id: 'RMLOTR1' },
{ category:
{ name: 'four weddings and a funeral',
group: 'movies',
object: 'ring' },
__v: 0,
_id: 'RMFWAAF1' },
{ category: { name: 'lord of the rings', group: 'movies', object: 'ring' },
__v: 0,
_id: 'RMLOTR2' } ]
[ { __v: 0,
counter: 2,
type: 'product',
prefix: 'RMLOTR',
_id: 57104cdaa774fcc73c1df0e8 },
{ __v: 0,
counter: 1,
type: 'product',
prefix: 'RMFWAAF',
_id: 57104cdaa774fcc73c1df0e9 } ]
要首先了解Counter
架构和模型,您基本上定义了一些您将要查找"唯一的"键,并附加一个数字字段到"增量"在比赛中。为方便起见,这只有两个字段组成了唯一的组合,并定义了复合索引。如果需要,这也可能是一个复合_id
。
另一个便利是virtual
的{{1}}方法,它只是对"前缀"进行连接。和"计数器"值。这里也是最好的做法,包括" type"因为您的nextId
模型可用于服务"计数器"用于多个集合源。因此,我们在Counter
模型的上下文中访问时使用"product"
,以区别于其他模型,您可能还会保留类似的序列计数器。只是一个值得关注的设计点。
对于实际的Product
模型本身,我们希望附加" pre save"中间件钩子以填充Product
内容。因此,在确定"前缀"的字符部分之后,操作然后关闭并查找"前缀"使用"产品"在_id
模型集合中组合数据。
.findOneAndUpdate()
的功能是寻找符合"计数器"中标准的文件。收集,然后在找到文件的地方,它将增加"使用Counter
更新运算符的当前counter
值。如果找不到该文档,则"upsert"
选项意味着将创建一个新文档,并且无论如何都会相同地增加"增加"也将在新文件中发生。
这里的$inc
选项意味着我们想要"修改"要返回的文档(新的或更改的),而不是在应用"new"
之前文档的样子。结果是"计数器"每次访问时,值始终会增加。
一旦完成并且$inc
的文档要么为其匹配的键增加或创建,那么您现在可以使用一些内容来分配Counter
中的_id
{1}}模型。如前所述,您可以在此处使用Product
以获取带有附加计数器值的前缀。
因此,只要您的文档始终由模型中的virtual
方法创建,或者使用.create()
然后使用new Product()
方法创建,那么附加到您的&#的方法34;模型"在你的代码中始终执行。
请注意,由于您希望在.save()
中使用此功能,因此作为主键,这是"不可变的"并且无法改变。因此,即使稍后更改了引用的字段中的内容,也无法更改_id
中的值,因此,如果已设置_id
值,此处的代码不会尝试。