NHibernate 3 full-text searching with table-per-subclass setup - nhibernate

I'm using FluentNHibernate and I have just moved to NHibernate 3.0. I have also changed my database schema to a table-per-subclass configuration which I really like. We used full-text search before using MS SQL 2005's built in full-text search which worked fine because all of our content was of one class and one table. Now that our data is spread out among different subclasses/tables with different fields to be indexed on each class we will typically want one search against all subclasses. What is the best way to accomplish this and how would I query it?
We have been using Linq more lately but I would be OK with HQL.

I eventually solved this by chaining by HQL queries like this...
string selectCat = #"from Cat c where freetext((c.Name),:keyword)";
string selectDog = #"from Dog d where freetext((d.Name,d.OwnerName),:keyword)";
var animals = session.CreateQuery(selectCat).SetString("keyword", keyword).List<BaseAnimal>().Concat<BaseAnimal>(session.CreateQuery(selectDog).SetString("keyword", keyword).List<BaseAnimal>()).ToList<BaseAnimal();

Related

Neo4j index for full text search

I am working on neo4j database version 2.0.I have following requirements :
Case 1. I want to fetch all records where name contains some string,for example if i am searching for Neo4j then all records having name Neo4j Data,Neo4j Database,Neo4jDatabase etc. should be returned.
Case 2. When i want to fire field less query,if a set of properties is having matching value then those records should be returned or it may also be global level instead of label level.
Case Sensitivity is also a point.
I have read multiple thing about like,index,full text search,legacy index etc.,so what will be the best fit for my case,or i have to use elastic search etc.
I am using spring-data-neo4j in my application,so provide some configuration for SDN
Annotate your name with #Indexed annotation:
#Indexed(indexName = "whateverIndexName", indexType = IndexType.FULLTEXT)
private String name;
Then query for it following way (example for method in SDN repository, you can use similar anywhere else you use cypher):
#Query("START n=node:whateverIndexName({query}) return n"
Set<Topic> findByName(#Param("query") String query);
Neo4j uses lucene as backend for indexing so the query value must be a valid lucene query, e.g. "name:neo4j" or "name:neo4j*".
There is an article that explains the confusion around various Neo4j indexes http://nigelsmall.com/neo4j/index-confusion.
I don't think you need to be using elastic search-- you can use the legacy indexes or the lucene indexes to do full text searches.
Check out Michael Hunger's blog: jexp.de/blog
thix post specifically: http://jexp.de/blog/2014/03/full-text-indexing-fts-in-neo4j-2-0/

Hibernate and dynamic SQL

do you know anything about Hibernate and its ability to generate dynamic sql queries with HQL?
I you have any links I would appreciate posting, I can't find nothing about it in Hibernate`s documentation.
Best regards
Gabe
//edit
so maybe I will precise what I mean. I am wondering if some HQL code generates SQL queries which uses something like EXECUTE (for postgres)
Not sure what you're aiming for here? HQL in Hibernate will always generate SQL, and you can put your HQL together differently based on input. It will always generate new SQL for each permutation and run. You can make Hibernate precompile/cache queries but that's just a performance optimization and shouldn't be your first concern.
I would also consider looking into the Criteria API which lets you stay a lot more object oriented instead of working with tons of strings.
If you're talking about static queries using dynamic arguments, the syntax is
select f from Foo f where f.bar = ? and f.zim = ?
or, with named parameters
select f from Foo f where f.bar = :bar and f.zim = :zim
If you're talking about completely dynamically created queries based on a set of criteria, then the API to use is... the Criteria API.
Both are largely covered in the Hibernate reference documentation.

Is there a way to combine results from two IQueryable<T> using NHibernate & Linq?

I have two separate queries that both return the same IQueryable, and I'd like to combine them prior to projection. It looks like neither Union or Concat are implemented in Linq to NHibernate? Does anyone know how I might go about achieving this?
It's not possible. You'll have to do it on the client.
Example:
var allItems = queryable1.AsEnumerable().Concat(queryable2)
#Diago Mijelshon gives a good answer, but I would like to add that depending on what you're doing with the data, you may need to first cast it to an array or list so that NHibernate doesn't try to do any funny stuff with your operations.
I've used Entity Framework for years and I'm well familiar with this, and I've only used NHibernate a little bit, but the two tools appear to be similar in this regard.

NHibernate: Lazyload single property

I have currently moved my blogengine over from Linq2Sql to NHIbernate.
I'm stuck at the following performance problem:
I got one table: 'Posts', that has Id, Title, PostContent, DateCreated collumns and so on.
The problem is that, when I'm creating the "Recent posts list", I don't want the whole PostContent.
In Linq2Sql you can set lazy loading on a single property, so it won't be part of the SQL query until you actually ask for the property.
I tried doing this with Fluent NHibernate, by doing this:
Map(x => x.PostContent).LazyLoad();
It didn't work out. Googling around, it seems that NHibernate doesn't support this, so my question is, how can I fix this?
Is it really not possible to lazy load my property without moving the content to a seperate table?
Thanks in advance!
Update: this capability is now available in the NHibernate trunk.
See details on Ayende's blog, where the sample is exactly the scenario you describe here.
Here is how you can achieve what you want (kind of lazy loading but not the same)
var posts = NHibernateSessionManager.Session.CreateQuery("Select p.Id as Id, p.Title as Title, p.DateCreated as DateCreated from Post p")
.SetResultTransformer(NHibernate.Transform.Transformers.AliasToBean(typeof(Post)))
.List<Post>();
What the AliasToBean is intended for is, doing selects for specific columns (usually from more than one entities) and return a strongly typed collection instead of a System.Object[].
Now if you Bean (class) is your Post class then it will popultate to that class ONLY the requested columns and nothing else.
What you will NOT be having though is a collection of NHibernate managed objects. All Posts in the returned lists are detached non controlled objects.
For doing things like "getting all recent posts" without having to get the most "heavyweight" columns of your entity while not having to create other classes to convert the data to, the above solution is kind of perfect.
Check this out for more info:
NHibernate: returning a strongly typed list having applied an aggregate function
This is not possible when PostContent is mapped to the same table, because the performance difference is not significant in 99% of the situations. When you are in the 1%, you can consider using handbuild sql instead of a orm for that case.
Lazy/Eager loading is not possible at all with linq to sql (out of the box) as far as I know.
What you can do create a different class with the data you want to select and just select that data into a new object.

Case-insensitive search using Hibernate

I'm using Hibernate for ORM of my Java app to an Oracle database (not that the database vendor matters, we may switch to another database one day), and I want to retrieve objects from the database according to user-provided strings. For example, when searching for people, if the user is looking for people who live in 'fran', I want to be able to give her people in San Francisco.
SQL is not my strong suit, and I prefer Hibernate's Criteria building code to hard-coded strings as it is. Can anyone point me in the right direction about how to do this in code, and if impossible, how the hard-coded SQL should look like?
Thanks,
Yuval =8-)
For the simple case you describe, look at Restrictions.ilike(), which does a case-insensitive search.
Criteria crit = session.createCriteria(Person.class);
crit.add(Restrictions.ilike('town', '%fran%');
List results = crit.list();
Criteria crit = session.createCriteria(Person.class);
crit.add(Restrictions.ilike('town', 'fran', MatchMode.ANYWHERE);
List results = crit.list();
If you use Spring's HibernateTemplate to interact with Hibernate, here is how you would do a case insensitive search on a user's email address:
getHibernateTemplate().find("from User where upper(email)=?", emailAddr.toUpperCase());
You also do not have to put in the '%' wildcards. You can pass MatchMode (docs for previous releases here) in to tell the search how to behave. START, ANYWHERE, EXACT, and END matches are the options.
The usual approach to ignoring case is to convert both the database values and the input value to upper or lower case - the resultant sql would have something like
select f.name from f where TO_UPPER(f.name) like '%FRAN%'
In hibernate criteria restrictions.like(...).ignoreCase()
I'm more familiar with Nhibernate so the syntax might not be 100% accurate
for some more info see pro hibernate 3 extract and hibernate docs 15.2. Narrowing the result set
This can also be done using the criterion Example, in the org.hibernate.criterion package.
public List findLike(Object entity, MatchMode matchMode) {
Example example = Example.create(entity);
example.enableLike(matchMode);
example.ignoreCase();
return getSession().createCriteria(entity.getClass()).add(
example).list();
}
Just another way that I find useful to accomplish the above.
Since Hibernate 5.2 session.createCriteria is deprecated. Below is solution using JPA 2 CriteriaBuilder. It uses like and upper:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> root = criteria.from(Person.class);
Expression<String> upper = builder.upper(root.get("town"));
criteria.where(builder.like(upper, "%FRAN%"));
session.createQuery(criteria.select(root)).getResultList();
Most default database collations are not case-sensitive, but in the SQL Server world it can be set at the instance, the database, and the column level.
You could look at using Compass a wrapper above lucene.
http://www.compass-project.org/
By adding a few annotations to your domain objects you get achieve this kind of thing.
Compass provides a simple API for working with Lucene. If you know how to use an ORM, then you will feel right at home with Compass with simple operations for save, and delete & query.
From the site itself.
"Building on top of Lucene, Compass simplifies common usage patterns of Lucene such as google-style search, index updates as well as more advanced concepts such as caching and index sharding (sub indexes). Compass also uses built in optimizations for concurrent commits and merges."
I have used this in the past and I find it great.