[Bio] / Sprout / ERDBQuery.pm Repository:
ViewVC logotype

View of /Sprout/ERDBQuery.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (download) (as text) (annotate)
Mon Mar 2 22:17:49 2009 UTC (10 years, 7 months ago) by parrello
Branch: MAIN
CVS Tags: rast_rel_2009_03_26
Changes since 1.2: +65 -13 lines
Refactored field and objecy name parsing.

package ERDBQuery;

    use strict;
    use DBKernel;
    use ERDBObject;
    use DBI;
    use Tracer;

=head1 Entity-Relationship Database Package Query Iterator

=head2 Introduction

This package defines the Iteration object for an Entity-Relationship Database. The iteration object
represents a filtered SELECT statement against an Entity-Relationship Database, and provides
methods for getting the appropriate records.

There are two common ways an iteration object can be created. An I<entity iterator> is created when the
client asks for objects of a given entity type. A I<relationship iterator> is created when the
client asks for objects across a relationship starting from a specific entity instance. The
entity iterator returns a single object at each position; the relationship iterator returns two
objects at each position-- one for the target entity, and one for the relationship instance
that connects it to the source entity.

For example, a client could ask for all B<Feature> instances that are marked active. This would
return an entity iterator. Each position in the iteration would consist of a single
B<Feature> instance. From a specific B<Feature> instance, the client could decide to cross the
B<IsLocatedIn> relationship to get all the B<Contig> instances which contain residues that
participate in the feature. This would return a relationship iterator. Each position in the
iterator would contain a single B<IsLocatedIn> instance and a single B<Contig> instance.

At each point in the result set, the iterator returns a B<ERDBObject>. The ERDBObject allows the
client to access the fields of the current entity or relationship instance.

It is also possible to ask for many different objects in a single iterator by chaining long
sequences of entities together by relationships. This is discussed in the documentation for the
B<ERDB> object's C<Get> method.

Finally, objects of this type should never by created directly. Instead, they are created
by the aforementioned C<Get> method and the B<ERDBObject>'s C<Cross> method.

=head2 Public Methods

=head3 Fetch

    my $dbObject = $dbQuery->Fetch();

Retrieve a record from this query. The record returned will be a B<ERDBObject>, which
may represent a single entity instance or a list of entity instances joined by relationships.
The first time this method is called it will return the first result from query. After that it
will continue sequentially. It will return an undefined value if we've reached the end of the
result set.

=cut

sub Fetch {
    # Get the parameters;
    my ($self) = @_;
    # Declare the return variable.
    my $retVal;
    # Fetch the next row in the query result set.
    my $sth = $self->{_sth};
    my @row = $sth->fetchrow;
    # Check to see if we got any results.
    if (@row == 0) {
        # Here we have no result. If we're at the end of the result set, this is okay, because
        # we'll be returning an undefined value in $retVal. If an error occurred, we need to abort.
        if ($sth->err) {
            Confess("FETCH error: " . $sth->err);
        } else {
            # Trace the number of results returned.
            Trace("$self->{_results} rows processed by query.") if T(SQL => 4);
        }
    } else {
        # Here we have a result, so we need to turn it into an instance object.
        $retVal = ERDBObject->_new($self, @row);
        $self->{_results}++;
    }
    # Return the result.
    return $retVal;
}

=head3 DefaultObjectName

    my $objectName = $query->DefaultObjectName();

Return the name of this query's default entity or relationship.

=cut

sub DefaultObjectName {
    # Get the parameters.
    my ($self) = @_;
    # Get the relation map.
    my $map = $self->{_objectNames};
    # Get the default object alias. This is always the first alias in the
    # relation map.
    my $retVal = $map->[0][0];
    # Return the result.
    return $retVal;
}


=head3 AnalyzeFieldName

    my ($objectName, $fieldName, $type) = $query->AnalyzeFieldName($name);

Analyze a field name (such as might be found in a [[ErdbPm#GetAll]]
parameter list) and return the real name of the relevant entity or
relationship, the field name itself, and the associated type object
(which will be a subclass of [[ERDBTypePm]].

=over 4

=item name

Field name to examine, in the standard field name format used by [[ErdbPm]].

=item RETURN

Returns a 3-tuple containing the name of the object containing the field, the
base field name, and a type object describing the field's type.

=back

=cut

sub AnalyzeFieldName {
    # Get the parameters.
    my ($self, $name) = @_;
    # Attempt to find the field's data.
    my ($objectName, $fieldName, $type) = $self->CheckFieldName($name);
    # Process errors.
    if (! defined $objectName) {
        Confess("Field identifier \"$name\" has an invalid format.");
    } elsif (! defined $fieldName) {
        Confess("Object name \"$objectName\" not found in query.");
    } elsif (! defined $type) {
        Confess("Field name \"$fieldName\" not found in \"$objectName\".");
    }
    # Return the results.
    return ($objectName, $fieldName, $type);
}


=head3 CheckFieldName

    my ($objectName, $fieldName, $type) = $query->CheckFieldName($name);

Analyze a field name (such as might be found in a [[ErdbPm#GetAll]]
parameter list) and return the real name of the relevant entity or
relationship, the field name itself, and the associated type object
(which will be a subclass of [[ERDBTypePm]]. Unlink L</AnalyzeFIeldName>,
this method always returns results. If the field name is invalid, one
or more of the three results will be undefined.

=over 4

=item name

Field name to examine, in the standard field name format used by [[ErdbPm]].

=item RETURN

Returns a 3-tuple containing the name of the object containing the field, the
base field name, and a type object describing the field's type. If the field
descriptor is invalid, the returned object name will be undefined. If the object
name is invalid, the returned field name will be undefined, and if the field
name is invalid, the returned type will be undefined.

=back

=cut

sub CheckFieldName {
    # Get the parameters.
    my ($self, $name) = @_;
    # Declare the return variables.
    my ($objectName, $fieldName, $type);
    # Get the relation map.
    my $map = $self->{_objectNames};
    # Get the default object alias. This is always the first alias in the
    # relation map.
    my $defaultName = $map->[0][0];
    # Parse the field name.
    my ($alias, $fieldThing) = ERDB::ParseFieldName($name, $defaultName);
    # Only proceed if we could successfully parse the field. If we couldn't,
    # everything will be going back undefined.
    if (defined $alias) {
        # Find the alias in the relation map.
        my ($aliasTuple) = grep { $_->[0] eq $alias } @$map;
        if (! defined $aliasTuple) {
            # We have a bad object name, so the object name is all
            # we return.
            $objectName = $alias;
        } else {
            # Get the real object name.
            $objectName = $aliasTuple->[1];
            # Now it's safe to return the field name.
            $fieldName = $fieldThing;
            # Ask the database for the field descriptor. If the field name is
            # invalid, this will throw an error.
            my $fieldData = $self->{_db}->_CheckField($objectName, $fieldName);
            # Only proceed if the field exists.
            if (defined $fieldData) {
                # Extract the field type.
                my $typeName = $fieldData->{type};
                # Get the corresponding type object.
                $type = ERDB::GetDataTypes()->{$typeName};
            }
        }
    }
    # Return the results.
    return ($objectName, $fieldName, $type);
}


=head2 Internal Methods

=head3 _new

    my $query = ERDBQuery->new($database, $sth, $relationMap, $searchObject);

Create a new query object.

=over 4

=item database

ERDB object for the relevant database.

=item sth

Statement handle for the SELECT clause generated by the query.

=item relationMap

Reference to a list of 2-tuples. Each tuple consists of an object name as used
in the query followed by the actual name of that object. This enables the
B<ERDBObject> to determine the order of the tables in the query and which object
name belongs to each mapped object name. Most of the time these two values are
the same; however, if a relation occurs twice in the query, the relation name in
the field list and WHERE clause will use a mapped name (generally the actual
relation name with a numeric suffix) that does not match the actual relation
name.

=item searchObject (optional)

If specified, then the query is a full-text search, and the first field will be a
relevance indicator for the named table.

=back

=cut

sub _new {
    # Get the parameters.
    my ($database, $sth, $relationMap, $searchObject) = @_;
    # Create this object.
    my $self = { _db => $database, _sth => $sth, _objectNames => $relationMap,
                 _fullText => $searchObject, _results => 0 };
    # Bless and return it.
    bless $self;
    return $self;
}

1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3