OntologyTermAdaptor.pm 31.6 KB
Newer Older
1 2
=head1 LICENSE

3
Copyright [1999-2013] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
4

5 6 7 8 9 10 11 12 13 14 15 16 17
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=cut
18 19 20 21 22


=head1 CONTACT

  Please email comments or questions to the public Ensembl
Magali Ruffier's avatar
Magali Ruffier committed
23
  developers list at <http://lists.ensembl.org/mailman/listinfo/dev>.
24 25

  Questions may also be sent to the Ensembl help desk at
Magali Ruffier's avatar
Magali Ruffier committed
26
  <http://www.ensembl.org/Help/Contact>.
27 28 29 30 31 32 33 34 35

=cut

=head1 NAME

Bio::EnsEMBL::DBSQL::OntologyTermAdaptor

=head1 SYNOPSIS

36 37
  my $goa =
    $registry->get_adaptor( 'Multi', 'Ontology', 'OntologyTerm' );
38

39
  my $term = $goa->fetch_by_accession('GO:0010885');
40

41 42
  my @children    = @{ $goa->fetch_all_by_parent_term($term) };
  my @descendants = @{ $goa->fetch_all_by_ancestor_term($term) };
43

44 45
  my @parents   = @{ $goa->fetch_all_by_child_term($term) };
  my @ancestors = @{ $goa->fetch_all_by_descendant_term($term) };
46

47
  my %ancestor_chart = %{ $goa->_fetch_ancestor_chart($term) };
48

49 50
=head1 DESCRIPTION

51 52
An abstract adaptor class for fetching ontology
terms, creates Bio::EnsEMBL::OntologyTerm objects.
53 54 55 56 57 58 59 60 61 62 63 64

=head1 METHODS

=cut

package Bio::EnsEMBL::DBSQL::OntologyTermAdaptor;

use strict;
use warnings;

use DBI qw( :sql_types );

65
use Bio::EnsEMBL::Utils::Exception qw( throw );
66
use Bio::EnsEMBL::Utils::Scalar qw( assert_ref );
67

68 69
use Bio::EnsEMBL::OntologyTerm;

70
use base qw( Bio::EnsEMBL::DBSQL::BaseAdaptor );
71

72 73
=head2 fetch_all_by_name

74 75
  Arg [1]       : String, name of term, or SQL pattern
  Arg [2]       : (optional) String, name of ontology
Andy Yates's avatar
Andy Yates committed
76
  Arg [3]       : (optional) Boolean, search through obsolete terms as well
77

78
  Description   : Fetches ontology term(s) given a name, a synonym, or a
79
                  SQL pattern like "%splice_site%"
80 81 82

  Example       :

83 84
    my ($term) =
      @{ $ot_adaptor->fetch_by_name( 'DNA_binding_site', 'SO' ) };
85

86 87 88
    # Will find terms in both SO and GO:
    my @terms = @{ $ot_adaptor->fetch_by_name('%splice_site%') };

89
  Return type   : listref of Bio::EnsEMBL::OntologyTerm
90 91 92 93

=cut

sub fetch_all_by_name {
94
  my ( $this, $pattern, $ontology, $include_obsolete ) = @_;
95 96

  my $statement = q(
97 98
SELECT DISTINCT
        term.term_id,
99 100 101 102
        term.accession,
        term.name,
        term.definition,
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
103
        term.is_root,
104
        term.is_obsolete,
105
        ontology.name,
106
        ontology.namespace
107 108
FROM    ontology
  JOIN  term USING (ontology_id)
109
  LEFT JOIN  synonym USING (term_id)
110
WHERE   ( term.name LIKE ? OR synonym.name LIKE ? ));
111 112

  if ( defined($ontology) ) {
113
    $statement .= " AND ontology.name = ?";
114
  }
115
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;
116 117

  my $sth = $this->prepare($statement);
118 119 120 121
  $sth->bind_param( 1, $pattern, SQL_VARCHAR );
  $sth->bind_param( 2, $pattern, SQL_VARCHAR );

  if ( defined($ontology) ) {
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
122
    $sth->bind_param( 3, $ontology, SQL_VARCHAR );
123
  }
124 125 126

  $sth->execute();

127
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $namespace );
128
  $sth->bind_columns(
129
     \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace ) );
130 131 132

  my @terms;

133
  while ( $sth->fetch() ) {
134 135 136 137
    $subsets ||= '';

    push @terms,
      Bio::EnsEMBL::OntologyTerm->new(
138 139 140
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
141
                               '-is_root'     => $is_root,
142
                               '-is_obsolete' => $is_obsolete,
143 144 145 146 147
                               '-ontology'    => $ontology,
                               '-namespace'   => $namespace,
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
Magali Ruffier's avatar
Magali Ruffier committed
148
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
149 150
    );

151 152 153
  }

  return \@terms;
154
} ## end sub fetch_all_by_name
155 156


157 158 159
=head2 fetch_by_accession

  Arg [1]       : String
Andy Yates's avatar
Andy Yates committed
160
  Arg [2]       : (optional) Boolean, search through obsolete terms as well
161 162 163 164 165 166 167 168 169 170 171 172

  Description   : Fetches an ontology term given an accession.

  Example       :

    my $term = $ot_adaptor->fetch_by_accession('GO:0030326');

  Return type   : Bio::EnsEMBL::OntologyTerm

=cut

sub fetch_by_accession {
173
  my ( $this, $accession, $include_obsolete ) = @_;
174 175 176

  my $statement = q(
SELECT  term.term_id,
177
        term.accession,
178 179
        term.name,
        term.definition,
180
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
181
        term.is_root,
182
        term.is_obsolete,
183
        ontology.name,
184
        ontology.namespace
185 186 187
FROM    ontology
  JOIN  term USING (ontology_id)
WHERE   term.accession = ?);
188

189 190
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;

191
  my $sth = $this->prepare($statement);
192
  $sth->bind_param( 1, $accession, SQL_VARCHAR );
193 194 195

  $sth->execute();

196
  my ( $dbid, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace );
197
  $sth->bind_columns(
198
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace ) );
199 200

  $sth->fetch();
Magali Ruffier's avatar
Magali Ruffier committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
  $sth->finish();

  my $term;
  if (!$dbid) {
    $term = $this->fetch_by_alt_id($accession);
  } else {
    $subsets ||= '';

    $term =
      Bio::EnsEMBL::OntologyTerm->new(
                    '-dbid'       => $dbid,
                    '-adaptor'    => $this,
                    '-accession'  => $accession,
                    '-is_root'    => $is_root,
                    '-is_obsolete'=> $is_obsolete,
                    '-ontology'   => $ontology,
                    '-namespace'  => $namespace,
                    '-subsets'    => [ split( /,/, $subsets ) ],
                    '-name'       => $name,
                    '-definition' => $definition,
                    '-synonyms' => $this->_fetch_synonyms_by_dbID($dbid)
      );
  }

  return $term;
} ## end sub fetch_by_accession


=head2 fetch_by_alt_id

  Arg [1]       : String

  Description   : Fetches an ontology term given an alt_id.

  Example       :

    my $term = $ot_adaptor->fetch_by_alt_id('GO:0019952');

  Return type   : Bio::EnsEMBL::OntologyTerm

=cut

sub fetch_by_alt_id {
  my ( $this, $accession ) = @_;

  my $statement = q(
SELECT  term.term_id,
        alt_id.accession,
        term.name,
        term.definition,
        term.subsets,
        term.is_root,
        term.is_obsolete,
        ontology.name,
        ontology.namespace
FROM    ontology
  JOIN  term USING (ontology_id)
  JOIN  alt_id USING (term_id)
WHERE   alt_id.accession = ?);

  my $sth = $this->prepare($statement);
  $sth->bind_param( 1, $accession, SQL_VARCHAR );

  $sth->execute();

  my ( $dbid, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace );
  $sth->bind_columns(
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace ) );

  $sth->fetch();

272 273
  # early exit in the event of bad $accession
  unless ($dbid) {return;}
274

Magali Ruffier's avatar
Magali Ruffier committed
275
  $subsets ||= '';
276 277
  my $term =
    Bio::EnsEMBL::OntologyTerm->new(
278 279 280 281 282 283 284 285 286 287
                    '-dbid'       => $dbid,
                    '-adaptor'    => $this,
                    '-accession'  => $accession,
                    '-ontology'   => $ontology,
                    '-namespace'  => $namespace,
                    '-subsets'    => [ split( /,/, $subsets ) ],
                    '-name'       => $name,
                    '-definition' => $definition,
                    '-synonyms' => $this->_fetch_synonyms_by_dbID($dbid)
    );
288 289 290
  $sth->finish();

  return $term;
Magali Ruffier's avatar
Magali Ruffier committed
291 292 293
} ## end sub fetch_by_alt_id


294

295
=head2 fetch_all_by_parent_term
296 297 298 299 300 301 302

  Arg [1]       : Bio::EnsEMBL::OntologyTerm
                  The term whose children terms should be fetched.

  Description   : Given a parent ontology term, returns a list of
                  its immediate children terms.

303 304 305 306
  Example       :

    my @children =
      @{ $ot_adaptor->fetch_all_by_parent_term($term) };
307 308 309 310 311

  Return type   : listref of Bio::EnsEMBL::OntologyTerm

=cut

312
sub fetch_all_by_parent_term {
313
  my ( $this, $term, $ontology, $include_obsolete ) = @_;
314

315
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
316

317 318 319 320 321 322 323 324
  my @terms;

  if ( !$term->{'child_terms_fetched'} ) {
    my $statement = q(
SELECT  child_term.term_id,
        child_term.accession,
        child_term.name,
        child_term.definition,
325
        child_term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
326
        child_term.is_root,
327
        child_term.is_obsolete,
328
        rt.name
329 330 331
FROM    term child_term
  JOIN  relation ON (relation.child_term_id = child_term.term_id)
  JOIN  relation_type rt USING (relation_type_id)
332 333 334
  JOIN  ontology ON (ontology.ontology_id = relation.ontology_id)
WHERE   relation.parent_term_id = ?
   AND  ontology.name = ?);
335

336 337
    $statement .= " AND child_term.is_obsolete = 0" unless $include_obsolete;

338
    my $sth = $this->prepare($statement);
339
    $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
340

341 342 343 344 345 346
    if (!defined $ontology) {
      $ontology = $term->{'ontology'};
    }
    $sth->bind_param( 2, $ontology, SQL_VARCHAR );


347 348
    $sth->execute();

349
    my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $relation );
350
    $sth->bind_columns(
351
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $relation ) );
352 353

    while ( $sth->fetch() ) {
354 355
      $subsets ||= '';

356 357
      my $child_term =
        Bio::EnsEMBL::OntologyTerm->new(
358 359 360
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
361
                               '-is_root'     => $is_root,
362 363 364 365 366 367 368
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition, 
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
369
      );
370 371 372 373 374 375 376 377 378 379 380 381 382

      push( @terms,                              $child_term );
      push( @{ $term->{'children'}{$relation} }, $child_term );
    }

    $term->{'child_terms_fetched'} = 1;
  } else {
    foreach my $relation ( values( %{ $term->{'children'} } ) ) {
      push( @terms, @{$relation} );
    }
  }

  return \@terms;
383
} ## end sub fetch_all_by_parent_term
384

385
=head2 fetch_all_by_ancestor_term
386 387 388 389 390 391

  Arg [1]       : Bio::EnsEMBL::OntologyTerm
                  The term whose descendant terms should be fetched.

  Description   : Given a parent ontology term, returns a list of
                  all its descendant terms, down to and including
392 393
                  any leaf terms.  Relations of the type 'is_a' and
                  'part_of' are followed.
394

395 396 397 398
  Example       :

    my @descendants =
      @{ $ot_adaptor->fetch_all_by_ancestor_term($term) };
399 400 401 402 403

  Return type   : listref of Bio::EnsEMBL::OntologyTerm

=cut

404
sub fetch_all_by_ancestor_term {
405
  my ( $this, $term, $ontology ) = @_;
406

407
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
408

409 410 411 412 413
  my $statement = q(
SELECT DISTINCT
        child_term.term_id,
        child_term.accession,
        child_term.name,
414
        child_term.definition,
Magali Ruffier's avatar
Magali Ruffier committed
415
        child_term.subsets,
416 417
        child_term.is_root,
        child_term.is_obsolete
418 419
FROM    term child_term
  JOIN  closure ON (closure.child_term_id = child_term.term_id)
420
  JOIN  ontology ON (closure.ontology_id = ontology.ontology_id)
421
WHERE   closure.parent_term_id = ?
422
  AND   closure.distance > 0
423 424
  AND   closure.ontology_id = child_term.ontology_id
  AND   ontology.name = ?
425 426
ORDER BY closure.distance, child_term.accession);

427
  my $sth = $this->prepare($statement);
428
  $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
429 430 431 432
  if (!defined $ontology) {
    $ontology = $term->{'ontology'};
  }
  $sth->bind_param( 2, $ontology, SQL_VARCHAR );
433 434
  $sth->execute();

435
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete );
436
  $sth->bind_columns(
437
                 \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete ) );
438 439

  my @terms;
440

441
  while ( $sth->fetch() ) {
442 443
    $subsets ||= '';

444 445
    push( @terms,
          Bio::EnsEMBL::OntologyTerm->new(
446 447 448
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
449
                               '-is_root'     => $is_root,
450 451 452 453 454 455 456
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
457
    ) );
458 459 460
  }

  return \@terms;
461
} ## end sub fetch_all_by_ancestor_term
462

463
=head2 fetch_all_by_child_term
464 465 466 467 468 469 470

  Arg [1]       : Bio::EnsEMBL::OntologyTerm
                  The term whose parent terms should be fetched.

  Description   : Given a child ontology term, returns a list of
                  its immediate parent terms.

471 472 473
  Example       :

    my @parents = @{ $ot_adaptor->fetch_all_by_child_term($term) };
474 475 476 477 478

  Return type   : listref of Bio::EnsEMBL::OntologyTerm

=cut

479
sub fetch_all_by_child_term {
480
  my ( $this, $term, $ontology ) = @_;
481

482
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
483

484 485 486 487 488 489 490 491
  my @terms;

  if ( !$term->{'parent_terms_fetched'} ) {
    my $statement = q(
SELECT  parent_term.term_id,
        parent_term.accession,
        parent_term.name,
        parent_term.definition,
492
        parent_term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
493
        parent_term.is_root,
494
        parent_term.is_obsolete,
495
        rt.name
496 497 498
FROM    term parent_term
  JOIN  relation ON (relation.parent_term_id = parent_term.term_id)
  JOIN  relation_type rt USING (relation_type_id)
499 500 501
  JOIN  ontology ON (ontology.ontology_id = relation.ontology_id)
WHERE   relation.child_term_id = ?
   AND  ontology.name = ?);
502

503
    my $sth = $this->prepare($statement);
504
    $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
505

506 507 508 509 510
    if (!defined $ontology) {
      $ontology = $term->{'ontology'};
    }
    $sth->bind_param( 2, $ontology, SQL_VARCHAR );

511 512
    $sth->execute();

513
    my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $relation );
514
    $sth->bind_columns(
515
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $relation ) );
516 517

    while ( $sth->fetch() ) {
518 519
      $subsets ||= '';

520 521
      my $parent_term =
        Bio::EnsEMBL::OntologyTerm->new(
522 523 524
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
525
                               '-is_root'     => $is_root,
526 527 528 529 530 531 532
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
533
      );
534 535 536 537 538 539 540 541 542 543 544 545 546

      push( @terms,                             $parent_term );
      push( @{ $term->{'parents'}{$relation} }, $parent_term );
    }

    $term->{'parent_terms_fetched'} = 1;
  } else {
    foreach my $relation ( values( %{ $term->{'parents'} } ) ) {
      push( @terms, @{$relation} );
    }
  }

  return \@terms;
547
} ## end sub fetch_all_by_child_term
548

549
=head2 fetch_all_by_descendant_term
550 551 552 553

  Arg [1]       : Bio::EnsEMBL::OntologyTerm
                  The term whose ancestor terms should be fetched.

554 555
  Arg [2]       : (optional) String
                  The subset within the ontolgy to which the query
556 557 558
                  should be restricted.  The subset may be specified as
                  a SQL pattern, e.g., "%goslim%" (but "goslim%" might
                  not do what you expect), or as a specific subset name,
559
                  e.g., "goslim_generic".
560 561 562

  Arg [3]       : (optional) Boolean
                  If true (non-zero), only return the closest
563 564 565
                  term(s).  If this argument is true, and the
                  previous argument is left undefined, this method
                  will return the parent(s) of the given term.
566 567 568 569 570
  
  Arg [4]       : (optional) Boolean
                  If true we will allow the retrieval of terms whose distance
                  to the current term is 0. If false then we will only return
                  those which are above the current term in the ontology
571 572 573 574 575 576 577 578

  Description   : Given a child ontology term, returns a list of
                  all its ancestor terms, up to and including any
                  root term.  Relations of the type 'is_a' and
                  'part_of' are followed.  Optionally, only terms in
                  a given subset of the ontology may be returned,
                  and additionally one may ask to only get the
                  closest term(s) to the given child term.
579

580 581 582 583
  Example       :

    my @ancestors =
      @{ $ot_adaptor->fetch_all_by_descendant_term($term) };
584 585 586 587 588

  Return type   : listref of Bio::EnsEMBL::OntologyTerm

=cut

589
sub fetch_all_by_descendant_term {
590
  my ( $this, $term, $subset, $closest_only, $allow_zero_distance, $ontology ) = @_;
591

592
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
593

594 595
  $closest_only ||= 0;

596 597 598 599 600
  my $statement = q(
SELECT DISTINCT
        parent_term.term_id,
        parent_term.accession,
        parent_term.name,
601
        parent_term.definition,
602
        parent_term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
603
        parent_term.is_root,
604
        parent_term.is_obsolete,
605
        closure.distance
606 607
FROM    term parent_term
  JOIN  closure ON (closure.parent_term_id = parent_term.term_id)
608
  JOIN  ontology ON (closure.ontology_id = ontology.ontology_id)
609
WHERE   closure.child_term_id = ?
610 611 612
  AND   closure.distance > ?
  AND   closure.ontology_id = parent_term.ontology_id
  AND   ontology.name = ?);
613 614

  if ( defined($subset) ) {
615 616
    if ( index( $subset, '%' ) != -1 ) {
      $statement .= q(
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
617
  AND   parent_term.subsets LIKE ?);
618 619 620 621
    } else {
      $statement .= q(
  AND   FIND_IN_SET(?, parent_term.subsets) > 0);
    }
622 623 624
  }

  $statement .= q(
625 626
ORDER BY closure.distance, parent_term.accession);

627
  my $sth = $this->prepare($statement);
628
  $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
629 630
  my $query_distance = ($allow_zero_distance) ? -1 : 0;
  $sth->bind_param( 2, $query_distance, SQL_INTEGER );
631 632 633 634
  if (!defined $ontology) {
    $ontology = $term->{'ontology'};
  }
  $sth->bind_param( 3, $ontology, SQL_VARCHAR );
635

636
  if ( defined($subset) ) {
637
    $sth->bind_param( 4, $subset, SQL_VARCHAR );
638 639
  }

640 641
  $sth->execute();

642
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $distance );
643
  $sth->bind_columns(
644
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $distance ) );
645 646

  my @terms;
647
  my $min_distance;
648

649
  while ( $sth->fetch() ) {
650
    $subsets ||= '';
651 652 653
    $min_distance ||= $distance;

    if ( !$closest_only || $distance == $min_distance ) {
654 655
      push( @terms,
            Bio::EnsEMBL::OntologyTerm->new(
656 657 658
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
659
                               '-is_root'     => $is_root,
660 661 662 663 664 665 666
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
667
    ) );
668
    } else {
669
      $sth->finish();
670
      last;
671
    }
672 673 674
  }

  return \@terms;
675
} ## end sub fetch_all_by_descendant_term
676

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
sub _fetch_synonyms_by_dbID {
  my ( $this, $dbID ) = @_;

  my $statement = q(
SELECT  synonym.name
FROM    synonym
WHERE   synonym.term_id = ?);

  my $sth = $this->prepare($statement);
  $sth->bind_param( 1, $dbID, SQL_INTEGER );

  $sth->execute();

  my $synonym;
  $sth->bind_col( 1, \$synonym );

693
  my @synonyms;
694 695 696 697 698 699 700 701 702
  while ( $sth->fetch() ) {
    push( @synonyms, $synonym );
  }

  return \@synonyms;
}



703
=head2 _fetch_ancestor_chart
704 705 706 707 708 709

  Arg [1]       : Bio::EnsEMBL::OntologyTerm
                  The term whose ancestor terms should be fetched.

  Description   : Given a child ontology term, returns a hash
                  structure containing its ancestor terms, up to and
710 711
                  including any root term.  Relations of the type
                  'is_a' and 'part_of' are included.
712

713 714 715
  Example       :

    my %chart = %{ $ot_adaptor->_fetch_ancestor_chart($term) };
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731

  Return type   : A reference to a hash structure like this:

    {
      'GO:XXXXXXX' => {
        'term' =>           # ref to Bio::EnsEMBL::OntologyTerm object
        'is_a'    => [...], # listref of Bio::EnsEMBL::OntologyTerm
        'part_of' => [...], # listref of Bio::EnsEMBL::OntologyTerm
      },
      'GO:YYYYYYY' => {
        # Similarly for all ancestors,
        # and including the query term itself.
      }
    }

=cut
732

733
sub _fetch_ancestor_chart {
734
  my ( $this, $term, $ontology ) = @_;
735

736
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
737

738
  my $statement = q(
739 740
SELECT  subparent_term.term_id,
        parent_term.term_id,
741
        relation_type.name
742 743 744
FROM    closure
  JOIN  relation
    ON (relation.parent_term_id = closure.parent_term_id
745 746
      AND relation.child_term_id = closure.subparent_term_id
      AND closure.ontology_id = relation.ontology_id)
747 748 749 750
  JOIN  relation_type USING (relation_type_id)
  JOIN  term subparent_term
    ON (subparent_term.term_id = closure.subparent_term_id)
  JOIN  term parent_term ON (parent_term.term_id = closure.parent_term_id)
751
  JOIN  ontology ON (ontology.ontology_id = closure.ontology_id)
752
WHERE   closure.child_term_id = ?
753
   AND  ontology.name = ?
754 755 756 757
ORDER BY closure.distance);

  my $sth = $this->prepare($statement);
  $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
758 759 760 761
  if (!defined $ontology) {
    $ontology = $term->{'ontology'};
  }
  $sth->bind_param( 2, $ontology, SQL_VARCHAR );
762 763 764 765 766 767

  $sth->execute();

  my ( $subparent_id, $parent_id, $relation );
  $sth->bind_columns( \( $subparent_id, $parent_id, $relation ) );

768 769 770
  my %id_chart;
  my %acc_chart;

771
  while ( $sth->fetch() ) {
772 773 774 775 776 777
    if ( !exists( $id_chart{$parent_id} ) ) {
      $id_chart{$parent_id} = {};
    }
    push( @{ $id_chart{$subparent_id}{$relation} }, $parent_id );
  }

778
  my @terms = @{ $this->fetch_all_by_dbID_list( [ keys(%id_chart) ] ) };
779 780 781 782 783 784 785 786 787 788 789 790 791 792

  foreach my $term (@terms) {
    $id_chart{ $term->dbID() }{'term'}       = $term;
    $acc_chart{ $term->accession() }{'term'} = $term;
  }

  foreach my $term (@terms) {
    my $accession = $term->accession();
    my $dbID      = $term->dbID();

    foreach my $relation ( keys( %{ $id_chart{$dbID} } ) ) {
      if ( $relation eq 'term' ) { next }

      foreach my $id ( @{ $id_chart{$dbID}{$relation} } ) {
793 794
        push( @{ $acc_chart{$accession}{$relation} },
              $id_chart{$id}{'term'} );
795
      }
796 797 798
    }
  }

799
  return \%acc_chart;
800
} ## end sub _fetch_ancestor_chart
801

Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
802 803 804 805 806
#-----------------------------------------------------------------------
# Useful public methods that implement functionality not properly
# provided by the parent class Bio::EnsEMBL::DBSQL::BaseAdaptor.

sub fetch_by_dbID {
807
  my ( $this, $dbid, $include_obsolete ) = @_;
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
808 809

  my $statement = q(
810 811
SELECT  term.term_id,
        term.accession,
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
812 813
        term.name,
        term.definition,
814
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
815
        term.is_root,
816
        term.is_obsolete,
817
        ontology.name,
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
818
        ontology.namespace
819 820 821
FROM    ontology
  JOIN  term USING (ontology_id)
WHERE   term.term_id = ?);
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
822

823 824
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;

825
  my $sth = $this->prepare($statement);
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
826 827 828 829
  $sth->bind_param( 1, $dbid, SQL_INTEGER );

  $sth->execute();

830
  my ( $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology,
831
       $namespace );
832
  $sth->bind_columns(
833
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace
834
      ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
835 836

  $sth->fetch();
837 838 839
  
  unless ($accession) {return;}
  
840
  $subsets ||= '';
841 842 843

  my $term =
    Bio::EnsEMBL::OntologyTerm->new(
844 845 846
                    '-dbid'        => $dbid,
                    '-adaptor'     => $this,
                    '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
847
                    '-is_root'     => $is_root,
848 849 850 851 852 853 854
                    '-is_obsolete' => $is_obsolete,
                    '-ontology'    => $ontology,
                    '-namespace'   => $namespace,
                    '-subsets'     => [ split( /,/, $subsets ) ],
                    '-name'        => $name,
                    '-definition'  => $definition,
                    '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
855
    );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
856 857 858 859 860
  $sth->finish();

  return $term;
} ## end sub fetch_by_dbID

861
sub fetch_all_by_dbID_list {
862
  my ( $this, $dbids, $include_obsolete ) = @_;
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
863 864 865 866 867 868 869 870

  if ( !@{$dbids} ) { return [] }

  my $stmt = q(
SELECT  term.term_id,
        term.accession,
        term.name,
        term.definition,
871
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
872
        term.is_root,
873
        term.is_obsolete,
874
        ontology.name,
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
875
        ontology.namespace
876 877 878
FROM    ontology
  JOIN  term USING (ontology_id)
WHERE   term.term_id IN (%s));
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
879 880 881

  my $statement = sprintf(
    $stmt,
882 883 884 885 886
    join(
      ',',
      map {
        $this->dbc()->db_handle()->quote( $_, SQL_INTEGER )
        } @{$dbids} ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
887

888 889
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;

890
  my $sth = $this->prepare($statement);
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
891 892 893

  $sth->execute();

894
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology,
895 896
       $namespace );
  $sth->bind_columns( \( $dbid,    $accession, $name, $definition,
897
                         $subsets, $is_root, $is_obsolete, $ontology,  $namespace ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
898 899

  my @terms;