rails 为子查询消毒

问题描述

我正在尝试使用子查询清理 sql 查询,但每次出现语法错误或无效几何错误时。为了消毒,我使用了 sanitize_sql_array 函数

sql 查询

SELECT ST_distance('SRID=4326;POINT(0 0)'::geometry,subqry.centroid)
FROM 
 (SELECT ST_AsText(ST_Centroid('SRID=4326;MULTIPOINT ( 0 0,0 0,0 0 )'))::geography as centroid
 ) as subqry;

第一种方法

ActiveRecord::Base::sanitize_sql_array(["select ST_distance('SRID=4326;POINT(? ?)'::geometry,:sub_query",sub_query: "(SELECT ST_Centroid(SRID=4326;'MULTIPOINT ( ? ?,? ?,? ? )'::geometry) as centroid) as sub_query",min_longitude,min_latitude,max_latitude,max_longitude,max_latitude])

第二种方法

这里我得到 "SRID=4326; SE"

ActiveRecord::Base::sanitize_sql_array(["ST_distance('SRID=4326;POINT(? ?)'::geometry,'SRID=4326; SELECT ST_AsText(ST_Centroid(MULTIPOINT ( ? ?,? ? ))) as centroid'::geometry)",max_latitude])

PostGIS 中 st_centroid & st_distance 方法示例

st_centroid

SELECT ST_AsText(ST_Centroid('MULTIPOINT ( -1 0,-1 2,-1 3,-1 4,-1 7,0 1,0 3,1 1,2 0,6 0,7 8,9 8,10 6 )'));


st_distance

SELECT ST_distance(
        'SRID=4326;POINT(-72.1235 42.3521)'::geometry,'SRID=4326;LInesTRING(-72.1260 42.45,-72.123 42.1546)'::geometry
    );

解决方法

您可以使用一些技巧和大量 Arel(它将为您处理卫生)构建您想要的任何查询。

警告:这可能看起来不太漂亮,但它非常灵活,应该会产生预期的结果。 (这只是生成所需的查询,如帖子中所示我不知道所需的语法是否正确,因为我从未使用过 PostGIS

 class PostGISQuery
   def initialize(srid,points,point)
     @srid = srid
     @points = points 
     @point = point 
   end 

   def build_query
     return @query if @query
     multi_point = Arel::Nodes::NamedFunction.new("MULTIPOINT",[lat_long(@points)])
     centroid = Arel::Nodes::NamedFunction.new('ST_AsText',[
            Arel::Nodes::NamedFunction.new('ST_Centroid',[
                Arel.sql("SRID=#{@srid};#{multi_point.to_sql}")
            ])
         ])
     mgr = Arel::SelectManager.new 
     mgr.project(cast_geometry(centroid).as('centroid'))
     table = Arel::Table.new('subqry')
     answer= Arel::Nodes::NamedFunction.new('ST_Distance',[
        cast_geometry(Arel.sql("SRID=#{@srid};#{Arel::Nodes::NamedFunction.new('POINT',lat_long([@point])).to_sql}")),table[:centroid]
     ])
     mgr2 = Arel::SelectManager.new(Arel::Nodes::As.new(mgr,Arel.sql(table.name)))
     @query = mgr2.project(answer.as('answer'))
   end 

   def execute 
     ActiveRecord::Base.connection.exec_query(build_query.to_sql)
   end
   private 
     def cast_geometry(arel)
       Arel::Nodes::NamedFunction.new("CAST",[arel.as('geometry')])
     end

     def lat_long(arr)
       arr.map {|a| Arel.sql(a.join(' '))}
     end
 end 

用法

srid = 4326
points = [[1,0],[-1,2],3],4],7],[0,1],[1,[2,[6,[7,8],[9,[10,6]]
point = [-72.1235,42.3521]
qry = PostGISQuery.new(srid,point) 
qry.execute
#=> ActiveRecord::Result

SQL 生成:

SELECT 
  ST_Distance(
    CAST('SRID=4326;POINT(-72.1235 42.3521)' AS geometry),"subqry"."centroid") AS answer 
  FROM (
    SELECT 
      CAST(ST_AsText(
             ST_Centroid(
               'SRID=4326;MULTIPOINT(1 0,-1 2,-1 3,-1 4,-1 7,0 1,0 3,1 1,2 0,6 0,7 8,9 8,10 6)'
             )
           ) AS geometry) AS centroid
    ) AS subqry