【问题标题】:Query documents with conditions which are based on subdocuments基于子文档的条件查询文档
【发布时间】:2018-06-06 18:52:23
【问题描述】:

我有一个像这样的 room 架构:

let roomSchema = new mongoose.Schema({
  events: [{type: mongoose.Schema.ObjectId, ref: 'Event'}],
  name: { type: String, required: true, index: { unique: true } }
});

它包含一个事件 ID 数组。 事件架构:

let eventSchema = new mongoose.Schema({
  title: { type: String, required: true },
  room: { type: mongoose.Schema.ObjectId, ref: 'Room', required: true },
  date: { type: Date, required: true },
  slot: { type: Number, required: true }
});

我想做的是:
"查询所有房间,不包含特定日期 AND slot的事件"。

因此,如果请求中的日期与房间和插槽的日期匹配,则该房间不应出现在响应中。如果只有一个字段匹配,则它应该在响应中。

我在这里找到了类似的问题,但没有针对我的场景:
https://stackoverflow.com/a/36371665/5115768
Mongoose query where value is not null

我尝试了类似的方法:

this.model.find(req.query).populate({
  path: 'events',
  match: {
    date: { $ne: req.query.date },
    slot: { $ne: req.query.slot }
  }
}).exec((err, rooms) => {

  rooms = rooms.filter((room) => {
    return room.events != null;
  });
  
  res.status(200).json(rooms);
});

但当然它不起作用(房间总是空数组)。我很难弄清楚这一点。

如何查询具有基于子文档(事件)条件的文档(房间)?

更新

我更改了架构和代码,使 slot 不再是一个数组。

如果我正确理解了@Veeram 的解决方案,它就无法使用,因为它会为“预留房间”返回空的events 数组。这样做的问题是我需要用空的events 数组过滤掉这些房间,这将包括一开始没有关联任何事件的房间(这些不应该被过滤掉)。

现在我设法获得了所有“预订房间”(包含与 req.query.datereq.query.slot 匹配的事件的房间):

this.model.find(req.query).populate({
  path: 'events',
  match: {
    $and: [
      { date: { $eq: date } },
      { slot: { $eq: slot } }
    ]
  }
}).exec((err, reservedRooms) => {
  reservedRooms = reservedRooms.filter(room => room.events.length > 0);
  res.status(200).json(reservedRooms);
});

这与我想要的完全相反,但这是一个开始,我该如何“扭转”它?

【问题讨论】:

  • 验证填充工作没有任何匹配,然后尝试match: { date: { $ne: req.query.date }, slot: { $nin: [req.query.slot] } }。如果它有效,那么当没有匹配时它将输出一个空的事件数组,您可以根据对事件数组的空检查来过滤房间。
  • 这有点帮助但不完全,请参阅我的 UPDATE
  • 对不起,我不清楚,没有涵盖所有情况。对于不存在的事件,您在房间中不会有事件字段,因此那里没有可填充的内容,并且事件字段将不存在。对于存在的事件,它将根据您的匹配条件填充匹配事件。例如; {room:"room1"} 没有事件,{room:"room2", events:[]} 没有匹配事件,{room:"room3", events:[{date:2017, title:"event1"]} 匹配事件。所以基本上当你得到输出时,你只需要显示所有存在事件数组的房间。
  • 或者,您可以将查询条件添加到 req.query 以仅考虑人口中存在的事件。像{events:{$exists:true}} 这样的东西。这样你得到的就是你的最终输出。
  • 另一个选项如果您对事件数据不感兴趣并且只需要房间,您可以通过加入服务器端的事件集合来运行 $lookup 聚合查询并仅在输出响应中返回匹配的房间。

标签: javascript node.js mongodb mongoose mongodb-query


【解决方案1】:

你试过了吗:

match: {
    $not: [{
        $and: [
            date: { $eq: req.query.date },
            slot: { $in: req.query.slot }
        ]
    }]
}

【讨论】:

  • 愚蠢的问题要问,但自从你更新了你的问题,你有没有尝试将$in更新为$eq
  • 是的,我的评论也是错误的。您的代码返回unknown top level operator: $not
【解决方案2】:

Populate 为事件数组中的每个事件应用匹配条件。

因此,当对事件数组的每个元素应用否定时,当您获得填充数组时,存在可用事件(匹配)的数组与没有可用事件(不匹配)的数组之间的区别就消失了。

因此,您必须使用服务器 $lookup 将条件应用于整个阵列。

下面的聚合查询将过滤事件(当存在时)不包含查询中给出的日期和槽的文档的房间。

使用$elemMatch 比较同一元素的查询条件,使用$not 返回没有数组元素包含查询条件的房间。

this.model.aggregate([
  {
    "$match":{"events":{"$exist":true}}
  },
  {
    "$lookup": {
      "from": "events", // Collection name not model or schema name
      "localField": "events",
      "foreignField": "_id",
      "as": "events"
    }
  },
  {
    "$match":{"$not":{"$elemMatch":{"date": date, "slot":slot}}}
  }
]).exec((err, reservedRooms) => {});

这将输出带有事件的房间。您可以使用带有排除的$project 从最终输出中删除事件。添加{$project:{"events":0}} 作为最后阶段。

【讨论】:

  • 这不会让我得到正确的结果。仍然为符合条件的事件留出空间。
  • 它似乎可以处理我创建的测试数据。能否提供一些数据进行测试?
猜你喜欢
  • 1970-01-01
  • 2017-10-01
  • 2020-07-16
  • 1970-01-01
  • 2017-02-27
  • 2017-11-18
  • 2018-12-20
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多