草庐IT

mongodb - 使用 $lookup 根据匹配条件对数组进行切片

coder 2023-10-31 原文

我发现了 Mongodb 及其使用管道的查询,我正在为一个案例而苦苦挣扎。

我正在寻找每个 pathsList 文档,我可以在其中找到从位置 B 到位置 C 的路径

  • 输入:10 和 12
  • 输出:[pathsList](我预计在此示例中只有 1 个结果,但更可能是稍后的数组)

假设我有 2 个来自 pathsListsCollection 的 pathsList 文档,它们有一组 path 文档

-------------
pathsList = {
    _id: ObjectId(...),
    pathIds: [path1Id, path2Id, path3Id]
}
-------------
path1 = {
    _id: ObjectId(...),
    positionStart: 8,
    positionFinal: 10,
    index:0
}
-------------
path2 = {
    _id: ObjectId(...),
    positionStart: 10,
    positionFinal: 12,
    index:1
}
-------------
path3 = {
    _id: ObjectId(...),
    positionStart: 12,
    positionFinal: 14,
    index:2
}
-------------

-------------
pathsList = {
    _id: ObjectId(...),
    pathIds: [path4Id, path5Id, path6Id]
}
-------------
path4 = {
    _id: ObjectId(...),
    positionStart: 14,
    positionFinal: 12,
    index:0
}
-------------
path5 = {
    _id: ObjectId(...),
    positionStart: 12,
    positionFinal: 10,
    index:1
}
-------------
path6 = {
    _id: ObjectId(...),
    positionStart: 10,
    positionFinal: 8,
    index:2
}
-------------

到目前为止,我已经做了这样的事情:

pathsListCollection.aggregate([
    {
    $lookup:{
        from: "pathsCollection",
        localField: "pathIds",
        foreignField: "_id",
        as: paths
        }
    },
    {
        $match:{
            paths.positionStart : 10 // first input value
        }
    },
    {
        $match:{
            paths.positionFinal : 12  // second input value
        }
    },
])

这样做我得到了 2 个 pathsList 文档。

现在,我如何更改此聚合以仅找到具有特定顺序的 positionStart = 10 和 positionFinal = 12 的聚合 如何仅在第一个表达式已经验证时才尝试验证第二个表达式?

我研究了 $slice,试图在第一个 $match 之后对路径数组进行切片,并继续查询其余部分,但找不到任何相关的语法来执行此操作。

即:使用第一个 pathsList,我用 paths array 到达第一个 $match :

[{
    _id: ObjectId(...),
    positionStart: 8,
    positionFinal: 10,
    index:0
},
{
    _id: ObjectId(...),
    positionStart: 10,     // first $match here
    positionFinal: 12,
    index:1
},
{
    _id: ObjectId(...),
    positionStart: 12,
    positionFinal: 14,
    index:2
}]

我想在这样的数组上进行下一个 $match :

[{
    _id: ObjectId(...),
    positionStart: 10,
    positionFinal: 12,    // second $match has to start from this elem of the array
    index:1
},
{
    _id: ObjectId(...),
    positionStart: 12,
    positionFinal: 14,
    index:2
}]

这有可能吗? 首先查询 pathsCollection 而不是 pathsListsCollection 会更容易吗?

但是,我愿意接受任何可以帮助我解决该问题的不同方法或想法 提前致谢

最佳答案

如果我得到这个,那么你想根据给定的匹配 positionStartpositionFinish 在这里“切片”。

最优情况

如果您确实在 "pathsCollection" 上开始查询,实际上性能会更好,因为这是您实际要匹配项目的地方。所以 $match应该“首先”完成并使用 $or在范围内“切片”的表达式:

然后用 $lookup然后,您将只返回 "pathsListCollection" 项,使用“切片”数组格式化:

在您的数据的最小样本上,从“开始”10 到“完成”12 将是:

db.getCollection('pathsCollection').aggregate([
  { "$match": {
    "positionStart": { "$gte": 10, "$lte": 12 },
    "positionFinal": { "$gte": 10, "$lte": 12 }
  }},
  { "$lookup": {
    "from": "pathsListCollection",
    "localField": "_id",
    "foreignField": "pathIds",
    "as": "pathsList"
  }},
  { "$unwind": "$pathsList" },
  { "$addFields": {
    "pathsList.pathIds": {
      "$filter": {
        "input": "$pathsList.pathIds",
        "as": "p",
        "cond": { "$eq": [ "$_id", "$$p" ] }
      }
    }
  }},
  { "$unwind": "$pathsList.pathIds" },
  { "$group": {
    "_id": "$pathsList._id",
    "pathIds": {
      "$push": {
        "_id": "$_id",
        "positionStart": "$positionStart",
        "positionFinal": "$positionFinal",
        "index": "$index"  
      }  
    }  
  }},
  { "$redact": {
    "$cond": {
      "if": {
        "$and": [
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionStart",
              0 
            ]},
            10
          ]},
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionFinal",
              -1
            ]},
            12
          ]}  
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"  
    }  
  }}
])

会产生:

/* 1 */
{
    "_id" : ObjectId("595db5d8f5f11516540d1185"),
    "pathIds" : [ 
        {
            "_id" : ObjectId("595db4c7f5f11516540d1183"),
            "positionStart" : 10.0,
            "positionFinal" : 12.0,
            "index" : 1.0
        }
    ]
}

并且将“范围”缩小到“开始”10 和“完成”14,这将是:

db.getCollection('pathsCollection').aggregate([
  { "$match": {
    "positionStart": { "$gte": 10, "$lte": 14 },
    "positionFinal": { "$gte": 10, "$lte": 14 }
  }},
  { "$lookup": {
    "from": "pathsListCollection",
    "localField": "_id",
    "foreignField": "pathIds",
    "as": "pathsList"
  }},
  { "$unwind": "$pathsList" },
  { "$addFields": {
    "pathsList.pathIds": {
      "$filter": {
        "input": "$pathsList.pathIds",
        "as": "p",
        "cond": { "$eq": [ "$_id", "$$p" ] }
      }
    }
  }},
  { "$unwind": "$pathsList.pathIds" },
  { "$group": {
    "_id": "$pathsList._id",
    "pathIds": {
      "$push": {
        "_id": "$_id",
        "positionStart": "$positionStart",
        "positionFinal": "$positionFinal",
        "index": "$index"  
      }  
    }  
  }},
  { "$redact": {
    "$cond": {
      "if": {
        "$and": [
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionStart",
              0 
            ]},
            10
          ]},
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionFinal",
              -1
            ]},
            14
          ]}  
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"  
    }  
  }}
])

制作:

/* 1 */
{
    "_id" : ObjectId("595db5d8f5f11516540d1185"),
    "pathIds" : [ 
        {
            "_id" : ObjectId("595db4c7f5f11516540d1183"),
            "positionStart" : 10.0,
            "positionFinal" : 12.0,
            "index" : 1.0
        }, 
        {
            "_id" : ObjectId("595db4c7f5f11516540d1184"),
            "positionStart" : 12.0,
            "positionFinal" : 14.0,
            "index" : 2.0
        }
    ]
}

反转大小写

它的语法可能看起来有点短,但它可能不是性能最好的选项,因为您不能在相反的情况下“查询”“pathsCollection”,直到 $lookup。实际执行:

db.pathsListCollection.aggregate([
  { "$lookup": {
    "from": "pathsCollection",
    "localField": "pathIds",
    "foreignField": "_id",
    "as": "pathIds"    
  }},
  { "$unwind": "$pathIds" },
  { "$match": {
    "pathIds.positionStart": { "$gte": 10, "$lte": 14 },
    "pathIds.positionFinal": { "$gte": 10, "$lte": 14 }
  }},
  { "$group": {
    "_id": "$_id",
    "pathIds": { "$push": "$pathIds" }    
  }},
  { "$redact": {
    "$cond": {
      "if": {
        "$and": [
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionStart",
              0 
            ]},
            10
          ]},
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionFinal",
              -1
            ]},
            14
          ]}  
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"  
    }  
  }}
])

这是关于 MongoDB 如何实际发布 $lookup 的“反向案例”的最佳形式。当应用于服务器时,可以在“解释”输出中看到:

    {
        "$lookup" : {
            "from" : "pathsCollection",
            "as" : "pathIds",
            "localField" : "pathIds",
            "foreignField" : "_id",
            "unwinding" : {
                "preserveNullAndEmptyArrays" : false
            },
            "matching" : {
                "$and" : [ 
                    {
                        "positionStart" : {
                            "$gte" : 10.0
                        }
                    }, 
                    {
                        "positionStart" : {
                            "$lte" : 14.0
                        }
                    }, 
                    {
                        "positionFinal" : {
                            "$gte" : 10.0
                        }
                    }, 
                    {
                        "positionFinal" : {
                            "$lte" : 14.0
                        }
                    }
                ]
            }
        }
    }, 
    {
        "$group" : {

在那里显示 $unwind$match已经“神奇地”消失了。他们现在当然已经“卷起”到 $lookup 中了。这样当查询相关数据时,你实际上只会那些符合给定条件的结果。

“非最佳”方法是 $filter反而。但实际上来自相关集合的 ALL 结果被返回,然后只有在“完整”数组已经存在时才被删除:

db.pathsListCollection.aggregate([
  { "$lookup": {
    "from": "pathsCollection",
    "localField": "pathIds",
    "foreignField": "_id",
    "as": "pathIds"    
  }},
  { "$addFields": {
    "pathIds": {
      "$filter": {
        "input": "$pathIds",
        "as": "p",
        "cond": {
          "$and": [
            { "$gte": [ "$$p.positionStart", 10 ] },
            { "$lte": [ "$$p.positionStart", 14 ] },            
            { "$gte": [ "$$p.positionFinal", 10 ] },
            { "$lte": [ "$$p.positionFinal", 14 ] },            
          ]
        }
      } 
    }  
  }},
  { "$match": {
    "pathIds": {
      "$elemMatch": {
        "positionStart": { "$gte": 10, "$lte": 14 },
        "positionFinal": { "$gte": 10, "$lte": 14 }
      }
    }
  }},
  { "$redact": {
    "$cond": {
      "if": {
        "$and": [
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionStart",
              0 
            ]},
            10
          ]},
          { "$eq": [
            { "$arrayElemAt": [
              "$pathIds.positionFinal",
              -1
            ]},
            14
          ]}  
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"  
    }  
  }}
])

还注意到您仍然需要 $match$redact鉴于在这种情况下生成的数组条目仍然满足条件,并且由于 $filter 的结果,数组实际上不是“空”的.


使用的样本

路径集合

/* 1 */
{
    "_id" : ObjectId("595db4c7f5f11516540d1182"),
    "positionStart" : 8.0,
    "positionFinal" : 10.0,
    "index" : 0.0
}

/* 2 */
{
    "_id" : ObjectId("595db4c7f5f11516540d1183"),
    "positionStart" : 10.0,
    "positionFinal" : 12.0,
    "index" : 1.0
}

/* 3 */
{
    "_id" : ObjectId("595db4c7f5f11516540d1184"),
    "positionStart" : 12.0,
    "positionFinal" : 14.0,
    "index" : 2.0
}

/* 4 */
{
    "_id" : ObjectId("595db616f5f11516540d1186"),
    "positionStart" : 14.0,
    "positionFinal" : 12.0,
    "index" : 0.0
}

/* 5 */
{
    "_id" : ObjectId("595db616f5f11516540d1187"),
    "positionStart" : 12.0,
    "positionFinal" : 10.0,
    "index" : 1.0
}

/* 6 */
{
    "_id" : ObjectId("595db616f5f11516540d1188"),
    "positionStart" : 10.0,
    "positionFinal" : 8.0,
    "index" : 2.0
}

路径列表集合

/* 1 */
{
    "_id" : ObjectId("595db5d8f5f11516540d1185"),
    "pathIds" : [ 
        ObjectId("595db4c7f5f11516540d1182"), 
        ObjectId("595db4c7f5f11516540d1183"), 
        ObjectId("595db4c7f5f11516540d1184")
    ]
}

/* 2 */
{
    "_id" : ObjectId("595db62df5f11516540d1189"),
    "pathIds" : [ 
        ObjectId("595db616f5f11516540d1186"), 
        ObjectId("595db616f5f11516540d1187"), 
        ObjectId("595db616f5f11516540d1188")
    ]
}

关于mongodb - 使用 $lookup 根据匹配条件对数组进行切片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44917069/

有关mongodb - 使用 $lookup 根据匹配条件对数组进行切片的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  9. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  10. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

随机推荐