【问题标题】:MongoDB aggregation - operator to read in documentsMongoDB 聚合 - 读取文档的运算符
【发布时间】:2020-12-03 16:27:03
【问题描述】:

由于 Mongo 仅支持 一个 $text 字段每个聚合管道(在第一个 $match 阶段内),这意味着您不能执行逻辑 AND,因为您不能 @987654325 @多次$text搜索的结果。

// Fails due to "too many text expressions"
db.Employees.aggregate([
    {$match: {$and: [
        {$text: {$search: "senior"}},
        {$text: {$search: "manager"}}
    ]}}
])

因此,我需要执行多个单独的$text 搜索,在我的 NodeJS 代码中组合结果,然后将该结果集传递回聚合管道以进行进一步处理(例如,$addFields$match$sort) .

有没有办法做类似的事情......

let results1 = db.Employees.find({"$text":{"$search":"senior"}}, {"score":{"$meta":"textScore"}})
let results2 = db.Employees.find({"$text":{"$search":"manager"}}, {"score":{"$meta":"textScore"}})
let combinedResults = _.intersectionWith(results1, results2, _.isEqual)
let finalResults = /* pass combinedResults into aggregation pipeline and execute it */

类似于$out operator 的反面,我在结果集中读取。

如果有帮助,我正在使用 NestJS 和 Mongoose。

【问题讨论】:

  • 您是否正在寻找文档包含Senior manager 的短语类型的搜索?
  • 我已经查看了短语搜索 docs.mongodb.com/manual/reference/operator/query/text/#phrases 但我希望它匹配,即使这些术语彼此不相邻 - 所以文档应该在某些字段中包含 senior,和manager 在某些领域。他们可以在同一个领域,但他们不必是。更好的例子可能是像“James AND manager”这样的搜索
  • 有没有办法直接将预定义的文档数组输入聚合管道?
  • 不幸的是,每个聚合只允许一个文本。你可以使用正则表达式吗?
  • 我可能会因为一些正则表达式而失败 - 这会让我把所有东西都粘到一个聚合中吗?

标签: mongodb mongoose nestjs aggregation data-ingestion


【解决方案1】:

$text 中有restrictions,你已经知道了,

如果您的字段有限,则有一个选项,使用$regexMatch,我不确定您有多少字段有文本索引,但是您可以将匹配条件与$and 运算符组合用于多个字段,

示例数据:

[
  { _id: 1, f1: "senior", f2: "manager" },
  { _id: 2, f1: "junior", f2: "manager" },
  { _id: 3, f1: "fresher", f2: "developer" },
  { _id: 4, f1: "manager", f2: "senior" }
]

聚合查询一:

  • $addFields 添加新字段 matchResult 用于布尔匹配状态
db.collection.aggregate([
  {
    $addFields: {
      matchResult: {
        $and: [
  • 首先$or条件匹配如果f1f2字段匹配senior然后返回true其他明智返回false
          {
            $or: [
              { $regexMatch: { input: "$f1", regex: "senior", options: "x" } },
              { $regexMatch: { input: "$f2", regex: "senior", options: "x" } }
            ]
          },
  • 第二个$or条件匹配如果f1f2字段匹配manager则返回true否则返回false
          {
            $or: [
              { $regexMatch: { input: "$f1", regex: "manager", options: "x" } },
              { $regexMatch: { input: "$f2", regex: "manager", options: "x" } }
            ]
          }
        ]
      }
    }
  },
  • $match条件返回结果有matchResult等于true
  { $match: { matchResult: true } }
])

Playground

聚合查询2:

  • 如果您不使用数组字段,那么这是排序方式,您可以直接将所有字段连接到一个字段上,这里我将f1f2allField 中的空格合并
db.collection.aggregate([
  {
    $addFields: {
      allField: { $concat: ["$f1", " ", "$f2"] }
    }
  },
  • 这将匹配两个单词匹配的$and 条件,如果两者都为真,则返回真,否则为假
  {
    $addFields: {
      matchResult: {
        $and: [
          { $regexMatch: { input: "$allField", regex: "senior", options: "x" } },
          { $regexMatch: { input: "$allField", regex: "manager", options: "x" } }
        ]
      }
    }
  },
  • $match条件返回结果有matchResult等于true
  { $match: { matchResult: true } }
])

Playground

注意:这是有限字段的替代方法,但如果超过 5 个字段进行成像,则会影响查询的速度和性能。

【讨论】:

  • Dang,最大的问题是我们在一个文档中有很多字段,所以基本上需要 $text 一次搜索所有字段(并进行所有花哨的词干提取等)
  • 我可以继续使用多个find() 操作和$text 来获得结果。回到主要问题然后我猜 - 我在聚合管道的下游有更多不相关的处理阶段(主要是更多$addFields 以根据其他标准修改结果分数)。是否有将$text 结果导入该管道的阶段,或者聚合总是必须从整个集合开始?
  • 是的,你是对的,我已经提到过,这是有限字段的选项,而使用 $text 是不可能的。
  • 我不知道,但可能是您的 2 个查询是很好的解决方案。您可以在问题链接中阅读的 $text 有很多限制。
猜你喜欢
  • 2015-07-14
  • 1970-01-01
  • 2017-05-21
  • 2016-09-11
  • 1970-01-01
  • 2021-08-24
  • 2017-03-18
  • 1970-01-01
  • 2017-09-13
相关资源
最近更新 更多