索引和upsert mongo子文档

时间:2014-08-30 03:36:42

标签: mongodb insert mongoose upsert subdocument

我正在研究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问题。谢谢!

1 个答案:

答案 0 :(得分:2)

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

  • upsert适用于文档级别,不能很好地适应架构的结构。如果在lons数组中找不到匹配项,则您希望将其推送到现有文档而不是创建新文档。

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

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

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

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

使用此修订架构,您的更新要求更加简单。

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

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

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

2。 if(lat,lon)对存在于此集合中,请更新子文档中的uid

     

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

     

4。 if(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
    }
)