上一篇文章: MongoDB指南---1四、特殊的索引和集合:固定集合、TTL索引、全文本索引
下一篇文章: MongoDB指南---1六、聚合
MongoDB支持幾種類型的地理空間索引。其中最經常使用的是2dsphere索引(用於地球表面類型的地圖)和2d索引(用於平面地圖和時間連續的數據)。
2dsphere容許使用GeoJSON格式(http://www.geojson.org)指定點、線和多邊形。點能夠用形如[longitude, latitude]([經度,緯度])的兩個元素的數組表示:git
{ "name" : "New York City", "loc" : { "type" : "Point", "coordinates" : [50, 2] } }
線能夠用一個由點組成的數組來表示:json
{ "name" : "Hudson River", "loc" : { "type" : "Line", "coordinates" : [[0,1], [0,2], [1,2]] } }
多邊形的表示方式與線同樣(都是一個由點組成的數組),可是"type"不一樣:segmentfault
{ "name" : "New England", "loc" : { "type" : "Polygon", "coordinates" : [[0,1], [0,2], [1,2]] } }
"loc"字段的名字能夠是任意的,可是其中的子對象是由GeoJSON指定的,不能改變。
在ensureIndex中使用"2dsphere"選項就能夠建立一個地理空間索引:數組
> db.world.ensureIndex({"loc" : "2dsphere"})
可使用多種不一樣類型的地理空間查詢:交集(intersection)、包含(within)以及接近(nearness)。查詢時,須要將但願查找的內容指定爲形如{"$geometry" : geoJsonDesc}的GeoJSON對象。
例如,可使用"$geoIntersects"操做符找出與查詢位置相交的文檔:服務器
> var eastVillage = { ... "type" : "Polygon", ... "coordinates" : [ ... [-73.9917900, 40.7264100], ... [-73.9917900, 40.7321400], ... [-73.9829300, 40.7321400], ... [-73.9829300, 40.7264100] ... ]} > db.open.street.map.find( ... {"loc" : {"$geoIntersects" : {"$geometry" : eastVillage}}})
這樣就會找到全部與East Village區域有交集的文檔。
可使用"$within"查詢徹底包含在某個區域的文檔,例如:「East Village有哪些餐館?」工具
> db.open.street.map.find({"loc" : {"$within" : {"$geometry" : eastVillage}}})
與第一個查詢不一樣,此次不會返回那些只是通過East Village(好比街道)或者部分重疊(好比用於表示曼哈頓的多邊形)的文檔。
最後,可使用"$near"查詢附近的位置:性能
> db.open.street.map.find({"loc" : {"$near" : {"$geometry" : eastVillage}}})
注意,"$near"是惟一一個會對查詢結果進行自動排序的地理空間操做符:"$near"的返回結果是按照距離由近及遠排序的。
地理位置查詢有一點很是有趣:不須要地理空間索引就可使用"$geoIntersects"或者"$within"("$near"須要使用索引)。可是,建議在用於表示地理位置的字段上創建地理空間索引,這樣能夠顯著提升查詢速度。優化
若是有其餘類型的索引,能夠將地理空間索引與其餘字段組合在一塊兒使用,以便對更復雜的查詢進行優化。上面提到過一種可能的查詢:「East Village有哪些餐館?」。若是僅僅使用地理空間索引,咱們只能查找到East Village內的全部東西,可是若是要將「restaurants」或者是「pizza」單獨查詢出來,就須要使用其餘索引中的字段了:命令行
> db.open.street.map.ensureIndex({"tags" : 1, "location" : "2dsphere"})
而後就可以很快地找到East Village內的披薩店了:rest
> db.open.street.map.find({"loc" : {"$within" : {"$geometry" : eastVillage}}, ... "tags" : "pizza"})
其餘索引字段能夠放在"2dsphere"字段前面也能夠放在後面,這取決於咱們但願首先使用其餘索引的字段進行過濾仍是首先使用位置進行過濾。應該將那個可以過濾掉儘量多的結果的字段放在前面。
對於非球面地圖(遊戲地圖、時間連續的數據等),可使用"2d"索引代替"2dsphere":
> db.hyrule.ensureIndex({"tile" : "2d"})
"2d"索引用於扁平表面,而不是球體表面。"2d"索引不該該用在球體表面上,不然極點附近會出現大量的扭曲變形。
文檔中應該使用包含兩個元素的數組表示2d索引字段(寫做本書時,這個字段還不是GeoJSON文檔)。示例以下:
{ "name" : "Water Temple", "tile" : [ 32, 22 ] }
"2d"索引只能對點進行索引。能夠保存一個由點組成的數組,可是它只會被保存爲由點組成的數組,不會被當成線。特別是對於"$within"查詢來講,這是一項重要的區別。若是將街道保存爲由點組成的數組,那麼若是其中的某個點位於給定的形狀以內,這個文檔就會與$within相匹配。可是,由這些點組成的線並不必定徹底包含在這個形狀以內。
默認狀況下,地理空間索引是假設你的值都介於-180~180。能夠根據須要在ensureIndex中設置更大或者更小的索引邊界值:
> db.star.trek.ensureIndex({"light-years" : "2d"}, {"min" : -1000, "max" : 1000})
這會建立一個2000×2000大小的空間索引。
使用"2d"索引進行查詢比使用"2dsphere"要簡單許多。能夠直接使用"$near"或者"$within",而沒必要帶有"$geometry"子對象。能夠直接指定座標:
> db.hyrule.find({"tile" : {"$near" : [20, 21]}})
這樣會返回hyrule集合內的所有文檔,按照距離(20,21)這個點的距離排序。若是沒有指定文檔數量限制,默認最多返回100個文檔。若是不須要這麼多結果,應該根據須要設置返回文檔的數量以節省服務器資源。例如,下面的代碼只會返回距離(20,21)最近的10個文檔:
> db.hyrule.find({"tile" : {"$near" : [20, 21]}}).limit(10)
"$within"能夠查詢出某個形狀(矩形、圓形或者是多邊形)範圍內的全部文檔。若是要使用矩形,能夠指定"$box"選項:
> db.hyrule.find({"tile" : {"$within" : {"$box" : [[10, 20], [15, 30]]}}})
"$box"接受一個兩元素的數組:第一個元素指定左下角的座標,第二個元素指定右上角的座標。
相似地,可使用"$center"選項返回圓形範圍內的全部文檔,這個選項也是接受一個兩元素數組做爲參數:第一個元素是一個點,用於指定圓心;第二個參數用於指定半徑:
> db.hyrule.find({"tile" : {"$within" : {"$center" : [[12, 25], 5]}}})
還可使用多個點組成的數組來指定多邊形:
> db.hyrule.find( ... {"tile" : {"$within" : {"$polygon" : [[0, 20], [10, 0], [-10, 0]]}}})
這個例子會查詢出包含給定三角形內的點的全部文檔。列表中的最後一個點會被鏈接到第一個點,以便組成多邊形。
GridFS是MongoDB的一種存儲機制,用來存儲大型二進制文件。下面列出了使用GridFS做爲文件存儲的理由。
GridFS也有一些缺點。
一般來講,若是你有一些不常改變可是常常須要連續訪問的大文件,那麼使用GridFS再合適不過了。
使用GridFS最簡單的方式是使用mongofiles工具。全部的MongoDB發行版中都包含了mongofiles,能夠用它在GridFS中上傳文件、下載文件、查看文件列表、搜索文件,以及刪除文件。
與其餘的命令行工具同樣,運行mongofiles --help就能夠查看它的可用選項了。
在下面這個會話中,首先用mongofiles從文件系統中上傳一個文件到GridFS,而後列出GridFS中的全部文件,最後再將以前上傳過的文件從GridFS中下載下來:
$ echo "Hello, world" > foo.txt $ ./mongofiles put foo.txt connected to: 127.0.0.1 added file: { _id: ObjectId('4c0d2a6c3052c25545139b88'), filename: "foo.txt", length: 13, chunkSize: 262144, uploadDate: new Date(1275931244818), md5: "a7966bf58e23583c9a5a4059383ff850" } done! $ ./mongofiles list connected to: 127.0.0.1 foo.txt 13 $ rm foo.txt $ ./mongofiles get foo.txt connected to: 127.0.0.1 done write to: foo.txt $ cat foo.txt Hello,world
在上面的例子中,使用mongofiles執行了三種基本操做:put、list和get。put操做能夠將文件系統中選定的文件上傳到GridFS;list操做能夠列出GridFS中的文件;get操做與put相反,用於將GridFS中的文件下載到文件系統中。mongofiles還支持另外兩種操做:用於在GridFS中搜索文件的search操做和用於從GridFS中刪除文件的delete操做。
全部客戶端驅動程序都提供了GridFS API。例如,能夠用PyMongo(MongoDB的Python驅動程序)執行與上面直接使用mongofiles同樣的操做:
>>> from pymongo import Connection >>> import gridfs >>> db = Connection().test >>> fs = gridfs.GridFS(db) >>> file_id = fs.put("Hello, world", filename="foo.txt") >>> fs.list() [u'foo.txt'] >>> fs.get(file_id).read() 'Hello, world'
PyMongo中用於操做GridFS的API與mongofiles很是像:能夠很方便地執行put、get和list操做。幾乎全部MongoDB驅動程序都遵循這種基本模式對GridFS進行操做,固然一般也會提供一些更高級的功能。關於特定驅動程序對GridFS的操做,能夠查詢相關驅動程序的文件。
GridFS是一種輕量級的文件存儲規範,用於存儲MongoDB中的普通文檔。MongoDB服務器幾乎不會對GridFS請求作「特殊」處理,全部處理都由客戶端的驅動程序和工具負責。
GridFS背後的理念是:能夠將大文件分割爲多個比較大的塊,將每一個塊做爲獨立的文檔進行存儲。因爲MongoDB支持在文檔中存儲二進制數據,因此能夠將塊存儲的開銷降到很是低。除了將文件的每個塊單獨存儲以外,還有一個文檔用於將這些塊組織在一塊兒並存儲該文件的元信息。
GridFS中的塊會被存儲到專用的集合中。塊默認使用的集合是fs.chunks,不過能夠修改成其餘集合。在塊集合內部,各個文檔的結構很是簡單:
{ "_id" : ObjectId("..."), "n" : 0, "data" : BinData("..."), "files_id" : ObjectId("...") }
與其餘的MongoDB文檔同樣,塊也都擁有一個惟一的"_id"。另外,還有以下幾個鍵。
塊所屬文件的元信息。
塊在文件中的相對位置。
塊所包含的二進制數據。
每一個文件的元信息被保存在一個單獨的集合中,默認狀況下這個集合是fs.files。這個文件集合中的每個文檔表示GridFS中的一個文件,文檔中能夠包含與這個文件相關的任意用戶自定義元信息。除用戶自定義的鍵以外,還有幾個鍵是GridFS規範規定必需要有的。
文件的惟一id,這個值就是文件的每一個塊文檔中"files_id"的值。
文件所包含的字節數。
組成文件的每一個塊的大小,單位是字節。這個值默認是256 KB,能夠在須要時進行調整。
文件被上傳到GridFS的日期。
文件內容的md5校驗值,這個值由服務器端計算獲得。
這些必須字段中最有意思(或者說可以見名知意)的一個多是"md5"。"md5"字段的值是由MongoDB服務器使用filemd5命令獲得的,這個命令能夠用來計算上傳到GridFS的塊的md5校驗值。這意味着,用戶能夠經過檢查文件的md5校驗值來確保文件上傳正確。
如上面所說,在fs.files中,除了這些必須字段外,可使用任何自定義的字段來保存必需的文件元信息。可能你但願在文件元信息中保存文件的下載次數、MIME類型或者用戶評分。
只要理解了GridFS底層的規範,本身就能夠很容易地實現一些驅動程序沒有提供的輔助功能。例如,可使用distinct命令獲得GridFS中保存文件的文件名集合(集合中的每一個文件名都是惟一的)。
> db.fs.files.distinct("filename") [ "foo.txt" , "bar.txt" , "baz.txt" ]
這樣,在加載或者收集文件相關信息時,應用程序能夠擁有很是大的靈活性.
上一篇文章: MongoDB指南---1四、特殊的索引和集合:固定集合、TTL索引、全文本索引
下一篇文章: MongoDB指南---1六、聚合