nHibernate 3.0 queries - nhibernate

Working through the summer of nHibernate tutorials have gotten to the section on queries. Seems there have been changes since that series was made. So I went to the online docs for nHB 3.0 but code such as:
IList cats = session.CreateCriteria(typeof(Cat))
.Add(Expression.Like("Name", "Fritz%"))
.Add(Expression.Between("Weight", minWeight, maxWeight))
.List();
Generates the error "The name 'Expression' does not exist in the current context"
Code like:
return session.CreateCriteria(typeof(DataTransfer.Customer))
.Add(new NHibernate.Criterion.LikeExpression("Firstname", firstname))
.Add(new NHibernate.Criterion.LikeExpression("Lastname", lastname))
.List<Customer>();
Works but it seems that it is missing a number of query methods like GtExpression.
Are the online docs up to date, and if so, why can't I use Expression...
If the online docs aren't up to date then where do I get a description of the Criterion interface?
Thanks

You forgot to add using NHibernate.Criterion;.
Anyway, the Expression class is deprecated. Use Restrictions instead.

Weird thing. I still use Expression.* static methods and these are still work. Are you sure you use the latest version of NH3.0? I use Alpha 2 version.
If you need to make it work urgently, let's try the QueryOver<> feature:
return session.QueryOver<DataTransfer.Customer>()
.WhereRestrictionOn(u => u.Name).IsLike("Fritz%")
.AndRestrictionOn(u => u.Weight).IsBetween(minWeight).And(maxWeight)
.List();
It works well for simple queries

Related

Open CMIS - Querying string property results in weird behavior

I'm executing the following SQL query:
SELECT doc.cmis:description, doc.cmis:name
FROM cmis:document doc
WHERE IN_FOLDER(doc,'folderID')
This result in something like below:
doc.cmis:description = "this is description"
doc.cmis:name = "fileName"
Now, if I add following statements, it returns zero result:
and doc.cmis:description = 'this is description'
However, if I modify and-statement with following, it works:
and doc.cmis:description like '%'
If I add one character (but not two interestingly...) as below, it also works:
and doc.cmis:description like '%t%'
It's very interesting to note that and-statement work very well with doc.cmis:name (as well as other properties).
Does anyone have clue as to why this strange / mysterious behavior is occurring?
The specifications delegate to the implementer if the cmis:description is queryable or not.
Anyway, which Alfresco version are you using ? There was an issue/bug time ago, but this should be solved: The cmis:description field should be queryable, although I don't know if it's fixed in enterprise or community.
By the way, I am currently using Alfresco Community 4.2.f and I have the same problem.

Arel: Left outer join using symbols

I have this use case where I get the symbolized deep associations from a certain model, and I have to perform certain queries that involve using outer joins. How can one do it WITHOUT resorting to write the full SQL by hand?
Answers I don't want:
- using includes (doesn't solve deep associations very well ( .includes(:cars => [:windows, :engine => [:ignition]..... works unexpectedly ) and I don't want its side-effects)
- writing the SQL myself (sorry, it's 2013, cross-db support, etc etc..., and the objects I fetch are read_only, more side-effects)
I'd like to have an Arel solution. I know that using the arel_table's from the models I can construct SQL expressions, there's also a DSL for the joins, but somehow i cannot use it in the joins method from the model:
car = Car.arel_table
engine = Engine.arel_table
eng_exp = car.join(engine).on(car[:engine_id].eq(engine[:id]))
eng_exp.to_sql #=> GOOD! very nice!
Car.joins(eng_exp) #=> Breaks!!
Why this doesn't work is beyond me. I don't know exactly what is missing. But it's the closest thing to a solution I have now. If somebody could help me completing my example or provide me with a nice work-around or tell me when will Rails include such an obviously necessary feature will have my everlasting gratitude.
This is an old question, but for the benefit of anyone finding it through search engines:
If you want something you can pass into .joins, you can either use .create_join and .create_on:
join_on = car.create_on(car[:engine_id].eq(engine[:id]))
eng_join = car.create_join(engine, join_on, Arel::Nodes::OuterJoin)
Car.joins(eng_join)
OR
use the .join_sources from your constructed join object:
eng_exp = car.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.joins(eng_exp.join_sources)
I found a blog post that purports to address this problem: http://blog.donwilson.net/2011/11/constructing-a-less-than-simple-query-with-rails-and-arel/
Based on this (and my own testing), the following should work for your situation:
car = Car.arel_table
engine = Engine.arel_table
sql = car.project(car[Arel.star])
.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.find_by_sql(sql)
If you don't mind adding a dependency and skipping AREL altogether, you could use Ernie Miller's excellent Squeel gem. It would be something like
Car.joins{engine.outer}.where(...)
This would require that the Car model be associated with Engine like so:
belongs_to :engine

NHibernate QueryOver order by first non-null value (coalescing)

What I'm trying to come up is something that's expressed like this:
var result = Session.QueryOver<Foo>().OrderBy(f => f.UpdatedAt ?? f.CreatedAt);
Sure enough, this doesn't work. Rough equivalent of this in T-SQL is
... order by coalesce(f.UpdatedAt, f.CreatedAt)
What's the kosher way to do "coalescing" in NHibernate QueryOver?
.OrderBy(Projections.SqlFunction("coalesce",
NHibernateUtil.DateTime,
Projections.Property<Foo>(x => x.UpdatedAt),
Projections.Property<Foo>(x => x.CreatedAt)))
Diego's answer is the way I came up with, but it seemed to be too verbose to me, so I asked a question, and got an excellent answer. Basically, you can register your own extensions and then just do
.OrderBy(f => f.UpdatedAt.IfNull(f.CreatedAt));
where IfNull is your new extension. I've even submitted an improvement proposal to NH Jira, but got no response yet.

NHibernate and NHibernate Search Version Problem

I have sample application on Nhibernate with Nhibernate Search with the following version nos,
Nhibernate - v2.0.0.1001
Nhibernate Search - v2.0.0.1001
I am not sure if it custom build, but everything seems to work fine here. But as soon as I change the Nhibernate version to v2.0.1.4000 (a later minor version and build), things start breaking at,
IList result = s.CreateCriteria(typeof(DomainObject)).Add(NHibernate.Search.Search.Query("Summary:NHibernate or Name:NHibernate"))
VStudio complains "'Query' is not supported language."
Has anyone had a similar issue? How could I get a port for v2.0.1.4000?
Thanks.
I guess the way to create a lucene query was to just use the Query Parser:
QueryParser queryP = new QueryParser("id", new StandardAnalyzer());
Lucene.Net.Search.Query q = queryP.Parse("Summary:NHibernate or Name:NHibernate");
IList result = s.CreateFullTextQuery(q, typeof(DomainObject)).List();

Rails 3 ActiveRecord query using both SQL IN and SQL OR operators

I'm writing a Rails 3 ActiveRecord query using the "where" syntax, that uses both the SQL IN and the SQL OR operator and can't figure out how to use both of them together.
This code works (in my User model):
Question.where(:user_id => self.friends.ids)
#note: self.friends.ids returns an array of integers
but this code
Question.where(:user_id => self.friends.ids OR :target => self.friends.usernames)
returns this error
syntax error, unexpected tCONSTANT, expecting ')'
...user_id => self.friends.ids OR :target => self.friends.usern...
Any idea how to write this in Rails, or just what the raw SQL query should be?
You don't need to use raw SQL, just provide the pattern as a string, and add named parameters:
Question.where('user_id in (:ids) or target in (:usernames)',
:ids => self.friends.ids, :usernames => self.friends.usernames)
Or positional parameters:
Question.where('user_id in (?) or target in (?)',
self.friends.ids, self.friends.usernames)
You can also use the excellent Squeel gem, as #erroric pointed out on his answer (the my { } block is only needed if you need access to self or instance variables):
Question.where { user_id.in(my { self.friends.ids }) |
target.in(my { self.friends.usernames }) }
Though Rails 3 AR doesn't give you an or operator you can still achieve the same result without going all the way down to SQL and use Arel directly. By that I mean that you can do it like this:
t = Question.arel_table
Question.where(t[:user_id].in(self.friends.ids).or(t[:username].in(self.friends.usernames)))
Some might say it ain't so pretty, some might say it's pretty simply because it includes no SQL. Anyhow it most certainly could be prettier and there's a gem for it too: MetaWhere
For more info see this railscast: http://railscasts.com/episodes/215-advanced-queries-in-rails-3
and MetaWhere site: http://metautonomo.us/projects/metawhere/
UPDATE: Later Ryan Bates has made another railscast about metawhere and metasearch: http://railscasts.com/episodes/251-metawhere-metasearch
Later though Metawhere (and search) have become more or less legacy gems. I.e. they don't even work with Rails 3.1. The author felt they (Metawhere and search) needed drastic rewrite. So much that he actually went for a new gem all together. The successor of Metawhere is Squeel. Read more about the authors announcement here:
http://erniemiller.org/2011/08/31/rails-3-1-and-the-future-of-metawhere-and-metasearch/
and check out the project home page:
http://erniemiller.org/projects/squeel/
"Metasearch 2.0" is called Ransack and you can read something about it from here:
http://erniemiller.org/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/
Alternatively, you could use Squeel. To my eyes, it is simpler. You can accomplish both the IN (>>) and OR (|) operations using the following syntax:
Question.where{(:user_id >> my{friends.id}) | (:target >> my{friends.usernames})}
I generally wrap my conditions in (...) to ensure the appropriate order of operation - both the INs happen before the OR.
The my{...} block executes methods from the self context as defined before the Squeel call - in this case Question. Inside of the Squeel block, self refers to a Squeel object and not the Question object (see the Squeel Readme for more). You get around this by using the my{...} wrapper to restore the original context.
raw SQL
SELECT *
FROM table
WHERE user_id in (LIST OF friend.ids) OR target in (LIST OF friends.usernames)
with each list comma separate. I don't know the Rails ActiveRecord stuff that well. For AND you would just put a comma between those two conditions, but idk about OR