1. 程式人生 > >淺嘗輒止MongoDB:基礎

淺嘗輒止MongoDB:基礎

        大部分摘自《MongoDB大資料處理權威指南》(第3版)。 

一、簡介

        MongoDB(源自單詞humongous)是一個只用於處理文件的資料庫。不同於關係資料庫管理系統(Relational Database Management System,RDBMS),它沒有表、模式、SQL或行的概念,也沒有事務、ACID相容性、連線、外來鍵等特性。正因如此,相對於關係資料庫來說,MongoDB最大的特點就是簡單、快速、易擴充套件。(雖然MongoDB不支援事務,但在同時使用至少兩臺伺服器時可以提供永續性,這也是生產環境部署時推薦使用的基本配置。主伺服器可以在複製伺服器確認接收到資料後,再確認資料已被接收,也就是一種全同步複製思想的實現。)

1. 基本概念

(1)資料庫         MongoDB中資料庫與和關係資料庫系統中的概念類似。關係資料庫系統中的一個數據庫是表的集合,而MongoDB資料庫可以看作是集合的集合。資料庫可以按需建立,比較自然的做法是為每個使用者建立一個數據庫。

(2)集合         MongoDB中的集合有點類似於關係資料庫中的表,但它更靈活,因為是無模式的,集合中的每個文件不要求有同樣的結構。在RDBMS中,表是嚴格定義的,只能將預定於好的資料行放入表中。在MongoDB中,集合就是一組元素的集合,其中的元素不必相似。允許在一個集合中混合各種不同的元素。         在第一次儲存文件時,MongoDB可以自動建立所引用的集合,這意味著可以按照需求即時建立集合,但並不建議這樣做。最好還是跟操作表一樣,先建立集合,再在其中建立文件。

(3)文件         一個文件代表了MongoDB中的一個儲存單元。在RDBMS中,儲存單元被稱為行,行是固定格式的,而MongoDB中的文件可以由任意數目的鍵值組成。鍵是一個標籤,大致相當於RDBMS中的列名,可以使用鍵引用文件中的資料。         在關係資料庫中,必須能夠通過某種方式唯一定位一條指定的記錄,否則將無法引用特定的行。為此,通常需要新增一個欄位用於儲存稱一個唯一值(稱為主鍵),或者一組能夠唯一定位指定行的欄位(稱為複合主鍵)。         出於相同的原因,MongoDB要求每個文件必須有唯一識別符號:在MongoDB中,該識別符號被稱為_id。除非該欄位指定某個值,否則MongoDB將自動建立該唯一值。現在人們更願意使用MongoDB建立的預設ID值,如果不確定鍵的唯一性或者不希望擔心這件事情,那麼最好還是使用MongoDB提供的預設鍵。

(4)鍵/值         文件由鍵和值組成,鍵和值總是成對出現。與RDBMS不同,RDBMS中的所有欄位必須有值,即使值是NULL,而MongoDB不要求文件必須含有特定的值。如果MongoDB中不含某個鍵/值對,那它就被認為是不存在的。

2. 儲存格式

        MongoDB使用一種稱為BSON(二進位制JSON的英文簡稱)的格式儲存資料,因此是無模式的。BSON通過使計算機更容易處理和搜尋文件的方式,使MongoDB處理速度變得更快。BSON還添加了一些標準JSON不支援的特性,包括儲存二進位制資料,以及處理特定資料型別。BSON可以儲存任何JSON文件,但有效的BSON文件可能不是有效的JSON。每種語言都有自己的驅動,可完成資料和BSON之間的轉換,而不需要使用JSON作為中間語言。MongoDB中的BSON資料是自包含的,儘管相似的資料文件被儲存在一起,但各個文件之間並沒有關係。這意味著所需要的一個文件在同一個地方。因為MongoDB查詢將在文件中尋找特定的鍵和值,該資訊可以輕鬆擴充套件到所有的可用伺服器上。每臺伺服器都將檢查該查詢,並返回結果。這樣,可擴充套件性與效能的提升幾乎是線性的。

二、安裝

1. 選擇版本

        MongoDB以C++編寫,官網提供了Linux、Mac OS、Windows和Solaris的二進位制包。32位版本的MongoDB資料庫大小被限制為小於等於2GB,因為MongoDB內部使用記憶體對映檔案來實現高效能。64位版本的MongoDB不含任何限制,所以在生產環境中應該優先使用64位版本。

        另外需要關注MongoDB軟體自己的版本:正式版、舊版和開發板。正式版表示它是最近可用的穩定版本。當新的版本釋出之後,之前的穩定版就成了舊版。開發版通常被認為是不穩定版本,該版本仍在開發中,其中包含許多修改,將其釋出主要用於公測。

        MongoDB使用的版本號方式為:奇數版本號代表開發版。如果版本號的第二個號碼是偶數,它就是穩定版,否則是開發版。版本號包含三部分數字:

  • 第一個數字代表主版本,只有在完整版本升級時才會改變。
  • 第二個數字代表釋出版,表示版本是開發版還是穩定版。
  • 第三個數字代表修訂號,用於解決缺陷和安全問題。

2. 安裝

        下面安裝從MongoDB官網下載的4.0.2版本。 (1)建立mongodb使用者

useradd mongodb

        基於通用的安全原因,不用root啟動應用程式。後面步驟用mongodb使用者執行。

(2)解壓

tar zxvf mongodb-linux-x86_64-rhel70-4.0.2.tgz
mv mongodb-linux-x86_64-rhel70-4.0.2 mongodb-4.0.2
mkdir /home/mongodb/mongodb-4.0.2/data/

(3)將執行檔案路徑加入資原始檔         在~/.bash_profile的PATH中加入:

PATH=$PATH:$HOME/.local/bin:$HOME/bin:/home/mongodb/mongodb-4.0.2/bin

        使得配置生效:

source ~/.bash_profile

(3)建立配置檔案/home/mongodb/mongodb-4.0.2/mongodb.conf,內容如下。

logpath    = /home/mongodb/mongodb-4.0.2/data/mongodb.log
pidfilepath = /home/mongodb/mongodb-4.0.2/data/mongodb.pid
dbpath = /home/mongodb/mongodb-4.0.2/data/

(4)啟動

mongod -f /home/mongodb/mongodb-4.0.2/mongodb.conf &

(5)登入

[[email protected]~]$mongo
MongoDB shell version v4.0.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 4.0.2
Server has startup warnings: 
2018-09-20T15:36:12.362+0800 I CONTROL  [initandlisten] 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] ** WARNING: This server is bound to localhost.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **          Remote systems will be unable to connect to this server. 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **          Start the server with --bind_ip <address> to specify which IP 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **          addresses it should serve responses from, or with --bind_ip_all to
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **          bind to all interfaces. If this behavior is desired, start the
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **          server with --bind_ip 127.0.0.1 to disable this warning.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] 
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] ** WARNING: soft rlimits too low. rlimits set to 10240 processes, 512000 files. Number of processes should be at least 256000 : 0.5 times number of files.
2018-09-20T15:36:12.363+0800 I CONTROL  [initandlisten] 
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---

>

        可以看到,用mongo登入後顯示5個警告資訊,下面依次加以說明並解決。         第一個警告指出預設MongoDB沒有加訪問控制,解決方法是新增使用者,並配置auth選項。

> use admin;
switched to db admin
> db.createUser(
... {
...   user: "wxy",
...   pwd: "123456",
...   roles: [ { role: "__system", db: "admin" } ]
... }
... )
Successfully added user: {
    "user" : "wxy",
    "roles" : [
        {
            "role" : "__system",
            "db" : "admin"
        }
    ]
}
> 

        在/home/mongodb/mongodb-4.0.2/mongodb.conf中新增:

auth = true

        重啟MongoDB:

> use admin
switched to db admin
> db.shutdownServer()
server should be down...
2018-09-20T16:02:10.479+0800 I NETWORK  [js] trying reconnect to 127.0.0.1:27017 failed
2018-09-20T16:02:10.479+0800 I NETWORK  [js] reconnect 127.0.0.1:27017 failed failed 
> exit
bye
2018-09-20T16:02:23.409+0800 I NETWORK  [js] trying reconnect to 127.0.0.1:27017 failed
2018-09-20T16:02:23.409+0800 I NETWORK  [js] reconnect 127.0.0.1:27017 failed failed 
2018-09-20T16:02:23.410+0800 I QUERY    [js] Failed to end session { id: UUID("d5cd31bd-f064-4e23-bb4b-1f2a47e1fc72") } due to SocketException: socket exception [CONNECT_ERROR] server [couldn't connect to server 127.0.0.1:27017, connection attempt failed: SocketException: Error connecting to 127.0.0.1:27017 :: caused by :: Connection refused]
[1]+  Done                    mongod -f /home/mongodb/mongodb-4.0.2/mongodb.conf  (wd: ~)
(wd now: ~/mongodb-4.0.2)
[[email protected]~/mongodb-4.0.2]$mongod -f /home/mongodb/mongodb-4.0.2/mongodb.conf &

        再次登入,第一個警告消失:

mongo -u "wxy" -p "rockey" --authenticationDatabase "admin"

        第二個警告是說預設只允許本機連線MongoDB伺服器。解決方法是設定bind選項。如在配置檔案中新增如下一行,允許所有主機連線:

bind_ip_all = true

        重啟MongoDB後再次登入,第二個警告消失。

        第三、四個警告是建議將transparent_hugepage的兩個引數設定為never,用root使用者執行下面的命令:

echo never >>  /sys/kernel/mm/transparent_hugepage/enabled
echo never >>  /sys/kernel/mm/transparent_hugepage/defrag

        重啟MongoDB後再次登入,這兩個警告消失。

        最後一個警告是說soft rlimits值太小,建議設定為256000,用root使用者執行下面的操作。在/etc/security/limits.conf檔案中新增如下兩行:

mongodb soft nproc 256000 
mongodb hard nproc 256000

在/etc/profile檔案中新增下面一行:

ulimit -u 256000

無需重啟MongoDB,重新登入,所有警告都沒有了。

三、資料模型

        MongoDB被稱為無模式資料庫,但並不意味著MongoDB的資料結構是完全沒有模式的。例如,在MongoDB中也需要定義集合和索引。然而不需要為新增加的文件預定義任何結構,這與使用關係資料庫時是不同的。簡單說MongoDB是一個極其動態的資料庫。

1. 設計資料庫

(1)集合         可以將集合看作儲存文件的容器。MongoDB中有幾種不同型別的集合。預設的集合按照大小進行擴充套件,新增的資料越多,集合就變得越大。還可以定義固定大小(capped)的集合,只可包含特定數量的資料,最老的文件將被新增加的文件代替。

        MongoDB中的一個數據庫中的集合都有唯一的名字,不同資料庫中的集合允許重名。通常建議使用9個字元以內的簡短名稱,MongoDB支援的集合名稱的最大長度為128。注意MongoDB中的物件名稱、命令、函式等都區分大小寫。

        執行MMAPv1儲存引擎的單個數據庫預設最多可以建立24000個名稱空間,WiredTiger儲存引擎沒有這個限制。每個集合至少包含兩個名稱空間:一個用於集合自身,另一個用於集合中建立的第一個索引。如果為每個集合新增更多索引,將使用更多名稱空間。這意味著從理論上講,如果每個集合都只含有一個索引,那麼每個資料庫最多可以擁有12000個集合。不過在執行MongoDB服務應用(mongod)時,可以通過提供nssize引數,把名稱空間的數目至多增加到2047MB。

(2)文件

        文件由鍵值對組成,鍵的型別為字串,但可以使用許多不同型別的資料作為值。下面是所有可以新增到文件中的資料型別:

  • String:字串型別,常用於儲存文字值,區分大小寫。
  • Integer(32位或64位):整數型別,常用於儲存數值。
  • Boolean:該資料型別的值要麼為真,要麼為假。
  • Double:用於儲存浮點數。
  • Min/Max keys:分別用於與BSON中的最低和最高值作比較。
  • Arrays:用於儲存陣列。
  • Timestamp:儲存時間戳,可以方便記錄文件修改或新增的時間。
  • Object:用於儲存嵌入文件。
  • Null:用於儲存null值。
  • Symbol:該資料型別的用法與字串一致,但通常將被語言保留用於特定的符號型別。
  • Date *:用於儲存UNIX時間格式的當前日期或時間(POSIX時間)。
  • Object ID *:用於儲存文件的ID。
  • Binary data *:用於儲存二進位制資料。
  • Regular expression *:該資料型別用於正則表示式,所有選項都通過按字母順序提供的特殊字元表示。
  • JavaScript Code *:用於JavaScript程式碼。

        最後5種帶有星號的資料型別都不是JSON型別,它們是BSON中使用的特殊資料型別。

(3)在文件中內嵌或引用資訊         可以選擇在文件中內嵌資訊,或者引用另一個文件中的資訊。內嵌資訊意味著在文件自身中新增某種型別的資料,引用資訊意味著建立對另一個包含了特定資料的文件的應用。通常,使用關係資料庫時會採取引用資訊的方式,如正規化設計,目的是消除資料冗餘,保證資料一致性。

        不過在MongoDB中,內嵌資訊會更加簡單,畢竟,文件天生能夠實現這樣的操作。採用這種方式將保持資料庫簡潔,保證所有相關的資訊都儲存在單個文件中,甚至因為資料在磁碟中儲存位置相近,處理速度會更快。

        例如在資料庫中儲存CD資料,關係資料庫中的資料結構如下:

|_media
    |_cds
        |_id, artist, title, genre, releasedate
    |_ cd_tracklists
        |_cd_id, songtitle, length

        在非關係資料庫中,資料結構如下:

|_media
    |_items
        |_<document>

        其中文件結構如下:

{
    "Type": "CD",
    "Artist": "Nirvana",
    "Title": "Nevermind",
    "Genre": "Grunge",
    "Releasedate": "1991.09.24",
    "Tracklist": [
        {
        "Track" : "1",
        "Title" : "Smells Like Teen Spirit",
        "Length" : "5:02"
        },
        {
        "Track" : "2",
        "Title" : "In Bloom",
        "Length" : "4:15"
        }
    ]
}

        關係資料庫中至少需要兩個表,而在非關係資料庫中,只要一個集合和一個文件,曲目列表資訊將內嵌在文件中。當獲取指定CD的資訊時,只需要將當個文件的資訊載入到記憶體中即可。MongoDB中的經驗法則是,儘可能使用內嵌資料,這種方式高效且總是可行的。其本質就是用資料冗餘替代表關聯,MongoDB中所有的引用都將在資料庫中產生另一個查詢。

2. 構建索引

        MongoDB中的索引是一種資料結構,用於收集集合中文件特定欄位的值的資訊。MongoDB的查詢優化器使用該資料結構對集合中的文件進行快速排序。這和關係資料庫的索引作用是一致的。

        索引保證了在文件中查詢資料的速度。基本上可以將索引看作已經執行並存儲了結果的預定義查詢。MongoDB中通用的經驗規則是:對於需要在MySQL中建立索引的場景,在MongoDB中也應該建立索引。建立索引的最大優點在於查詢常用資訊時會很快,因為這些查詢不需要遍歷整個資料庫以收集該資訊。

        每個集合最多可以擁有40個索引。新增索引將提高查詢速度,但也會降低插入或刪除的速度。最好在讀操作多於寫操作的集合中新增索引。當寫操作多於讀操作時,索引可能降低效能。

        所有索引資訊都儲存在資料庫的system.indexes集合中。可以執行db.indexes.find()命令來檢視目前已經儲存的索引。為了檢視某個集合中建立的索引,可以使用命令db.collection.getIndexes()。

三、使用地理空間索引

        MongoDB從版本1.4開始就已經實現了對地理空間索引的支援,可用於處理基於位置的查詢。要新增地理空間資訊的文件必須含有一個子物件或陣列(第一個元素指定物件型別,緊接著是該元素的經緯度),例如:

> db.restaurants.insert({name:"Kimono",loc:{type:"Point",coordinates:[52.370451,5.217497]}});
WriteResult({ "nInserted" : 1 })
>

        引數type可用於指定文件的物件型別,可以是Point、LineString或Polygon。Point型別用於指定某個條目所在的準確位置,因此需要兩個值:經度和維度。型別LineString可用於指定某個沿著特定路線擴充套件的條目(例如街道),因此需要起點和終點:

> db.streets.insert( {name: "Westblaak", loc: { type: "LineString", coordinates: [ [52.36881,4.890286],[52.368762,4.890021] ] } } );
WriteResult({ "nInserted" : 1 })
>

        Polygon型別可用於指定圖形。指定該型別時,需要保證起點和終點是一致的,從而可以閉合這個環。另外,該點的座標將通過在陣列中內嵌陣列的方式提供:

> db.stores.insert( {name: "SuperMall", loc: { type: "Polygon", coordinates: [ [ [52.146917,5.374337], [52.146966,5.375471], [52.146722,5.375085], [52.146744,5.37437], [52.146917,5.374337] ] ] } } );
WriteResult({ "nInserted" : 1 })
>

        所有Multi版本()是選中資料型別的陣列,如下面的MultiPoint:

> db.restaurants.insert({name: "Shabu Shabu", loc: { type: "MultiPoint", coordinates: [[52.1487441, 5.3873406], [52.3569665,4.890517]] }});
WriteResult({ "nInserted" : 1 })
>

        在大多數情況下,Point型別是適用的。

        一旦在文件中新增地理空間資訊,就可以建立此種類型的索引(當然也可以提前建立索引),為ensureIndex()函式提供2dsphere引數:

> db.restaurants.ensureIndex( { loc: "2dsphere" } );
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}
>

        ensureIndex()函式用於新增自定義索引。引數2dsphere將告訴ensureIndex(),它在索引座標或類地球球體上的其它形式的二維資訊。預設情況下,ensureIndex()將假設提供的是經度和維度,並認為它們的範圍是-180到180,但可以使用min和max引數改寫這些資訊:

> db.restaurants.ensureIndex( { loc: "2dsphere" }, { min : -500 , max : 500 } );
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}
>

        還可以通過使用輔助鍵值(也稱為複合索引)擴充套件地理空間索引。如果希望查詢多個值,例如位置(地理空間資訊)或分類(升序),那麼該結構是非常有用的;

> db.restaurants.ensureIndex( { loc: "2dsphere", category: 1 } );
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 2,
    "numIndexesAfter" : 3,
    "ok" : 1
}
>

        查詢地理空間資訊。

# 選擇資料庫

> use restaurants;
switched to db restaurants

# 檢視集合為空

> show collections;

# 插入資料

> db.restaurants.insert( { name: "Kimono", loc: { type: "Point", coordinates: [ 52.370451, 5.217497] } } );
WriteResult({ "nInserted" : 1 })
> db.restaurants.insert( {name: "Shabu Shabu", loc: { type: "Point", coordinates: [51.915288,4.472786] } } );
WriteResult({ "nInserted" : 1 })
> db.restaurants.insert( {name: "Tokyo Cafe", loc: { type: "Point", coordinates: [52.368736, 4.890530] } } );
WriteResult({ "nInserted" : 1 })

# 檢視集合,自動建立restaurants

> show collections;
restaurants

# 在loc上建立地理空間索引

> db.restaurants.ensureIndex ( { loc: "2dsphere" } );
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

# 精確搜尋,結果為空

> db.restaurants.find( { loc : [52,5] } );
> 

        前面的搜尋未返回結果,因為該查詢太具體了。本例中更好的方式應該是搜尋某個包含接近指定值的資訊的文件。可以使用$near操作符實現該操作。注意這種方式要求type操作符:

> db.restaurants.find( { loc : { $near : { $geometry : { type : "Point",
... coordinates: [52.338433,5.513629] } } } } );
{ "_id" : ObjectId("5ba9d49ff272e645164d5776"), "name" : "Kimono", "loc" : { "type" : "Point", "coordinates" : [ 52.370451, 5.217497 ] } }
{ "_id" : ObjectId("5ba9d4adf272e645164d5778"), "name" : "Tokyo Cafe", "loc" : { "type" : "Point", "coordinates" : [ 52.368736, 4.89053 ] } }
{ "_id" : ObjectId("5ba9d4a5f272e645164d5777"), "name" : "Shabu Shabu", "loc" : { "type" : "Point", "coordinates" : [ 51.915288, 4.472786 ] } }
> 

        儘管該結果看著好多了,但仍然存在著一個問題:所有的文件都被返回了。在不使用任何其它操作符的情況下,$near將返回頭100條記錄,並按照它們與指定座標的距離進行排序。可以限制返回結果,例如使用limit函式返回開始的兩條記錄:

> db.restaurants.find( { loc : { $near : { $geometry : { type : "Point", coordinates: [52.338433,5.513629] } } } } ).limit(2);
{ "_id" : ObjectId("5ba9d49ff272e645164d5776"), "name" : "Kimono", "loc" : { "type" : "Point", "coordinates" : [ 52.370451, 5.217497 ] } }
{ "_id" : ObjectId("5ba9d4adf272e645164d5778"), "name" : "Tokyo Cafe", "loc" : { "type" : "Point", "coordinates" : [ 52.368736, 4.89053 ] } }
> 

        或者只返回指定範圍內的結果,這可以通過新增$maxDistance或$minDistance操作符實現。使用其中一個操作符將告訴MongoDB只返回在從指定點開始的最大或最小距離(按米計算)之內的結果:

> db.restaurants.find( { loc : { $near : { $geometry : { type : "Point",
... coordinates: [52.338433,5.513629] }, $maxDistance : 40000 } } } );
{ "_id" : ObjectId("5ba9d49ff272e645164d5776"), "name" : "Kimono", "loc" : { "type" : "Point", "coordinates" : [ 52.370451, 5.217497 ] } }
> 

        可以看出,該查詢只返回了一個結果:從起點開始40公里範圍內的一家餐館。注意返回結果的數目與執行查詢所需的時間存在直接關係。

        除了$near操作符之外,MongoDB還有$geoWithin操作符。可以使用該操作符尋找特定圖形中的所有記錄。這時候就可以查詢位於$box、$polygon、$center和$centerSphere圖形中的記錄,$box代表矩形,$polygon代表選擇的特殊圖形,$center代表圓形,$centerSphere定義球體上的圓形。下面通過一些例子來演示它們的用法。注意在MongoDB 2.4中,$within操作符被棄用,取而代之的是$geoWithin。該操作符並不嚴格要求使用地理空間索引。另外,與$near操作符不同,$geoWithin將返回未排序的結果,這提高了查詢的效能。

        為了使用$box圖形,首先需要指定矩形的左下角和右上角座標:

> db.restaurants.find( { loc: { $geoWithin : { $box : [ [52.368549,4.890238],
... [52.368849,4.89094] ] } } } );
{ "_id" : ObjectId("5ba9d4adf272e645164d5778"), "name" : "Tokyo Cafe", "loc" : { "type" : "Point", "coordinates" : [ 52.368736, 4.89053 ] } }
> 

        類似地,為了查詢特定圖形中的記錄,需要指定一組包含了點座標資訊的巢狀陣列。另外,注意第一個和最後一個座標必須是一致的,用於正確地閉合圖形所形成的環:

> db.restaurants.find( { loc :
...  { $geoWithin :
...     { $geometry :
...        { type : "Polygon" ,
...          coordinates : [ [
...             [52.368739,4.890203], [52.368872,4.890477], [52.368726,4.890793],
...             [52.368608,4.89049], [52.368739,4.890203]
...           ] ]
...        }
...      }
...  } });
{ "_id" : ObjectId("5ba9d4adf272e645164d5778"), "name" : "Tokyo Cafe", "loc" : { "type" : "Point", "coordinates" : [ 52.368736, 4.89053 ] } }
>

        查詢基本$circle圖形中記錄的程式碼非常簡單。在這種情況下,只需要在執行find()函式之前指定圓的中心和半徑(使用座標系統使用的單位)即可:

> db.restaurants.find( { loc: { $geoWithin : { $center : [ [52.370524, 5.217682], 10] } } } );
{ "_id" : ObjectId("5ba9d49ff272e645164d5776"), "name" : "Kimono", "loc" : { "type" : "Point", "coordinates" : [ 52.370451, 5.217497 ] } }
{ "_id" : ObjectId("5ba9d4a5f272e645164d5777"), "name" : "Shabu Shabu", "loc" : { "type" : "Point", "coordinates" : [ 51.915288, 4.472786 ] } }
{ "_id" : ObjectId("5ba9d4adf272e645164d5778"), "name" : "Tokyo Cafe", "loc" : { "type" : "Point", "coordinates" : [ 52.368736, 4.89053 ] } }
>

        注意,從MongoDB 2.2.3開始,$center操作符可以與非地理空間索引一起使用。不過,還是推薦建立地理空間索引,這樣可以提高效能。為了查詢球體上某個圓形之內的記錄,可以使用$centerSphere操作符,該操作符類似於$center:

> db.restaurants.find( { loc: { $geoWithin : { $centerSphere : [ [52.370524, 5.217682], 10]
... } } } );
{ "_id" : ObjectId("5ba9d49ff272e645164d5776"), "name" : "Kimono", "loc" : { "type" : "Point", "coordinates" : [ 52.370451, 5.217497 ] } }
{ "_id" : ObjectId("5ba9d4a5f272e645164d5777"), "name" : "Shabu Shabu", "loc" : { "type" : "Point", "coordinates" : [ 51.915288, 4.472786 ] } }
{ "_id" : ObjectId("5ba9d4adf272e645164d5778"), "name" : "Tokyo Cafe", "loc" : { "type" : "Point", "coordinates" : [ 52.368736, 4.89053 ] } }
>

        MongoDB還提供了geoNear()函式,它的工作方式與find()一樣,不過它還在結果中提供了從指定點到每個記錄的距離。函式geoNear()中還包含一些額外的診斷資訊:

> db.runCommand( { geoNear : "restaurants", near : { type : "Point", coordinates:
... [52.338433,5.513629] }, spherical : true});
{
    "results" : [
        {
            "dis" : 33155.51908653251,
            "obj" : {
                "_id" : ObjectId("5ba9d49ff272e645164d5776"),
                "name" : "Kimono",
                "loc" : {
                    "type" : "Point",
                    "coordinates" : [
                        52.370451,
                        5.217497
                    ]
                }
            }
        },
        {
            "dis" : 69443.96447626318,
            "obj" : {
                "_id" : ObjectId("5ba9d4adf272e645164d5778"),
                "name" : "Tokyo Cafe",
                "loc" : {
                    "type" : "Point",
                    "coordinates" : [
                        52.368736,
                        4.89053
                    ]
                }
            }
        },
        {
            "dis" : 125006.87091822099,
            "obj" : {
                "_id" : ObjectId("5ba9d4a5f272e645164d5777"),
                "name" : "Shabu Shabu",
                "loc" : {
                    "type" : "Point",
                    "coordinates" : [
                        51.915288,
                        4.472786
                    ]
                }
            }
        }
    ],
    "stats" : {
        "nscanned" : 10,
        "objectsLoaded" : 3,
        "avgDistance" : 75868.78482700557,
        "maxDistance" : 125006.87091822099,
        "time" : 3450
    },
    "ok" : 1
}
>