2017-05-20 14 views
1

私は、顧客とその属性、さらにイベントの形での行動を追跡する分析システムを持っています。これは、Node.jsとMongoDB(Mongooseを使用)を使用して実装されています。MongoDBを使用したユーザセグメンテーションエンジン

ここでは、保存されたユーザーを特定の条件に基づいてセグメントにグループ化することができるセグメンテーション機能を実装する必要があります。例えば、フロントエンドでpurchases > 3 AND country = 'Netherlands'

のようなものが、これはこのようなものになります。

enter image description here

をここで重要な要件は、セグメントがリアルタイムで更新されていないだけで、定期的にしてしまうことがあります。これは、基本的には、ユーザーの属性が変更されるか、新しいイベントがトリガーされるたびに、どのセグメントに属しているかを再度確認する必要があります。

私の現在のアプローチは、セグメントの条件をMongoDBのクエリとして保存し、それをユーザコレクションで実行して、特定のセグメントに属するユーザを特定することです。ユーザーは、私は、彼が直接のGmailユーザーのセグメントに属していることを格納します条件に一致した場合

{ 
    _id: '591638bf833f8c843e4fef24', 
    name: 'Gmail Users', 
    condition: {'email': { $regex : '.*gmail.*'}} 
} 

:Gmailのを使用しているすべてのユーザーをフィルタリングするためのセグメントは、このようになります。例えば

ユーザーのドキュメントに:

{ 
    username: 'john.doe', 
    email: '[email protected]', 
    segments: ['591638bf833f8c843e4fef24'] 
} 

ただし、これを行うことで、私はすべてのセグメントのためのユーザーのデータが変更されるたびに、すべてのクエリを実行しなければならないので、彼はセグメントの一部であるかどうか、私は確認することができます。これは、パフォーマンスの観点から、少し複雑で煩わしいと感じています。

これに対処する別の方法はありますか?ルールエンジンを使用し、データベースではなくアプリケーションで処理しますか?

+0

あなたはすでにセグメントコレクションにクエリを保存していますが、そのユーザーがセグメント内にあるかどうかに影響を与えるフィールドも格納しないのはなぜですか?ユーザーのデータが変更されると、どのフィールドが変更されているかがわかります。フィールドをセグメントと比較するだけです。つまり、現在とは逆の方向に進みます。 –

+0

btw、これは「リアルタイム」ではありません。UIにリクエストがあるたびにセグメンテーションクエリを作成するだけで、「リアルタイム」の結果を得ることができます。 –

答えて

1

残念ながら私はより良いアプローチを知らないが、このソリューションを少しでも最適化することができます。

私は同じだろう:(segments

を使用すると、一致するユーザーを見つけたら、コレクション
    • ストアセグメントの条件をユーザーのドキュメント内のセグメントIDを保存

      重要な要件は、セグメントがリアルタイムで更新されるだけでなく、定期的に更新されることです。

  • 選択肢がありません。セグメントが変更されるたびにセグメント化クエリを実行する必要があります。

      私はすべてのセグメントのためのユーザーのデータは、私は、あなたのソリューションを変更します

      これはを変更するたびに、すべてのクエリを実行する必要があります実際にちょうどそれを少し最適化

    • コレクション全体でセグメンテーションクエリを実行する必要はありません。ユーザIDを$andというクエリに入れると、Mongodbは最初にユーザをフェッチし、その後、残りのセグメンテーション条件をチェックします。 Mongodbがユーザの_idをインデックスとして使用することを確認する必要があります。このためには、.explain()を使用して確認するか、.hint()を使用します。残念ながら、N個のセグメントがある場合は、N + 1個のクエリを実行する必要があります(+1はユーザーの更新用です)。

    • 私はすべてのセグメントをフェッチしてキャッシュに保存します。誰かがセグメントを変更した場合は、キャッシュも更新します。 (または単にキャッシュを無効にし、次のクエリは残りの部分を処理しますが、実装に依存します)。要は、データベースを取得せずにすべてのセグメントを取得し、ユーザーがレコードを更新した場合、Node.jsのすべてのセグメントを調べ、条件でユーザーを検証し、ユーザーのsegments配列を元の更新クエリで更新できる追加のデータベース操作は必要ありません。

    ...私はそれがこのような何かを実装するお尻の痛みかもしれない知っているが、それはデータベースに過負荷をかけていない更新

    私はあなたに私の第二についてのいくつかの技術的な詳細を挙げてみましょう提案: (!これは単なる擬似コードである)

    セグメントキャッシュ

    module.exporst = function() { 
        return new Promise(resolve) { 
        Redis.get('cache:segments', function(err, segments) { 
         // handle error 
    
         // Segments are cached 
         if(segments) { 
         segments = JSON.parse(segments); 
         return resolve(segments); 
         } 
    
         //fetch segments and save it to the cache 
         Segments.find().exec(function(err, segments) { 
         // handle error 
    
         segments = JSON.stringify(segments); 
    
         // Save to the database but set 60 seconds as an expiration 
         Redis.set('cache:segments', segments, 'EX', 60, function(err) { 
          // handle error 
    
          return resolve(segments); 
         }) 
         }); 
        }) 
    
        } 
    } 
    

    ユーザー更新

    // ...  
    let user = user.findOne(_id: ObjectId(req.body.userId)); 
    // etc ... 
    
    // fetch segments from cache or from the database 
    let segments = yield segmentCache(); 
    
    let userSegments = []; 
    segments.forEach(function(segment) { 
        if(checkSegment(user, segment)) { 
        userSegments.push(segment._id) 
        } 
    }); 
    
    // Override user's segments with userSegments 
    

    魔法が起こる場所です、何とかあなたがif文でそれらを使用することができる方法で、条件を定義する必要があります。

    ヒント:Lodashはこの機能があります。_.gt、_.gte、_.eq ...

    チェックセグメント

    module.exports = function(user, segment) { 
        let keys = Object.keys(segment.condition); 
        keys.forEach(function(key) {     
        if(user[key] === segment.condition[key]) { 
         return false; 
        } 
        }) 
    
        return true; 
    } 
    
    +0

    あなたの答えをありがとう! $とクエリのアイデアは間違いなく良い最適化のアイディアです。セグメントのキャッシングに関して、私はキャッシュ内のセグメントをどのように再計算するのだろうと思いますか?ユーザーがセグメントの一部であるかどうかを確認するにはどうすればよいですか。あなたはredisまたはアプリケーションでnode.jsを使ってクエリを実行するのですか? – benjiman

    +0

    @benjimanよろしくお願いします。ハハ、そういうわけで、実装が簡単ではないので、私はそれがお尻の痛みだと書いています。私は、後でいくつかの詳細と私の答えを更新します。 – Festo

    +0

    Allright。それを楽しみにしています!どうも。 – benjiman

    1

    あなたはすでに全体のセグメント "クエリ" を記憶していますセグメントコレクションのドキュメント - ユーザードキュメントのどのフィールドが特定のセグメントのメンバーシップに影響するかを列挙するフィールドを同じドキュメントに含めることをおすすめします。

    ユーザデータを変更すると、どのフィールドが変更されているか知ることができるため、フィールドを変更して計算されたセグメントのみをフェッチして、再実行する必要があるセグメンテーション「クエリ」のサイズを大幅に削減できます。

    ユーザーのデータを変更すると、現在メンバーになっていないセグメントに追加される可能性があるため、ユーザーに現在格納されているセグメントのみを確認するだけでは不十分です。

    関連する問題