0

SQL に慣れた後、mongoDB でこの問題に遭遇しました。まず、マングースを使用しています。

さて、問題です。という名前のコレクションがありUserます。

var UserSchema = new Schema ({
    id : ObjectId,
    name : {type : String, trim : true, required : true},
    email: {type:String, trim:true, required: true, index: { unique: true }},
    password: {type:String, required: true, set: passwordToMD5},
    age: {type:Number, min: 18, required: true, default: 18},
    gender: {type: Number, default:0, required: true},

    height: {type: Number, default:180, min: 140, max: 220},
    _eye_color: {type: ObjectId, default: null},
    location: {
            lon: {type: Number, default: 0},
            lat: {type: Number, default: 0}
    },
    status: {type:Number, required: true, default:0}
    },{
        toObject: { virtuals: true },
        toJSON: { virtuals: true },
        collection:"user"});

次に、このコレクションからすべてのユーザーを選択し、特別な属性 (「ランク」など) で並べ替える必要があります。このランクは、ポイントからの距離、指定された年齢と比較した年齢などに応じて、特定のロジックで計算されます...

それで、このランクを選択してソートに使用する方法を考えていましたか?仮想フィールドを使用しようとしましたが、追加情報をカウントするのに便利ですが、残念ながらfind()仮想フィールドで結果を並べ替えることができません。もちろん、このランクを仮想で計算し、すべてのレコードを選択してから、コールバックで JavaScript を実行できます。しかし、この場合、すべてのユーザーを選択して並べ替えてから制限するため、javascript の部分に時間がかかりすぎる可能性があります... mapreduceを使用することを考えていましたが、私が望むようになるかどうかはわかりません。私のタスクがmongoDB/mongooseで実行できる場合、誰かがヒントをくれますか?

編集1

私も集計フレームワークを使用しようとしましたが、最初はそれが機能を備えた最良のソリューションのように思えました$project。しかし、その後、ランク計算を行う必要が生じたときに、集約が 、 、 などの多くの数学関数をサポートしていないことがわかりsinましcossqrt。また、事前に定義された通常の JavaScript 関数をプロジェクションで使用することもできませんでした。つまり、関数が呼び出されましたが、現在のレコード フィールドを渡すことができませんでした。

{$project: {
  distance_from_user: mUtils.getDistance(point, this.location)
}

関数内の 2 番目の属性は「未定義」でした。

したがって、集計フレームワークでランク計算を行うことは不可能だと思います。

EDIT 2 わかりました、リアルタイムクエリには適していないため、mapreduceを使用しないように誰もが私に言っていることは知っていますが、集計を使用できないため、mapreduceを試してみると思います。では、このマップを削減したとしましょう。

function map() {
            emit(1, // Or put a GROUP BY key here
                {name: this.name, // the field you want stats for
                    age: this.age,
                    lat: this.location.lat,
                    lon: this.location.lon,
                    distance:0,
                    rank:0

                });
        }

        function reduce(key, values) {


            return val;
        }

        function finalize(key, value){

            return value;
        }


        var command = {'mapreduce': "user", 'map': map.toString(), 'reduce': reduce.toString(), query:{$and: [{gender: user_params.gender}, {_id: {$ne: current_user_id}}]}, 'out': {inline:1}};

        mongoose.connection.db.executeDbCommand(command, function(error, result){
            if(error) {
                log(error);
                return;
            }
            log(result);
            return;
        });

すべてのユーザーのランクを計算するには、reduce (または change map) に何を記述すればよいですか?

4

3 に答える 3

1

唯一の本当の解決策は、各ドキュメントのランクを計算し、それをドキュメントに保存することです。ドキュメント内の値が一定である限り、この値は一定であるため、この値に影響を与えるフィールドを更新するたびに、この値を簡単に計算できます。

Map/reduce は確かにこれに適したソリューションではなく、他のタイプの集計でもありません。MongoDB を使用している場合、ランクを事前に計算してドキュメントと共に保存することが唯一のオプションです。

于 2012-11-21T13:49:14.037 に答える
1

そのようなことが必要な計算の量を認識しています-ユーザーがログインするたびにそれを行うと、多くの人がより短い時間でログインするときに興味深い負荷ピークが発生します-そしてあなたのページ(インターフェース)リソースに大きく依存します (これは良くありません)。
ログオンしているすべてのユーザーのランキングを維持し、間隔を置いて更新することをお勧めします。「短いセッション」と「長いセッション」(長いセッション - Web ブラウザーで使用するセッションと短いセッション - 「オンライン」) を維持します。 、現在サイトを使用している」)、「短時間アクティブな」ユーザーに対してのみ定期的にランクを生成し、長いセッションでログオンしたユーザーに対してはめったにランクを生成しません。5分おきとか。
そのような場合は mapredurce を使用できます。マップ関数は、特定のユーザーのランクをカウントするために必要なデータ (年齢、緯度、経度、必要なものなど) と、テストされたユーザーの結果 (ランク) のみを出力する必要があります (emit空です)。reduce 関数については、 mapreduce を使用した並べ替えを確認する必要があります (ランクの作成方法に大きく依存します)。また、他のユーザーのランク (または何らかのサブ値) をカウントします。

于 2012-11-21T18:12:54.763 に答える
0

MongoDB + Hadoopの良いユースケースのようです。

このプレゼンテーションは、この組み合わせの可能性のいくつかを示しています。

于 2012-11-21T14:29:51.800 に答える