如何在$内部使用$ regex或作为聚合表达式

时间:2018-05-31 21:42:55

标签: mongodb mongodb-query aggregation-framework

我有一个查询,允许用户使用以下格式过滤某些字符串字段:"最新检查的描述是以下任何一种:foo或{{1 }}" 。这适用于以下查询:

bar

我想要的是让用户能够使用我变成正则表达式的通配符:"最新检查的描述是:db.getCollection('permits').find({ '$expr': { '$let': { vars: { latestInspection: { '$arrayElemAt': ['$inspections', { '$indexOfArray': ['$inspections.inspectionDate', { '$max': '$inspections.inspectionDate' }] }] } }, in: { '$in': ['$$latestInspection.description', ['Fire inspection on property', 'Health inspection']] } } } }) 或{{1} }"

我得到的正则表达式,不需要帮助。我面临的问题显然是the aggregation $in operator does not support matching by regular expressions。所以我认为我是使用Health inspection构建的,因为文档说我不能使用正则表达式。这是我最好的尝试:

Found a * at the property

除了我收到错误:

$or

我认为我无法使用db.getCollection('permits').find({ '$expr': { '$let': { vars: { latestInspection: { '$arrayElemAt': ['$inspections', { '$indexOfArray': ['$inspections.inspectionDate', { '$max': '$inspections.inspectionDate' }] }] } }, in: { '$or': [{ '$$latestInspection.description': { '$regex': /^Found a .* at the property$/ } }, { '$$latestInspection.description': 'Health inspection' }] } } } }) 作为对象键,但我不确定(我的知识在这里是有限的)我无法弄清楚另一个做我想做的事。所以你发现我甚至无法走得足够远,看看我是否可以在"Unrecognized expression '$$latestInspection.description'" 中使用$$latestInspection.description。我很感激能得到的所有帮助。

1 个答案:

答案 0 :(得分:1)

$expr内的所有内容都是一个聚合表达式,文档可能不会"说你不能明确地" ,而是lack of any named operator和{{ 3}}当然可以这么说。因此,如果您需要正则表达式,那么除了使用JIRA issue SERVER-11947之外别无其他选择:

db.getCollection('permits').find({
  "$where": function() {
    var description = this.inspections
       .sort((a,b) => b.inspectionDate.valueOf() - a.inspectionDate.valueOf())
       .shift().description;

     return /^Found a .* at the property$/.test(description) ||
           description === "Health Inspection";

  }
})

您仍然可以使用$where和聚合表达式进行完全匹配,或者只是在$expr内保持比较。但是目前MongoDB理解的唯一正则表达式是$where内的$regex

如果您实际上"要求" 聚合管道表达式阻止您使用"query" expression,那么唯一当前有效的方法是首先"项目"该字段与数组分开,然后$where与常规查询表达式:

db.getCollection('permits').aggregate([
  { "$addFields": {
     "lastDescription": {
       "$arrayElemAt": [
         "$inspections.description",
         { "$indexOfArray": [
           "$inspections.inspectionDate",
           { "$max": "$inspections.inspectionDate" }
         ]}
       ]
     }
  }},
  { "$match": {
    "lastDescription": {
      "$in": [/^Found a .* at the property$/,/Health Inspection/]
    }
  }}
])

这导致我们看起来您正在寻找具有最大日期值的数组中的项目。 JavaScript语法应该清楚地表明这里正确的方法是$match数组在"更新"。以这种方式"第一"数组中的项目可以是"最新的"。这是您可以使用常规查询执行的操作。

要维护订单,请确保将新项目添加到包含$sort$push的数组中,如下所示:

db.getCollection('permits').updateOne(
  { "_id": _idOfDocument },
  {
    "$push": {
      "inspections": {
        "$each": [{ /* Detail of inspection object */ }],
        "$sort": { "inspectionDate": -1 }
      }
    }
  }
)

事实上,对于$sort的空数组参数,$each将更新所有现有文档:

db.getCollection('permits').updateMany(
  { },
  {
    "$push": {
      "inspections": {
        "$each": [],
        "$sort": { "inspectionDate": -1 }
      }
    }
  }
)

当你实际上"改变"这些真的应该是必要的。更新期间存储的日期,这些更新最好与updateMany()一起发布,以有效地执行"两者"更新和"排序"数组:

db.getCollection('permits').bulkWrite([
  { "updateOne": {
    "filter": { "_id": _idOfDocument, "inspections._id": indentifierForArrayElement },
    "update": {
      "$set": { "inspections.$.inspectionDate": new Date() }
    }
  }},
  { "updateOne": {
    "filter": { "_id": _idOfDocument },
    "update": {
      "$push": { "inspections": { "$each": [], "$sort": { "inspectionDate": -1 } } }
    }
  }}
])

但是,如果你实际上没有真正改变"日期,那么简单地使用bulkWrite()修饰符和" pre-pend"可能更有意义。到数组而不是"追加",并避免$position的任何开销:

db.getCollection('permits').updateOne(
  { "_id": _idOfDocument },
  { 
    "$push": { 
      "inspections": {
        "$each": [{ /* Detail of inspection object */ }],
        "$position": 0
      }
    }
  }
)

对阵列进行永久排序或至少构造成最新的"最新的"日期实际上始终是"第一个"输入,然后您可以简单地使用常规查询表达式:

db.getCollection('permits').find({
  "inspections.0.description": { 
    "$in": [/^Found a .* at the property$/,/Health Inspection/]
  }
})

因此,这里的教训是不要试图根据你真正不需要的逻辑强制计算表达式。应该没有令人信服的理由说明为什么你不能将数组内容命令为"存储"让"最新日期第一个" ,即使你认为你需要任何其他顺序的阵列,那么你可能应该权衡哪个用法案件更重要。

一旦重新编码,只要正则表达式锚定到字符串的开头或至少查询表达式中的其他内容完全匹配,您甚至可以在某种程度上利用索引。

如果您觉得无法对数组重新排序,那么在JIRA问题解决之前,$sort查询是您唯一的当前选项。实际上,对于目前有针对性的4.1版本,实际上有希望实现这一目标,但这最多可能是6个月到一年的最佳估计值。