日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 數據庫 > access > 深入淺出Symfony2 - 結合MongoDB開發LBS應用

深入淺出Symfony2 - 結合MongoDB開發LBS應用

來源:程序員人生   發布時間:2014-10-18 08:00:00 閱讀次數:5917次

http://www.infoq.com/cn/articles/depth-study-of-Symfony2

簡介

隨著近幾年各類移動終端的迅速普及,基于地理位置的服務(LBS)和相關應用也越來越多,而支撐這些應用的最基礎技術之一,就是基于地理位置信息的處理。我所在的項目也正從事相關系統的開發,我們使用的是Symfony2+Doctrine2 ODM+MongoDB的組合。

我們將這些技術要點整理成文,希望能夠通過本文的介紹和案例,詳細解釋如何使用MongoDB進行地理位置信息的查詢和處理。在文章的開頭,我們也會先介紹一下業界通常用來處理地理位置信息的一些方案并進行比較,讓讀者逐步了解使用MongoDB查詢及處理地理位置信息的優勢。

本文使用了Symfony2和Doctrine2作為Web應用的開發框架,對于想了解Symfony2的數據庫操作的讀者來說閱讀本文也可以了解和掌握相關的技術和使用方法。

 


不管是什么LBS應用,一個共同的特點就是:他們的數據都或多或少包含了地理位置信息。而如何對這些信息進行查詢、處理、分析,也就成為了支撐LBS應用的最基礎也是最關鍵的技術問題。1. LBS類應用特點

而由于地理位置信息的特殊性,在開發中經常會有比較難以處理的問題出現,比如:由于用戶所在位置的不固定性,用戶可能會在很小范圍內移動,而此時經緯度值也會隨之變化;甚至在同一個位置,通過GPS設備獲取到的位置信息也可能不一樣。所以如果通過經緯度去獲取周邊信息時,就很難像傳統數據庫那樣做查詢并進行緩存。

對于這個問題,有讀者可能會說有別的處理方案,沒錯,比如只按經緯度固定的幾位小數點做索引,比如按矩陣將用戶劃分到某固定小范圍的區域(可以參考后文將會提到的geohash)等方式,雖然可以繞個彎子解決,但或多或少操作起來比較麻煩,也會犧牲一些精度,甚至無法做到性能的最優化,所以不能算作是最佳的解決辦法。

 

而最近幾年,直接支持地理位置操作的數據庫層出不窮,其操作友好、性能高的特性也開始被我們慢慢重視起來,其中的佼佼者當屬MongoDB。

MongoDB在地理位置信息的處理上有什么優勢?下面我們通過一個簡單的案例來對比一下各種技術方案之間進行進行地理位置信息處理的差異。

2. 幾個地理位置信息處理方案的對比和分析

1. 確定功能需求

對于任何LBS應用來說,讓用戶尋找周圍的好友可能都是一個必不可少的功能,我們就以這個功能為例,來看看各種處理方案之間的差異和區別。

我們假設有如下功能需求:

  • 顯示我附近的人
  • 由近到遠排序
  • 顯示距離

2. 可能的技術方案

排除一些不通用和難以實現的技術,我們羅列出以下幾種方案:

  1. 基于MySQL數據庫
  2. 采用GeoHash索引,基于MySQL
  3. MySQL空間存儲(MySQL Spatial Extensions)
  4. 使用MongoDB存儲地理位置信息

我們一個個來分析這幾種方案。

方案1:基于MySQL數據庫

MySQL的使用非常簡單。對于大部分已經使用MySQL的網站來說,使用這種方案沒有任何遷移和部署成本。

而在MySQL中查詢“最近的人”也僅需一條SQL即可,

SELECT id, ( 6371 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians ( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM places HAVING distance < 25 ORDER BY distance LIMIT 0 , 100;

注:這條SQL查詢的是在lat,lng這個坐標附近的目標,并且按距離正序排列,SQL中的distance單位為公里。

但使用SQL語句進行查詢的缺點也顯而易見,每條SQL的計算量都會非常大,性能將會是嚴重的問題。

先別放棄,我們嘗試對這條SQL做一些優化。

可以將圓形區域抽象為正方形,如下圖

根據維基百科上的球面計算公式,可以根據圓心坐標計算出正方形四個點的坐標。

然后,查詢這個正方形內的目標點。

SQL語句可以簡化如下:

SELECT * FROM places WHERE ((lat BETWEEN ? AND ?) AND (lng BETWEEN ? AND ?))

這樣優化后,雖然數據不完全精確,但性能提升很明顯,并且可以通過給lat lng字段做索引的方式進一步加快這條SQL的查詢速度。對精度有要求的應用也可以在這個結果上再進行計算,排除那些在方塊范圍內但不在原型范圍內的數據,已達到對精度的要求。

可是這樣查詢出來的結果,是沒有排序的,除非再進行一些SQL計算。但那又會在查詢的過程中產生臨時表排序,可能會造成性能問題。

方案2:GeoHash索引,基于MySQL

GeoHash是一種地址編碼,通過切分地圖區域為小方塊(切分次數越多,精度越高),它能把二維的經緯度編碼成一維的字符串。也就是說,理論上geohash字符串表示的并不是一個點,而是一個矩形區域,只要矩形區域足夠小,達到所需精度即可。(其實MongoDB的索引也是基于geohash)

如:wtw3ued9m就是目前我所在的位置,降低一些精度,就會是wtw3ued,再降低一些精度,就會是wtw3u。(點擊鏈接查看坐標編碼對應Google地圖的位置)

所以這樣一來,我們就可以在MySQL中用LIKE ‘wtw3u%’來限定區域范圍查詢目標點,并且可以對結果集做緩存。更不會因為微小的經緯度變化而無法用上數據庫的Query Cache。

這種方案的優點顯而易見,僅用一個字符串保存經緯度信息,并且精度由字符串從頭到尾的長度決定,可以方便索引。

但這種方案的缺點是:從geohash的編碼算法中可以看出,靠近每個方塊邊界兩側的點雖然十分接近,但所屬的編碼會完全不同。實際應用中,雖然可以通過去搜索環繞當前方塊周圍的8個方塊來解決該問題,但一下子將原來只需要1次SQL查詢變成了需要查詢9次,這樣不僅增大了查詢量,也將原本簡單的方案復雜化了。

除此之外,這個方案也無法直接得到距離,需要程序協助進行后續的排序計算。

方案3:MySQL空間存儲

MySQL的空間擴展(MySQL Spatial Extensions),它允許在MySQL中直接處理、保存和分析地理位置相關的信息,看起來這是使用MySQL處理地理位置信息的“官方解決方案”。但恰恰很可惜的是:它卻不支持某些最基本的地理位置操作,比如查詢在半徑范圍內的所有數據。它甚至連兩坐標點之間的距離計算方法都沒有(MySQL Spatial的distance方法在5.*版本中不支持)

官方指南的做法是這樣的:

GLength(LineStringFromWKB(LineString(point1, point2)))

這條語句的處理邏輯是先通過兩個點產生一個LineString的類型的數據,然后調用GLength得到這個LineString的實際長度。

這么做雖然有些復雜,貌似也解決了距離計算的問題,但讀者需要注意的是:這種方法計算的是歐式空間的距離,簡單來說,它給出的結果是兩個點在三維空間中的直線距離,不是飛機在地球上飛的那條軌跡,而是筆直穿過地球的那條直線。

所以如果你的地理位置信息是用經緯度進行存儲的,你就無法簡單的直接使用這種方式進行距離計算。

方案4:使用MongoDB存儲地理位置信息

MongoDB原生支持地理位置索引,可以直接用于位置距離計算和查詢。

另外,它也是如今最流行的NoSQL數據庫之一,除了能夠很好地支持地理位置計算之外,還擁有諸如面向集合存儲、模式自由、高性能、支持復雜查詢、支持完全索引等等特性。

對于我們的需求,在MongoDB只需一個命令即可得到所需要的結果:

db.runCommand( { geoNear: "places", near: [ 121.4905, 31.2646 ], num:100 })

查詢結果默認將會由近到遠排序,而且查詢結果也包含目標點對象、距離目標點的距離等信息。

由于geoNear是MongoDB原生支持的查詢函數,所以性能上也做到了高度的優化,完全可以應付生產環境的壓力。

方案總結

基于MongoDB做附近查詢是很方便的一件事情。

MongoDB在地理位置信息方面的表現遠遠不限于此,它還支持更多更加方便的功能,如范圍查詢、距離自動計算等。

接下來,我們結合Symfony2來詳細地演示一些使用MongoDB進行地理位置信息處理的例子。

3. 結合Symfony2演示

運行環境

參考環境:Nginx1.2 + PHP5.4 + MongoDB2.4.3 + Symfony2.1

建立coordinate和places兩個document文件,前者是作為places內的一個embed字段. 為方便演示效果,這里同時設置了兩個索引 2d 和 2dsphere

Document/Coordinate.php /** * @MongoDBEmbeddedDocument */ class Coordinate { /** * @MongoDBField(type="float") */ public $longitude; /** * @MongoDBField(type="float") */ public $latitude; ... } Document/Place.php /** * @MongoDBDocument(collection="places") * @MongoDBChangeTrackingPolicy("DEFERRED_EXPLICIT") * @MongoDBIndexes({ * @MongoDBIndex(keys={"coordinate"="2d"}), * @MongoDBIndex(keys={"coordinate"="2dsphere"}) * }) */ class Place { /** * * @MongoDBId(strategy="INCREMENT") */ protected $id; /** * @MongoDBField(type="string") */ protected $title; /** * @MongoDBField(type="string") */ protected $address; /** * @MongoDBEmbedOne(targetDocument="HenterGEOGEOBundleDocumentCoordinate") */ protected $coordinate; /** * @MongoDBDistance */ public $distance; ... }

坐標保存以longitude, latitude這個順序(沒有明確的限制和區別,但我們在此遵循官方的推薦)。

另外,為直觀顯示查詢效果,默認使用百度地圖標記查詢數據。

程序說明

我們用到的代碼包是doctrine/mongodb-odm-bundle(下文稱ODM),這個代碼包提供了在Symfony2環境下的MongoDB數據庫支持,使用這個代碼包,可以讓我們更加方便的在Symfony2環境下操作MongoDB數據庫。。

ODM封裝了MongoDB中常用的一些地理位置函數,如周邊搜索和范圍搜索。

ODM中的操作默認距離單位是度,只有geoSphere支持弧度單位(必須在參數中指定spherical(true))

4. MongoDB的地理位置查詢

注意事項

  1. 下文大多數直接對MongoDB的數據庫操作將使用Mongo Shell進行演示。在演示網站頁面和功能時,將結合Symfony2、Doctrine-MongoDB進行演示。
  2. 本文演示所用的MongoDB版本為2.4.3,版本號比較新,所以某些查詢方式在低版本里面并不支持。
  3. 以places這個collection為例,大部分例子都需要類似下面格式的測試數據支持: { "_id" : 2, "coordinate" : { "longitude" : 121.3449, "latitude" : 31.17528 }, "title" : "僅售75元,市場價210元的頂呱呱田雞火鍋3-4人套餐,無餐具費,冬日暖鍋,歡迎品嘗", "address" : "閔行區航新路634號" }

地理位置索引:

MongoDB地理位置索引常用的有兩種。

  • 2d 平面坐標索引,適用于基于平面的坐標計算。也支持球面距離計算,不過官方推薦使用2dsphere索引。
  • 2dsphere 幾何球體索引,適用于球面幾何運算

關于兩個坐標之間的距離,官方推薦2dsphere:

MongoDB supports rudimentary spherical queries on flat 2d indexes for legacy reasons. In general, spherical calculations should use a 2dsphere index, as described in 2dsphere Indexes.

不過,只要坐標跨度不太大(比如幾百幾千公里),這兩個索引計算出的距離相差幾乎可以忽略不計。

建立索引:

> db.places.ensureIndex({'coordinate':'2d'}) > db.places.ensureIndex({'coordinate':'2dsphere'})

查詢方式:

查詢方式分三種情況:

  1. Inclusion。范圍查詢,如百度地圖“視野內搜索”。
  2. Inetersection。交集查詢。不常用。
  3. Proximity。周邊查詢,如“附近500內的餐廳”。

而查詢坐標參數則分兩種:

  1. 坐標對(經緯度)根據查詢命令的不同,$maxDistance距離單位可能是 弧度 和 平面單位(經緯度的“度”):

    db..find( { : { $nearSphere: [ , ] , $maxDistance: } } )
  2. GeoJson $maxDistance距離單位默認為米:

    db..find( { : { $nearSphere : { $geometry : { type : "Point" , coordinates : [ , ] } , $maxDistance : } } } )

案例A:附近的人

查詢當前坐標附近的目標,由近到遠排列。

可以通過$near或$nearSphere,這兩個方法類似,但默認情況下所用到的索引和距離單位不同。

查詢方式:

> db.places.find({'coordinate':{$near: [121.4905, 31.2646]}}) > db.places.find({'coordinate':{$nearSphere: [121.4905, 31.2646]}})

查詢結果:

{ "_id" : 115, "coordinate" : { "longitude" : 121.4915, "latitude" : 31.25933 }, "title" : "僅售148元,市場價298元的星程上服假日酒店全日房一間入住一天, 節假日通用,精致生活,品質享受", "address" : "虹口區天水路90號" } …(100條)

上述查詢坐標[121.4905, 31.2646]附近的100個點,從最近到最遠排序。

默認返回100條數據,也可以用limit()指定結果數量,如

> db.places.find({'coordinate':{$near: [121.4905, 31.2646]}}).limit(2)

指定最大距離 $maxDistance

> db.places.find({'coordinate':{$near: [121.4905, 31.2646], $maxDistance:2}})

結合Symfony2進行演示:

這里用near,默認以度為單位,公里數除以111(關于該距離單位后文有詳細解釋)。

/** * @Route("/near", name="near") * @Template() */ public function nearAction(){ $longitude = (float)$this->getRequest()->get('lon',121.4905); $latitude = (float)$this->getRequest()->get('lat',31.2646); //2km $max = (float)$this->getRequest()->get('max', 2); $places = $this->getPlaceRepository()->createQueryBuilder() ->field('coordinate')->near($longitude, $latitude) ->maxDistance($max/111) ->getQuery()->toarray(); return compact('places','max','longitude','latitude'); }

通過 domain.dev/near 訪問,效果如下:

longitude: xxx, latitude: xxx為當前位置,我們在地圖上顯示了周邊100條目標記錄

案例B:區域內搜索

MongoDB中的

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久99精品久久久久久久久久久久 | 三级在线观看 | 欧美黄色a视频 | 青青久久网| 亚洲成人观看 | 最新黄色免费网站 | 国产黄色在线播放 | 免费黄色在线看 | 婷婷激情综合网 | av高清| 日韩国产一区在线 | 黄色特级一级片 | 欧美精品国产 | 91中文视频 | 国产精品久久久久久久电影 | 99免费视频 | 高清在线一区二区 | 欧美日韩不卡 | 亚洲精品乱码久久久久久按摩观 | 亚洲精品一区二区三 | 高清久久久 | 97视频免费在线 | 欧美精品久久 | 亚洲欧美综合精品久久成人 | 中文字幕日韩欧美一区二区三区 | 久久久久久免费电影 | 人操人 | 久久国产精品区 | 亚洲一区二区三区四区在线视频 | 淫男乱女笨蛋英子 | 免费a在线看 | 又黄又湿的网站 | baoyu123成人免费看视频 | 日韩精品一区二区三区四区 | 久久久女女女女999久久 | 国产精品爽爽爽爽爽爽在线观看 | 日韩在线视频播放 | 亚洲经典在线观看 | 亚洲一区久久 | 91麻豆精品国产自产在线观看一区 | 成人污|