草庐IT

javascript - 至少将数组的 “N”元素与条件列表匹配

coder 2023-10-29 原文

我有以下情况:
我的一个mongo馆藏包含以下格式的文档:

user: "test",
tracks: [{artist: "A", ...}, {artist: "B", ...}, ..., { artist: "N", ...}]

我想提取所有曲目,它们的艺术家在给定的数组arr中。为此,我使用以下查询(工作正常)。
collection.find({ tracks: { $elemMatch: { artist: { $in: arr }}}})

但是,现在我想修改查询,使其仅返回集合中具有至少由arr数组中的3位艺术家执行的曲目的文档。我怎样才能做到这一点(除了将结果从数据库返回后进行过滤之外,这不是一种选择)?

最佳答案

您的问题对我来说有两种可能性,但也许有一些解释可以帮助您入门。

首先,我需要向您解释您误解了 $elemMatch 的意图,并且在这种情况下它被滥用了。

$elemMatch 的想法是创建一个“查询文档”,该文档实际上应用于数组的元素。目的是在数组中的文档上具有“多个条件”,以便在成员文档中而不是在外部文档的整个数组中进行离散匹配。即:

{
   "data": [
       { "a": 1, "b": 3 },
       { "a": 2, "b": 2 }
   ]
}

即使该数组中没有实际的单个元素匹配,以下查询也将起作用,但是整个文档都可以:

db.collection.find({ "data.a": 1, "data.b": 2 })

但是要检查实际元素是否同时符合这两个条件,请在此处使用$elemMatch:

db.collection.find({ "data": { "a": 1, "b": 2 } })

因此,该示例中没有匹配项,并且仅在特定数组元素同时具有这两个元素的地方匹配。

现在,我们解释了$elemMatch,这是您的简化查询:

db.collection.find({ "tracks.artist": { "$in": arr } })

简单得多,它的工作方式是通过单个字段查看所有数组成员,然后返回文档中任何元素至少包含那些可能结果之一的位置。

但不是您要问的问题,等等。如果通读了最后一条语句,您应该意识到 $in 实际上是 $or 条件。它只是对文档中相同元素询问“或”的简化形式。

考虑到这一点,您要求的核心是一个“和” 操作,其中包含所有“三个”值。假设您在测试中仅发送“三个”项目,则可以使用 $and 的形式,该形式是 $all 的缩写形式:

db.collection.find({ "tracks.artist": { "$all": arr } })

那只会返回文档,该文档的数组成员中的元素与测试条件中指定的元素的“所有”匹配。那很可能就是您想要的,但是在某些情况下,您当然要指定一个要说“四名或更多”的艺术家的列表,而只希望其中的“三名”或更少的数字 $all 运算符过于简洁。

但是有一种逻辑方法可以解决此问题,它只需要对基本查询不可用但aggregation framework可用的运算符进行更多处理:

var arr = ["A","B","C","D"];     // List for testing

db.collection.aggregate([
    // Match conditions for documents to narrow down
    { "$match": {
        "tracks.artist": { "$in": arr },
        "tracks.2": { "$exists": true }      // you would construct in code
    }},

    // Test the array conditions
    { "$project": {
        "user": 1,
        "tracks": 1,                         // any fields you want to keep
        "matched": {
            "$gte": [
                 { "$size": {
                     "$setIntersection": [
                         { "$map": {
                             "input": "$tracks",
                             "as": "t",
                             "in": { "$$t.artist" }
                         }},
                         arr
                     ]
                 }},
                 3
             ]
        }
    }},

    // Filter out anything that did not match
    { "$match": { "matched": true } }
])

第一阶段实现标准查询 $match 条件,以便将文档筛选为仅“可能”匹配条件的文档。此处的逻辑情况是像以前一样使用 $in ,它将发现那些文档,其中“测试”数组中存在的至少一个元素存在于文档自身数组中的至少一个成员字段中。

下一个子句是您理想中应该在代码中构建的东西,因为它与数组的“长度”有关。这里的想法是,您希望至少有“三个”匹配项,然后在文档中测试的数组必须至少有“三个”元素才能满足要求,因此检索具有“两个”或更少数组元素的文档毫无意义因为它们永远无法匹配“三个”。

由于所有MongoDB查询本质上只是数据结构的一种表示形式,因此使其非常易于构建。即对于JavaScript:

var matchCount = 3;    // how many matches we want

var match1 = { "$match": { "tracks.artist": { "$in": arr } } };

match1["$match"]["tracks."+ (matchCount-1)] = { "$exits": true };

逻辑上是,带有 $exists 的“点表示法”形式会测试指定索引(n-1)处元素的存在,并且该数组至少要具有该长度才能存在。

理想情况下,其余范围的缩小将使用 $setIntersection 方法,以便在实际数组和测试数组之间返回匹配的元素。由于文档中的数组与“测试数组”的结构不匹配,因此需要通过 $map 操作对其进行转换,该操作设置为仅返回每个数组元素中的“artist”字段。

当完成这两个数组的“交集”时,最后对其进行测试,以查找得到的常见元素列表中的 $size ,在其中进行测试以查看发现这些元素中的“至少三个”是相同的。

最后,您只是使用 $match 条件“过滤”了所有不正确的内容。

理想情况下,您使用的是MongoDB 2.6或更高版本,以便使这些运算符可用。对于2.2.x和2.4.x的早期版本,仍然可以实现,但是需要更多的工作和处理开销:

db.collection.aggregate([
    // Match conditions for documents to narrow down
    { "$match": {
        "tracks.artist": { "$in": arr },
        "tracks.2": { "$exists": true }      // you would construct in code
    }},

    // Unwind the document array
    { "$unwind": "$tracks" },

    // Filter the content
    { "$match": { "tracks.artist": { "$in": arr } }},

    // Group for distinct values
    { "$group": {
        "_id": { 
           "_id": "$_id",
           "artist": "$tracks.artist"
        }
    }},

    // Make arrays with length
    { "$group": {
        "_id": "$_id._id",
        "artist": { "$push": "$_id.artist" },
        "length": { "$sum": 1 }
    }},

    // Filter out the sizes
    { "$match": { "length": { "$gte": 3 } }}
])

关于javascript - 至少将数组的 “N”元素与条件列表匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28153179/

有关javascript - 至少将数组的 “N”元素与条件列表匹配的更多相关文章

  1. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  2. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  3. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  4. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  5. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  6. ruby - 匹配未转义的平衡定界符对 - 2

    如何匹配未被反斜杠转义的平衡定界符对(其本身未被反斜杠转义)(无需考虑嵌套)?例如对于反引号,我试过了,但是转义的反引号没有像转义那样工作。regex=/(?!$1:"how\\"#expected"how\\`are"上面的正则表达式不考虑由反斜杠转义并位于反引号前面的反斜杠,但我愿意考虑。StackOverflow如何做到这一点?这样做的目的并不复杂。我有文档文本,其中包括内联代码的反引号,就像StackOverflow一样,我想在HTML文件中显示它,内联代码用一些spanMaterial装饰。不会有嵌套,但转义反引号或转义反斜杠可能出现在任何地方。

  7. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  8. ruby - RVM 使用列表[0] - 2

    是否有类似“RVMuse1”或“RVMuselist[0]”之类的内容而不是键入整个版本号。在任何时候,我们都会看到一个可能包含5个或更多ruby的列表,我们可以轻松地键入一个数字而不是X.X.X。这也有助于rvmgemset。 最佳答案 这在RVM2.0中是可能的=>https://docs.google.com/document/d/1xW9GeEpLOWPcddDg_hOPvK4oeLxJmU3Q5FiCNT7nTAc/edit?usp=sharing-知道链接的任何人都可以发表评论

  9. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  10. ruby - 在 Ruby 中用键盘诅咒数组浏览 - 2

    我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作

随机推荐