+17

Seeder và faker để tạo dữ liệu mẫu cho MongoDB / Nodejs

I. Giới thiệu

Lần trước mình có viết một bài hướng dẫn tạo đa ngôn ngữ cho ứng dụng web nodejs đa ngôn ngữ cho ứng dụng web nodejs, cũng được một bạn vào comment cám ơn - vui quá trời.. Hehe. Tại đây là lần đầu tiên mình viết bài trên viblo, cũng là lần đâu tiên chia sẽ một cái gì đó.. Nên n phấn khởi hơn bình thường ☺️ ☺️ ☺️ Nay mình xin mạo muộn chia sẻ thêm việc tạo dữ liệu mẫu trong ứng dụng nodejs. Nếu mn đã dùng qua laravel hẳn đều biết đến việc tạo dữ liệu mẫu dùng seederfaker, sau đó chỉ cần chạy lệnh php artiasan db:seed là ra bao nhiêu dữ liệu tùy ý, tha hồ truy vấn thử mà không cần nhập dữ liệu mẫu một cách thủ công. Nay mình xin hướng dẫn các bạn cũng làm được như vậy cho ứng dụng nodejs dùng mongodb.... Chỉ với một lệnh node db_seed là có thể tạo ra dữ liệu mẫu ở các collection mong muốn... 🎩

II. Tạo một project

Mình vẫn sẽ dùng express-generator để tạo nhanh một project nhé:

  1. Nếu bạn chưa có express-generator: Gõ npm install express-generator -g Để cài đặt express-generator ở chế độ global.
  2. Sau đó chuyển vào thư mục bạn muốn lưu project: Gõ express --view=ejs yourapp để tạo một project mới với tên là yourapp. Mình dùng view-engine là ejs nên mình để là --view=ejs nếu bạn dùng jade hay pug..v.v. thì sửa tham số nhé...
  3. Sau đó, bạn sẽ được một project như dưới đây :

III. Nodejs seeder và faker

Để tạo dữ liệu mẫu, mình dùng hai package là mongoose-seedfaker. Bạn gõ npm install mongoose-seed --savenpm install faker --save để tải và lưu hai package này vào project của mình... Sau đó trong project của bạn, tạo thêm thư mục model, database và thư mục con seeder ( Cho nó chuyên nghiệp. Hehe ). trong model bạn dùng mongoose để định nghĩa các schema tương ứng cho các collection của bạn nhé. Ví dụ như mình có một schema Category như sau, ví dụ thôi nhé :


var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var CategorySchema = new mongoose.Schema({
    name : String,
    descript : String,
    type : Number,
    createdOn: { type: Date, 'default': Date.now }
});

module.exports = mongoose.model('Category', CategorySchema);

Xong công đoạn chuẩn bị râu ria rồi nè. Giờ giả dụ bạn cần seed dữ liệu cho collection category trên chẳng hạn. Ví dụ mình sẽ seed 15 mẫu dữ liệu nhé :


const seeder = require('mongoose-seed');
const faker = require('faker');

let items = [];
for(i=0; i < 15; i++){
    items.push(
        {
            name : faker.commerce.productName(),
            descript : faker.lorem.text(),
            type : faker.random.number()
        }
    )
}

let data = [{
    'model': 'Category',
    'documents': items
}]

// connect mongodb
seeder.connect('mongodb://localhost:27017/havana', function() {
  seeder.loadModels([
    '../../model/category'  // load mongoose model 
  ]);
  seeder.clearModels(['Category'], function() {
    seeder.populateModels(data, function() {
      seeder.disconnect();
    });
  });
});

Xong, bây giờ bạn chỉ cần chuyển terminal đến thư mục chứa file category_seeder này và chạy lệnh node category_seeder là đã có thể thấy 15 mẫu dữ liệu được tạo trong categories collection như dưới đây :

IV. Dữ liệu có quan hệ reference

Đến đây bạn có thể thắc mắc rằng seed kiểu này thì đơn giản vấn đề là giữa các collection có quan hệ reference thì phải làm sao để lấy được ObjectId của document mà n tham chiếu đến để còn truy vấn thử ? Với cả nhỡ có nhiều collection thì phải chạy từng file à ? Và mình đã mất mất cả buổi chiều để giải quyết chuyện này... 🤣 🤣 🤣 Ví dụ mình có Bill schema như sau :


var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var BillSchema = new mongoose.Schema({
    total : Number,
    status : Number,
    note: String,
    address : String,
    phone: String,
    user_id : { type: ObjectId, ref : 'User'},
    detais : [
        {
            product_id : { type: ObjectId, ref : 'Product'},
            product_name : String,
            price : Number,
            quantity : Number
        }
    ],
    createdOn: { type: Date, 'default': Date.now }
});

module.exports = mongoose.model('Bill', BillSchema);

Như bạn có thể thấy, Bill có quan hệ reference với UserProduct vấn đề là chúng ta làm sao lấy và sinh ngẫu nhiên được ObjectId của thằng UserProduct ? Và đây là giải pháp của cá nhân mình, truy vấn ra ObjectId của User và Product, ta có được hai mảng chứa object id của User và của Product... Sau đó dùng Lodash để lấy ngẫu nhiêu các phần tử trong hai mảng này. Đại khái code như dưới đây :


var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
const seeder = require('mongoose-seed');
const async = require('async');
const Product = require('../../model/product');
const User = require('../../model/user');
const _ = require('lodash');
var faker = require('faker');

new Promise((resolve) => {
    mongoose.connect('mongodb://localhost:27017/havana', {
        useMongoClient: true,
        promiseLibrary: require('bluebird')
    });
    async.parallel([
        (callback) => {
            Product.find({}, { _id : 1 })
            .exec((err, product_ids) => {
                callback(null, product_ids);
            }); 
        },
        (callback) => {
            User.find({})
            .exec((err, user_ids) => {
                callback(null, user_ids);
            });
        }
    ], 
    (err, results) => {
        resolve(results);
        mongoose.connection.close();
    });
}).then((results) => {
    return new Promise((resolve) => {
        let items = [];
        let status = [1, 2]
        for(i=0; i< 150; i++){
            items.push(
                {
                    total : faker.commerce.price(),
                    status : _.sample(status),
                    note: faker.lorem.text(),
                    address : faker.address.streetAddress,
                    phone: faker.phone.phoneNumber,
                    user_id : _.sample(results[0])._id,
                    detais : [
                        {
                            product_id : _.sample(results[1])._id,
                            product_name : _.sample(results[1]).name,
                            price : _.sample(results[1]).unit_price,
                            quantity : faker.random.number()
                        },
                        {
                            product_id : _.sample(results[1])._id,
                            product_name : _.sample(results[1]).name,
                            price : _.sample(results[1]).unit_price,
                            quantity : faker.random.number()
                        }
                    ]
                }
            );
        }
        resolve(items);
    });
}).then((items) => {
    seeder.connect('mongodb://localhost:27017/havana', function() {
        let data = [{
            'model': 'Bill',
            'documents': items
        }]
        seeder.loadModels([
            '../../model/bill'
        ]);
        seeder.clearModels(['Bill'], function() {
            seeder.populateModels(data, function() {
            seeder.disconnect();
            });
        });
     });
});

V. Chạy nhiều seeder bằng một lệnh.

Cái này chỉ còn là việc tạo một module nhỏ rồi require các file seeder đã viết ở trên và chạy chúng thôi không có gì đặc biệt cả. Điều cần lưu ý là bạn nên để seeder nào chạy trước cái nào chaỵ sau. Ví dụ như Product chứa ObjectId của Category thì ta để category_seeder chạy trước, product_seeder chạy sau. Ở đây mình dùng asyn await để chạy chúng tho thứ tự, ví dụ đây sẽ là file db_seed.js :


const bill = require('./bill_seeder');
const category = require('./category_seeder');
const product = require('./product_seeder');
const user = require('./user_seeder');

async function dbseed() {
    await category();
    await product();
    await user();
    await bill();
    console.log('data generate successfull');
}

Giờ thì thay vì chạy node abc để chạy từng file seeder, babnj chỉ cần chạy một file db_seed này với câu lệnh node db_seed là đã có thê seed hêt các file dữ liệu mong muốn.

VI. Kết luận.

Vừa xong mình vừa tạo một module nhỏ, nhằm giúp bản thân có thể tiết kiện thời gian trong quá trình thiết kế và thử nghệm database. Vì mình cũng mới bập bõm dùng mongodb, nên cứ đập đi xây lại miết... Mà cứ mỗi lần như vậy lại phải chèn thêm ít dữ liệu để thử truy vấn thử lại, ấm ức quá trời. Nên mới tìm cách và mạo muộn chia sẽ lại cho mọi người, hy vọng nhận được sự góp ý cũng như chỉ dẫn cách khác tốt hơn nếu có... ☺️ ☺️ ☺️ Để xem rõ hơn các bạn vào xem ducoment của Mongoose-seed tại đây Còn Faker.js tại đây


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.