LoginSignup
4
4

More than 5 years have passed since last update.

MongoDBとElasticsearchを同期させる

Last updated at Posted at 2015-06-01

それRiverでできるじゃん

はい、そうです
素直にRiver Plugin使えばいいのですが

  • River用にレプリケーションを組まなきゃない
  • Elasticsearch本体のバージョンとプラグインのバージョン違いが思わぬところで地雷になる
  • その他の事情

などの理由でRiverを使わない場合はどうすんべという話

Mongooseにはmiddlewareという機能があり、他のRDBMSのTrigger機能のように各種のメソッドコールに処理を差し込めるので、これを使うことにします

実装

予めElasticsearch側のインデックスを作っておく

そしてMongooseとElasticsearchを結ぶようなモデルを作る
個人的にはapp.jsの下にlib/model.jsだったりlib/model/db名.jsとか作るのが好みです

lib/model.js
module.exports = function(){
    var mongoose = require('mongoose');
    var connection = mongoose.createConnection('mongodb://localhost:27017/foobar');
    var Model = {};
    var Schema = {};

    var elasticsearch = require('elasticsearch');
    var es = new elasticsearch.Client({
        host: 'localhost:9200'
    });

    Schema.Foo = new mongoose.Schema({
        /**
        *   Schema定義をいろいろ
        */
        message: {type: String, default: ''}
    });

    Schema.Foo.post('save', function(doc){
        es.index({
            index: 'foobar',
            type: 'foo',
            id: doc._id.toString(),
            body: JSON.parse(JSON.stringify(doc))
        }, function(err, response){
            next(err);
        });
    });

    Schema.Foo.post('remove', function(doc){
        es.delete({
            index: 'foobar',
            type: 'foo',
            id: doc._id.toString()
        }, function(err, response){
            next(err);
        });
    });

    Model.Foo = connection.model('Foo', Schema.Foo, 'foo');

    return Model;
};


使うときは

var Model = require('lib/model.js')();
var data = new Model.Foo();
data.message = 'あぁ^~心がぴょんぴょんするんじゃぁ^~';

data.save(function(err){
    console.log(err);
});

として普通にModel経由で操作をしてれば更新があったときにElasticsearch側も裏側で更新されてるという寸法

ところが

この方法だとMongooseのsave()メソッドのみにおいて有効で、findOneAndUpdateやupdateなどネイティブなクエリ構文を使う場合に更新されない

これは割と前からIssueとしてあげられていましたが、middlewareのページに曰くMongoose4.xだと、

Middleware (also called pre and post hooks) are functions which are passed control during execution of asynchronous functions. Middleware is specified on the schema level and is useful for writing plugins. Mongoose 4.0 has 2 types of middleware: document middleware and query middleware. Document middleware is supported for the following document functions.

  • init
  • validate
  • save
  • remove

Query middleware is supported for the following Model and Query functions.
* count
* find
* findOne
* findOneAndUpdate
* update

Both document middleware and query middleware support pre and post hooks. How pre and post hooks work is described in more detail below.

とあり、クエリを使う場合のミドルウェアはクエリごとに書いてね、とあるので

var foo = function(doc){
    es.index({
        index: 'foobar',
        type: 'foo',
        id: doc._id.toString(),
        body: JSON.parse(JSON.stringify(doc))
    }, function(err, response){
        next(err);
    });
};

Schema.Foo.post('save', foo);
Schema.Foo.post('update', foo);
Schema.Foo.post('findOneAndUpdate', foo);

のような形がいいのかな?と思います

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4