2016-06-27 6 views
3

私のレールアプリには、現在ログインしているユーザーに最も近いユーザーを見つける機能があります。私はGeocoder gemを使っています。 Userモデルでは私はこのような範囲を持っている:索引を追加してジオコーダの検索速度を向上させる

scope :close_to, -> (user:, distance:) { 
    where.not(id: user.id) 
    .near([user.latitude, user.longitude], distance) 
    } 

これは非常にうまく動作しますが、それはユーザの大きな集まりに遅いです。私はそのためにインデックスを作成しようとしているが、彼らは動作しませ

SELECT users.*, 6371.0 * 2 * ASIN(SQRT(POWER(SIN((48.471645 - users.latitude) * PI()/180/2), 2) + COS(48.471645 * PI()/180) * COS(users.latitude * PI()/180) * POWER(SIN((-83.102801 - users.longitude) * PI()/180/2), 2))) AS distance, MOD(CAST((ATAN2(((users.longitude - -83.102801)/57.2957795), ((users.latitude - 48.471645)/57.2957795)) * 57.2957795) + 360 AS decimal), 360) AS bearing FROM "users" WHERE ("users"."id" != 43362) AND (users.latitude BETWEEN 39.4784289408127 AND 57.46486105918731 AND users.longitude BETWEEN -96.6674214298497 AND -69.5381805701503 AND (6371.0 * 2 * ASIN(SQRT(POWER(SIN((48.471645 - users.latitude) * PI()/180/2), 2) + COS(48.471645 * PI()/180) * COS(users.latitude * PI()/180) * POWER(SIN((-83.102801 - users.longitude) * PI()/180/2), 2)))) BETWEEN 0.0 AND 1000) ORDER BY distance ASC; 

:私はこのスコープを呼び出すときには、次のSQLクエリを生成します。私は以下の組み合わせを試していました:

1. 
    add_index :users, [:id, :latitude] 
    add_index :users, [:id, :longitude] 

2. add_index :users, [:id, :latitude, :longitude] 

3. add_index :users, [:latitude] 
    add_index :users, [:longitude] 

4. add_index :users, [:id, :latitude] 

このクエリをスピードアップするにはどうすればよいですか?

編集:私の緯度と経度の列は小数点以下の桁であることを追加しています。

Sort (cost=7141.66..7142.14 rows=191 width=327) (actual time=575.995..585.543 rows=36598 loops=1) 
    Sort Key: ((12742::double precision * asin(sqrt((power(sin((((((48.471645 - latitude))::double precision * 3.14159265358979::double precision)/180::double precision)/2::double precision)), 2::double precision) + ((0.662990616338754::double precision * cos((((latitude)::double precision * 3.14159265358979::double precision)/180::double precision))) * power(sin(((((((-83.102801) - longitude))::double precision * 3.14159265358979::double precision)/180::double precision)/2::double precision)), 2::double precision))))))) 
    Sort Method: external merge Disk: 4672kB 
    -> Seq Scan on users (cost=0.00..7134.43 rows=191 width=327) (actual time=0.381..517.615 rows=36598 loops=1) 
     Filter: ((id <> 43362) AND (latitude >= 39.4784289408127) AND (latitude <= 57.46486105918731) AND (longitude >= (-96.6674214298497)) AND (longitude <= (-69.5381805701503)) AND ((12742::double precision * asin(sqrt((power(sin((((((48.471645 - latitude))::double precision * 3.14159265358979::double precision)/180::double precision)/2::double precision)), 2::double precision) + ((0.662990616338754::double precision * cos((((latitude)::double precision * 3.14159265358979::double precision)/180::double precision))) * power(sin(((((((-83.102801) - longitude))::double precision * 3.14159265358979::double precision)/180::double precision)/2::double precision)), 2::double precision)))))) >= 0::double precision) AND ((12742::double precision * asin(sqrt((power(sin((((((48.471645 - latitude))::double precision * 3.14159265358979::double precision)/180::double precision)/2::double precision)), 2::double precision) + ((0.662990616338754::double precision * cos((((latitude)::double precision * 3.14159265358979::double precision)/180::double precision))) * power(sin(((((((-83.102801) - longitude))::double precision * 3.14159265358979::double precision)/180::double precision)/2::double precision)), 2::double precision)))))) <= 1000::double precision)) 
     Rows Removed by Filter: 6756 
Planning time: 1.041 ms 
Execution time: 587.695 ms 
(8 rows) 

EDIT 2:

私はPostgreSQLが私は小さな距離EXを入力だけで、私の

add_index :users, [:latitude, :longitude] 

使用していることに気づいた

このクエリのANALYZEはそのような何かを返します。 10キロ近くのユーザー。

答えて

1

スローダウンは、表のデータをフェッチするのではなく、数学演算によって発生する可能性があります。あなたの基準の一部は、レコードフィールドに対してではなく、他のレコードに対する算術演算の結果に対して、O(N )になっています。

Postgresがインデックスを使用せず、代わりにSeqスキャンを選択する理由は、クエリ中にテーブルレコードの大部分をフェッチする必要があるためです。テーブル内のほとんどのレコードをフェッチするときは、インデックスがあれば多くのメリットはありません。

スピードアップするには、空間インデックスと近傍ベースの検索をPostGis、またはGeo Distance Queryのエラスティックサーチを使用することを検討する必要があります。

関連する問題