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

3
  Copyright (c) 1999-2013 The European Bioinformatics Institute and
4 5 6 7 8 9 10 11 12 13
  Genome Research Limited.  All rights reserved.

  This software is distributed under a modified Apache license.
  For license details, please see

    http://www.ensembl.org/info/about/code_licence.html

=head1 CONTACT

  Please email comments or questions to the public Ensembl
14
  developers list at <dev@ensembl.org>.
15 16 17 18 19 20 21 22 23 24 25 26

  Questions may also be sent to the Ensembl help desk at
  <helpdesk@ensembl.org>.

=cut

=head1 NAME

Bio::EnsEMBL::DBSQL::OntologyTermAdaptor

=head1 SYNOPSIS

27 28
  my $goa =
    $registry->get_adaptor( 'Multi', 'Ontology', 'OntologyTerm' );
29

30
  my $term = $goa->fetch_by_accession('GO:0010885');
31

32 33
  my @children    = @{ $goa->fetch_all_by_parent_term($term) };
  my @descendants = @{ $goa->fetch_all_by_ancestor_term($term) };
34

35 36
  my @parents   = @{ $goa->fetch_all_by_child_term($term) };
  my @ancestors = @{ $goa->fetch_all_by_descendant_term($term) };
37

38
  my %ancestor_chart = %{ $goa->_fetch_ancestor_chart($term) };
39

40 41
=head1 DESCRIPTION

42 43
An abstract adaptor class for fetching ontology
terms, creates Bio::EnsEMBL::OntologyTerm objects.
44 45 46 47 48 49 50 51 52 53 54 55

=head1 METHODS

=cut

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

use strict;
use warnings;

use DBI qw( :sql_types );

56
use Bio::EnsEMBL::Utils::Exception qw( throw );
57
use Bio::EnsEMBL::Utils::Scalar qw( assert_ref );
58

59 60
use Bio::EnsEMBL::OntologyTerm;

61
use base qw( Bio::EnsEMBL::DBSQL::BaseAdaptor );
62

63 64
=head2 fetch_all_by_name

65 66
  Arg [1]       : String, name of term, or SQL pattern
  Arg [2]       : (optional) String, name of ontology
67

68
  Description   : Fetches ontology term(s) given a name, a synonym, or a
69
                  SQL pattern like "%splice_site%"
70 71 72

  Example       :

73 74
    my ($term) =
      @{ $ot_adaptor->fetch_by_name( 'DNA_binding_site', 'SO' ) };
75

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

79
  Return type   : listref of Bio::EnsEMBL::OntologyTerm
80 81 82 83

=cut

sub fetch_all_by_name {
84
  my ( $this, $pattern, $ontology, $include_obsolete ) = @_;
85 86

  my $statement = q(
87 88
SELECT DISTINCT
        term.term_id,
89 90 91 92
        term.accession,
        term.name,
        term.definition,
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
93
        term.is_root,
94
        term.is_obsolete,
95
        ontology.name,
96
        ontology.namespace
97 98
FROM    ontology
  JOIN  term USING (ontology_id)
99
  LEFT JOIN  synonym USING (term_id)
100
WHERE   ( term.name LIKE ? OR synonym.name LIKE ? ));
101 102

  if ( defined($ontology) ) {
103
    $statement .= " AND ontology.name = ?";
104
  }
105
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;
106 107

  my $sth = $this->prepare($statement);
108 109 110 111
  $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
112
    $sth->bind_param( 3, $ontology, SQL_VARCHAR );
113
  }
114 115 116

  $sth->execute();

117
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $namespace );
118
  $sth->bind_columns(
119
     \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace ) );
120 121 122

  my @terms;

123
  while ( $sth->fetch() ) {
124 125 126 127
    $subsets ||= '';

    push @terms,
      Bio::EnsEMBL::OntologyTerm->new(
128 129 130
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
131
                               '-is_root'     => $is_root,
132
                               '-is_obsolete' => $is_obsolete,
133 134 135 136 137
                               '-ontology'    => $ontology,
                               '-namespace'   => $namespace,
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
Magali Ruffier's avatar
Magali Ruffier committed
138
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
139 140
    );

141 142 143
  }

  return \@terms;
144
} ## end sub fetch_all_by_name
145 146


147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
=head2 fetch_by_accession

  Arg [1]       : String

  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 {
162
  my ( $this, $accession, $include_obsolete ) = @_;
163 164 165

  my $statement = q(
SELECT  term.term_id,
166
        term.accession,
167 168
        term.name,
        term.definition,
169
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
170
        term.is_root,
171
        term.is_obsolete,
172
        ontology.name,
173
        ontology.namespace
174 175 176
FROM    ontology
  JOIN  term USING (ontology_id)
WHERE   term.accession = ?);
177

178 179
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;

180
  my $sth = $this->prepare($statement);
181
  $sth->bind_param( 1, $accession, SQL_VARCHAR );
182 183 184

  $sth->execute();

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

  $sth->fetch();
190 191 192 193
  
  # early exit in the event of bad $accession
  unless ($dbid) {return;}
  
194
  $subsets ||= '';
195 196 197

  my $term =
    Bio::EnsEMBL::OntologyTerm->new(
198 199 200
                    '-dbid'       => $dbid,
                    '-adaptor'    => $this,
                    '-accession'  => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
201
                    '-is_root'    => $is_root,
202
                    '-is_obsolete'=> $is_obsolete,
203 204 205 206 207 208 209
                    '-ontology'   => $ontology,
                    '-namespace'  => $namespace,
                    '-subsets'    => [ split( /,/, $subsets ) ],
                    '-name'       => $name,
                    '-definition' => $definition,
                    '-synonyms' => $this->_fetch_synonyms_by_dbID($dbid)
    );
210 211 212 213 214
  $sth->finish();

  return $term;
} ## end sub fetch_by_accession

215
=head2 fetch_all_by_parent_term
216 217 218 219 220 221 222

  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.

223 224 225 226
  Example       :

    my @children =
      @{ $ot_adaptor->fetch_all_by_parent_term($term) };
227 228 229 230 231

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

=cut

232
sub fetch_all_by_parent_term {
233
  my ( $this, $term, $ontology, $include_obsolete ) = @_;
234

235
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
236

237 238 239 240 241 242 243 244
  my @terms;

  if ( !$term->{'child_terms_fetched'} ) {
    my $statement = q(
SELECT  child_term.term_id,
        child_term.accession,
        child_term.name,
        child_term.definition,
245
        child_term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
246
        child_term.is_root,
247
        child_term.is_obsolete,
248
        rt.name
249 250 251
FROM    term child_term
  JOIN  relation ON (relation.child_term_id = child_term.term_id)
  JOIN  relation_type rt USING (relation_type_id)
252 253 254
  JOIN  ontology ON (ontology.ontology_id = relation.ontology_id)
WHERE   relation.parent_term_id = ?
   AND  ontology.name = ?);
255

256 257
    $statement .= " AND child_term.is_obsolete = 0" unless $include_obsolete;

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

261 262 263 264 265 266
    if (!defined $ontology) {
      $ontology = $term->{'ontology'};
    }
    $sth->bind_param( 2, $ontology, SQL_VARCHAR );


267 268
    $sth->execute();

269
    my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $relation );
270
    $sth->bind_columns(
271
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $relation ) );
272 273

    while ( $sth->fetch() ) {
274 275
      $subsets ||= '';

276 277
      my $child_term =
        Bio::EnsEMBL::OntologyTerm->new(
278 279 280
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
281
                               '-is_root'     => $is_root,
282 283 284 285 286 287 288
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition, 
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
289
      );
290 291 292 293 294 295 296 297 298 299 300 301 302

      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;
303
} ## end sub fetch_all_by_parent_term
304

305
=head2 fetch_all_by_ancestor_term
306 307 308 309 310 311

  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
312 313
                  any leaf terms.  Relations of the type 'is_a' and
                  'part_of' are followed.
314

315 316 317 318
  Example       :

    my @descendants =
      @{ $ot_adaptor->fetch_all_by_ancestor_term($term) };
319 320 321 322 323

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

=cut

324
sub fetch_all_by_ancestor_term {
325
  my ( $this, $term, $ontology ) = @_;
326

327
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
328

329 330 331 332 333
  my $statement = q(
SELECT DISTINCT
        child_term.term_id,
        child_term.accession,
        child_term.name,
334
        child_term.definition,
Magali Ruffier's avatar
Magali Ruffier committed
335
        child_term.subsets,
336 337
        child_term.is_root,
        child_term.is_obsolete
338 339
FROM    term child_term
  JOIN  closure ON (closure.child_term_id = child_term.term_id)
340
  JOIN  ontology ON (closure.ontology_id = ontology.ontology_id)
341
WHERE   closure.parent_term_id = ?
342
  AND   closure.distance > 0
343 344
  AND   closure.ontology_id = child_term.ontology_id
  AND   ontology.name = ?
345 346
ORDER BY closure.distance, child_term.accession);

347
  my $sth = $this->prepare($statement);
348
  $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
349 350 351 352
  if (!defined $ontology) {
    $ontology = $term->{'ontology'};
  }
  $sth->bind_param( 2, $ontology, SQL_VARCHAR );
353 354
  $sth->execute();

355
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete );
356
  $sth->bind_columns(
357
                 \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete ) );
358 359

  my @terms;
360

361
  while ( $sth->fetch() ) {
362 363
    $subsets ||= '';

364 365
    push( @terms,
          Bio::EnsEMBL::OntologyTerm->new(
366 367 368
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
369
                               '-is_root'     => $is_root,
370 371 372 373 374 375 376
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
377
    ) );
378 379 380
  }

  return \@terms;
381
} ## end sub fetch_all_by_ancestor_term
382

383
=head2 fetch_all_by_child_term
384 385 386 387 388 389 390

  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.

391 392 393
  Example       :

    my @parents = @{ $ot_adaptor->fetch_all_by_child_term($term) };
394 395 396 397 398

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

=cut

399
sub fetch_all_by_child_term {
400
  my ( $this, $term, $ontology ) = @_;
401

402
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
403

404 405 406 407 408 409 410 411
  my @terms;

  if ( !$term->{'parent_terms_fetched'} ) {
    my $statement = q(
SELECT  parent_term.term_id,
        parent_term.accession,
        parent_term.name,
        parent_term.definition,
412
        parent_term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
413
        parent_term.is_root,
414
        parent_term.is_obsolete,
415
        rt.name
416 417 418
FROM    term parent_term
  JOIN  relation ON (relation.parent_term_id = parent_term.term_id)
  JOIN  relation_type rt USING (relation_type_id)
419 420 421
  JOIN  ontology ON (ontology.ontology_id = relation.ontology_id)
WHERE   relation.child_term_id = ?
   AND  ontology.name = ?);
422

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

426 427 428 429 430
    if (!defined $ontology) {
      $ontology = $term->{'ontology'};
    }
    $sth->bind_param( 2, $ontology, SQL_VARCHAR );

431 432
    $sth->execute();

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

    while ( $sth->fetch() ) {
438 439
      $subsets ||= '';

440 441
      my $parent_term =
        Bio::EnsEMBL::OntologyTerm->new(
442 443 444
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
445
                               '-is_root'     => $is_root,
446 447 448 449 450 451 452
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
453
      );
454 455 456 457 458 459 460 461 462 463 464 465 466

      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;
467
} ## end sub fetch_all_by_child_term
468

469
=head2 fetch_all_by_descendant_term
470 471 472 473

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

474 475
  Arg [2]       : (optional) String
                  The subset within the ontolgy to which the query
476 477 478
                  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,
479
                  e.g., "goslim_generic".
480 481 482

  Arg [3]       : (optional) Boolean
                  If true (non-zero), only return the closest
483 484 485
                  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.
486 487 488 489 490
  
  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
491 492 493 494 495 496 497 498

  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.
499

500 501 502 503
  Example       :

    my @ancestors =
      @{ $ot_adaptor->fetch_all_by_descendant_term($term) };
504 505 506 507 508

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

=cut

509
sub fetch_all_by_descendant_term {
510
  my ( $this, $term, $subset, $closest_only, $allow_zero_distance, $ontology ) = @_;
511

512
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
513

514 515
  $closest_only ||= 0;

516 517 518 519 520
  my $statement = q(
SELECT DISTINCT
        parent_term.term_id,
        parent_term.accession,
        parent_term.name,
521
        parent_term.definition,
522
        parent_term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
523
        parent_term.is_root,
524
        parent_term.is_obsolete,
525
        closure.distance
526 527
FROM    term parent_term
  JOIN  closure ON (closure.parent_term_id = parent_term.term_id)
528
  JOIN  ontology ON (closure.ontology_id = ontology.ontology_id)
529
WHERE   closure.child_term_id = ?
530 531 532
  AND   closure.distance > ?
  AND   closure.ontology_id = parent_term.ontology_id
  AND   ontology.name = ?);
533 534

  if ( defined($subset) ) {
535 536
    if ( index( $subset, '%' ) != -1 ) {
      $statement .= q(
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
537
  AND   parent_term.subsets LIKE ?);
538 539 540 541
    } else {
      $statement .= q(
  AND   FIND_IN_SET(?, parent_term.subsets) > 0);
    }
542 543 544
  }

  $statement .= q(
545 546
ORDER BY closure.distance, parent_term.accession);

547
  my $sth = $this->prepare($statement);
548
  $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
549 550
  my $query_distance = ($allow_zero_distance) ? -1 : 0;
  $sth->bind_param( 2, $query_distance, SQL_INTEGER );
551 552 553 554
  if (!defined $ontology) {
    $ontology = $term->{'ontology'};
  }
  $sth->bind_param( 3, $ontology, SQL_VARCHAR );
555

556
  if ( defined($subset) ) {
557
    $sth->bind_param( 4, $subset, SQL_VARCHAR );
558 559
  }

560 561
  $sth->execute();

562
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $distance );
563
  $sth->bind_columns(
564
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $distance ) );
565 566

  my @terms;
567
  my $min_distance;
568

569
  while ( $sth->fetch() ) {
570
    $subsets ||= '';
571 572 573
    $min_distance ||= $distance;

    if ( !$closest_only || $distance == $min_distance ) {
574 575
      push( @terms,
            Bio::EnsEMBL::OntologyTerm->new(
576 577 578
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
579
                               '-is_root'     => $is_root,
580 581 582 583 584 585 586
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $term->{'ontology'},
                               '-namespace'   => $term->{'namespace'},
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
587
    ) );
588
    } else {
589
      $sth->finish();
590
      last;
591
    }
592 593 594
  }

  return \@terms;
595
} ## end sub fetch_all_by_descendant_term
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
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 );

613
  my @synonyms;
614 615 616 617 618 619 620 621 622
  while ( $sth->fetch() ) {
    push( @synonyms, $synonym );
  }

  return \@synonyms;
}



623
=head2 _fetch_ancestor_chart
624 625 626 627 628 629

  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
630 631
                  including any root term.  Relations of the type
                  'is_a' and 'part_of' are included.
632

633 634 635
  Example       :

    my %chart = %{ $ot_adaptor->_fetch_ancestor_chart($term) };
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651

  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
652

653
sub _fetch_ancestor_chart {
654
  my ( $this, $term, $ontology ) = @_;
655

656
  assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
657

658
  my $statement = q(
659 660
SELECT  subparent_term.term_id,
        parent_term.term_id,
661
        relation_type.name
662 663 664
FROM    closure
  JOIN  relation
    ON (relation.parent_term_id = closure.parent_term_id
665 666
      AND relation.child_term_id = closure.subparent_term_id
      AND closure.ontology_id = relation.ontology_id)
667 668 669 670
  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)
671
  JOIN  ontology ON (ontology.ontology_id = closure.ontology_id)
672
WHERE   closure.child_term_id = ?
673
   AND  ontology.name = ?
674 675 676 677
ORDER BY closure.distance);

  my $sth = $this->prepare($statement);
  $sth->bind_param( 1, $term->dbID(), SQL_INTEGER );
678 679 680 681
  if (!defined $ontology) {
    $ontology = $term->{'ontology'};
  }
  $sth->bind_param( 2, $ontology, SQL_VARCHAR );
682 683 684 685 686 687

  $sth->execute();

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

688 689 690
  my %id_chart;
  my %acc_chart;

691
  while ( $sth->fetch() ) {
692 693 694 695 696 697
    if ( !exists( $id_chart{$parent_id} ) ) {
      $id_chart{$parent_id} = {};
    }
    push( @{ $id_chart{$subparent_id}{$relation} }, $parent_id );
  }

698
  my @terms = @{ $this->fetch_all_by_dbID_list( [ keys(%id_chart) ] ) };
699 700 701 702 703 704 705 706 707 708 709 710 711 712

  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} } ) {
713 714
        push( @{ $acc_chart{$accession}{$relation} },
              $id_chart{$id}{'term'} );
715
      }
716 717 718
    }
  }

719
  return \%acc_chart;
720
} ## end sub _fetch_ancestor_chart
721

Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
722 723 724 725 726
#-----------------------------------------------------------------------
# Useful public methods that implement functionality not properly
# provided by the parent class Bio::EnsEMBL::DBSQL::BaseAdaptor.

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

  my $statement = q(
730 731
SELECT  term.term_id,
        term.accession,
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
732 733
        term.name,
        term.definition,
734
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
735
        term.is_root,
736
        term.is_obsolete,
737
        ontology.name,
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
738
        ontology.namespace
739 740 741
FROM    ontology
  JOIN  term USING (ontology_id)
WHERE   term.term_id = ?);
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
742

743 744
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;

745
  my $sth = $this->prepare($statement);
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
746 747 748 749
  $sth->bind_param( 1, $dbid, SQL_INTEGER );

  $sth->execute();

750
  my ( $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology,
751
       $namespace );
752
  $sth->bind_columns(
753
      \( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology, $namespace
754
      ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
755 756

  $sth->fetch();
757 758 759
  
  unless ($accession) {return;}
  
760
  $subsets ||= '';
761 762 763

  my $term =
    Bio::EnsEMBL::OntologyTerm->new(
764 765 766
                    '-dbid'        => $dbid,
                    '-adaptor'     => $this,
                    '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
767
                    '-is_root'     => $is_root,
768 769 770 771 772 773 774
                    '-is_obsolete' => $is_obsolete,
                    '-ontology'    => $ontology,
                    '-namespace'   => $namespace,
                    '-subsets'     => [ split( /,/, $subsets ) ],
                    '-name'        => $name,
                    '-definition'  => $definition,
                    '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
775
    );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
776 777 778 779 780
  $sth->finish();

  return $term;
} ## end sub fetch_by_dbID

781
sub fetch_all_by_dbID_list {
782
  my ( $this, $dbids, $include_obsolete ) = @_;
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
783 784 785 786 787 788 789 790

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

  my $stmt = q(
SELECT  term.term_id,
        term.accession,
        term.name,
        term.definition,
791
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
792
        term.is_root,
793
        term.is_obsolete,
794
        ontology.name,
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
795
        ontology.namespace
796 797 798
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
799 800 801

  my $statement = sprintf(
    $stmt,
802 803 804 805 806
    join(
      ',',
      map {
        $this->dbc()->db_handle()->quote( $_, SQL_INTEGER )
        } @{$dbids} ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
807

808 809
  $statement .= " AND term.is_obsolete = 0" unless $include_obsolete;

810
  my $sth = $this->prepare($statement);
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
811 812 813

  $sth->execute();

814
  my ( $dbid, $accession, $name, $definition, $subsets, $is_root, $is_obsolete, $ontology,
815 816
       $namespace );
  $sth->bind_columns( \( $dbid,    $accession, $name, $definition,
817
                         $subsets, $is_root, $is_obsolete, $ontology,  $namespace ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
818 819

  my @terms;
820

Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
821
  while ( $sth->fetch() ) {
822 823
    $subsets ||= '';

824 825
    push( @terms,
          Bio::EnsEMBL::OntologyTerm->new(
826 827 828
                               '-dbid'        => $dbid,
                               '-adaptor'     => $this,
                               '-accession'   => $accession,
Magali Ruffier's avatar
Magali Ruffier committed
829
                               '-is_root'     => $is_root,
830 831 832 833 834 835 836
                               '-is_obsolete' => $is_obsolete,
                               '-ontology'    => $ontology,
                               '-namespace'   => $namespace,
                               '-subsets'     => [ split( /,/, $subsets ) ],
                               '-name'        => $name,
                               '-definition'  => $definition,
                               '-synonyms'    => $this->_fetch_synonyms_by_dbID($dbid)
837
    ) );
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
838 839 840
  }

  return \@terms;
841
} ## end sub fetch_all_by_dbID_list
Andreas Kusalananda Kähäri's avatar
Andreas Kusalananda Kähäri committed
842

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871

=head2 fetch_all_roots

  Arg [1]       : (optional) String, name of ontology

  Description   : Fetches all roots for all ontologies
                  Optionally, can be restricted to a given ontology

  Example       :

    my ($terms) =
      @{ $ot_adaptor->fetch_all_roots( 'SO' ) };

    # Will find terms in EFO, SO and GO:
    my @terms = @{ $ot_adaptor->fetch_all_roots() };

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

=cut

sub fetch_all_roots {
  my ($this, $ontology_name) = @_;

  my $statement = q(
SELECT  term.term_id,
        term.accession,
        term.name,
        term.definition,
        term.subsets,
Magali Ruffier's avatar
Magali Ruffier committed
872
        term.is_root,
873
        term.is_obsolete,