If you haven't already,look into using Projections (not supported in 1.0.4 I think but is in 1.2) and Subqueries as part of your Criteria (or DetachedCriteria) usage.
I won't show all the classes in this query, but it should be obvious how the first three DetachedCriteria are used as subqueries (and only the necessary columns are brought in to filter the results) with the final DetachedCriteria using those subqueries to find the results.
Code:
DetachedCriteria selfCrit = DetachedCriteria.For(typeof(Contact), "self")
.SetProjection(Projections.Property("Id"))
.Add(Expression.And(Property.ForName("Id").Eq(adot_Main.Id),
Property.ForName("Id").EqProperty("agency.Id")));
DetachedCriteria parentCrit = DetachedCriteria.For(typeof(Contact), "parentCon")
.SetProjection(Projections.Property("Id"))
.Add(Property.ForName("Id").Eq(adot_Main.Id))
.CreateCriteria("Parent", "parent")
.SetProjection(Projections.Property("Id"))
.Add(Property.ForName("Id").EqProperty("agency.Id"));
DetachedCriteria associationCrit = DetachedCriteria.For(typeof(Contact), "assCon")
.SetProjection(Projections.Property("Id"))
.Add(Property.ForName("Id").Eq(adot_Main.Id))
.CreateCriteria("Associations", "ass")
.SetProjection(Projections.Property("Id"))
.Add(Property.ForName("Id").EqProperty("agency.Id"));
DetachedCriteria specByContactCrit =
DetachedCriteria.For(typeof(SieveAnalysisDataSpecification), "spec")
.Add(Expression.Eq("MaterialCode", pb))
.CreateCriteria("Agency", "agency")
.Add(Expression.Disjunction().Add(
Subqueries.Exists(selfCrit)).Add(Subqueries.Exists(parentCrit)).Add(
Subqueries.Exists(associationCrit)));