简体   繁体   English

使用基于子文档的条件查询文档

[英]Query documents with conditions which are based on subdocuments

I have a room schema like this: 我有一个这样的房间架构:

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

It contains an array of event ids. 它包含事件ID的数组。 Event schema: 事件架构:

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 }
});

What I am trying to do, is: 我正在尝试做的是:
"query all rooms, that don't contain events of a particular date AND slot ". “查询不包含特定日期时段的事件的所有房间”。

So if the date from the request matches the date of a room AND the slot, then that room should not be in the response. 因此,如果请求中的日期与会议室和广告位的日期相匹配,则该会议室不应出现在响应中。 If only one of the fields match it should be in the response. 如果只有一个字段匹配,则应在响应中。

I found similar questions here, but none for my scenario: 我在这里找到了类似的问题,但是对于我的情况却没有:
https://stackoverflow.com/a/36371665/5115768 https://stackoverflow.com/a/36371665/5115768
Mongoose query where value is not null 猫鼬查询,其中值不为null

I tried something like: 我尝试了类似的东西:

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);
});

But of course it doesn't work (rooms is always empty array). 但是,当然这是行不通的(房间总是空数组)。 I have a really hard time figure this out. 我很难解决这个问题。

How can I query documents (rooms) with conditions which are based on subdocuments (events)? 如何查询基于子文档(事件)的条件的文档(房间)?

UPDATE 更新

I changed my schema and code so that slot is not an array anymore. 我更改了架构和代码,以使slot不再是数组。

If I understood @Veeram's solution correctly, it can't be used, because it would return empty events arrays for "reserved rooms". 如果我正确理解@Veeram的解决方案,将无法使用它,因为它将为“保留的房间”返回空的events数组。 The problem with this is that I need to filter out these rooms with empty events array, which would include rooms that didn't have any events associated in the first place (these shouldn't be filtered out). 问题是我需要用空的events数组过滤掉这些房间,这将包括那些没有任何事件关联的房间(这些事件不应该被过滤掉)。

Now I managed to get all "reserved rooms" (the ones that contain an event that matches req.query.date AND req.query.slot ): 现在,我设法获取了所有“保留房间”(包含与req.query.datereq.query.slot匹配的事件的req.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);
});

This is the exact opposite of what I want but it's a start, how can I "reverse" that? 这与我想要的完全相反,但这只是一个开始,我如何才能“逆转”呢?

Have you tried: 你有没有尝试过:

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

Populate applies the matching criteria for each event in event array. 填充为事件数组中的每个事件应用匹配条件。

So when applied negation on each element of event array the distinction is lost between arrays where there is available event (match) versus when there is no available event (no match) when you get the populated array. 因此,当对事件数组的每个元素应用否定时,在获得可用数组(匹配)的情况下与获取填充数组时没有可用事件(不匹配)的数组之间的区别就会消失。

So you have to use server $lookup to apply criteria on a whole array. 因此,您必须使用服务器$lookup将条件应用于整个阵列。

The below aggregation query will filter rooms where events ( when present ) doesn't not contain the document with date and slot as given in query. 下面的汇总查询将过滤房间(其中存在),其中事件(如果存在)不包含查询中给定的带有日期和时间段的文档。

Use $elemMatch to compare the query criteria on the same element and $not to return the rooms where no array elements contain the query criteria. 使用$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) => {});

This will output the rooms with events. 这将输出带有事件的房间。 You can remove the events from the final output by using $project with exclusion. 您可以使用$project与排除项从最终输出中删除事件。 Add {$project:{"events":0}} as last stage. 在最后阶段添加{$project:{"events":0}}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM