Ecto: Where - Or - phoenix-framework

It looks like a simple feature is coming for this issue, but Phoenix Ecto doesn't have it yet. What is a workaround in the meantime for a where-or query in Ecto?
For example:
from(u in User,
left_join: up in assoc(u, :user_projects),
# does not work
where: up.project_id != ^project.id OR up.project_id IS NULL,
select: {u.id, u.username})

I believe you've misunderstood what or_where will do -- what you want can be done with a simple or and is_nil:
where: up.project_id != ^project.id or is_nil(up.project_id)
What or_where does is enable you to join multiple where: expr in an Ecto query with an OR instead of AND. In the current version of Ecto (2.0), there is no straightforward way to do that -- Ecto joins all where expressions with an AND.

Just replace OR by and with not
from(u in User,
left_join: up in assoc(u, :user_projects),
where: up.project_id != ^project.id AND not(is_nil(up.project_id)),
select: {u.id, u.username})
This is more ecto way, by the way

Related

Using multiple keywords with LIKE in Postgresql through Spring Data JPA

I need to define a JPA query that translates as:
SELECT
*
from
items_table
where
lower(CONCAT(item_id,' ', color,' ',details,' ')) like all ('{"%blue%", "%plastic%"}') and
store='Main' and
status='In Stock' and
item_type='Container';
the parameter list in all will be an input along with the store, status and item_type from the front end.
I understand it's the first WHERE condition you're having a problem with. If a String[]-typed parameter doesn't cut it, try using this approach, hopefully the LIKE ALL will pass through to the generated query - sth like:
#Query("SELECT i FROM Item i WHERE LOWER(CONCAT(...)) LIKE ALL :terms AND ...")
List<Item> findByTerms(TypedParameterValue terms, ...)
itemRepository.findByTerms(new TypedParameterValue(StringArrayType.INSTANCE, terms), ...)
If it turns out JPA doesn't like LIKE ALL, you'll have to keep using the native query.

Sequel query with join and condition on the other table

I'm pretty new to Sequel and I'm scratching my head trying to figure out how to get Sequel's API to generate the following trivial SQL:
select f.* from first f
join second s on f.second_id = s.id
where s.deactivated = false
The best I could come up with is:
First.join(:second, deactivated: false, id: :second_id)
.paged_each do |first|
# magic happens here
end
But that does select * not select first.* and as a result gets confused about what id to order by for paging and throws PG::AmbiguousColumn: ERROR: ORDER BY "id" is ambiguous
This is using Sequel 5.9.0 and Postres 10.
Solution
Thanks to #engineersmnky's suggestion about qualify and some further reading here's the approach I ended up using.
First.join(:second, id: :second_id, deactivated: false)
.qualify
.stream.each do |first|
# magic happens here
end
The qualify method call resolves the ambiguity (and ensures only first table gets returned.
I also added sequel_pg gem so I can use stream.each rather than paged_each. This has a better performance but also removes the need for the order by id that was causing me grief initially.
Disclaimer: I have never actually used sequel
There appears to be a method Sequel::Dataset#qualify that will do exactly what you are asking and should result in:
select first.* from first
join second on first.second_id = second.id
where second.deactivated = false
I think the implementation would look like:
First.join(:second, id: :second_id)
.where(Sequel[:second][:deactivated] => false)
#OR .where("second.deactivated": false)
#OR .where{[[second[:deactivated],false]]}
.qualify
.paged_each do |first|
# magic happens here
end
Now if First and Second are properly associated Sequel::Models it appears the join condition can be inferred through association_join See Here from the docs e.g.
First.association_join(:second)
.where(Sequel[:second][:deactivated] => false)
.qualify
.paged_each do |first|
# magic happens here
end
Disclosure
Not an Sequel expert. But I do use it.
I think where statements that come after joins are NOT virtual rows. You'd have to qualify them in some way - here's one way - and there are probably other ways too:
Solution
DB[:F].join(:S, id: :second_id).where(Sequel[:S][:deactivated] => false).sql
SQL produced:
SELECT * FROM F INNER JOIN S ON (S.id = F.second_id) WHERE (S.deactivated IS FALSE)
To Check:
Clone the Sequel Gem: https://github.com/jeremyevans/sequel
Run the following command in the root directory of the gem:
bin/sequel -c "puts DB[:F].join(:S, id: :second_id).where(Sequel[:S][:deactivated] => false).sql"

How to solve AmbiguousColumn error in postgresql with ruby project

I would like to join two query result.
First one is
Gig.where(id:gigsInRadioIDS)
When I convert result to json I get this result
[{"id":1,"address":"test1"},{"id":2,"address":"test2"}
Second is.
Slot.where(status:"available").where(Sequel.lit('("from" > ?) AND ("to" < ?)', fromdate, todate))
when I convert result to json I get this result
[{"id":15,"status":"available","gig_id":1}]
What I want is to inner join to result
so I hope to get this result(I abbreviated some non related snippet)
[{"id":15,"status":"available","gig_id":1,"id":1,"address":"test1"}]
What I have tried is
Gig.where(id:gigsInRadioIDS).as(:tb_gig).join(Slot.where(status:"available").where(Sequel.lit('("from" > ?) AND ("to" < ?)', fromdate, todate)), gig_id: :id)
And I got this error
Sequel::DatabaseError - PG::AmbiguousColumn: ERROR: column reference "id" is ambiguous
LINE 1: ...) AS "t1" ON ("t1"."gig_id" = "gigs"."id") WHERE ("id" IN (2...
Thanks for reading this complex code.
Any news will be big help. Thanks.
There are a bunch of ways to approach it. It looks like you are querying Slots with some Gigs data mixed in, so you may want to start with your Slot model. The regular join syntax is a little easier to understand and makes it easier to select columns from both tables (i.e. instead of WHERE fk IN (..)). As a bonus, you can mix your gig_id filter into the JOIN clause. Finally, you don't need to use Sequel.lit to do greater than / less than in your WHERE clause, and you should be explicit about the columns you want to select.
Here's how I would write it:
Slot.join(:gigs, [[:id, :gig_id], [:id, gigsInRadioIDS]])
.where(status: 'available') { (from() > fromdate) & (to() < todate) }
.select(Sequel[:slots][:id].as(:slot_id), :status, :gig_id, :address)
Getting .all of the records and outputting JSON should yield something like:
[{"slot_id":15,"status":"available","gig_id":1,"address":"test1"}]
Note the Sequel[:table_name][:column_name] syntax. This is the new style of fully-qualifying identifiers in Sequel 4.49+. (In past versions of Sequel, it would have been written like :table_name__column_name, but that syntax is now deprecated.)
Your where(id:gigsInRadioIDS) translates into WHERE ("id" IN (...)), and since both gigs and slots have an "id" column, the database doesn't know which id you want.
You need to explicitly specify the table with where{{gigs[:id] => gigsInRadioIDS}}
Querying with Sequel

DBnull in Linq query causing problems

i am doing a query thus:
int numberInInterval = (from dsStatistics.J2RespondentRow item in jStats.J2Respondent
where item.EndTime > dtIntervalLower && item.EndTime <= dtIntervalUpper
select item).count();
there appear to be some dbnulls in the endtime column..
any way i can avoid these?
tried adding && where item.endtime != null.. and even != dbnull.value
do i have to do a second (first) query to grab all that arent null then run the above one?
im sure its super simple fix, but im still missing it.. as per
thanks
nat
I think you want to use item.EndTime.HasValue, and not item.EndTime == null.
The simplest way to do it is to use .GetValueOrDefault(...Some reasonable default...) on the value that can be null and it will avoid the error.
The way I typically do this in a T-SQL query is to use ISNULL on the date, and set the date to something like '12/31/2099' when it's null.
With Linq it could be something like this:
from t in MyTable
where
Convert.ToString((Convert.ToString(t.MyDateColumn) ?? "12/31/2099")) < MyTargetValue

NHibernate.Linq LIKE

How can I produce this query using NHibernate.Linq?
WHERE this_.Name LIKE #p0; #p0 = 'test' // Notice NO % wild card
Note, this is not Linq To Sql or Entity Framework. This is NHibernate.
Edit:
Here is the desired query using ICriteria:
criteria.Add(Expression.Like("Name", "test"));
return criteria.List<Theater>();
Whilst this has been marked as resolved, which was correct at the time, may I also note that NHibernate has some extensions now so you can do the following:
Session.QueryOver<MyEntity>()
.Where(x => x.Property.IsLike("something", MatchMode.Anywhere))
.List();
This will do a LIKE '%something%' for you.
I believe this is what you are looking for:
var theaters = from theater in Session.Linq<Theater>()
where theater.Name.Contains("test")
select theater;
According to my tests it generates an SQL 'LIKE' statement:
"... WHERE theater.Name LIKE %test%"
which is exactly the output of the criteria snippet you have provided.
I had the same problem in my project and found a solution :
session.Linq<Theater>()
.Where(x => x.Name.StartsWith("test") && x.Name.EndsWith("test");
This translates in SQL to
SELECT ... WHERE Name LIKE '%test' AND Name LIKE 'test%'
With NH 4 (and probably a bit earlier), a built-in Like string extension is available within NHibernate.Linq namespace: Like(this string matchExpression, string sqlLikePattern). (It is defined on NHibernate.Linq.SqlMethods extension class.)
using NHibernate.Linq;
...
session.Query<Theater>()
.Where(t => t.Name.Like("test"));

Resources