Death to recursion! Re: Child vs parent records

Ed Kleban Ed at Kleban.com
Sun Dec 11 07:30:25 CST 2005




On 11/14/05 3:38 PM, "Ruslan Zasukhin" <sunshine at public.kherson.ua> wrote:

>>> RUSLAN:
>>> P.S. Hmm... May be in fact we can make MORE functions to make things more
>>> clean...
>>> 
>>>     FindLinked( recid, T1, T2 )
>>>     FindLinkedRecursive( recid, T, inRecursionDirection  )

Ed:
No!  Bad idea.


>> ED:
>> An interesting idea, but sheds [no] light on the question I have.

> RUSLAN:
> Hmm, or even better idea!
> It looks more natural as
> 
>   set = TableA.FindLinkedTo( ThisRecID, ByThisLink, InTableB );
> 
> // and for recursive links:
> 
>         TableA.FindChildsOf( ThisRcID, ByThisLink )
>         TableA.FindParentsOf( ThisRcID, ByThisLink )
> 
> Problem is that somebody can use this for non-recursive ...
> May be its possible to prevent this ... It needs to think about this .

Ed:
No!  Bad idea.

====

Sleep is a wonderful thing.  Last night in bed I finally figured out what
the problem is with this whole mess, why the "parent/child" nomenclature has
been so confusing for me, and how this nomenclature ends up taking a very
simple mechanism and makes it confusing -- with no need.

I've never understood why you even needed to make a special case for
handling recursion in the case when TableA = TableB.  And now I'm convinced
that the fact is you do NOT need to talk about recursion at all.  To do so
only makes things unnecessarily confusing for the user.

... or possibly I've got this wrong and I'm still the one who is very
confused.  Let's find out:

=======

Here's the problem:

When I DEFINE a binary link I do so using the following call:

    CreateBinaryLink(
     inName as String,
     inLeftTable as VTable,
     inRightTable as VTable,
     inLeftPower as EVLinkType = kOne,
     inRightPower as EVLinkType = kMany,
     inOnDelete as EVOnDelete = kSetNull,
     inStorageType as EVStorageType = kDefault
     inTemporary as Boolean = false ) as VBinaryLink

This is very good. It is simple. I understand it. I like it.
But note: This call uses the terms "Left", "Right", "One", and "Many".
NOWHERE do I see the terms "Parent" or "Child".

======

Then there is the documentation for how to add a link:

    LinkRecords( inRecID() as Integer )

    Parameter:  inRecID
    Description: The RecID of a record of the left table.

    Establishes a link between records of linked Tables, specified
    as an array of RecID values

    (Valentina 2.0 supports 2-branch links only, so 2 records
    must be specified).

    The array must contains the correct number of values,
    in the order of branches of this link.
    The order of branches corresponds to the order of Tables on
    link creation. 

    Example: 

         dim recs(1) as integer  // allocate array with 2 items.
         // Link record 1 of the left table to record 3 of
         // the right table of the Link.

        recs(0) = 1
        recs(1) = 3
        Link.LinkRecords( recs )

This is less good.  It is simple, but it is confusing.
I read this 5 times and came up with three understandings.
Only the example made it possible for me to understand this.
And a different choice of values in the example would be helpful.

Here is a proposal for what I think it means:

    LinkRecords( inRecIdPair() as Integer )

    where inRecIdPair is an array of two integers containing:
         ( leftTableRecId, rightTableRecId )

    Establishes a link between records of linked tables,
    specified as an ordered array of RecID values.

    (Valentina 2.0 supports 2-branch links only, so 2 records
    must be specified).

    The array must contain only two elements with the recId for
    the leftTable in the first, and the recId for the rightTable
    in the second as defined in the CreateBinaryLink call used
    to define the link.
  
    Example: 

         dim recs(1) as integer  // allocate array with 2 items.
         // Link record 5 of the left table to record 9 of
         // the right table of the Link.

        recs(0) = 5  // LeftTable RecId
        recs(1) = 9  // RightTable RecId
        Link.LinkRecords( recs )

This call also uses the terms "Left" and "Right"
NOWHERE do I see the terms "Parent" or "Child".
 
This is not a horrible interface in terms of efficiency, because I can
simply create an array called recs and repeatedly call LinkRecords with
that same array, just as you show in your example.

But since "Valentina 2.0 supports 2-branch links only", it would be
much more convenient for me, and arguably faster code, if I could
simply call:

    Link.Link2Records( leftTableRecID, rightTableRecID )

The call may not be generalized for future use of longer "recs(N)" arrays,
but it sure would be nice for those of us using this today.

===============

Then comes the access call to use the link:

     FindLinked( 
      inRecID as Integer,
      inTableA as VTable,
      inTableB as VTable,
      inRecursionDirection as EVRecursionDirection = kFromParentToChild )
      as VArraySet 

And all of a sudden I am presented with new concepts I have never before
seen -- AND SHOULD NOT NEED!   Pointing out that when inTableA = inTableB
the relationship happens to be recursive is interesting I suppose, but
neither useful nor helpful.  Requiring the user to now think in new terms
they have never used before, namely "parent" and "child" is both confusing
and unnecessary.  

I would far rather use one of the following alternatives:

    FindLeftLink( rightRecID )
    FindRightLink( leftRecID )

or if necessary:

    FindLinked( inRecID, EVLinkSide )

    where EVLinkSide.kGetLeftLink returns a left link.
    and   EVLinkSide.kGetRightLink returns a right link.

Since inTableA and inTableB have already been established when I defined the
link, why should I have to refer to them here?

On 12/11/05 3:34 AM, "Ruslan Zasukhin" <sunshine at public.kherson.ua> wrote:
>> ED:
>> Woops!  Since this is a M:M link, does "kFromParentToChild" have any meaning
>> here?  
> 
>> How do I signify I want recId's for right links rather than left
>> links for recId = 2 ?

> RUSLAN: 
> Yes still have
> 
> I like this example:
> 
>     Table Person with recursive link HaveBorn
> 
> So we have here in fact link 2 : M which still is M : M.
> Right?
> 
> Now let we take Person with RecId = 1024,
>  Let this will be Ruslan.
> 
>         res = Born.FindLinked( 1024, Person, Person, ???? )
> 
> So whom you want find?
> 
> A) Children of Ruslan:
> 
>     childs = Born.FindLinked( 1024, Person, Person, kFromPArentToChilds)
> 
> b) Children of Ruslan:
> 
>      parents = Born.FindLinked( 1024, Person, Person, kFromChildsToParent)
> 

Based on this response, my guess is that that what you mean by this is the
same as:

     FindLinked( inRecID, inTableA, inTableB, anEVRecursionDirection )

Where:
        EVRecursionDirection.kFromParentToChild means kGetTableALink
           implying that inRecID is aTableBRecId

        EVRecursionDirection.kFromChildToParent means kGetTableBLink
           implying that inRecID is aTableARecId

If you say this is true, then I now understand how FindLinked works and I
will define these constants and ignore all of the discussion of recursion in
the documentation -- which I believe is irrelevant, confusing, and
unnecessary.  

If you say this is not true, then I am still confused.
 
======

Furthermore, if the above is true, then...

======

On 11/15/05 1:41 AM, "Ruslan Zasukhin" <sunshine at public.kherson.ua> wrote:

>> ED:
>> Ok, now we're getting somewhere, and somewhere that makes some good
>> intuitive sense.
>> 
>> Based on the above, would the following statements be true?
>> 
>> S1)  Creating a non-unique ObjectPtr field that contains recID values
>> referring to records in the same table that contains the ObjectPtr field
>> inherently defines a one to many relationship between a record indexed by a
>> specific recID value, and all of the records that contain that recID value
>> in their ObjectPtr field.
> 
> YES. The same correct for 2 different tables.
>  
>> S2)  If the ObjectPtr field referring to recId values in the same table that
>> contains the field is also defined as having unique values, then the
>> relationship established is inherently one to one.
> 
> YES. The same correct for 2 different tables.
>  
>> S3)  A one to many relationship can also be referred to as a "parent/child"
>> relationship in which any one record associated with a given recID value is
>> called the parent of all child records that contain that recID value in
>> their ObjectPtr field.
> 
> YES
>  
>> If these are true, then these are the "missing" implicit information that
>> experienced database users may take for granted, but which novices (or picky
>> readers like myself) would appreciate seeing explicitly stated in the
>> documentation.
> 
> AGREE

DISAGREE!  The documentation should have no need to refer to Parent/Child
relationships in order to understand how to use the FindLinked function.  A
discussion of the recursive nature of relationships when inTableA = inTableB
may be appropriate for a nice side-bar comment, but there is no need for it
the description for defining or using FindLinked and similar functions.
  
>> This is still insufficient however, to explicitly explain what values of
>> inRecursionDirection actually do.  Let me attempt to define those as well:
> 
> Yes, I think it needs add some pictures also

Not really. It just needs a simpler explanation.
  
>> S4) When calling aLink.FindLinked( inRecID, inTableA, inTableB,
>> inRecursionDirection )  with inTableA = inTableB, then a value of
>> inRecursionDirect = kFromParentToChild returns a VArraySet with the recID of
>> zero, one, or many children records having inRecID as the value of their
>> ObjectPtr field; whereas
>  
>> S5) inRecursionDirect = kFromChildToParent returns a VArraySet with at most
>> a single parent record identified by the non-zero value in the ObjectPtr
>> field value of the child record indexed by recID.  If the ObjectPtr field
>> contains zero then the returned VArraySet is empty.
> 
> Right.

Wrong.  The unstated implication of my description above that was in my mind
when I wrote these was that FindLinked was trying to be clever by looking at
the inLeftPower and inRightPower arguments to CreateBinaryLink to determine
whether the Left side was the parent of the Right side or whether the Right
side was the Parent of the left side.

This is why I stated:
 
>>>> ED: 
>>>> Woops!  Since this is a M:M link, does "kFromParentToChild"
>>>> have any meaning here?
 
But your latest answers suggests that FindLinked is not trying to do
anything so clever as this, and that all the EVRecursionDirection
argument serves to do is signify "Find a left link" or "Find a right link".

True?

The unstated implication in many of your responses has been that whenever
you define a relationship as a link from TableA to TableB that the parent
side is always named first and the child side is always named second.

Thus when you say:
 
> RUSLAN
> I like this example:
> 
>     Table Person with recursive link HaveBorn
> 
> So we have here in fact link 2 : M which still is M : M.
> Right?
> 
> Now let we take Person with RecId = 1024,
>  Let this will be Ruslan.
> 
>         res = Born.FindLinked( 1024, Person, Person, ???? )
> 
> So whom you want find?
> 
> A) Children of Ruslan:
> 
>     childs = Born.FindLinked( 1024, Person, Person, kFromPArentToChilds)
> 
> b) Children of Ruslan:
> 
>      parents = Born.FindLinked( 1024, Person, Person, kFromChildsToParent)
> 

This example ONLY works because in your own mind you assumed that "HaveBorn"
(which I would have named "isParentOf") means that when you define the link
you will always specify the parents on the left side of the link and the
children on the right side of the link.  And because you do this it just so
happens that kFromParentToChild and kFromChildToParent work as you intended.

But what if in my mind I choose to implement the "isChildOf" relation
instead?  That would put the children on the left and the parents on the
right.  In that case kFromParentToChild would not return me what I want or
expect.

Nowhere is this dependency made clear in the documentation.  Nor should it
have to be.  If we simply use "Left" and "Right" instead of "Parent" and
"Child" then there is never any ambiguity because we use the same terms for
access as we used for definition.  And there is no "recursion" mess to have
to worry about or explain.
 
> RUSLAN
> Only not complete yet:
> 
> * you talk only about ObjectPtr always.
> 
> * Valentina 2 have also link - FOREIGN KEY.
>     it is similar to ObjectPtr by nature. It is a field.
> 
> * Valentina 2 also have new link -- BbinaryLink. This is not a field at all.
> 
> Methods of Vlink class work for ALL link kinds. So it needs write text in
> general form.

Yes, I understand that much better now.





More information about the Valentina mailing list