如何在不使用封顶集合的情况下在MongoDB中存储一组有序文档 [英] How to store an ordered set of documents in MongoDB without using a capped collection

查看:57
本文介绍了如何在不使用封顶集合的情况下在MongoDB中存储一组有序文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在顺序重要的MongoDB中存储一组文档的好方法是什么?我需要轻松地将文档插入任意位置,并可能在以后重新排序.

What's a good way to store a set of documents in MongoDB where order is important? I need to easily insert documents at an arbitrary position and possibly reorder them later.

我可以为每个项目分配一个递增的编号并以此排序,或者可以按_id进行排序,但是我不知道如何在另一个文档之间插入另一个文档.假设我想在sequence5的元素和sequence6的元素之间插入一些内容?

I could assign each item an increasing number and sort by that, or I could sort by _id, but I don't know how I could then insert another document in between other documents. Say I want to insert something between an element with a sequence of 5 and an element with a sequence of 6?

我的第一个猜测是增加以下所有元素的sequence,以便使用诸如db.items.update({"sequence":{$gte:6}}, {$inc:{"sequence":1}})之类的查询为新元素留出空间.我对数据库管理的有限了解告诉我,这样的查询会很慢,而且通常是个坏主意,但我很高兴得到纠正.

My first guess would be to increment the sequence of all of the following elements so that there would be space for the new element using a query something like db.items.update({"sequence":{$gte:6}}, {$inc:{"sequence":1}}). My limited understanding of Database Administration tells me that a query like that would be slow and generally a bad idea, but I'm happy to be corrected.

我想我可以将新元素的sequence设置为5.5,但是我认为那会很快变得混乱. (再次,如果我错了,请纠正我.)

I guess I could set the new element's sequence to 5.5, but I think that would get messy rather quickly. (Again, correct me if I'm wrong.)

我可以使用有上限的集合,该集合有保证的顺序,但是如果我需要增加集合的数量,那么我会遇到问题. (再次,我可能也错了.)

I could use a capped collection, which has a guaranteed order, but then I'd run into issues if I needed to grow the collection. (Yet again, I might be wrong about that one too.)

我可以让每个文档都包含对下一个文档的引用,但这将需要查询列表中的每个项目. (您将获得一个项目,将其推入结果数组,并基于当前项目的next字段获得另一个项目.)除了明显的性能问题之外,我也无法传递经过排序的mongo将光标移到我的{#each}空格键块表达式中,并使其随数据库更改而实时更新. (我正在使用 Meteor 全栈JavaScript框架.)

I could have each document contain a reference to the next document, but that would require a query for each item in the list. (You'd get an item, push it onto the results array, and get another item based on the next field of the current item.) Aside from the obvious performance issues, I would also not be able to pass a sorted mongo cursor to my {#each} spacebars block expression and let it live update as the database changed. (I'm using the Meteor full-stack javascript framework.)

我知道每种方法都有其优点和缺点,我可能只需要使用上面列出的选项之一,但是我想知道是否有更好的方法来做这件事.

I know that everything has it's advantages and disadvantages, and I might just have to use one of the options listed above, but I'd like to know if there is a better way to do things.

推荐答案

根据您的要求,一种方法可能是设计模式,以使每个文档都具有功能可以容纳一个以上的文档,并且其本身就是一个封顶的容器.

Based on your requirement, one of the approaches could be to design your schema, in such a way that each document has the capability to hold more than one document and in itself act as a capped container.

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

集合中的每个文档将充当一个加盖的容器,并且这些文档将作为数组存储在doc字段中. doc字段是一个数组,将保持插入顺序. 您可以将文档数量限制为n.因此,每个容器文档的_id字段将按n递增,表示容器文档可以容纳的文档数.

Each document in the collection will act as a capped container, and the documents will be stored as array in the doc field. The doc field being an array, will maintain the order of insertion. You can limit the number of documents to n. So the _id field of each container document will be incremental by n, indicating the number of documents a container document can hold.

通过执行这些操作,您避免extra fields添加到文档extra indicesunnecessary sorts.

By doing these you avoid adding extra fields to the document, extra indices, unnecessary sorts.

即集合为空时.

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

插入后续记录

  • 标识最后一个容器文档的_idnumber 它拥有的文件.
  • 如果所保存的文档数少于n,请更新 包含新文档的容器文档,否则创建一个新容器 文档.
  • Inserting subsequent records

    • Identify the last container document's _id, and the number of documents it holds.
    • If the number of documents it holds is less than n, then update the container document with the new document, else create a new container document.
    • 说,每个container document最多可以容纳5个文档,我们要插入一个新文档.

      Say, that each container document can hold 5 documents at most,and we want to insert a new document.

      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中.因此,您需要修改上面的代码以仅从单个文档中进行选择,而不是迭代游标.

      Note that the aggregation, runs on the server side and is very efficient, also note that the aggregation would return you a document rather than a cursor in versions previous to 2.6. So you would need to modify the above code to just select from a single document rather than iterating a cursor.

      现在,如果您想在文档12之间插入新文档,我们知道该文档应以_id=0落入容器内,并应放置在文档中的second位置. doc该容器的数组.

      Now, if you would like to insert a new document between documents 1 and 2, we know that the document should fall inside the container with _id=0 and should be placed in the second position in the doc array of that container.

      因此,我们利用$each$position运算符将其插入到特定位置.

      so, we make use of the $each and $position operators for inserting into specific positions.

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

      处理流量

      现在,我们需要照顾每个container中的文档overflowing,例如,我们将两者之间的新文档插入到带有_id=0的容器中.如果容器已经有5个文档,则需要move the last document to the next container这样做,直到所有容器都在其容量范围内保存文档为止,如果需要,最后需要创建一个容器来保存溢出的文档.

      Handling Over Flow

      Now, we need to take care of documents overflowing in each container, say we insert a new document in between, in container with _id=0. If the container already has 5 documents, we need to move the last document to the next container and do so till all the containers hold documents within their capacity, if required at last we need to create a container to hold the overflowing documents.

      此复杂的操作服务器端上完成.为了解决这个问题,我们可以创建一个脚本,例如下面的脚本,然后使用mongodb register脚本.

      This complex operation should be done on the server side. To handle this, we can create a script such as the one below and register it with 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,我们可以通过传递容器ID handleOverFlow(containerId)来调用function.

      So that after every insertion in between , we can invoke this function by passing the container id, handleOverFlow(containerId).

      只需在aggregate pipeline中使用$unwind运算符即可.

      Just use the $unwind operator in the aggregate pipeline.

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

      重新订购文件

      您可以使用"_id"字段将每个文档存储在加盖的容器中:

      Re-Ordering Documents

      You can store each document in a capped container with an "_id" field:

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

      获取所需封顶容器的"doc"数组 重新排序商品.

      Get hold of the "doc" array of the capped container of which you want to reorder items.

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

      更新其ID,以便在排序后更改项目的顺序.

      Update their ids so that after sorting the order of the item will change.

      根据数组的_id对数组进行排序.

      Sort the array based on their _ids.

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

      使用新的doc数组更新加盖的容器.

      update the capped container back, with the new doc array.

      但是再说一遍,一切都归结为哪种方法可行并最适合您的需求.

      But then again, everything boils down to which approach is feasible and suits your requirement best.

      提出您的问题:

      在顺序重要的MongoDB中存储一组文档的一种好方法是什么?我需要轻松地在任意位置插入文档 位置,并可能在以后重新排序.

      What's a good way to store a set of documents in MongoDB where order is important?I need to easily insert documents at an arbitrary position and possibly reorder them later.

      文档为数组.

      说我想在顺序为5的元素和顺序为6的元素之间插入一些内容吗?

      如我的回答所述,在db.collection.update()函数中使用$each$position运算符.

      use the $each and $position operators in the db.collection.update() function as depicted in my answer.

      我对数据库管理的有限了解告诉我, 这样的查询会很慢,通常是个坏主意,但我很高兴 予以纠正.

      My limited understanding of Database Administration tells me that a query like that would be slow and generally a bad idea, but I'm happy to be corrected.

      是的.除非集合中的数据很少,否则它将影响性能.

      Yes. It would impact the performance, unless the collection has very less data.

      我可以使用有上限的集合,该集合有保证的顺序,但是如果我需要扩展集合,则会遇到问题. (然而 再次,我可能也错了.)

      I could use a capped collection, which has a guaranteed order, but then I'd run into issues if I needed to grow the collection. (Yet again, I might be wrong about that one too.)

      是的.使用上限集合,您可能会丢失数据.

      Yes. With Capped Collections, you may lose data.

      这篇关于如何在不使用封顶集合的情况下在MongoDB中存储一组有序文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆