Calling PostGIS functions within Ruby - ruby

I need to execute the PostGIS function st_intersection within an SQL SELECT clause in Ruby. At the moment I am doing it as raw SQL query:
sql_query = "SELECT id, ST_ASEWKT(ST_INTERSECTION(geometry, ?)) FROM trips WHERE status='active';"
intersections = Trip.execute_sql(sql_query, self[:geometry].to_s)
This way has the disadvantage that I receive the result as text and I need to parse the objects out of the strings. Much nicer would be the use of the ActiveRecord interface to make queries. However, I could not find any solution yet to run PostGIS functions (e.g. st_intersection) within ActiveRecord.
An earlier version of the activerecord-postgis-adapter's README showed a nice example using the gem squeel:
my_polygon = get_my_polygon # Obtain the polygon as an RGeo geometry
MySpatialTable.where{st_intersects(lonlat, my_polygon)}.first
As this is not part of the current README anymore, I am wondering whether this is not recommended or if there are any better alternatives.

There are two problems to solve here.
The first is using an SQL function within a .select clause. Ordinarily this is pretty easy—you just use AS to give the result a name. Here's an example from the ActiveRecord Rails Guide:
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")
The resulting Order objects would have ordered_date and total_price attributes.
This brings us to the second problem, which is that Rails doesn't give us an easy way to parameterize a select (i.e. use a ? placeholder), so (as far as I can tell) you'll need to do it yourself with sanitize_sql_array:
sql_for_select_intersection = sanitize_sql_array([
"ST_ASEWKT(ST_INTERSECTION(geometry, ?)) AS intersection",
geometry,
])
This will return a sanitized SQL fragment like ST_ASEWKT(ST_INTERSECTION(geometry, '...')), which you can then use to specify a field in select:
Trip.where(status: "active").select(:id, sql_for_select_intersection))
The resulting query will return Trip objects with id and intersection attributes.

Related

Does hsqldb provide a function similar to listagg?

I am looking for a function (or a group of functions) in HSQLDB that does something similar to Oracle's LISTAGG.
I have this as part of a larger select and would like to keep the syntax as similar as possible in HSQLDB:
SELECT LISTAGG(owner_nm, ', ') WITHIN GROUP (ORDER BY owner_nm)
FROM OWNERSHIP WHERE FK_BIZ_ID = BIZ.BIZ_DATA_ID) AS CURRENT_OWNER
The point of this is that we're trying to use HSQLDB for remote work and Oracle for working on site, prod, etc so I want to change the DDLs as little as possible to achieve that.
Looking at ARRAY_AGG, it doesn't seem like it does anything similar (as far as being able to pull from a separate table like we're doing above with OWNERSHIP). Any suggestions for how I may accomplish this?
group_concat is probably what you are looking for:
http://www.hsqldb.org/doc/2.0/guide/dataaccess-chapt.html#dac_aggregate_funcs
Quote from the manual:
GROUP_CONCAT is a specialised function derived from ARRAY_AGG. This function computes the array in the same way as ARRAY_AGG, removes all the NULL elements, then returns a string that is a concatenation of the elements of the array

SQL column type from Arel::Attributes::Attribute object

tl;dr Given an Arel::Attributes::Attribue object, say Model.arel_table[:created_at] how do get it's SQL type?
Context: I'm bypassing the ActiveRecord infrastructure in favour of Arel to write some SQL reports that need to be generated really efficiently. Using Arel's to_sql method I'm generating the final SQL and executing it directly via ActiveRecord::Base.connection.execute. However, I need to apply SQL transformations to certain columns (eg. change timezone of timestamps stored in GMT). Since the number of columns is large (and varying, based on user input), I don't want to hard code these transformations. I'd like to look at the SQL type of the columns being selected and apply the transformation accordingly.
If you have the ActiveRecord class set up then you have access to its columns and columns_hash methods. Those will give you column objects (instances of ActiveRecord::ConnectionAdapters::Column) and there you should find type and sql_type methods. For example:
> Model.columns_hash['created_at'].type
=> :datetime
> Model.columns_hash['created_at'].sql_type
=> "timestamp without time zone"
The sql_type will be database-specific (that's PostgreSQL above), the type will match the type in your migrations so you probably want to use that instead of sql_type.
That said, you could probably get away with use the usual ActiveRecord relation methods (which should deal with conversions and time zones for you) and then call to_sql at the end:
sql = Model.where('created_at > ?', some_time).select('pancakes').to_sql
and then feed that SQL into execute or select_rows. That will let you use most of the usual ActiveRecord stuff while avoiding the overhead of creating a bunch of ActiveRecord wrappers that you don't care about.
Something that might be helpful specifically in arel is type_cast_for_database. This can be used on an arel table:
Model.arel_table.type_cast_for_database(:id, 'test')
=> 0
Model.arel_table.type_cast_for_database(:id, '47test')
=> 47
While you don't get the type specifically you can see if values like strings are going to be converted to a number or something.
EDIT
It's important to note that this only works if the arel table has a type_caster able_to_type_cast?. If you get it from the model like above, it should have a type caster.

complex EF graph linq join

So, that's my model .. nice and complex.
I'm looking to get Areas by a UserID. If I were doing this in SQL, I would do a bunch of joins all the way up to the Users table. How would you do this in LINQ query syntax or method chaining?
I can do this pretty straightforward from the other way around, but then I have to do a lot of extra work to flatten the resulting graph and it also requires pulling back all the entities in between.
If I can optionally include AreasPermissions & Permissions, that would be gravy .. but at this point I wouldn't mind an additional query to fetch those.
Another option I was floating was using a function import to a sproc and map that to an Area .. but when I start needing to include other entities it makes that option less elegant. I'm also trying to avoid using sprocs just to use sprocs because it's always a slippery slope with folks .. 'use a sproc for one thing' tends to mold into 'don't use EF (table access) for anything'.
var userByID = new Func<User, bool>(x => x.UserId.Equals(userID, StringComparison.InvariantCultureIgnoreCase));
var user = this._context.Users
.Include("TeamsUsers.Team.TeamsRoles.Role.RolesAreasPermissions.AreaPermission.Area")
.Include("TeamsUsers.Team.TeamsRoles.Role.RolesAreasPermissions.AreaPermission.Permission")
.Single(userByID);
I have no way of testing this, but I think it should work:
var result =
from user in _context.Users
where user.Id == userId
from teamUser in user.TeamUsers
from teamRole in teamUser.Team.TeamRoles
from roleAreaPermission in teamRole.Role.RoleAreaPermissions
select roleAreaPermission.AreaPermission.Area;

Nhibernate Update timestamp

Is there a way to do
"UPDATE Item SET start_date = CURRENT_TIMESTAMP" ?
in Nhibernate without using hql/sql.
I am trying to avoid hql/sql because the rest of my code is in criteria. I want to do something like :
var item = session.get<Item>(id)
item.start_date = current_timestamp
There are two ways and sql is correct one.
Either you will
load all entities, change, update and commit, or
write sql query and let dbms handle most of the work
I am trying to avoid hql/sql because the rest of my code is in criteria
That is not a valid argument. Criteria is an API intended for relational search, and it does not support mass updates.
Different tasks, different APIs.
In this case, you can use either HQL or SQL, as the syntax is the same. I recommend the former, because you'll be using your entity/property names instead of table/column ones.

Linq stored procedure with dynamic results

So I'm extremely new to Linq in .Net 3.5 and have a question. I use to use a custom class that would handle the following results from a store procedure:
Set 1: ID Name Age
Set 2: ID Address City
Set 3: ID Product Price
With my custom class, I would have received back from the database a single DataSet with 3 DataTables inside of it with columns based on what was returned from the DB.
My question is how to I achive this with LINQ? I'm going to need to hit the database 1 time and return multiple sets with different types of data in it.
Also, how would I use LINQ to return a dynamic amount of sets depending on the parameters (could get 1 set back, could get N amount back)?
I've looked at this article, but didn't find anything explaining multiple sets (just a single set that could be dynamic or a single scalar value and a single set).
Any articles/comments will help.
Thanks
I believe this is what you're looking for
Linq to SQL Stored Procedures with Multiple Results - IMultipleResults
I'm not very familiar with LINQ myself but here is MSDN's site on LINQ Samples that might be able to help you out.
EDIT: I apologize, I somehow missed the title where you mentioned you wanted help using LINQ with Stored Procedures, my below answer does not address that at all and unfortunately I haven't had the need to use sprocs with LINQ so I'm unsure if my below answer will help.
LINQ to SQL is able hydrate multiple sets of data into a object graph while hitting the database once. However, I don't think LINQ is going to achieve what you ultimately want -- which as far as I can tell is a completely dynamic set of data that is defined outside of the query itself. Perhaps I am misunderstanding the question, maybe it would help if you provide some sample code that your existing application is using?
Here is a quick example of how I could hydrate a anonymous type with a single database call, maybe it will help:
var query = from p in db.Products
select new
{
Product = p,
NumberOfOrders = p.Orders.Count(),
LastOrderDate = p.Orders.OrderByDescending().Take(1).Select(o => o.OrderDate),
Orders = p.Orders
};

Resources