-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: $30.00 offered for a criteria query solution.
PostPosted: Mon Dec 03, 2007 3:23 pm 
Newbie

Joined: Sat Dec 01, 2007 10:37 am
Posts: 12
[Sorry for double post, but I didn't do it)

I am willing to paypal $30.00 to whoever can come up with a nice simple criteria query in c# or java code block for the following (nhibernate mapping files below)

The spec states the following:

Quote:
· findQualifiers: This collection of findQualifier elements can be used to alter the default behavior of search functionality.


FindQualifiers
Quote:
· orLikeKeys: when a bag container contains multiple keyedReference elements (i.e., categoryBag or identifierBag), any keyedReference filters that come from the same namespace (e.g. have the same tModelRefKey value) are OR’d together rather than AND’d. This allows one to say “any of these four values from this namespace, and any of these two values from this namespace”.

· orAllKeys: this changes the behavior to OR keys rather than AND them. This qualifier negates any AND treatment as well as the effect of orLikeKeys.
· andAllKeys: this changes the behavior to AND keys rather than OR them.


I have a function which takes a name (from tmodel) and an categoryBag which is an array of categories each having the following properties
TmodelKeyRef
KeyName
KeyValue

It also takes a string representing one of the above findQualifiers.

If findQualifier is andAllKeys I need to return any tmodels having ALL of the categories specified

If orAllKeys tmodels having ANY of the categories specified will do

and if orLikeKeys (my biggest headache) then I need to return tmodels having at least one matching category for each unique tmodelKeyRef specified in the categoryBag.

Hope this is easily digestible, but if not I am more than willing to elaborate.


Tmodel.hbm.xml

Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly="Data.Hibernate" namespace="Data.Hibernate.Domain">
  <class name="Tmodel" table="tmodel">
    <id name="TmodelKey" type="string">
      <column name="tmodel_key" length="41" />
      <generator class="uuid.hex">
        <param name="format">P</param>
        <param name="seperator">-</param>
      </generator>
    </id>
    <property name="AuthorizedName" type="string">
      <column name="authorized_name" not-null="true"/>
    </property>
    <property name="PublisherId" type="string">
      <column name="publisher_id" length="20"/>
    </property>
    <property name="Operator" type="string">
      <column name="operator" not-null="true"/>
    </property>
    <property name="Name" type="string">
      <column name="name" not-null="true"/>
    </property>
    <property name="OverviewUrl" type="string">
      <column name="overview_url"/>
    </property>
    <property name="DeletionDate" type="DateTime">
      <column name="deletion_date" />
    </property>
    <property name="LastUpdate" type="DateTime">
      <column name="last_update" not-null="true"/>
    </property>
    <bag name="TmodelCategories" inverse="true" cascade="all-delete-orphan" lazy="true">
      <key>
        <column name="tmodel_key" length="41" not-null="true"/>
      </key>
      <one-to-many class="TmodelCategory" />
    </bag>
    <bag name="TmodelDocDescrs" inverse="true" cascade="all-delete-orphan" lazy="true">
      <key>
        <column name="tmodel_key" length="41" not-null="true"/>
      </key>
      <one-to-many class="TmodelDocDescr" />
    </bag>
    <bag name="TmodelDescrs" inverse="true" cascade="all-delete-orphan" lazy="true">
      <key>
        <column name="tmodel_key" length="41" not-null="true"/>
      </key>
      <one-to-many class="TmodelDescr" />
    </bag>
    <bag name="TmodelIdentifiers" inverse="true" cascade="all-delete-orphan" lazy="true">
      <key>
        <column name="tmodel_key" length="41" not-null="true"/>
      </key>
      <one-to-many class="TmodelIdentifier" />
    </bag>
  </class>
</hibernate-mapping>


TmodelCategories.hbm.xml

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly="Data.Hibernate" namespace="Data.Hibernate.Domain">
    <class name="TmodelCategory" table="tmodel_category">
        <id name="TmodelCategoryId" type="int">
            <column name="tmodel_category_id" />
            <generator class="native" />
        </id>
        <many-to-one name="Tmodel" class="Tmodel" fetch="select">
            <column name="tmodel_key" length="41" not-null="true"/>
        </many-to-one>
        <property name="TmodelKeyRef" type="string">
            <column name="tmodel_key_ref"/>
        </property>
        <property name="KeyName" type="string">
            <column name="key_name"/>
        </property>
        <property name="KeyValue" type="string">
            <column name="key_value" not-null="true"/>
        </property>
    </class>
</hibernate-mapping>


Last edited by jomblo on Tue Dec 04, 2007 12:06 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 03, 2007 10:53 pm 
Newbie

Joined: Mon Dec 03, 2007 10:50 pm
Posts: 6
A couple questions, if you don't mind.

#1: I assume you are OK with C# loops/if, etc, used to create the criteria query?

#2: what database? I'm thinking that a subquery might be the solution for you, but not all databases will support that.

thanks,
--randy


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 03, 2007 11:58 pm 
Newbie

Joined: Sat Dec 01, 2007 10:37 am
Posts: 12
wilbur4321 wrote:
A couple questions, if you don't mind.

#1: I assume you are OK with C# loops/if, etc, used to create the criteria query?

#2: what database? I'm thinking that a subquery might be the solution for you, but not all databases will support that.

thanks,
--randy


Absolutely. I do understand that the criteria would have to be built programmatically - The subselect thing would however be a problem. We are under orders to keep our code DB portable.

Here is what I have come up with so far. It works, but I don't like it - I have to do 1 query per category specified in the search criteria (also, I'm afraid of looking like a dick in the new job I blagged my way into if I commit it :-)


Code:
if (categoryBag != null && categoryBag.keyedReference != null && categoryBag.keyedReference.Length > 0) {

   // Delegate method to fetch tmodel categories on a single set of criteria
   KeyedReferenceQuery fetchCategories = delegate(string tModelKey, string keyName, string keyValue) {

      ICriteria tModelCriteria = tModelDao.NHibernateSession.CreateCriteria(typeof(Tmodel));
      tModelCriteria.AddOrder(nameOrder);
      ICriteria categoryCriteria = tModelCriteria.CreateCriteria("TmodelCategories");               
      
      //  the keyValues are identical
      categoryCriteria.Add(Expression.Eq("KeyValue", keyValue).IgnoreCase());

      //  The tModelKeys refer to the same tModel.
      categoryCriteria.Add(Expression.Eq("TmodelKeyRef", tModelKey).IgnoreCase());

      //  If the tModelKey involved is that of uddi-org:general_keywords, the keyNames are identical
      if (tModelKey.ToUpper().Equals(Core.Datatype.TModel.GENERAL_KEYWORDS_TMODEL_KEY.ToUpper())) {
         categoryCriteria.Add(Expression.Eq("KeyName", keyName).IgnoreCase());
      }
      
      return (ArrayList)categoryCriteria.List();
      
   };

   ListSet categorySet = (ListSet)getQualifiedKeyedReference(categoryBag.keyedReference, "andAllKeys", fetchCategories);

   if (categorySet.Count == 0) {
      return new Core.Datatype.TModel[0];
   }
                              
   //   The name field is optional - if it was not specified, then the targetSet will still
   //   be empty in which case the categorySet should be the base result
   if (targetSet.Count == 0)   {
      targetSet = categorySet;
   } else {
      //   Overall result at this point is the intersection of both name + category                                    
      targetSet = targetSet.Intersect(categorySet);
      if (targetSet.Count == 0) {
         //   there is no intersection - early bath
         return new Core.Datatype.TModel[0];
      }
   }
}



I farmed this out to a seperate method because there are loads of places where the same type of scenario crops up in the spec. for different tables and criteria

Code:
internal ISet getQualifiedKeyedReference(Core.Datatype.KeyedReference[] keyedReferences, string searchQualifier, KeyedReferenceQuery fetchDelegate) {
   Dictionary<string, ISet> orLikeKeysDictionary = new Dictionary<string, ISet>();
   ISet targetSet = new ListSet();

   // Iterate through the KeyedReference array to find entities matching each of those specified           
   foreach (Core.Datatype.KeyedReference keyedReference in keyedReferences) {

      //  Use the passed delegate to perform the actual query
      ArrayList resultList = fetchDelegate(keyedReference.tModelKey, keyedReference.keyName, keyedReference.keyValue);

      if (searchQualifier.Equals("orAllKeys")) {
         //  ----------------------------------------------------------------------------------------
         //  This changes the behavior for tModelBag and categoryBag to OR keys rather than AND them.
         //  This qualifier negates any AND treatment as well as the effect of orLikeKeys. Applies to:
         //  find_binding, find_business, find_service, and find_tModel.
         //  ----------------------------------------------------------------------------------------

         //  Add each result to a single set (because this is orAllKeys we can disregard it if there
         //   are no results
         targetSet.AddAll(resultList);
      } else if (searchQualifier.Equals("orLikeKeys")) {
         //  ----------------------------------------------------------------------------------------
         //  When a bag contains multiple keyedReference elements (i.e., categoryBag or
         //  identifierBag), any keyedReference filters that come from the same namespace (e.g. have
         //  the same tModelKey value) are OR’d together rather than AND’d.  This allows one to say “any
         //  of these four values from this namespace, and any of these two values from this namespace”.
         //  ----------------------------------------------------------------------------------------                 

         //  Create a seperate result set for each unique tModelKey in the bag.
         if (orLikeKeysDictionary.ContainsKey(keyedReference.tModelKey.ToLower())) {
            //  this key has already been met in the bag - Add the result to the
            //  existing result set grouped by the key
            orLikeKeysDictionary[keyedReference.tModelKey.ToLower()].AddAll(new ListSet(resultList));
         } else {
            //  this key has NOT already been met in the bag - create a new keyed set
            //  and add the results to it
            orLikeKeysDictionary.Add(keyedReference.tModelKey.ToLower(), new ListSet(resultList));
         }
      } else if (searchQualifier.Equals("andAllKeys")) {
         //  ----------------------------------------------------------------------------------------
         //  Elements matching all of the keyedReferences passed (logical AND by default).                   
         //  ----------------------------------------------------------------------------------------

         //  if the ANDing then there must be a result and that result must also intersect with all
         //   prior results
         if (resultList.Count > 0) {
            if (targetSet.Count > 0) {
               targetSet = targetSet.Intersect((ISet)new ListSet(resultList));
               if (targetSet.Count < 1) return new ListSet();
            } else {
               targetSet.AddAll(resultList);
            }
         } else {
            return new ListSet();
         }
      }
   }
   //   orLikeKeys - the result is the intersection between each individual keyed set if any set
   //   is empty then as expected, the intersection will have 0 elements
   if (searchQualifier.Equals("orLikeKeys")) {
      foreach (ListSet keyedSet in orLikeKeysDictionary.Values) {
         if (keyedSet.Count > 0) {
            if (targetSet.Count > 0) {
               targetSet = targetSet.Intersect(keyedSet);
               if (targetSet.Count < 1) return new ListSet();
            } else {
               targetSet.AddAll(keyedSet);
            }
         } else {
            return new ListSet();
         }
      }            
   }
   return targetSet;      
}
Code:


Last edited by jomblo on Tue Dec 04, 2007 2:07 am, edited 2 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 12:52 am 
Newbie

Joined: Mon Dec 03, 2007 10:50 pm
Posts: 6
Hrm. There are only two clean ways that I can think of to implement this (conceptually), although I could easily be missing something.

#1: correlated sub-selects (one per each item in the bag) -- but you can't do that due to DB constraints

#2: multiple left joins on the set of categories (again, one per each item in the bag). Unfortunately, the Criteria API does not allow this (see discussion in http://opensource.atlassian.com/project ... se/HHH-879).

Now, HQL should allow #2. Is that an option?

--randy


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 2:04 am 
Newbie

Joined: Sat Dec 01, 2007 10:37 am
Posts: 12
wilbur4321 wrote:
Hrm. There are only two clean ways that I can think of to implement this (conceptually), although I could easily be missing something.

#1: correlated sub-selects (one per each item in the bag) -- but you can't do that due to DB constraints

#2: multiple left joins on the set of categories (again, one per each item in the bag). Unfortunately, the Criteria API does not allow this (see discussion in http://opensource.atlassian.com/project ... se/HHH-879).

Now, HQL should allow #2. Is that an option?

--randy



HQL is definitely an option if it can do the trick - Thanks Randy


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 2:17 am 
Newbie

Joined: Mon Dec 03, 2007 10:50 pm
Posts: 6
OK. I'm thinking on the fly here and haven't tried this of course, but something similar to the following may do it.

select from tmodel tm
join elements(tm.TmodelCategories) j1
join elements(tm.TmodelCategories) j2
where j1.TmodelKeyRef=:tkr1 and j1.KeyName=:kn1 and j1.KeyValue=:kv1
j2.TmodelKeyRef=:tkr2 and j2.KeyName=:kn2 and j2.KeyValue=:kv2

You would repeat the join and the three equals clauses as many times as you have items in your categoryBag, and set the values of the parameters in the query as appropriate (tkr1 being the TmodelKeyRef from the first item in the bag, etc) -- don't be tempted to skip the named parameters, because you open yourself to potential problems.

I didn't fully understand the optional KeyName check in your code; you may need to add/remove that from the HQL dynamically?

This also doesn't cover the three different options; it sounds like you can get the other two easily once you have this one.

Let me know if this helps!
--randy


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 2:18 am 
Newbie

Joined: Sat Dec 01, 2007 10:37 am
Posts: 12
P.S That link you gave has really set my mind at ease that there is no 'easy' way to do this as such. I thought I was missing something simple!

I did try adding multiple associations at the beginning both as example TmodelCategory objects and also as criteria disjunctions. Was really puzzled as to why they wouldn't work and couldn't find anything from searching.

thx.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 2:26 am 
Newbie

Joined: Sat Dec 01, 2007 10:37 am
Posts: 12
wilbur4321 wrote:
OK. I'm thinking on the fly here and haven't tried this of course, but something similar to the following may do it.

select from tmodel tm
join elements(tm.TmodelCategories) j1
join elements(tm.TmodelCategories) j2
where j1.TmodelKeyRef=:tkr1 and j1.KeyName=:kn1 and j1.KeyValue=:kv1
j2.TmodelKeyRef=:tkr2 and j2.KeyName=:kn2 and j2.KeyValue=:kv2

You would repeat the join and the three equals clauses as many times as you have items in your categoryBag, and set the values of the parameters in the query as appropriate (tkr1 being the TmodelKeyRef from the first item in the bag, etc) -- don't be tempted to skip the named parameters, because you open yourself to potential problems.

I didn't fully understand the optional KeyName check in your code; you may need to add/remove that from the HQL dynamically?

This also doesn't cover the three different options; it sounds like you can get the other two easily once you have this one.

Let me know if this helps!
--randy


OK thanks, I'll have a play about with it later today and if it works out, I'll be happy to forward on the bounty - thanks again Randy


-Tom


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 1:00 pm 
Newbie

Joined: Sat Dec 01, 2007 10:37 am
Posts: 12
Randy, you're a star!

Send me an email to mysomicra AT atmail diT c.o.m and post here when you've done it. I'll send you on the dough.

thx -Tom


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 04, 2007 2:08 pm 
Newbie

Joined: Mon Dec 03, 2007 10:50 pm
Posts: 6
Glad it worked for you!

--randy


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.