2012-03-15 10 views
1

このクエリはアプリケーションで非常に時間がかかります。データベースには約200万レコードが含まれています。DoctrineでのMongoDBクエリの最適化

$results = $queryBuilder 
     ->field('extra.targetPlayerId')->exists(FALSE) 
     ->field('permission')->equals('public') 
     ->field('time')->gt($weekEndTime) // variable is timestamp 
     ->field('time')->lte($startTime) // variable is timestamp 
     ->map(
      'function() { 
       var total = 0; 
       if (this.comments) { 
        total += this.comments.length; 
       } 
       if (this.likes) { 
        total += this.likes.length; 
       }      
       if (total > 0) { 
        emit(this.playerId, total); 
       } 
      }' 
     ) 
     ->reduce(
      'function(key, values) { 
       var total = 0; 
       for (value in values) { 
        total += values[value]; 
       }; 
       return total; 
      }' 
     )->getQuery()->execute(); 

このクエリはどのように最適化できますか?あなたは私にインデックスの提案を教えてもらえますか?

答えて

4
->field('extra.targetPlayerId')->exists(FALSE) 

おそらく、クエリを高速化するための最大の障害があります。 the docsから

v2.0の前に、$が存在するには、インデックスを使用することができません。他のフィールドのインデックスは引き続き使用されます。 $ existsはインデックスとespでも効率的ではありません。 {$ exists:true}はすべてのインデックス値を効果的にスキャンする必要があるためです。

おそらく存在の代わりに真偽をテストできるようなブール値フィールドを追加し、そのフィールドをインデックスに含めることを検討してください。

あるいはあなたのマップ関数に、特定の条件を移動:あなたが大量のデータを持っている場合

function() { 
    var total = 0; 

    if (!this.extra || !this.extra.targetPlayerId) { 
     return; //bail - count nothing 
    } 

    if (this.comments) { 
     total += this.comments.length; 
    } 
    if (this.likes) { 
     total += this.likes.length; 
    }      
    if (total > 0) { 
     emit(this.playerId, total); 
    } 
} 

インデックスは、例えば、黒と白の答えではありません。

{ 
    time: 1, 
    permission: 1, 
    extra.hasTargetPlayer: 1 
} 

は、mongoが最初の基準として適切な日付範囲に集中できるようになるため、おそらくかなりうまくいくでしょう。

それ以外にも、単にデータスキーマを変更する方が適切かもしれません。あなたはそのクエリの結果を保存していますか?

すなわちクエリ一度、結果を保存し、すべての後続の要求のためにあなただけ行うことができるように:_id年とweeknumberある

db.appstats.findOne({_id: "201152"}) 

を。週ごとに統計情報を生成している場合は、スケジュール(cron)で大量のクエリを実行することができます。したがって、ユーザーはクエリの速度の影響を受けず、実際に結果を計算します。