Search code examples
c#nhibernatequeryover

NHibernate QueryOver Where Or on property and nested collection


I have classes Foo and Bar

class Foo {
    string Name;
    IList<Bar> Bars;
}

class Bar {
    string Name;
}

I have an input q and I want to write an NHibernate query that returns a list of Foo objects if Foo.Name contains q or any of the Bars objects' Names contain q.

I know how I can make separate queries but I am not sure how to combine it into 1 with the OR operator in between. Basically if a Foo object contains q in its name or in any of its Bars names, I want the Foo object to be in the output.

this.sessionFactory.GetCurrentSession()
    .QueryOver<Foo>()
    .Where(x => x.Name.IsInsensitiveLike(q))
    .List();
this.sessionFactory.GetCurrentSession()
    .QueryOver<Foo>()
    .JoinQueryOver<Bar>(x => x.Bars)
    .Where(x => x.Name.IsInsensitiveLike(q))
    .List();

Solution

  • Let's start with declaring aliases, for later use in queries

    Foo foo = null;
    Bar bar = null;
    

    The first approach now, is to join collection (and get a bit ugly results, with multiplied parent for each matching child)

    .QueryOver<Foo>(() => foo)
    .JoinQueryOver<Bar>(x => x.Occupations, () => bar)
    .Where(x => foo.Name.IsInsensitiveLike(q)
             || bar.Name.IsInsensitiveLike(q))
    

    This will work, but could return some rows repeating parent Foo, in case that there are more matching children Bar...

    Foo1, Bar1
    Foo1, Bar2
    

    To get clean parent result-set only, we need to replace JOIN with subquery

    .QueryOver<Foo>(() => foo)
    .Where(Restrictions
        .Or(
            Restrictions.Where(() => foo.Name.IsInsensitiveLike(q)),
            Subqueries
                .WhereProperty(() => foo.ID)
                    .In(QueryOver.Of<Bar>(() => bar)
                        .Where(() => bar.Name.IsInsensitiveLike(q))
                        .Select(y => bar.Foo.ID)
                    )
        )
    )
    

    That also expects, that child Bar has back reference to Foo... which is absolutely legal and business domain model supporting.