按日期时间过滤子文档

伊万·佩拉尔塔(Ivan Peralta)

我有以下型号

var messageSchema   = new Schema({
    creationDate:   { type: Date, default: Date.now },
    comment:        { type: String },
    author:         { type: Schema.Types.ObjectId }
});
var conversationSchema = new Schema({
    title:          { type: String },
    author:         { type : Schema.Types.ObjectId },
    members:        [ { type: Schema.Types.ObjectId } ],
    creationDate:   { type: Date, default: Date.now },
    lastUpdate:     { type: Date, default: Date.now },
    comments:       [ messageSchema ]
});

我想创建两个方法来获取由用户或对话ID在日期之后生成的注释。

按用户

我尝试了以下方法

var query = { 
    members : { $all : [ userId, otherUserId ], "$size" : 2 }
    , comments : { $elemMatch : { creationDate : { $gte: from } } } 
};

如果在指定日期之后(自)起没有注释,则该方法返回[]或null

通过对话ID

当我尝试通过用户ID获取时,也会发生同样的情况

var query = { _id : conversationId
    , comments : { $elemMatch : { creationDate : { $gte: from } } } 
};

有什么方法可以使该方法返回带有空注释的对话信息?

谢谢!

布雷克七世

在这里听起来像是几个问题,但逐步解决所有这些问题

为了从数组中获得不止一个匹配“或”,需要mapReduce的聚合框架来执行此操作。您可以尝试使用“投射”,$elemMatch但这只能返回“第一个”匹配。IE:

{ "a": [1,2,3] }

db.collection.find({ },{ "$elemMatch": { "$gte": 2 } })

{ "a": [2] }

因此,标准投影对此不起作用。它可以返回一个“空”数组,但也只能返回匹配的“第一个”数组。

继续前进,您的代码中也包含以下内容:

{ $all : [ userId, otherUserId ], "$site" : 2 }

哪里$site不是有效的运算符。我认为您的意思是,$size但是实际上有两个“两个”运算符具有该名称,您的意图在这里可能不清楚。

如果您表示要测试的数组必须具有“仅两个”元素,则这是适合您的运算符。如果您的意思是两个人之间匹配的对话必须在比赛中相等,那么$all无论如何$size都要这样做,因此在任何情况下都变得多余,除非您不希望对话中的任何其他人。

关于聚合问题。您需要以“非破坏性方式”“过滤”数组的内容,以获取多个匹配项或一个空数组。

最好的方法是使用2.6中提供的现代MongoDB功能,该功能允许对数组内容进行过滤而不进行处理$unwind

Model.aggregate(
    [
        { "$match": {
            "members": { "$all": [userId,otherUserId] }
        }},
        { "$project": {
            "title": 1,
            "author": 1,
            "members": 1,
            "creationDate": 1,
            "lastUpdate": 1,
            "comments": {
                "$setDifference": [
                    { "$map": {
                        "input": "$comments",
                        "as": "c",
                        "in": { "$cond": [
                            { "$gte": [ "$$c.creationDate", from ] },
                            "$$c",
                            false
                        ]}
                    }},
                    [false]
                ]
            }
        }}
    ],
    function(err,result) {

    }
);

那使用$map可以针对每个数组元素处理表达式。在这种情况下,值在$cond三元数下进行测试,以返回条件所在的数组元素,true或者返回false为元素。

然后由$setDifference运算符对它们进行“过滤”,运算符实际上将的结果数组$map与另一个数组进行比较[false]false将从结果数组中删除所有值,并且仅保留匹配的元素或根本不保留任何元素。

可能有一个替代方法,$redact但是由于您的文档在多个级别上包含“ creationDate”,因此这与$$DESCEND操作符使用的逻辑混淆这排除了该行动。

在早期版本中,“不破坏”阵列需要谨慎对待。因此,您需要对结果执行相同的“过滤器”操作,以获得所需的“空”数组:

Model.aggregate(
    [
        { "$match": {
            "$and": [ 
                { "members": userId },
                { "members": otherUserId }
        }},
        { "$unwind": "$comments" },
        { "$group": {
            "_id": "$_id",
            "title": { "$first": "$title" },
            "author": { "$first": "$author" },
            "members": { "$first": "$members" },
            "creationDate": { "$first": "$creationDate" },
            "lastUpdate": { "$first": "$lastUpdate" },
            "comments": {
                "$addToSet": {
                    "$cond": [
                        { "$gte": [ "$comments.creationDate", from ] },
                        "$comments",
                        false
                    ]
                }
            },
            "matchedSize": { 
                "$sum": {
                    "$cond": [
                        { "$gte": [ "$comments.creationDate", from ] },
                        1,
                        0
                    ]
                }
            }            
        }},
        { "$unwind": "$comments" },
        { "$match": {
            "$or": [
                { "comments": { "$ne": false } },
                { "matchedSize": 0 }
            ]
        }},
        { "$group": {
            "_id": "$_id",
            "title": { "$first": "$title" },
            "author": { "$first": "$author" },
            "members": { "$first": "$members" },
            "creationDate": { "$first": "$creationDate" },
            "lastUpdate": { "$first": "$lastUpdate" },
            "comments": { "$push": "$comments" }
        }},
        { "$project": {
            "title": 1,
            "author": 1,
            "members": 1,
            "creationDate": 1,
            "lastUpdate": 1,
            "comments": { 
                "$cond": [
                    { "$eq": [ "$comments", [false] ] },
                    { "$const": [] },
                    "$comments"
                ]
            }
        }}
    ],
    function(err,result) {

    }
)

这可以做很多相同的事情,但是需要更长的时间。为了查看数组内容,您需要$unwind该内容。当您$group返回时,您将查看每个元素以查看其是否符合条件以决定要返回的内容,同时还要保留匹配项的计数。

这是要放些(一个$addToSetfalse结果数组或只是一个数组与条目在false那里有没有比赛。因此,您$match可以使用匹配的“计数”过滤掉这些,并进行测试,以查看是否未找到匹配项。如果找不到匹配项,则不会丢弃该项目。

相反,您可以[false]在final中将数组替换为空数组$project

因此,根据您的MongoDB版本,这是“快速/容易”或“缓慢/困难”的处理。令人信服的理由来更新已经存在多年的版本。


工作实例

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/aggtest');

var memberSchema = new Schema({
  name:         { type: String }
});

var messageSchema = new Schema({
  creationDate: { type: Date, default: Date.now },
  comment:      { type: String },
});

var conversationSchema = new Schema({
  members:      [ { type: Schema.Types.ObjectId } ],
  comments:     [messageSchema]
});

var Member = mongoose.model( 'Member', memberSchema );
var Conversation = mongoose.model( 'Conversation', conversationSchema );

async.waterfall(
  [
    // Clean
    function(callback) {
      async.each([Member,Conversation],function(model,callback) {
        model.remove({},callback);
      },
      function(err) {
        callback(err);
      });
    },

    // add some people
    function(callback) {
      async.map(["bill","ted","fred"],function(name,callback) {
        Member.create({ "name": name },callback);
      },callback);
    },

    // Create a conversation
    function(names,callback) {
      var conv = new Conversation();
      names.forEach(function(el) {
        conv.members.push(el._id);
      });

      conv.save(function(err,conv) {
        callback(err,conv,names)
      });
    },

    // add some comments
    function(conv,names,callback) {
      async.eachSeries(names,function(name,callback) {
        Conversation.update(
          { "_id": conv._id },
          { "$push": { "comments": { "comment": name.name } } },
          callback
        );
      },function(err) {
        callback(err,names);
      });
    },

    function(names,callback) {
      Conversation.findOne({},function(err,conv) {
        callback(err,names,conv.comments[1].creationDate);
      });
    },

    function(names,from,callback) {
      var ids = names.map(function(el) {
        return el._id
      });

      var pipeline = [
        { "$match": {
          "$and": [
            { "members": ids[0] },
            { "members": ids[1] }
          ]
        }},
        { "$project": {
          "members": 1,
          "comments": {
            "$setDifference": [
              { "$map": {
                "input": "$comments",
                "as": "c",
                "in": { "$cond": [
                  { "$gte": [ "$$c.creationDate", from ] },
                  "$$c",
                  false
                ]}
              }},
              [false]
            ]
          }
        }}
      ];

      //console.log(JSON.stringify(pipeline, undefined, 2 ));

      Conversation.aggregate(
        pipeline,
        function(err,result) {
          if(err) throw err;
          console.log(JSON.stringify(result, undefined, 2 ));
          callback(err);
        }
      )
    }


  ],
  function(err) {
    if (err) throw err;
    process.exit();
  }
);

产生以下输出:

[
  {
    "_id": "55a63133dcbf671918b51a93",
    "comments": [
      {
        "comment": "ted",
        "_id": "55a63133dcbf671918b51a95",
        "creationDate": "2015-07-15T10:08:51.217Z"
      },
      {
        "comment": "fred",
        "_id": "55a63133dcbf671918b51a96",
        "creationDate": "2015-07-15T10:08:51.220Z"
      }
    ],
    "members": [
      "55a63133dcbf671918b51a90",
      "55a63133dcbf671918b51a91",
      "55a63133dcbf671918b51a92"
    ]
  }
]

请注意,“评论”仅包含最后两个条目,它们与用作输入的日期(大于第二个评论的日期)“大于或等于”。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

按日期时间过滤图表

来自分类Dev

如何按日期过滤日期时间字段?

来自分类Dev

按日期在Notes视图中过滤文档?

来自分类Dev

按日期在Notes视图中过滤文档?

来自分类Dev

MongoDB聚合-按子文档过滤

来自分类Dev

按bash中的日期和时间过滤

来自分类Dev

按日期时间过滤GraphQL文章

来自分类Dev

节点MongoDB本机-按日期/时间过滤

来自分类Dev

按某个时间间隔(不是范围)过滤日期时间列表

来自分类Dev

按过滤后的子文档数组元素计数

来自分类Dev

按过滤后的子文档数组元素的数量排序

来自分类Dev

MongoDB:按日期/时间获取每个ID的最新完整文档

来自分类Dev

gnuplot:按日期过滤,不带xdata蜂鸣时间

来自分类Dev

在时间戳字段中按日期过滤结果

来自分类Dev

使用Linq按可为空的日期时间字段过滤

来自分类Dev

按开始和结束时间用日期过滤ArrayList

来自分类Dev

如何按日期时间优化数组过滤?

来自分类Dev

sqlite-按现在日期时间过滤数据

来自分类Dev

按开始和结束时间用日期过滤ArrayList

来自分类Dev

按bash中的月份,日期和时间过滤日志文件

来自分类Dev

SQL存储过程日期时间按位置过滤

来自分类Dev

如何按日期和时间过滤MongoDB记录?

来自分类Dev

按日期过滤记录时忽略时间部分

来自分类Dev

在显示所有父文档mongoose mongodb时按子文档过滤

来自分类Dev

仅从日期时间/日期选择器快速按日期过滤

来自分类Dev

按日期过滤报告

来自分类Dev

按日期范围过滤

来自分类Dev

BigQuery 按日期过滤

来自分类Dev

按日期过滤 - VBA