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

Annotation of /Sprout/ERDBQuery.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13 - (view) (download) (as text)

1 : parrello 1.1 package ERDBQuery;
2 :    
3 :     use strict;
4 :     use DBKernel;
5 :     use ERDBObject;
6 :     use DBI;
7 :     use Tracer;
8 :    
9 :     =head1 Entity-Relationship Database Package Query Iterator
10 :    
11 :     =head2 Introduction
12 :    
13 :     This package defines the Iteration object for an Entity-Relationship Database. The iteration object
14 :     represents a filtered SELECT statement against an Entity-Relationship Database, and provides
15 :     methods for getting the appropriate records.
16 :    
17 :     There are two common ways an iteration object can be created. An I<entity iterator> is created when the
18 :     client asks for objects of a given entity type. A I<relationship iterator> is created when the
19 :     client asks for objects across a relationship starting from a specific entity instance. The
20 :     entity iterator returns a single object at each position; the relationship iterator returns two
21 :     objects at each position-- one for the target entity, and one for the relationship instance
22 :     that connects it to the source entity.
23 :    
24 :     For example, a client could ask for all B<Feature> instances that are marked active. This would
25 :     return an entity iterator. Each position in the iteration would consist of a single
26 :     B<Feature> instance. From a specific B<Feature> instance, the client could decide to cross the
27 :     B<IsLocatedIn> relationship to get all the B<Contig> instances which contain residues that
28 :     participate in the feature. This would return a relationship iterator. Each position in the
29 :     iterator would contain a single B<IsLocatedIn> instance and a single B<Contig> instance.
30 :    
31 :     At each point in the result set, the iterator returns a B<ERDBObject>. The ERDBObject allows the
32 :     client to access the fields of the current entity or relationship instance.
33 :    
34 :     It is also possible to ask for many different objects in a single iterator by chaining long
35 :     sequences of entities together by relationships. This is discussed in the documentation for the
36 :     B<ERDB> object's C<Get> method.
37 :    
38 :     Finally, objects of this type should never by created directly. Instead, they are created
39 :     by the aforementioned C<Get> method and the B<ERDBObject>'s C<Cross> method.
40 :    
41 :     =head2 Public Methods
42 :    
43 :     =head3 Fetch
44 :    
45 :     my $dbObject = $dbQuery->Fetch();
46 :    
47 :     Retrieve a record from this query. The record returned will be a B<ERDBObject>, which
48 :     may represent a single entity instance or a list of entity instances joined by relationships.
49 :     The first time this method is called it will return the first result from query. After that it
50 :     will continue sequentially. It will return an undefined value if we've reached the end of the
51 :     result set.
52 :    
53 :     =cut
54 :    
55 : parrello 1.10 use constant FROMTO => { 'from-link' => 'to-link', 'to-link' => 'from-link' };
56 :    
57 : parrello 1.1 sub Fetch {
58 :     # Get the parameters;
59 :     my ($self) = @_;
60 :     # Declare the return variable.
61 :     my $retVal;
62 : parrello 1.4 # Do we have a statement handle?
63 :     my $sth = $self->{_sth};
64 :     if (! defined $sth) {
65 :     # No, so we have a prepared statement. Start the query.
66 :     $sth = $self->_Run();
67 :     }
68 : parrello 1.10 # Do we have the field information?
69 :     if (! defined $self->{_fieldInfo}) {
70 :     # No. We put together the field information array to help us process
71 :     # the results. Pull out the ERDB object and the relationship map.
72 :     my $erdb = $self->{_db};
73 :     my $relationMap = $self->{_objectNames};
74 :     # Get the metadata.
75 :     my $metadata = $erdb->{_metaData};
76 :     # We put the target object name in here. It's the alias name (position 0)
77 :     # for the first object in the map (also position 0).
78 :     $self->{_targetObject} = $relationMap->[0][0];
79 :     # Loop through the object names, extracting each object's fields. We will
80 :     # strip each field from the value array and add it to the hash table using
81 :     # the field's standard-format name.
82 :     my @field_info;
83 :     for my $mappingTuple (@{$relationMap}) {
84 :     # Get the real object name for this mapped name.
85 :     my ($alias, $objectName, $converseFlag) = @{$mappingTuple};
86 :     # Get the relation descriptor for this object.
87 :     my $relationData = $erdb->FindRelation($objectName);
88 :     # Loop through the field list.
89 :     for my $field (@{$relationData->{Fields}}) {
90 :     # Get the current field's name.
91 :     my $fieldName = $field->{name};
92 :     # If we're converse, swap FROM and TO.
93 :     if ($converseFlag && exists FROMTO->{$fieldName}) {
94 :     $fieldName = FROMTO->{$fieldName};
95 :     }
96 :     # Add the field's data to the field info list.
97 :     my $fieldKey = "$alias($fieldName)";
98 :     push(@field_info, [$fieldName, "$objectName($fieldName)", $fieldKey]);
99 :     }
100 :     }
101 :     # Save the field information array.
102 :     $self->{_fieldInfo} = \@field_info;
103 :     }
104 : parrello 1.1 # Fetch the next row in the query result set.
105 :     my @row = $sth->fetchrow;
106 :     # Check to see if we got any results.
107 :     if (@row == 0) {
108 : parrello 1.12 # Here we have no result. If we're at the end of the result set, this is
109 :     # okay, because we'll be returning an undefined value in $retVal. If an
110 :     # error occurred, we need to abort.
111 : parrello 1.1 if ($sth->err) {
112 : parrello 1.12 # Get the error message from the DBKernel object.
113 :     my $dbh = $self->{_database}->{_dbh};
114 :     my $msg = $dbh->ErrorMessage($sth);
115 :     # Throw an error with it.
116 :     Confess($msg);
117 : parrello 1.1 } else {
118 :     # Trace the number of results returned.
119 : parrello 1.8 Trace("$self->{_results} rows processed by query.") if T(SQL => 4);
120 : parrello 1.1 }
121 :     } else {
122 :     # Here we have a result, so we need to turn it into an instance object.
123 : parrello 1.10 $retVal = ERDBObject->_new($self, @row);
124 :     # Count this result.
125 : parrello 1.1 $self->{_results}++;
126 :     }
127 :     # Return the result.
128 :     return $retVal;
129 :     }
130 :    
131 : parrello 1.2 =head3 DefaultObjectName
132 :    
133 :     my $objectName = $query->DefaultObjectName();
134 :    
135 :     Return the name of this query's default entity or relationship.
136 :    
137 :     =cut
138 :    
139 :     sub DefaultObjectName {
140 :     # Get the parameters.
141 :     my ($self) = @_;
142 :     # Get the relation map.
143 :     my $map = $self->{_objectNames};
144 :     # Get the default object alias. This is always the first alias in the
145 :     # relation map.
146 :     my $retVal = $map->[0][0];
147 :     # Return the result.
148 :     return $retVal;
149 :     }
150 :    
151 :    
152 : parrello 1.1 =head3 AnalyzeFieldName
153 :    
154 :     my ($objectName, $fieldName, $type) = $query->AnalyzeFieldName($name);
155 :    
156 : parrello 1.6 Analyze a field name (such as might be found in a L<ERDB/GetAll>
157 : parrello 1.1 parameter list) and return the real name of the relevant entity or
158 :     relationship, the field name itself, and the associated type object
159 : parrello 1.6 (which will be a subclass of L<ERDBType>).
160 : parrello 1.1
161 :     =over 4
162 :    
163 :     =item name
164 :    
165 : parrello 1.6 Field name to examine, in the standard field name format used by L<ERDB>.
166 : parrello 1.1
167 :     =item RETURN
168 :    
169 :     Returns a 3-tuple containing the name of the object containing the field, the
170 :     base field name, and a type object describing the field's type.
171 :    
172 :     =back
173 :    
174 :     =cut
175 :    
176 :     sub AnalyzeFieldName {
177 :     # Get the parameters.
178 :     my ($self, $name) = @_;
179 : parrello 1.3 # Attempt to find the field's data.
180 :     my ($objectName, $fieldName, $type) = $self->CheckFieldName($name);
181 :     # Process errors.
182 :     if (! defined $objectName) {
183 :     Confess("Field identifier \"$name\" has an invalid format.");
184 :     } elsif (! defined $fieldName) {
185 :     Confess("Object name \"$objectName\" not found in query.");
186 :     } elsif (! defined $type) {
187 :     Confess("Field name \"$fieldName\" not found in \"$objectName\".");
188 :     }
189 :     # Return the results.
190 :     return ($objectName, $fieldName, $type);
191 :     }
192 :    
193 :    
194 :     =head3 CheckFieldName
195 :    
196 :     my ($objectName, $fieldName, $type) = $query->CheckFieldName($name);
197 :    
198 : parrello 1.6 Analyze a field name (such as might be found in a L<ERDB/GetAll>.
199 : parrello 1.3 parameter list) and return the real name of the relevant entity or
200 :     relationship, the field name itself, and the associated type object
201 : parrello 1.6 (which will be a subclass of L<ERDBType>. Unlike L</AnalyzeFieldName>,
202 : parrello 1.3 this method always returns results. If the field name is invalid, one
203 :     or more of the three results will be undefined.
204 :    
205 :     =over 4
206 :    
207 :     =item name
208 :    
209 : parrello 1.6 Field name to examine, in the standard field name format used by L<ERDB>.
210 : parrello 1.3
211 :     =item RETURN
212 :    
213 :     Returns a 3-tuple containing the name of the object containing the field, the
214 :     base field name, and a type object describing the field's type. If the field
215 :     descriptor is invalid, the returned object name will be undefined. If the object
216 :     name is invalid, the returned field name will be undefined, and if the field
217 :     name is invalid, the returned type will be undefined.
218 :    
219 :     =back
220 :    
221 :     =cut
222 :    
223 :     sub CheckFieldName {
224 :     # Get the parameters.
225 :     my ($self, $name) = @_;
226 : parrello 1.1 # Declare the return variables.
227 :     my ($objectName, $fieldName, $type);
228 :     # Get the relation map.
229 :     my $map = $self->{_objectNames};
230 :     # Get the default object alias. This is always the first alias in the
231 :     # relation map.
232 :     my $defaultName = $map->[0][0];
233 :     # Parse the field name.
234 : parrello 1.3 my ($alias, $fieldThing) = ERDB::ParseFieldName($name, $defaultName);
235 :     # Only proceed if we could successfully parse the field. If we couldn't,
236 :     # everything will be going back undefined.
237 :     if (defined $alias) {
238 : parrello 1.1 # Find the alias in the relation map.
239 :     my ($aliasTuple) = grep { $_->[0] eq $alias } @$map;
240 :     if (! defined $aliasTuple) {
241 : parrello 1.3 # We have a bad object name, so the object name is all
242 :     # we return.
243 :     $objectName = $alias;
244 : parrello 1.1 } else {
245 :     # Get the real object name.
246 : parrello 1.3 $objectName = $aliasTuple->[1];
247 :     # Now it's safe to return the field name.
248 :     $fieldName = $fieldThing;
249 : parrello 1.1 # Ask the database for the field descriptor. If the field name is
250 :     # invalid, this will throw an error.
251 : parrello 1.3 my $fieldData = $self->{_db}->_CheckField($objectName, $fieldName);
252 :     # Only proceed if the field exists.
253 :     if (defined $fieldData) {
254 :     # Extract the field type.
255 :     my $typeName = $fieldData->{type};
256 :     # Get the corresponding type object.
257 :     $type = ERDB::GetDataTypes()->{$typeName};
258 :     }
259 : parrello 1.1 }
260 :     }
261 :     # Return the results.
262 :     return ($objectName, $fieldName, $type);
263 :     }
264 :    
265 : parrello 1.13 =head3 GetObjectNames
266 :    
267 :     my $nameHash = $query->GetObjectNames();
268 :    
269 :     Return a hash that maps the name of each object in this query to its
270 :     actual name in the database.
271 :    
272 :     =cut
273 :    
274 :     sub GetObjectNames {
275 :     # Get the parameters.
276 :     my ($self) = @_;
277 :     # Extract the relation map.
278 :     my $relationMap = $self->{_objectNames};
279 :     # Convert it to a hash.
280 :     my %retVal;
281 :     for my $tuple (@$relationMap) {
282 :     my ($label, $object) = @$tuple;
283 :     $retVal{$label} = $object;
284 :     }
285 :     # Return the result.
286 :     return \%retVal;
287 :     }
288 :    
289 : parrello 1.1 =head2 Internal Methods
290 :    
291 :     =head3 _new
292 :    
293 :     my $query = ERDBQuery->new($database, $sth, $relationMap, $searchObject);
294 :    
295 :     Create a new query object.
296 :    
297 :     =over 4
298 :    
299 :     =item database
300 :    
301 :     ERDB object for the relevant database.
302 :    
303 :     =item sth
304 :    
305 :     Statement handle for the SELECT clause generated by the query.
306 :    
307 :     =item relationMap
308 :    
309 :     Reference to a list of 2-tuples. Each tuple consists of an object name as used
310 :     in the query followed by the actual name of that object. This enables the
311 :     B<ERDBObject> to determine the order of the tables in the query and which object
312 :     name belongs to each mapped object name. Most of the time these two values are
313 :     the same; however, if a relation occurs twice in the query, the relation name in
314 :     the field list and WHERE clause will use a mapped name (generally the actual
315 :     relation name with a numeric suffix) that does not match the actual relation
316 :     name.
317 :    
318 :     =item searchObject (optional)
319 :    
320 :     If specified, then the query is a full-text search, and the first field will be a
321 :     relevance indicator for the named table.
322 :    
323 :     =back
324 :    
325 :     =cut
326 :    
327 :     sub _new {
328 :     # Get the parameters.
329 :     my ($database, $sth, $relationMap, $searchObject) = @_;
330 :     # Create this object.
331 :     my $self = { _db => $database, _sth => $sth, _objectNames => $relationMap,
332 : parrello 1.9 _fullText => $searchObject, _results => 0, _parsed => {} };
333 : parrello 1.1 # Bless and return it.
334 :     bless $self;
335 :     return $self;
336 :     }
337 :    
338 : parrello 1.4 =head3 _Prepare
339 :    
340 :     $query->_Prepare($command, $parms);
341 :    
342 :     Cache the SQL command and parameter list for this query. The information
343 :     can be used to run the query at a future point.
344 :    
345 :     =over 4
346 :    
347 :     =item command
348 :    
349 :     SQL command to execute for this query.
350 :    
351 :     =item parms
352 :    
353 :     Parameters to feed to the query.
354 :    
355 :     =back
356 :    
357 :     =cut
358 :    
359 :     sub _Prepare {
360 :     # Get the parameters.
361 :     my ($self, $command, $parms) = @_;
362 :     # Stash the SQL command and the parameters.
363 :     $self->{_sql} = $command;
364 :     $self->{_parms} = $parms;
365 :     }
366 :    
367 :     =head3 _Run
368 :    
369 :     $query->_Run();
370 :    
371 :     Run this query. This method is used the first time Fetch is called
372 :     on a prepared query.
373 :    
374 :     =cut
375 :    
376 :     sub _Run {
377 :     # Get the parameters.
378 :     my ($self) = @_;
379 :     # Declare the return variable.
380 :     my $retVal;
381 :     # Get the database object and the SQL command.
382 :     my $erdb = $self->{_db};
383 :     my $command = $self->{_sql};
384 :     # Insure both are valid.
385 :     if (! defined $erdb) {
386 :     Confess("No database available to run this query.");
387 :     } elsif (! defined $command) {
388 :     Confess("Attempt to get results from an unprepared query.");
389 :     } else {
390 :     # Get the parameters. If there are no parameters, we use an empty list.
391 :     my $parms = $self->{_parms} || [];
392 :     # Create the statement handle and run the query.
393 :     $retVal = $erdb->_GetStatementHandle($command, $parms);
394 :     # Save the statement handle for Fetch to use.
395 :     $self->{_sth} = $retVal;
396 :     }
397 :     # Return it.
398 :     return $retVal;
399 :     }
400 :    
401 :    
402 : parrello 1.1 1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3