History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: NEO-66
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: Erik Doernenburg
Reporter: Romano Zabini
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Neo

PathQualifier on self referencing table creates wrong SQL

Created: 28/Feb/07 04:49 AM   Updated: 28/Feb/07 04:49 AM
Component/s: Core
Affects Version/s: 1.4.2
Fix Version/s: None

Time Tracking:
Not Specified


 Description  « Hide

I have modeled a parent-child hierarchy by means of a self referencing table, as stated in docs (http://docs.codehaus.org/display/NEO/Neo+Newbie+Notes). Now I need to find all the objects whose parent has a certain property, so I'm using a PathQualifier. Referring to the example above, something like this:

Qualifier.Format("ParentCategory.Name={0}", "parentName")

Now when NEO build up the SQL query, the parent column and the child column are inverted, e.g. while I'm expecting something like:

... where ParentId in (select AnalysisCategoryId From AnalysisCategory where Name='parentName')

The generated query reads:

... where AnalysisCategoryId in (select ParentId From AnalysisCategory where Name='parentName')

I tracked it down to the following lines in GenericSql92Builder.cs (method WritePathQualifier)

if(relInfo.ParentEntity == emap)
{
    newEmap = relInfo.ChildEntity;
	leftColumn = relInfo.ParentKey;
	rightColumn = relInfo.ChildKey;
}
else
{
	newEmap = relInfo.ParentEntity;
	leftColumn = relInfo.ChildKey;
	rightColumn = relInfo.ParentKey;
}

To determine the column names to use on either side of the subquery, the direction of the relationship is inferred by noticing whether we are searching the ParentEntity or the ChildEntity. When ParentEntity and ChildEntity are different, all goes well. But when there is a self referencing table, the information on the direction is lost, because the RelationInfo class doesn't retain this information.

Possible solution
I made the following changes to the NEO code I'm using in my project:

1. Defined a SelfRelationInfo subclass of RelationInfo: this class has a boolean data member "IsParentToChild" which states the direction of the relation.

public class SelfRelationInfo : RelationInfo{
            private bool parentToChild;
            
            public SelfRelationInfo(bool parentToChild, IEntityMap parentEntity, IEntityMap childEntity, string parentKey, string childKey) 
: base(parentEntity, childEntity, parentKey, childKey){
                
                this.parentToChild = parentToChild;
            }
        
            public SelfRelationInfo(bool parentToChild, IEntityMapFactory factory, Type parentType, Type childType, string parentKey, string childKey) 
: base(factory, parentType, childType, parentKey, childKey){
                this.parentToChild = parentToChild;
                
            }
        
            public bool IsParentToChild{
                get{ return parentToChild;}
            }
        }

2. Modified the logic in Sql92Builder as follows:

if( DetermineIfParentToChild(relInfo, emap)) {
	newEmap = relInfo.ChildEntity;
	leftColumn = relInfo.ParentKey;
	rightColumn = relInfo.ChildKey;
}
else{
	newEmap = relInfo.ParentEntity;
	leftColumn = relInfo.ChildKey;
	rightColumn = relInfo.ParentKey;
}

and added the method:

private bool DetermineIfParentToChild(RelationInfo relInfo, IEntityMap emap){
    if(relInfo is SelfRelationInfo)
          return (relInfo as SelfRelationInfo).IsParentToChild;
    else // different tables: infer direction
          return relInfo.ParentEntity == emap;
}

3. Last, we have to create the SelfRelationInfo object instead of the RelationInfo object in the method GetRelationInfos(), generated by the Velocity template NeoSupport.vtl. So in NeoSupport.vtl:

protected override IDictionary GetRelationInfos()
{
    	IDictionary infos = new Hashtable($entity.Relationships.Count);
#foreach($rel in $entity.Relationships)
    #if($rel.LocalEntity.ClassName==$rel.ForeignEntity.ClassName) /*self reference */
        #if($rel.Direction.ToString == "Parent") /* IsParentToChild has to be true */
        		infos.Add("$rel.DotNetName", new SelfRelationInfo(true, Factory, typeof($rel.LocalEntity.ClassName), typeof($rel.ForeignEntity.ClassName), "$rel.LocalKey", "$rel.ForeignKey"));
        #else /* IsParentToChild has to be false */
        		infos.Add("$rel.DotNetName", new SelfRelationInfo(false, Factory, typeof($rel.ForeignEntity.ClassName), typeof($rel.LocalEntity.ClassName), "$rel.ForeignKey", "$rel.LocalKey"));
        #end
    #else /*different tables: no change */
        #if($rel.Direction.ToString == "Parent")
        		infos.Add("$rel.DotNetName", new RelationInfo(Factory, typeof($rel.LocalEntity.ClassName), typeof($rel.ForeignEntity.ClassName), "$rel.LocalKey", "$rel.ForeignKey"));
        #else
        		infos.Add("$rel.DotNetName", new RelationInfo(Factory, typeof($rel.ForeignEntity.ClassName), typeof($rel.LocalEntity.ClassName), "$rel.ForeignKey", "$rel.LocalKey"));
        #end
    #end
#end
	return infos;    	
}


 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
There are no comments yet on this issue.