I'm trying to do an order query that finds records nearest to the current_user.
I know the distance between the two points is: current_location.euclidean_distance(#record.position)
How can I work this into a PostGIS (or active_record/spatial_adapter) query?
To get the 5 closest:
SELECT * FROM your_table
ORDER BY ST_Distance(your_table.geom, ST_Geomfromtext(your point as wkt))
limit 5;
If you have a big dataset and know that you don't want to search further than , say 1 km, the query will be more efficient if you do:
SELECT * FROM your_table
WHERE ST_DWithin(your_table.geom, ST_Geomfromtext(your point as wkt, 1000)
ORDER BY ST_Distance(your_table.geom, ST_Geomfromtext(your point as wkt))
limit 5;
/Nicklas
Just in case somebody stumbles upon this issue in rails 4.
I am using rgeo gem and this works for me
scope :closest, ->(point) { order("ST_Distance(lonlat, ST_GeomFromText('# {point.as_text}', #{SRID}))").limit(5) }
To wrap this up, with everyone's help I've got it working how I wanted:
order("ST_Distance(items.position, ST_GeomFromText('POINT (#{current_location.y} #{current_location.x})', #{SRID}))")
If you really want to find literally the 5 records nearest to the current_user, consider neighborhood search, which is supported by KNN index in PostGIS 2.0 (see '<->' operator):
SELECT * FROM your_table ORDER BY your_table.geom <-> ST_Geomfromtext(your point as wkt, 1000) LIMIT 5
Look at the ST_Distance documentation in PostGIS.
If you dont NEED to use PostGIS, geo-kit does this perfectly using google or yahoo (I've only used Google) and in your queries you can sort by distance, its awesome..
Related
I'm using GeoCoder in my application. Now I need to search for objects in my database which are close to a position OR have specific attribute set. I would like to perform this action in one database query, because the database is realy huge.
I would like to have something like
Spot.near([lat,long],distance).where("visited = ?",true).
The distance and the visited attribute should be combined with an OR, not with an AND.
Does anyone have an idea how to do this?
Thank you!
Based off of this answer, you should be able to do something like:
near = Spot.near([lat, long], distance)
visited = Spot.where(visited: true)
near = near.where_values.reduce(:and)
visited = visited.where_values.reduce(:and)
Spot.where(near.or(visited))
I'm in the process of upgrading a Rails application from Rails 4 to Rails 7 and ran into this problem. While I have no doubt Luke's suggestion worked in earlier versions, it doesn't work in Rails 7 (I'm currently running activerecord-7.0.3.1.
In my particular case, I am using the geocoder near() method to return results that are within a 20 mile radius of the query, but I also wanted to use OR conditions to return results where the query was similar to the text values in either the name or location columns from the items table in an attempt to return relevant items that haven't been assigned latitude and longitude values.
In Rails 4, my solution was:
select("items.*").near(q, 20, select: :geo_only).tap do |near_query|
near_query.where_values.last << sanitize_sql([" OR items.location LIKE ? OR items.name LIKE ?", "%#{q}%", "%#{q}%"])
end
In Rails/ActiveRecord 7, the where_values() method no longer exists. Searching for an alternate solution led me to this post. I wound up spending a fair amount of time perusing the latest ActiveRecord and Arel code for a solution. Here's what I came up with,
Rails 7 solution:
t = Item.arel_table
arel_geo_conditions = Item.near(q, 20).where_clause.ast # ast: Abstract Syntax Tree
Item.where(arel_geo_conditions.or(t[:location].matches("%#{q}%").or(t[:name].matches("%#{q}%"))))
I currently use a MSSQL query that allows me to find items that are in a certain radius.
It looks like this :
select distinct ID,longitude,latitude,
(#unite * ACOS(cos(radians(#latitude))*cos(radians(latitude))*cos(radians(longitude)
-radians(#longitude))+sin(radians(#latitude))*sin(radians(latitude)))) as distance
from myTable
where (#unite * ACOS(cos(radians(#latitude))*cos(radians(latitude)) *
cos(radians(longitude)-radians(#longitude))+
sin(radians(#latitude))*sin(radians(latitude))))<#radius
I would like to know if it's possible to convert this query into Objective C (I use CoreData in my app and I have more than 8 000 records).
I found an interesting article about my need using sqlite
http://www.thismuchiknow.co.uk/?p=71
Thanks
Ruby 1.9.2 / rails 3.1 / deploy onto heroku --> posgresql
Hi, Once a number of rows relating to an object goes over a certain amount, I wish to pull back every nth row instead. It's simply because the rows are used (in part) to display data for graphing, so once the number of rows returned goes above say 20, it's good to return every second one, and so forth.
This question seemed to point in the right direction:
ActiveRecord Find - Skipping Records or Getting Every Nth Record
Doing a mod on row number makes sense, but using basically:
#widgetstats = self.widgetstats.find(:all,:conditions => 'MOD(ROW_NUMBER(),3) = 0 ')
doesn't work, it returns an error:
PGError: ERROR: window function call requires an OVER clause
And any attempt to solve that with e.g. basing my OVER clause syntax on things I see in the answer on this question:
Row numbering in PostgreSQL
ends in syntax errors and I can't get a result.
Am I missing a more obvious way of efficiently returning every nth task or if I'm on the right track any pointers on the way to go? Obviously returning all the data and fixing it in rails afterwards is possible, but terribly inefficient.
Thank you!
I think you are looking for a query like this one:
SELECT * FROM (SELECT widgetstats.*, row_number() OVER () AS rownum FROM widgetstats ORDER BY id) stats WHERE mod(rownum,3) = 0
This is difficult to build using ActiveRecord, so you might be forced to do something like:
#widgetstats = self.widgetstats.find_by_sql(
%{
SELECT * FROM
(
SELECT widgetstats.*, row_number() OVER () AS rownum FROM widgetstats ORDER BY id
) AS stats
WHERE mod(rownum,3) = 0
}
)
You'll obviously want to change the ordering used and add any WHERE clauses or other modifications to suit your needs.
Were I to solve this, I would either just write the SQL myself, like the SQL that you linked to. You can do this with
my_model.connection.execute('...')
or just get the id numbers and find by id
ids = (1..30).step(2)
my_model.where(id => ids)
My SQL query is:
SELECT * FROM 1910640 WHERE stype='P' OR stype='ERC' OR stype='PERC' ORDER BY ST_DISTANCE(geometry, LATLNG(-0.12623619999999391,51.5001524)) LIMIT 6
This results in a "parseerror". If I replace OR with AND the query returns success:
SELECT * FROM 1910640 WHERE stype='P' AND stype='ERC' AND stype='PERC' ORDER BY ST_DISTANCE(geometry, LATLNG(-0.12623619999999391,51.5001524)) LIMIT 6
Anyone else ran into this with Fusion Tables and have a solution/workaround?
The API doc does imply only AND is allowed, which came as a big surprise to me.
http://code.google.com/apis/fusiontables/docs/developers_guide.html#Querying
OR isn't supported - look at the API ref filter_conditions
IN is supported - so you can IN all your OR conditions for SType
Excuse me if I'm saying foolery, since I never have use fusion-tables, but OR queries can be split into 2 queries, one for each condition.
The main problem in this workaround is that you must implement the ORDER in programming language when merging both queries.
You can use IN instead of OR because OR is not supported by fusion tables.
http://www.w3schools.com/sql/sql_in.asp
Give two parameters which correspond to two attributes on an object how can one find 20 records in a database that are closest to those two numbers.
The parameters you have are x, and y. The object also has those attributes. For example. x = 1, and y = 9999. You need to find the record that is the closest to x and y.
That depends on how you define the distance between two points. If you are using a two-dimensional cartesian coordinate system, this SQL statement will work:
SELECT id, x, y FROM points ORDER BY SQRT(POWER((X-x),2)+POWER((Y-y),2)) ASC LIMIT 20;
Where X,Y are the inputs.
It sounds like you're using geolocated data. If your database backend is Postgres, check to see if you have or can install the PostGIS extensions. This gives you very fast tools which give you searches like 'search for the nearest thing to this point', 'search for everything within this circle', 'search for everything within this square', and so on.
http://postgis.refractions.net/
You would do something like this:
CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometrycolumn] gist_geometry_ops);
Then you can do something like this - find everything within 100 metres of a point:
SELECT * FROM GEOTABLE WHERE
GEOM && GeometryFromText(’BOX3D(900 900,1100 1100)’,-1) AND
Distance(GeometryFromText(’POINT(1000 1000)’,-1),GEOM) < 100;
Examples from the manual.