你有两种方法可以解决这个问题,这取决于你真正想要如何使用数据。无论哪种情况,首先要解决的就是你的“理想数据结构”,而且主要是因为它是无效的。这是错误的部分:
'heardpoints' : {
'point' : { 'lat' : 36.12345, 'long' : -75.234564 },
'point' : { 'lat' : 36.34567, 'long' : -75.345678 }
}
所以这个“散列/图”是无效的,因为你有同样的“钥匙”命名两次。你不能做到这一点,你可能想,当你想,你必须使用地理空间查询后的希望“阵列”,而不是,以及东西:
阵手法
"heardpoints": [
{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
},
"time": ISODate("2014-11-04T21:09:18.437Z")
},
{
"geometry": {
"type": "Point",
"coordinates": [ -75.345678, 36.34567 ]
},
"time": ISODate("2014-11-04T21:10:28.919Z")
}
]
和正确的按照如何遵循MongoDB和GeoJSON规范的顺序排列“lon”和“lat”。
现在,这是针对您要将所有“听过的数据”保存在每个“bssid”值的“单个文档”中的表单,每个位置都保存在一个数组中。请注意,除非在第一个创建实例中,否则它本身并不一定是"upsert"。主要目的是“更新”相同的“bssid”值文件。就在现在与Java语法翻译后壳形式:
db.collection.update(
{ "bssid": "ca:fe:de:ad:be:ef" },
{
"$setOnInsert": { "channel": 6 },
"$push": {
"heardpoints": {
"$each": [{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
},
"time": ISODate("2014-11-04T21:09:18.437Z")
}],
"$sort": { "time": -1 },
"$slice": 20
}
}
},
{ "upsert": true }
);
不管是什么语言和API表示,基本上有部分到MongoDB的更新操作。本质上这:
[ <Query>, <Update> ]
根据API呈现有技术上的“三”部分,其中第三是Options
但上的“更新插入”选项的基本考虑,理解是很重要的是如何既Query
和Update
文档部分在更新操作中处理。
适用于Update
文件最重要的是它有两种形式。如果您只是在标准对象形式中提供“键”和“值”,则提供的内容将“覆盖”匹配文档中的任何现有内容。另一种形式(将在所有示例中使用)是使用"update operators",其允许文档的“部分”被修改或“增强”。这是重要的区别。但与例子。
在空白集或至少一个,其中所指定的“BSSID”值不存在,则一个新的文档将被创建包含“BSSID”字段值。此外还有一些其他行为将会发生。
有一个特殊的“更新操作”,在这里叫$setOnInsert
。就像在声明的Query
部分规定的条件,这里所提到的任何字段和值只插入一个“新”文档时,“创造”在文档中。因此,如果找到与查询条件匹配的文档,那么这里没有任何操作实际执行来更改找到的文档。这是一个设置初始值的好地方,也可以将文档上的写入活动限制在需要的地方。
Update
文档中的第二部分是另一个称为$push
的“更新运算符”。正如计算语言中的常见术语所预期的那样,这“将项目”添加到“数组”中。所以在创建文档时,会创建一个新数组,并将这些项添加或添加到查找文档中的“现有”数组内容中。
这里有一些有趣的改性剂有自己的目的。 $each
是一种修饰符,允许一次将多个项目发送给像$push
这样的操作员。我们仅将它用于单个项目,但通常与我们感兴趣的其他两个修改器一起使用它。
接下来是$sort
,它应用于文档中存在的数组元素以“排序“他们的条件。在这种情况下,数组元素上有一个“时间”字段,所以“排序”可以确保在添加新元素时,数组的内容总是有序的,这样“最新”条目总是在阵列。
最后有$slice
这是补充$sort
通过本质上指定一个数组“封顶量”。因此,为了确保文档不会太大,修改器将在“修改器”之后应用修改器,该修改器已完成它的工作,然后“移除”超出指定“最大”条目的任何条目,并维护该数字的“最大”长度。非常有用的功能。
当然,如果您不关心“时间”值,那么还有另一种方法来处理这种情况,以便“坐标”数据仅保留为“独特”组合。这种方式是使用$addToSet
运营商管理阵列或自行“设定”条目:
db.collection.update(
{ "bssid": "ca:fe:de:ad:be:ef" },
{
"$setOnInsert": { "channel": 6 },
"$addToSet": {
"heardpoints": {
"$each": [{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
}
}]
}
}
},
{ "upsert": true }
);
现在实际上并不需要的$each
修改,但它只是留在那里未来点。 $addToSet
本质上看着现有的数组内容,并比较它做你提供的元素。如果这一数据不准确比赛的东西已经存在的数组,那么它被添加到“设置”的。否则,由于数据已经存在,因此没有任何反应。
所以,如果你只是想收集数据的具体点,他们不同,那么这是一个很好的方法。但是有一个“捕捉”,其实是值得一提的一对夫妇。
假设您只想保留前面提到的20个条目。虽然$addToSet
支持$each
修改,不幸的是,其他改性剂如$slice
不被支持。所以,你不能“保持帽”有一个更新语句,你会实际上有为了实现这一颁发的“二”更新操作:
db.collection.update(
{ "bssid": "ca:fe:de:ad:be:ef" },
{
"$setOnInsert": { "channel": 6 },
"$addToSet": {
"heardpoints": {
"$each": [{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
}
}]
}
}
},
{ "upsert": true }
);
db.collection.update(
{ "bssid": "ca:fe:de:ad:be:ef" },
{
"$setOnInsert": { "channel": 6 },
"$push": {
"heardpoints": {
"$each": [],
"$slice": 20
}
}
}
)
但即便如此,我们在这里有一个新的问题。除了现在计算在“两个”操作中,保持这个上限还有另一个问题,基本上是一个“集合”是以任何方式“不是有序的”。因此,您可以使用第二次更新限制列表中的项目总数,但无法删除“最老”的项目。
为了做到这一点,那么你需要一个“时间”字段的“最后更新”,但是有再次捕捉。一旦你提供了一个“时间”值,那么使得“设置”的“不同数据”不再是真实的。一个$addToSet
操作认为,下列因素是两个“不同”条目各个领域,而不仅仅是“协调”的数据被认为是:
"heardpoints": [
{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
},
"time": ISODate("2014-11-04T21:09:18.437Z")
},
{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
},
"time": ISODate("2014-11-04T21:10:28.919Z")
}
]
凡意图是在现有的只是“更新时间”指向给定的坐标,那么你需要采取不同的方法。但是,这又是两次更新,反过来,你会尝试先更新一个文件,然后做一些其他的事情,如果没有成功。意义上的“更新插入”的尝试是第二操作:
var result = db.collection.update(
{
"bssid": "ca:fe:de:ad:be:ef",
"heardpoints.geometry.coordinates": [-75.234564, 36.12345 ]
},
{
"$set": {
"heardpoints.$.time": ISODate("2014-11-04T21:10:28.919Z")
}
}
);
// If result did not match and modify anything existing then perform the upsert
if () {
db.collection.update(
{ "bssid": "ca:fe:de:ad:be:ef" }, // just this key and not the array
{
"$setOnInsert": { "channel": 6 },
"$push": {
"heardpoints": {
"$each": [{
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
},
"time": ISODate("2014-11-04T21:09:18.437Z")
}],
"$sort": { "time": -1 },
"$slice": 20
}
}
},
{ "upsert": true }
);
}
所以其中一个试图“更新”现有阵列由第一查询为那个位置条目2个sepations。第一个操作不能是upsert,因为它会创建一个具有相同“bssid”和未找到的数组条目的新文档。如果可以的话,但这不允许positional $
运算符使用找到的元素的匹配位置,以便可以通过$set
运算符更改该元素。
在Java调用有返回一个WriteResult
类型的可以这样使用:
WriteResult writeResult = collection.update(query1, update1, false, false);
if (writeResult.getN() == 0) {
// Upsert would be tried if the array item was not found
writeResult = collection.update(query2, update2, true, false);
}
如果没有更新的东西,然后序列化的内容是这样的:
{ "serverUsed" : "192.168.2.3:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : true}
这意味着你基本上嵌套n
值来查看发生了什么,并根据查询匹配该数组项的位置来决定是“更新”数组项还是“推送”一个新项。
文档方法
总的结论从上面的是要保持不同的数据为“坐标”,只是修改了“时间”项,则上述过程会导致混乱。这些操作并不是理想的原子,虽然可以进行一些调整,但它可能不适合大批量更新。
这是一种情况,其逻辑是“移除”数组存储,然后将每个不同的“点”存储在其自己的文档中,并将相关的“bssid”字段存储。这简化了是否更新或“插入”新操作模型的情况。集合中的文档现在看起来是这样的:
{
"bssid": "ca:fe:de:ad:be:ef",
"channel": 6,
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
},
"time": ISODate("2014-11-04T21:09:18.437Z")
},
{
"bssid": "ca:fe:de:ad:be:ef",
"channel": 6,
"geometry": {
"type": "Point",
"coordinates": [ -75.345678, 36.34567 ]
},
"time": ISODate("2014-11-04T21:10:28.919Z")
}
的不同之处在自己的收藏和数组下在同一文件中未绑定。有数据复制,但“升级”过程现在简单得多:
db.collection.update(
{
"bssid": "ca:fe:de:ad:be:ef",
"geometry": {
"type": "Point",
"coordinates": [-75.234564, 36.12345 ]
}
},
{
"$setOnInsert": { "channel": 6 },
"$set": { "time": ISODate("2014-11-04T21:10:28.919Z") }
}
{ "upsert": true }
)
和所有的确实将匹配基于所提供的“BSSID”和“点”值不是“更新”“时间的文件“它匹配的位置,或者只是插入一个新文档,其中包含未找到该”bssid“和”point“数据的所有值。
整体的情况是,其中这开始了与简单的需求,这是细到“嵌入”阵列到阵列,保持更复杂的需求可以是一个可能的疼痛使用该存储形式。另一方面,在集合中使用单独的文档有一方面的好处,但是您必须自己做一些工作来“清理”超出您可能想要的任何上限的条目。但可能不一定需要成为“实时”操作是有争议的。
不同的方法,所以与最适合你的方法一起工作。这只是以任何方式实施并显示缺陷和解决方案的指南。什么最适合你,只有你可以告诉。
这实际上比特定的Java编码更关注技术。这一部分并不难,所以这里只是上面最难以参考的部分结构:
DBObject update = new BasicDBObject(
"$setOnInsert", new BasicDBObject(
"channel", 6
)
).append(
"$push", new BasicDBObject(
"heardpoints", new BasicDBObject(
"$each", new DBObject[]{
new BasicDBObject(
"geometry",
new BasicDBObject("type","Point").append(
"coordinates", new double[]{-75.234564, 36.12345}
)
).append(
"time", new DateTime(2014,1,1,0,0,DateTimeZone.UTC).toDate()
)
}
).append(
"$sort", new BasicDBObject(
"time", -1
)
).append("$slice", 20)
)
);
make it,coll.update(query,network,true,true); – BatScream 2014-11-05 01:15:12
@BatScream当你正确地阅读实现时,它并不是简单的“只使用upserts”。有几种处理这种数据的方法。同时也指出了OP在这里做什么的主要问题,只是提出了“upsert”的建议。 – 2014-11-05 02:19:33
@neil lunn-hmm。是的,你是对的。我刚刚阅读了问题的最后部分,并留下了评论。我的错。 – BatScream 2014-11-05 04:32:38