gpt4 book ai didi

java - 如何检查MongoDB对象是否存在并分别创建/更新?

转载 作者:行者123 更新时间:2023-12-04 16:10:02 31 4
gpt4 key购买 nike

我正在开发一种使用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 }
}


到目前为止,这是我尝试过的。似乎添加了初始点,但是在创建第一个点后并没有添加其他点。

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);
}


我觉得我对这个保留意见很遥远。任何支持都会有所帮助,非常感谢!

更新
我正在使用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();
}

最佳答案

您有两种方法可以真正解决此问题,这取决于您实际如何使用数据。无论哪种情况,首先要解决的是您的“理想数据结构”,主要是因为它无效。这是错误的部分:

  'heardpoints' : { 
'point' : { 'lat' : 36.12345, 'long' : -75.234564 },
'point' : { 'lat' : 36.34567, 'long' : -75.345678 }
}


因此,此“哈希/映射”无效,因为您具有两次命名的相同“密钥”。您无法执行此操作,而可能需要使用“数组”,以及希望稍后在以下位置使用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")
}
]


并按照MongoDB及其遵循的GeoJSON规范正确地对“ lon”和“ lat”进行排序。

现在,这是用于将每个“ bssid”值的所有“ hearddata”保存在“单个文档”中的表格,每个位置都保存在一个数组中。注意,除了第一个创建实例外,这实际上并不一定是 "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,但基于对“ upsert”选项的基本考虑,了解如何同时处理 QueryUpdate文档部分非常重要在更新操作中。

应用于 Update文档的最重要的事情是它具有两种形式。如果仅以标准对象形式提供“键”和“值”,则所提供的内容将“覆盖”匹配文档中的任何现有内容。另一种形式(将在所有示例中使用)是使用 "update operators",它允许修改或“扩充”文档的“部分”。这是重要的区别。但是上面的例子。

在空白集合或至少一个不存在指定的“ bssid”值的集合上,然后将创建一个包含该“ bssid”字段值的新文档。此外,还会发生其他一些行为。

这里有一个特殊的“更新运算符”,称为 $setOnInsert。就像语句的 Query部分中指定的条件一样,此处插入的任何字段和值仅在插入“新”文档时才在文档中“创建”。因此,如果找到与查询条件匹配的文档,则实际上不会执行此处的任何操作来更改找到的文档。这是设置初始值的好地方,也可以将对文档的写活动限制为仅需要它的字段。

Update文档的第二部分是另一个称为 $push的“更新运算符”。正如计算语言中的通用术语所期望的那样,这将“添加项目”到“数组”中。因此,在创建文档时,将创建一个新的数组,并将这些项目附加或以其他方式添加到找到的文档中的“现有”数组内容中。

这里有一些有趣的修饰符,它们有各自的用途。 $each是一种修饰符,它允许一次将多个项目发送给像 $push这样的运算符。我们仅将其用于单个项目,但通常将它与我们感兴趣的其他两个修饰符一起使用。

下一个是 $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 }
);


现在实际上并不需要 $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")
}
]


如果要在给定坐标上仅“更新”现有点上的时间,则需要采用其他方法。但是,这又是两次更新,相反,您尝试先更新文档,然后再执行其他操作(如果未成功)。意味着“ 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 }
);

}


因此,有两个分离,其中一个试图通过首先查询该位置来“更新”现有阵列条目。该第一个操作不能成为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”和“ 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)
)
);

关于java - 如何检查MongoDB对象是否存在并分别创建/更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26744764/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com