草庐IT

mongodb - 查询连续值的范围mongo

coder 2023-10-28 原文

我有一个包含 number 字段的文档。一个进程添加那些 number 值不在集合中的文档,但首先,它检查是否存在具有该 number 的文档。

考虑 number 从 0 到 234、number 从 653 到 667 和 number 从 10543 到 22000 的文档集合。间隙存在对于 number 从 235 到 652 和 668 到 10542 的文件需要导入。

是否可以构建一个查询来返回集合中存在的连续值的范围? (即 0 到 234 和 653 到 667 以及 10543 到 22000)

有了这些信息,我会立即知道在 235 到 652 和 668 到 10542 之间填写缺失的文档,然后在 22001 继续......

最佳答案

如果您可以接受取回所有丢失的个人 ID,而不是范围,那么这就是您的查询:

collection.aggregate({
    $group: {
        "_id": null, // group all documents into the same bucket
        "numbers":
        {
            $push: "$number" // create an array of all "number" fields
        }
    }
}, {
    $project: {
        "_id": 0, // get rid of the "_id" field - not really needed
        "numbers": {
            $setDifference: [ { // compute the difference between...
                $range: [ 0, 10 ] // ... all numbers from 0 to 10 - adjust this to your needs...
            }, "$numbers" ] // ...and the available values for "number"
        }
    }
})

有一些方法可以计算出这些信息的范围,但我觉得在您的情况下甚至可能不需要。

更新(根据您的评论):这是一个更长的版本,它添加了一些额外的阶段以从离散数字到范围 - 代码不是很漂亮而且可能不是很快但它至少应该工作......

collection.aggregate({
    $sort: {
        "number": 1 // we need to sort in order to find ranges later
    }
},
{
    $group: {
        "_id": null, // group all documents into the same bucket
        "numbers":
        {
            $push: "$number" // create an array of all "number" fields
        }
    }
}, {
    $project: {
        "_id": 0, // get rid of the "_id" field - not really needed
        "numbers": {
            $setDifference: [ { // compute the difference between...
                $range: [ 0, 10 ] // ... all numbers from 0 to 10 - adjust this to your needs...
            }, "$numbers" ] // ...and the available values for "number"
        }
    }
},
{
    $project: {
        "numbers": "$numbers", // ...we create two identical arrays
        "numbers2": "$numbers" // ...by duplicating our missing numbers array
    }
},
{
    $unwind: "$numbers" // this will flatten one of the two created number arrays
},
{
    $project: {
        "number": "$numbers",
        "precedingNumber": {
            $arrayElemAt: [
                "$numbers2", // use the second (remaining) numbers array to find the previous number...
                { $max: [0, { $add: [ { $indexOfArray: [ "$numbers2", "$numbers" ] }, -1 ] } ] } // ...which needs to sit in that sorted array at the position of the element we're looking at right now - 1
            ]
        },
        "followingNumber": {
            $arrayElemAt: [
                "$numbers2", // use the second (remaining) numbers array to find the next number...
                { $add: [ { $indexOfArray: [ "$numbers2", "$numbers" ] }, 1 ] } // ...which needs to sit in that sorted array at the position of the element we're looking at right now + 1
            ]
        }
    }
}, {
    $project: {
        "number": 1, // include number 
        "precedingInRange": { $cond: [ { $eq: [ { $add: [ "$number", -1 ] }, "$precedingNumber" ] }, true, false ] },
        "followingInRange": { $cond: [ { $eq: [ { $add: [ "$number", 1 ] }, "$followingNumber" ] }, true, false ] }
    }
}, {
    $match: {
        $or: [ // filter out all items that are inside a range (or rather: include only the outer items of each range)
            { "precedingInRange": false },
            { "followingInRange": false }
        ]
    }
}, {
    $project: { // some beautification of the ouput to help deal with the data in your application
        "singleNumber": { $cond: [ { $not: { $or: [ "$precedingInRange", "$followingInRange" ] } }, "$number", null ] },
        "startOfRange": { $cond: [ "$followingInRange", "$number", null ] },
        "endOfRange": { $cond: [ "$precedingInRange", "$number", null ] }
    }
})

更新 2:

我有一种感觉,我找到了一种更好的方法来很好地获得范围,而无需涉及太多魔法:

collection.aggregate({
    $sort: {
        "number": 1 // we need to sort by numbers in order to be able to do the range magic later
    }
}, {
    $group: {
        "_id": null, // group all documents into the same bucket
        "numbers":
        {
            $push: "$number" // create an array of all "number" fields
        }
    }
}, {
    $project: {
        "numbers": {
            $reduce: {
                input: "$numbers",
                initialValue: [],
                in: {
                    "start": { 
                        $concatArrays: [
                            "$$value.start",
                            {
                                $cond: { // if preceding element in array of numbers is not "current element - 1" then add it, otherwise skip
                                    if: { $ne: [ { $add: [ "$$this", -1 ] }, { $arrayElemAt: [ "$numbers", { $add: [ { $indexOfArray: [ "$numbers", "$$this" ] }, -1 ] } ] } ] },
                                    then: [ "$$this" ],
                                    else: []
                                }
                            }
                        ]
                    },
                    "end": { 
                        $concatArrays: [
                            "$$value.end",
                            {
                                $cond: { // if following element in array of numbers is not "current element + 1" then add it, otherwise skip
                                    if: { $ne: [ { $add: [ "$$this", 1 ] }, { $arrayElemAt: [ "$numbers", { $add: [ { $indexOfArray: [ "$numbers", "$$this" ] }, 1 ] } ] } ] },
                                    then: [ "$$this" ],
                                    else: []
                                }
                            }
                        ]
                    }
                }
            }
        }
    }
}, {
    $project: {
        "ranges": {
            $zip: {
                inputs: [ "$numbers.start", "$numbers.end" ],
            }
        }
    }
})

关于mongodb - 查询连续值的范围mongo,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46467707/

有关mongodb - 查询连续值的范围mongo的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  4. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  5. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  6. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  7. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  8. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

  9. Ruby 日期参数超出范围 - 2

    我正在尝试使用在我的代码中是动态的Time.local来安排时间。在每个月的第一天,我传递的值是Time.local(2009,9,-1,0)。在PHP中,这会将时间设置为上个月的最后一天。在ruby​​中,我只是得到“ArgumentError:参数超出范围”。是我用错了方法还是什么?谢谢。 最佳答案 您应该使用DateTime类而不是Time。(您可能需要先require'date'并安装activesupportgem。)它比Time更通用,并且可以用DateTime.civil(2009,9-1,-1,0)做你想做的事。为天

  10. ruby-on-rails - ruby 范围 : operators in case statement - 2

    我想检查my_number是否在某个范围内,包括较高的值。在IF语句中我会简单地使用“x>100&&x但是我应该在Ruby案例中做什么(开关)?使用:casemy_numberwhenmy_number不起作用。备注:标准范围不包括my_number恰好为500的情况,并且我不想添加第二个“when”,因为我必须编写双重内容casemy_number#between100and500when100..500puts"Correct,dosomething"when500puts"Correct,dosomethingagain"end 最佳答案

随机推荐