pyR*_*bit 2 java mongodb mongodb-query
我正在开发一个使用Java(Swing GUI)和MongoDB数据存储解决方案构建的无线网络调查工具.我是MongoDB的新手,几乎不是Java大师,所以我需要一些帮助.我想查找我的数据库中是否存在网络,并将听到的点添加到网络文档中.如果网络不存在,我想为该网络创建一个文档并添加听到的点.我一直试图解决这个问题,但我似乎无法解决这个问题.此外,如果BSSID是唯一ID,那将是很好的,所以我没有得到任何重复的网络.我理想的数据结构如下所示:
{ 'bssid' : 'ca:fe:de:ad:be:ef',
'channel' : 6,
'heardpoints' : {
'point' : { 'lat' : 36.12345, 'long' : -75.234564 },
'point' : { 'lat' : 36.34567, 'long' : -75.345678 }
}
Run Code Online (Sandbox Code Playgroud)
这是我到目前为止所尝试的.它似乎添加了初始点,但在第一个点之后它没有添加额外的点.
BasicDBObject query = new BasicDBObject();
query.put("bssid", pkt[1]);
DBCursor cursor = coll.find(query);
if (!cursor.hasNext()) {
// Document doesnt exist so create one
BasicDBObject document = new BasicDBObject();
document.put("bssid", pkt[1]);
BasicDBObject heardpoints = new BasicDBObject();
BasicDBObject point = new BasicDBObject();
point.put("lat", latitude);
point.put("long", longitude);
heardpoints.put("point", point);
document.put("heardpoints", heardpoints);
coll.insert(document);
} else {
// Document exists so we will update here
DBObject network = cursor.next();
BasicDBObject heardpoints = new BasicDBObject();
BasicDBObject point = new BasicDBObject();
point.put("lat", latitude);
point.put("long", longitude);
heardpoints.put("point", point);
network.put("heardpoints", heardpoints);
coll.save(network);
}
Run Code Online (Sandbox Code Playgroud)
我觉得我已经不在这个预订上了.任何支持都会有所帮助,非常感谢!
更新 我正在使用upsert建议但仍有一些问题.毫无疑问,这对我有用,我只是没有正确地做到这一点.在第一个补充之后,我仍然没有得到任何新的观点.
BasicDBObject query = new BasicDBObject("bssid", pkt[1]);
System.out.println(query);
DBCursor cursor = coll.find(query);
System.out.println(cursor);
try {
DBObject network = cursor.next();
System.out.println(network);
network.put("heardpoints", new BasicDBObject("point",
new BasicDBObject("lat", latitude)
.append("long", longitude)));
coll.update(query, network, true, false);
} catch (NoSuchElementException ex) {
System.err.println("mongo error");
} finally {
cursor.close();
}
Run Code Online (Sandbox Code Playgroud)
您有两种方法可以解决这个问题,它只取决于您实际想要如何使用数据.在任何一种情况下,要解决的第一件事是你的"理想数据结构",主要是因为它是无效的.这是错误的部分:
'heardpoints' : {
'point' : { 'lat' : 36.12345, 'long' : -75.234564 },
'point' : { 'lat' : 36.34567, 'long' : -75.345678 }
}
Run Code Online (Sandbox Code Playgroud)
所以这个"散列/图"是无效的,因为你有相同的"键"命名两次.您不能这样做而且您可能想要和"数组"相反,以及您希望以后在需要时使用GeoSpatial查询的东西:
"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")
}
]
Run Code Online (Sandbox Code Playgroud)
正如MongoDB和GeoJSON规范所遵循的"lon"和"lat"的正确排序一样.
现在,这是用于将每个"bssid"值保存在"单个文档"中的所有"heardata"的表单,每个位置都保存在一个数组中.请注意,除了第一个创建实例外,这本身并不一定是"upsert".主要目的是"更新"相同的"bssid"值文档.现在只是在shell中使用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 }
);
Run Code Online (Sandbox Code Playgroud)
无论语言和API表示如何,MongoDB更新操作基本上都有两个部分.基本上这个:
[ < Query >, < Update > ]
Run Code Online (Sandbox Code Playgroud)
根据API表示,技术上有"三个"部分,其中第三部分是Options基于"upsert"选项的基本考虑,重要的是要理解在更新操作中如何处理文档部分Query和Update文档部分.
应用于该Update文档最重要的是它有两种形式.如果您只是以标准对象形式提供"键"和"值",那么提供的任何内容都将"覆盖"匹配文档中的任何现有内容.另一种形式(将在所有示例中使用)是使用"更新操作符",其允许修改或"增加"文档的"部分".这是一个重要的区别.但是就这些例子而言.
在空白集合或至少一个指定的"bssid"值不存在的情况下,将创建一个包含该"bssid"字段值的新文档.此外,还会发生一些其他行为.
这里有一个特殊的"更新操作符" $setOnInsert.就像Query语句部分中指定的条件一样,此处提到的任何字段和值仅在插入"新"文档时在文档中"创建".因此,如果找到与查询条件匹配的文档,则实际上不执行任何操作来更改找到的文档.这是设置初始值并将文档的写入活动限制为仅需要的字段的好地方.
Update文档的第二部分是另一个名为的"更新操作符" $push.正如计算语言中的常用术语所预期的那样,这将"添加项目"添加到"数组"中.因此,在创建文档时,将创建一个新数组,并将项目附加或以其他方式添加到找到的文档中的"现有"数组内容中.
这里有一些有趣的修饰符,它们有自己的用途.$each是一个修饰符,允许一次将多个项目发送给操作员$push.我们只将它用于单个项目,但它通常需要与我们感兴趣的其他两个修饰符一起使用.
下一个是$sort应用于文档中存在的数组元素,以便按条件"排序"它们.在这种情况下,数组元素上有一个"time"字段,因此"sort"确保在添加新元素时,数组的内容始终排序,以便"最新"条目始终位于数组.
最后$slice一个是$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 }
);
Run Code Online (Sandbox Code Playgroud)
现在,实际上并不需要的$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
}
}
}
)
Run Code Online (Sandbox Code Playgroud)
但即便如此,我们在这里遇到了一个新问题.除了现在计入"两个"操作之外,保持这个上限有另一个问题,这基本上就是"套装"以任何方式"未订购".因此,您可以使用第二次更新来限制列表中的项目总数,但是例如,无法删除"最旧"项目.
为了做到这一点,你需要一个"时间"字段用于"最后更新",但是是的,还有一个问题.一旦提供"时间"值,那么产生"设置"的"不同数据"就不再成立.一个$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")
}
]
Run Code Online (Sandbox Code Playgroud)
意图是在给定坐标处的现有点上"更新时间",那么您需要采取不同的方法.但是这又是两个更新,反过来,你首先尝试更新文档,然后如果不成功则执行其他操作.意味着"upsert"尝试是第二个操作:
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 }
);
}
Run Code Online (Sandbox Code Playgroud)
所以有两个sepations,其中一个尝试通过首先查询该位置来"更新"现有的数组条目.第一个操作不能是upsert,因为它会创建一个具有相同"bssid"的新文档和未找到的数组条目.如果可能的话,但是使用找到的元素的匹配位置的位置$操作符不允许这样,以便可以通过$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);
}
Run Code Online (Sandbox Code Playgroud)
如果没有更新某些内容,那么序列化内容如下所示:
{ "serverUsed" : "192.168.2.3:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : true}
Run Code Online (Sandbox Code Playgroud)
这意味着您基本上嵌套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")
}
Run Code Online (Sandbox Code Playgroud)
不同于他们自己的集合而不是绑定在数组下的同一文档中.存在数据重复,但"更新"过程现在大大简化了:
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 }
)
Run Code Online (Sandbox Code Playgroud)
所有这一切都将与基于提供的"bssid"和"point"值的文档相匹配,或者"更新"匹配的"时间",或者只是插入一个新文档,其中包含"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)
)
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4450 次 |
| 最近记录: |