你有兩種方法可以解決這個問題,這取決於你真正想要如何使用數據。無論哪種情況,首先要解決的就是你的「理想數據結構」,而且主要是因爲它是無效的。這是錯誤的部分:
'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