beaglemapを少しだけ改修しました

みなさん、こんにちは。beaglesoftの真鍋です。

先日のりといきおいで公開したbeaglemap - beaglemapですが、少しだけ改修しました。主な回収内容は表示と不具合です。

表示

地図を表示するために住所を検索するのですが、この処理が遅いので処理中表示を行うように修正しました。利用したのはjQuery BlockUI Pluginです。以前と違って検索中にはこのような感じの表示となります。

Mz4dGh4tSL.gif (2.4 MB)

また、初期表示時の縮尺も複数のエリアが表示された時のためにやや縮小表示にしました。

不具合

前回のリリース後にパフォーマンスをアップするため入力された内容から都道府県を取得して検索キーとするようにしたのですが、この処理で不具合がありました。例えば、「京都府京都市」を検索すると存在しない事になってしまっていました。

原因は利用していた正規表現が間違っていたのですが、こちらを参考に正規表現は諦めて都道府県の配列とのマッチングを行うように修正しました。

なるべく短い正規表現で住所を「都道府県/市区町村/それ以降」に分けるエクストリームスポーツ - Qiita

パフォーマンス

今回はAzureのSQL Databaseを利用しています。また、実装はEntityFrameworkを中心に利用していますが、検索結果の取得に時間がかかっていました。例えば、「北海道札幌市」を検索すると10秒ほどかかっていました。

何が原因かを探ってみたところ、以下の内容が原因とわかりました。

  1. EntityFrameworkで生成しているSQLに無駄が多い
  2. インデックスが作成されていないまたは、有効に利用されていない

よくあるパターンですね。そこで、SQLをチューニングしてそのまま利用するためにStackExchange/dapper-dot-net: Dapper - a simple object mapper for .Netを利用したのですが、ローカルのSQL Serverでは問題なく動作するのですが、AzureのSQL Databaseではエラーのため動作しませんでした。

原因は正直良くわかっていないのですが、おそらくDbGeographySqlGeographyの変換でうまく行かなかったのだと思います。この時はc# - Query spatial data with dapper - Stack Overflowを参考に実装をしました。

message:Error parsing column 38 (PosRange=88216 - Int32)
stackTrace:   at Dapper.SqlMapper.ThrowDataException(Exception ex, Int32 index, IDataReader reader, Object value)
   at Deserialize78bb7d5d-92f9-4c31-b34e-e768902c7ca2(IDataReader )
   at Dapper.SqlMapper.<>c__DisplayClass73_0`8.<GenerateMapper>b__0(IDataReader r)
   at Dapper.SqlMapper.<MultiMapImpl>d__71`8.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Dapper.SqlMapper.MultiMap[TFirst,TSecond,TThird,TFourth,TFifth,TSixth,TSeventh,TReturn](IDbConnection cnn, String sql, Delegate map, Object param, IDbTransaction transaction, Boolean buffered, String splitOn, Nullable`1 commandTimeout, Nullable`1 commandType)
   at GeoData.Repository.ChomokuRepository.FindByAddress(String addressString, Address address)
   at GeoData.Service.ChomokuService.FindByAddress(String addressString)
   at YahooGeo.Controllers.HomeController.GetPointRangeByAddress(String address)
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<>c__DisplayClass2b.<BeginInvokeAction>b__1c()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult)

あまり時間がなかったため、こちらは深追いせずにEntityFrameworkのSQLをどうにかできないかという路線で調査を行い、そこそこに納得の行く内容になったので今回のリリースとなりました。ただ、Dapperを利用して再チャレンジはしたいと思います。