sql >> データベース >  >> NoSQL >> MongoDB

上限付きコレクションを使用せずに、順序付けられたドキュメントのセットをMongoDBに保存する方法

    要件に基づいて、アプローチの1つは、各ドキュメントが機能を備えているようにスキーマを設計することです。 複数のドキュメントを保持し、それ自体がキャップされたコンテナとして機能する 。

    {
      "_id":Number,
      "doc":Array
    }
    

    コレクション内の各ドキュメントは、キャップされたコンテナとして機能します。 、ドキュメントはdocに配列として保存されます 分野。 doc フィールドは配列であり、挿入の順序を維持します。ドキュメントの数をnに制限できます。 。したがって、_id 各コンテナドキュメントのフィールドは、nずつ増分されます 、コンテナドキュメントが保持できるドキュメントの数を示します。

    これらを行うことにより、回避 extra fieldsを追加する ドキュメントに、extra indicesunnecessary sorts

    最初のレコードを挿入する

    つまり、コレクションが空の場合。

    var record = {"name" : "first"};
    db.col.insert({"_id":0,"doc":[record]});
    

    後続のレコードの挿入

    • 最後のコンテナドキュメントの_idを特定します 、およびnumber 保持しているドキュメントの数。
    • 保持するドキュメントの数がn未満の場合 、次に更新 新しいドキュメントを含むコンテナドキュメント、それ以外の場合は作成 新しいコンテナドキュメント。

    たとえば、各container document 5を保持できます せいぜいドキュメントであり、新しいドキュメントを挿入したい。

    var record = {"name" : "newlyAdded"};
    
    // using aggregation, get the _id of the last inserted container, and the 
    // number of record it currently holds.
    db.col.aggregate( [ {
        $group : {
            "_id" : null,
            "max" : {
                $max : "$_id"
            },
            "lastDocSize" : {
                $last : "$doc"
            }
        }
    }, {
        $project : {
            "currentMaxId" : "$max",
            "capSize" : {
                $size : "$lastDocSize"
            },
            "_id" : 0
        }
    // once obtained, check if you need to update the last container or 
    // create a new container and insert the document in it.
    } ]).forEach( function(check) {
        if (check.capSize < 5) {
            print("updating");
            // UPDATE
            db.col.update( {
                "_id" : check.currentMaxId
            }, {
                $push : {
                    "doc" : record
                }
            });
        } else {
            print("inserting");
            //insert
            db.col.insert( {
                "_id" : check.currentMaxId + 5,
                "doc" : [ record ]
            });
        }
    })
    

    aggregationに注意してください 、サーバー側で実行され、非常に効率的です。また、aggregationにも注意してください。 ドキュメントが返されます カーソルではなく previous to 2.6 。したがって、カーソルを繰り返すのではなく、単一のドキュメントから選択するように上記のコードを変更する必要があります。

    ドキュメントの間に新しいドキュメントを挿入する

    ここで、ドキュメントの間に新しいドキュメントを挿入する場合1 および2 、ドキュメントは_id=0のコンテナ内に収まる必要があることがわかっています secondに配置する必要があります doc内の位置 そのコンテナの配列。

    そのため、$eachを利用します および$position 特定の位置に挿入するための演算子。

    var record = {"name" : "insertInMiddle"};
    
    db.col.update(
    {
        "_id" : 0
    }, {
        $push : {
            "doc" : {
                $each : [record],
                $position : 1
            }
        }
    }
    );
    

    フローの処理

    次に、ドキュメントのoverflowingを処理する必要があります 各container たとえば、_id=0のコンテナに新しいドキュメントを挿入するとします。 。コンテナにすでに5がある場合 ドキュメントの場合、move the last document to the next container必要があります そして、すべてのコンテナがその容量内にドキュメントを保持するまでこれを行います。必要に応じて、最後にオーバーフローしたドキュメントを保持するコンテナを作成する必要があります。

    この複雑な操作はすべき サーバー側で実行する 。これを処理するために、次のようなスクリプトを作成してregisterを作成できます。 mongodbでそれを。

    db.system.js.save( {
        "_id" : "handleOverFlow",
        "value" : function handleOverFlow(id) {
            var currDocArr = db.col.find( {
                "_id" : id
            })[0].doc;
            print(currDocArr);
            var count = currDocArr.length;
            var nextColId = id + 5;
            // check if the collection size has exceeded
        if (count <= 5)
            return;
        else {
            // need to take the last doc and push it to the next capped 
        // container's array
        print("updating collection: " + id);
        var record = currDocArr.splice(currDocArr.length - 1, 1);
        // update the next collection
        db.col.update( {
            "_id" : nextColId
        }, {
            $push : {
                "doc" : {
                    $each : record,
                    $position : 0
                }
            }
        });
        // remove from original collection
        db.col.update( {
            "_id" : id
        }, {
            "doc" : currDocArr
        });
        // check overflow for the subsequent containers, recursively.
        handleOverFlow(nextColId);
    }
    }
    

    そのため、after every insertion in between 、このfunctionを呼び出すことができます コンテナIDを渡すことにより、handleOverFlow(containerId)

    すべてのレコードを順番に取得する

    $unwindを使用するだけです aggregate pipelineの演算子 。

    db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
    

    ドキュメントの並べ替え

    各ドキュメントは、「_id」フィールドを使用して上限付きのコンテナに保存できます。

    .."doc":[{"_id":0,","name":"xyz",...}..]..
    

    アイテムを並べ替えるキャップ付きコンテナの「doc」配列を取得します。

    var docArray = db.col.find({"_id":0})[0];
    

    IDを更新して、並べ替え後にアイテムの順序が変わるようにします。

    _idsに基づいて配列を並べ替えます。

    docArray.sort( function(a, b) {
        return a._id - b._id;
    });
    

    新しいドキュメント配列を使用して、上限のあるコンテナを更新し直します。

    しかし、繰り返しになりますが、すべてが実行可能であり、要件に最も適したアプローチに要約されます。

    あなたの質問に来る:

    配列としてのドキュメント。

    $eachを使用します および$position db.collection.update()の演算子 私の答えに示されているように機能します。

    はい。コレクションのデータが非常に少ない場合を除いて、パフォーマンスに影響します。

    はい。上限付きコレクションを使用すると、データが失われる可能性があります。



    1. 集計中に一致クエリの周囲にデータを分割する

    2. MongoDb idの配列を使用して複数のドキュメントを取得するにはどうすればよいですか?

    3. ElasticSearchとPHPの複数のフィールドでの検索

    4. ネストされたオプションオブジェクトを持つマングーススキーマ