首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Mongo中$unset嵌套对象图并在投影中公开某一对象图的深度

如何在Mongo中$unset嵌套对象图并在投影中公开某一对象图的深度
EN

Stack Overflow用户
提问于 2019-10-30 21:07:34
回答 1查看 53关注 0票数 1

我的问题是使用Mongo优化嵌套文档中陈旧的数据,我使用的是Java实现,但我认为这并不重要。

我使用了一个' stat‘集合来记录我的分钟、月、年和总统计数据,并且每个stat都有自己的文档,例如,stat名称可以是“内存”,或者“请求”,什么都可以。

举个例子..。

代码语言:javascript
复制
{
    "_id" : ObjectId("5b47269748cbb4a1e57d5f0a"),
    "stat" : "my-stat-1",
    "app" : {
        "total" : {
            "total" : NumberLong(15201),
            "yearly" : {
                "2018" : 8396,
                "2019" : NumberLong(6805)
            },
            "monthly" : {
                "Jul 2018" : 306,
                "Aug 2018" : 389,
                "Sep 2018" : 107,
                "Oct 2018" : 6959,
                "Nov 2018" : 532,
                "Dec 2018" : 103,
                "Jan 2019" : 67
            },
            "minutes" : {
                "2019-10-28T15:06" : 1,
                "2019-10-29T15:07" : 1,
                "2019-10-28T15:08" : 2,
                "2019-10-28T15:09" : 3,
                "2019-10-28T15:11" : 2,
                "2019-10-28T15:12" : 2,
                "2019-10-28T15:25" : 3,
                "2019-10-28T15:26" : 9,
                "2019-10-28T15:27" : 2,
                "2019-10-28T16:48" : 5
            }
        },
        "api-1" : {
            "total" : 713,
            "yearly" : {
                "2018" : 187,
                "2019" : 526
            },
            "monthly" : {
                "Jul 2018" : 71,
                "Aug 2018" : 77,
                "Sep 2018" : 3,
                "Nov 2018" : 12,
                "Dec 2018" : 24,
            },
            "minutes" : {}
        },
        "api-2" : {
            "total" : 3490,
            "yearly" : {
                "2018" : 1021,
                "2019" : 2469
            },
            "monthly" : {
                "Jul 2018" : 211,
                "Aug 2018" : 119,
                "Sep 2018" : 37,
                "Oct 2018" : 77,
                "Nov 2018" : 499,
                "Dec 2018" : 78,
                "Jan 2019" : 66,
            },
            "minutes" : {
                "2019-10-28T20:10" : 14,
                "2019-10-28T20:11" : 1,
                "2019-10-28T20:20" : 18,
                "2019-10-28T20:21" : 3,
                "2019-10-28T20:22" : 3,
                "2019-10-30T11:45" : 3,
                "2019-10-30T17:02" : 7,
                "2019-10-30T19:55" : 20
            }
        },
        ...
    }
}

我试图保持“文档”的焦点,通过将所有相关的统计数据放在一起,我可以使用具有这种结构的集合.

  • 日期(每分钟粒度)
  • statName
  • 价值

但是,基于对象的方法的优点是,我可以轻松地获得我的月、年和总统计数据,而不需要聚合,而且我还可以在一个很好的包文档中获得所有适用的统计数据。

我的图表服务只需连接到app.total。然后非常快地枚举这些值--它运行得很好。

问题是,这是一个真正的痛苦,当涉及删除陈腐的分钟数据(我没有计划删除每月或每年的统计数据)。

我想我犯了一个错误,我把我的微小粒度统计数据存储在一个对象中,而不是一个数组中,但是我真的,真的不想在这个时候改变结构。我想知道我是否能有效地运用我所拥有的东西。

为“分钟”stat使用对象结构的优点是,我可以使用$min和$max更新运算符有条件地更新记录,例如,只有在新值高于异常持久记录时才覆盖统计数据。因为有不止一台服务器,而且我不想先执行读操作,这似乎是一种很好的处理方法。这是专业人士似乎停止的地方!

我一直说,分钟粒度统计数据将有助于最大2-3天,所以我以前写了一个方法,删除了分钟,通过迭代我的‘统计’集合,然后试图清除任何‘分钟’大于两天。

我发现的问题是我找不到一种未设置嵌套字段的通配符方法,特别是当我事先不知道文档中存在哪些API键时,例如这样的.

代码语言:javascript
复制
db.getCollection("stats").update{},{"$unset":{"app.*.minutes.2019-10-28*",""})

在读取整个该死的文档之前,我不知道对象中有什么内容,但实际上,我不希望整个文档返回,只是为了看需要删除什么。

有用的是,如果我能够创建一个投影,说“在app下找到所有的子节点到一个最大深度”,这将使我能够在不加载所有统计数据的情况下发现API名称。像这样的事情..。

代码语言:javascript
复制
db.getCollection("stats").find({},{"app.*":1})

揭发

代码语言:javascript
复制
"app.total"
"app.api-1"
"app.api-2"

从理论上讲,我可以构建一个修改语句,用于$unset这些路径,例如某种字段正则表达式(虽然看起来不可能).

代码语言:javascript
复制
"app.total.minutes.2019-10-28*"
"app.api-1.minutes.2019-10-28*"
"app.api-2.minutes.2019-10-28*"

但是考虑到regex问题,我可能得用水合物把这些水补充到$unset 5分钟的陈腐期,就像这样.

代码语言:javascript
复制
"app.total.minutes.2019-10-28T15:01"
"app.total.minutes.2019-10-28T15:02"
"app.total.minutes.2019-10-28T15:03"
"app.total.minutes.2019-10-28T15:04"
"app.total.minutes.2019-10-28T15:05"

但是,对于每个api键,我必须在stat集合中重复这一点。

我目前的解决方案是将完整的" My -stat-1“集合加载到内存中,然后遍历'app‘密钥集,然后遍历’my‘键集,如果日期超过2天,我将其添加到一个列表中,然后在一个语句中对整个列表进行$unset。

当然,这是低效的,但在不改变文档结构的情况下,我还需要考虑什么来一般性地优化旧的分钟记录的删除吗?

EN

回答 1

Stack Overflow用户

发布于 2019-11-05 14:46:59

关于一种方法的一些想法,其中你有一个(1)特别的解决方案,和(2)一个新的结构。

我将在下面提及这一领域:

代码语言:javascript
复制
"minutes" : {
                "2019-10-28T15:06" : 1,
                "2019-10-29T15:07" : 1,
                ...
}

在临时解决方案中:

将“分钟”字段替换为更新版本的“分钟”字段:

代码语言:javascript
复制
"minutes" : [
              {
                "date" : ISODate("2019-10-28T00:00:00Z"),
                "counts_by_min" : [
                                    {
                                      "timestamp" : "2019-10-28T20:10",
                                       "count" : 14
                                    },
                                    {
                                      "timestamp" : "2019-10-28T21:11",
                                       "count" : 6
                                    }
                                  ]
               },
               {
                 "date" : ISODate("2019-10-29T00:00:00Z"),
                 "counts_by_min" : [ ...] 
               }
]

需要注意的是,分钟字段是一个数组,每天都有一桶时间戳(或者这可以是一个日期范围,视需要而定)。“时间戳”字段和“计数”具有与原始文档数据相同的数据。如果时间戳也是一个日期字段,则更好。

现在,(1)如何将数据放入集合,(2)如何处理?这有什么用?

处理方面将变得更简单(参见下面的示例代码)。

获取数据到集合中的方式以及如何将其与其他应用程序一起使用,我不能说;我没有任何与此相关的信息。但是,我建议并行引入这个新的“分钟”字段(与现有版本一起),并与其他应用程序一起分阶段应用。我想这将是某种转变。

过程:

查询将需要一个日期输入,根据该日期输入的“分钟”数据将从输入文档中删除。

输入文档(基于发布的原始文档,省略一些字段):

代码语言:javascript
复制
   {
        "_id" : 1,
        "stat" : "my-stat-1",
        "app" : {
                "total" : {
                        "monthly" : {
                                "Jul 2018" : 306
                        },
                        "minutes" : [ ]
                },
                "api-1" : {
                        "monthly" : {
                                "Jul 2018" : 71
                        },
                        "minutes" : [
                                {
                                        "date" : ISODate("2019-10-28T00:00:00Z"),
                                        "counts_by_min" : [
                                                {
                                                        "timestamp" : "2019-10-28T20:10",
                                                        "count" : 14
                                                },
                                                {
                                                        "timestamp" : "2019-10-28T21:11",
                                                        "count" : 6
                                                }
                                        ]
                                },
                                {
                                        "date" : ISODate("2019-10-29T00:00:00Z"),
                                        "counts_by_min" : [
                                                {
                                                        "timestamp" : "2019-10-29T20:10",
                                                        "count" : 14
                                                }
                                        ]
                                }
                        ]
                }
        }
}

输入日期和查询:

注意,查询更新是原始文档。并且,查询从shell中运行。

代码语言:javascript
复制
var MINS_FILTER = ISODate("2019-10-28T00:00:00Z");

db.stats3.aggregate( [
  { $addFields: { app: { $objectToArray: "$app" } } },
  { $unwind: "$app" },
  { $addFields: { "app.v.minutes": { $filter: {
                                                input: "$app.v.minutes",
                                                as: "mins",
                                                cond: { $ne: [ "$$mins.date", MINS_FILTER ] }
  } } } },
  { $group: { _id: { stat: "$stat", _id: "$_id" }, app: { $push: { k: "$app.k", v: "$app.v" } } } },
  { $addFields: { _id: "$_id._id", stat: "$_id.stat", app: { $arrayToObject: "$app" } } },
]).forEach( doc => db.stats3.save(doc) );

集合中更新的文档删除了当天的( ISODate("2019-10-28T00:00:00Z") )数据。

代码语言:javascript
复制
{
        "_id" : 1,
        "app" : {
                "total" : {
                        "monthly" : {
                                "Jul 2018" : 306
                        },
                        "minutes" : [ ]
                },
                "api-1" : {
                        "monthly" : {
                                "Jul 2018" : 71
                        },
                        "minutes" : [
                                {
                                        "date" : ISODate("2019-10-29T00:00:00Z"),
                                        "counts_by_min" : [
                                                {
                                                        "timestamp" : "2019-10-29T20:10",
                                                        "count" : 14
                                                }
                                        ]
                                }
                        ]
                }
        },
        "stat" : "my-stat-1"
}

关于设计的注记:

桶设计模式使用将数据组合成“桶”的概念。这在IoT应用程序中非常有用。它还专门用于MongoDB的灵活模式数据及其设计。我认为这与你的情况有关,以防你计划重组数据。

更多信息在这里:桶型设计模式

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58633671

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档