【问题标题】:Index and upsert a mongo subdocument索引并插入一个 mongo 子文档
【发布时间】:2014-10-24 02:02:51
【问题描述】:

我正在开发 mongo,我想做以下事情:

when a set of (lat, lon, uid) comes in:

1. the collection has lat as unique index, also for each lat the lon index is unique
2. if (lat, lon) pair exists in this collection, update uid in the sub-document
3. if (lat) exists in this document , insert (lon, uid) in the lons sub-document
4. if (lat) document doesn't exist, create lat document and do 2

[{
    "lat" : 1,  (doc is unique by lat)
    "lons" : [ 
        {
            "lon" : 2,   (the subdocument is unique by lon)
            "uid" : 3
        },
        {
            "lon" : 3,
            "uid" : 3
        }
    ]
},
{
    "lat" : 2,
    "lons" : [ 
        {
            "lon" : 2,
            "uid" : 4
        }
    ]
}]

我尝试做以下事情,但显然它没有像我想象的那样工作。

db.zones.update({'lat': 90}, {$push: {lons: {'uid' : 0, 'lon': -18}}}, { upsert: true })
db.zones.ensureIndex({'lat': -1, 'lons.lon':1}, {unique: true})

我检查了这篇帖子 Can mongo upsert array data? 和其他一些帖子,但不知何故,它们都没有工作。我不知道这是我的问题还是 mongo 的问题。谢谢!

【问题讨论】:

    标签: mongodb insert mongoose upsert subdocument


    【解决方案1】:

    我建议您重新考虑您的架构:

    • upsert 适用于文档级别,这与架构的结构不太匹配。如果在 lons 数组中未找到匹配项,您希望推送到现有文档而不是创建新文档。

    • 包含无限增长的数组的文档可能会导致频繁的文档移动和性能问题(请参阅:Why shouldn't I embed large arrays in my documents?

    • 您的架构不适合地理空间索引(这需要经度/纬度对作为数组或嵌入文档)。我猜这对您的用例并不重要,因为您要确保正常的唯一索引,但这可能值得考虑。

    更好的架构(假设您不打算使用地理空间查询)是:

    {
        lon: -74.0059,
        lat: 40.7127,
        uid: 3
    }
    

    有了这个修改后的架构,您的更新要求就更直接了。

    1. 集合具有 lat 作为唯一索引,对于每个 lat,lon 索引也是唯一的

    您仍然希望确保唯一索引:

          db.zones.ensureIndex({'lat': 1, 'lon':1}, {unique: true})
    

    2。如果此集合中存在 (lat, lon) 对,则更新子文档中的 uid

    3。如果 (lat) 存在于该文档中,则在 lons 子文档中插入 (lon, uid)

    4。如果 (lat) 文档不存在,则创建 lat 文档并执行 2

    所有这些逻辑现在都可以由upsert 处理:

    db.zones.update(
    
        // query criteria
        { lat: 40.7127, lon: -74.0060 },
    
        // update
        { $set: {
            uid: 3
        }},
    
        // options
        {
            upsert: true
        }
    )
    

    如果您想在更新现有文档时保留uid,也可以使用$setOnInsert 运算符(而不是$set):

    db.zones.update(
    
        // query criteria
        { lat: 40.7127, lon: -74.0060 },
    
        // update
        { $setOnInsert: {
            uid: 3
        }},
    
        // options
        {
            upsert: true
        }
    )
    

    【讨论】:

    • 我之前也在考虑这个问题。问题是我将在 lon 中有大约 10k 点,在 lat 中有 10k 点,这意味着我将以这种方式创建 100m 文档。这还是一个好的设计吗?
    • 绝对!阅读我在large embedded arrays 上引用的博文。在这两种情况下,对于唯一的纬度/经度对,您拥有相同数量的索引条目,因此将索引视为大致相同的大小。但是,如果您在每个 lat 文档中嵌入了 10k lon 值的数组,则由于文档超出了磁盘空间(每次文档移动,该文档的所有索引条目也必须是更新),如果您只需要检索文档中的一个点,服务器必须将整个文档加载到 RAM 中。
    • 我明白了。对于以这种方式创建的 NxN 文档,查询时间是 O(1) 还是 O(N^2) ?您还可以为我推荐一些更多的查询资源,更新 mongo 的费用吗?大O的东西?非常感谢!!
    • 我打算对 NxN 表执行的操作是,我会在其中插入一些 uid,在完成 N^2 次插入后,除了查询(很长一段时间。 )。在这种情况下,您仍然认为 NxN 文档是一个好的设计吗?
    猜你喜欢
    • 2013-05-22
    • 2014-08-02
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    • 2016-05-15
    • 1970-01-01
    • 2016-12-14
    相关资源
    最近更新 更多