0

私はmongo/pythonで次のことを行うためのエレガントな方法を見つけようとしています。2つのコレクションがあります。1つは人と属性のリストを含み、もう1つは「人口サブセット」である人のサブセットを含みます。マップリデュースジョブを実行して、大きなリストのいくつかの集計統計を計算しますが、母集団サンプルに表示される人の名前のみを使用します。レコードのセットの例を次に示します。

master_list: [{ Name: Jim }, { Age: 24}
              { Name: Bill}, { Age: 38}
              { Name: Mary}, { Age: 55}]

subset : [{ Name: Jim}
          { Name: Mary}]

アイデアは、年齢の平均を計算することですが、サブセットにリストされているように、master_listの3つのレコードのうち2つだけを使用します。mongoのmap_reduceがクエリパラメーターをサポートしていることは知っていますが、上記に対処するための最良の方法が結合なしである場合は明確ではありません。1つのオプションは、master_listを前処理し、使用するレコードにフラグを立てる属性'include'を作成してから、map_reduceフィルターでそのレコードを操作することです。しかし、不器用なようで、データベースに永続的なフラグを作成しますが、これはさまざまな理由で煩わしいものです。

アップデート

クエリにリストを埋め込むための提案を読んだ後、私は以下で必要なものを得ることができました

map_reduce(mapper, reducer, out = {'merge': 'Stats'}, 
           finalize = finalizer, scope = {'atts': f},
           query = {'Name' : { '$in' : pop }})

ここで、popはPythonの名前のリストです。ありがとう!

4

1 に答える 1

4

MongoDBでこれを解決するには、2つのアプローチがあります。

  1. サブセットがかなり小さい場合は、サブセットに対してクエリを実行してすべてのメンバーを検索し、そのクエリの結果をmap-reduce呼び出しの最初のクエリとして使用できます。

  2. ただし、サブセットが非常に大きい場合、これは不可能な場合があります。次にできることは、「reduce」出力オプションを使用して2つのmap-reduce呼び出しを使用して結合をシミュレートし、同じターゲットコレクションに縮小することです。これにより、ドキュメントが次のようになる中間コレクションが作成されます。

    {Name: Jim, Age: 24, inSubset: true}
    {Name: Bill, Age: 38, inSubset: false}
    {Name: Mary, Age: 55, inSubset: true}
    

    最後に、この中間コレクションで3番目のマップリデュースを実行して、を含むすべてのドキュメントを平均することができますinSubset: true

pymongoドライバーを使用したPythonの2.オプション(3つのmap-reduces)のコードは次のとおりです。

from pymongo import Connection
from bson import ObjectId, Code

con = Connection(port=30000)  # add host/port here if different from default
db = con['test']    # or the database name you are using

# insert documents
db.master.insert({'_id': ObjectId(), 'Name': 'Jim', 'Age': 24})
db.master.insert({'_id': ObjectId(), 'Name': 'Bill', 'Age': 38})
db.master.insert({'_id': ObjectId(), 'Name': 'Mary', 'Age': 55})

db.subset.insert({'_id': ObjectId(), 'Name': 'Jim'})
db.subset.insert({'_id': ObjectId(), 'Name': 'Mary'})

# map function for master collection
mapf_master = Code(""" function () {
    emit(this.Name, {'age': this.Age, 'inSubset': false});
} """)

# map function for subset collection
mapf_subset = Code(""" function() {
    emit(this.Name, {'age': 0, 'inSubset': true});
} """)

# reduce function for both master and subset
reducef = Code(""" function(key, values) {
    var result = {'age': 0, 'inSubset': false};

    values.forEach( function(value) {
        result.age += value.age;
        result.inSubset = result.inSubset || value.inSubset;
    });

    return result;
} """)

# call map-reduce on master and subset (simulates a join)
db.master.map_reduce(mapf_master, reducef, out={'reduce': 'join'})
db.subset.map_reduce(mapf_subset, reducef, out={'reduce': 'join'})


# final map function for third map-reduce call
mapf_final = Code(""" function() {
    if (this.value.inSubset) {
        emit('total', {'age': this.value.age, 'count': 1});
    }
} """)

# final reduce function for third map-reduce call
reducef_final = Code(""" function(key, values) {
    var result = {'age': 0, 'count': 0};

    values.forEach( function(value) {
        result.age += value.age;
        result.count += value.count;
    });

    return result;
} """)


# final finalize function, calculates the average
finalizef_final = Code(""" function(key, value) {
    if (value.count > 0) {
        value.averageAge = value.age / value.count;
    }
    return value;
} """)


# call final map-reduce 
db.join.map_reduce(mapf_final, reducef_final, finalize=finalizef_final, out={'merge': 'result'})

結果のコレクションは次のようになります(mongoシェルから照会):

> db.result.find()
{ "_id" : "total", "value" : { "age" : 79, "count" : 2, "averageAge" : 39.5 } }

最終的な平均はvalue.averageAgeフィールドに保存されます。

于 2012-09-14T03:59:50.323 に答える