| 60 | | private final SimpleGdb.QueryLifeCycle qAttribute = new SimpleGdb.QueryLifeCycle( |
| 61 | | "SELECT attrvalue FROM attribute " + |
| 62 | | " WHERE id = ? AND code = ? AND attrname = ?" |
| 63 | | ); |
| 64 | | private final SimpleGdb.QueryLifeCycle qAllAttributes = new SimpleGdb.QueryLifeCycle( |
| 65 | | "SELECT attrname, attrvalue FROM attribute " + |
| 66 | | " WHERE id = ? AND code = ?" |
| 67 | | ); |
| 68 | | private final SimpleGdb.QueryLifeCycle qAttributesSet = new SimpleGdb.QueryLifeCycle( |
| 69 | | "SELECT attrname FROM attribute GROUP BY attrname" |
| 70 | | ); |
| 71 | | private final SimpleGdb.QueryLifeCycle qCrossRefs = new SimpleGdb.QueryLifeCycle ( |
| 72 | | "SELECT dest.idRight, dest.codeRight FROM link AS src JOIN link AS dest " + |
| 73 | | "ON src.idLeft = dest.idLeft and src.codeLeft = dest.codeLeft " + |
| 74 | | "WHERE src.idRight = ? AND src.codeRight = ?" |
| 75 | | ); |
| 76 | | private final SimpleGdb.QueryLifeCycle qCrossRefsWithCode = new SimpleGdb.QueryLifeCycle ( |
| 77 | | "SELECT dest.idRight, dest.codeRight FROM link AS src JOIN link AS dest " + |
| 78 | | "ON src.idLeft = dest.idLeft and src.codeLeft = dest.codeLeft " + |
| 79 | | "WHERE src.idRight = ? AND src.codeRight = ? AND dest.codeRight = ?" |
| 80 | | ); |
| 81 | | private final SimpleGdb.QueryLifeCycle qRefsByAttribute = new SimpleGdb.QueryLifeCycle ( |
| 82 | | "SELECT datanode.id, datanode.code FROM datanode " + |
| 83 | | " LEFT JOIN attribute ON attribute.code = datanode.code AND attribute.id = datanode.id " + |
| 84 | | "WHERE attrName = ? AND attrValue = ?" |
| 85 | | ); |
| 86 | | private final SimpleGdb.QueryLifeCycle qFreeSearch = new SimpleGdb.QueryLifeCycle ( |
| 87 | | "SELECT id, code FROM datanode WHERE " + |
| 88 | | "LOWER(ID) LIKE ?" |
| 89 | | ); |
| 90 | | private final SimpleGdb.QueryLifeCycle qAttributeSearch = new SimpleGdb.QueryLifeCycle ( |
| 91 | | "SELECT id, code, attrvalue FROM attribute WHERE " + |
| 92 | | "attrname = 'Symbol' AND LOWER(attrvalue) LIKE ?" |
| 93 | | ); |
| 94 | | private final SimpleGdb.QueryLifeCycle qIdSearchWithAttributes = new SimpleGdb.QueryLifeCycle ( |
| 95 | | "SELECT id, code, attrvalue FROM attribute WHERE " + |
| 96 | | "attrname = 'Symbol' AND LOWER(ID) LIKE ?" |
| 97 | | ); |
| 98 | | |
| 99 | | /** {@inheritDoc} */ |
| 100 | | public boolean xrefExists(Xref xref) throws IDMapperException |
| 101 | | { |
| 102 | | final QueryLifeCycle pst = qXrefExists; |
| 103 | | try |
| 104 | | { |
| 105 | | pst.init(); |
| 106 | | pst.setString(1, xref.getId()); |
| 107 | | pst.setString(2, xref.getDataSource().getSystemCode()); |
| 108 | | ResultSet r = pst.executeQuery(); |
| 109 | | |
| 110 | | while(r.next()) |
| 111 | | { |
| 112 | | return true; |
| 113 | | } |
| 114 | | } |
| 115 | | catch (SQLException e) |
| 116 | | { |
| 117 | | throw new IDMapperException (e); |
| 118 | | } |
| 119 | | finally {pst.cleanup(); } |
| 120 | | return false; |
| 121 | | } |
| 122 | | |
| 123 | | /** |
| 124 | | * Read the info table and return as properties. |
| 125 | | * @return a map where keys are column names and values are the fields in the first row. |
| 126 | | * @throws IDMapperException when the database became unavailable |
| 127 | | */ |
| 128 | | private Map<String, String> getInfo() throws IDMapperException |
| 129 | | { |
| 130 | | final QueryLifeCycle pst = qInfo; |
| 131 | | Map<String, String> result = new HashMap<String, String>(); |
| 132 | | try |
| 133 | | { |
| 134 | | pst.init(); |
| 135 | | ResultSet rs = pst.executeQuery(); |
| 136 | | |
| 137 | | if (rs.next()) |
| 138 | | { |
| 139 | | ResultSetMetaData rsmd = rs.getMetaData(); |
| 140 | | for (int i = 1; i <= rsmd.getColumnCount(); ++i) |
| 141 | | { |
| 142 | | String key = rsmd.getColumnName(i); |
| 143 | | String val = rs.getString(i); |
| 144 | | result.put (key, val); |
| 145 | | } |
| 146 | | } |
| 147 | | } |
| 148 | | catch (SQLException ex) |
| 149 | | { |
| 150 | | throw new IDMapperException (ex); |
| 151 | | } |
| 152 | | |
| 153 | | return result; |
| 154 | | } |
| 155 | | |
| 183 | | /** {@inheritDoc} */ |
| 184 | | public Set<Xref> mapID (Xref idc, DataSource... resultDs) throws IDMapperException |
| 185 | | { |
| 186 | | final QueryLifeCycle pst = resultDs.length != 1 ? qCrossRefs : qCrossRefsWithCode; |
| 187 | | Set<Xref> refs = new HashSet<Xref>(); |
| 188 | | |
| 189 | | if (idc.getDataSource() == null) return refs; |
| 190 | | try |
| 191 | | { |
| 192 | | pst.init(); |
| 193 | | pst.setString(1, idc.getId()); |
| 194 | | pst.setString(2, idc.getDataSource().getSystemCode()); |
| 195 | | if (resultDs.length == 1) pst.setString(3, resultDs[0].getSystemCode()); |
| 196 | | |
| 197 | | Set<DataSource> dsFilter = new HashSet<DataSource>(Arrays.asList(resultDs)); |
| 198 | | |
| 199 | | ResultSet rs = pst.executeQuery(); |
| 200 | | while (rs.next()) |
| 201 | | { |
| 202 | | DataSource ds = DataSource.getBySystemCode(rs.getString(2)); |
| 203 | | if (resultDs.length == 0 || dsFilter.contains(ds)) |
| 204 | | { |
| 205 | | refs.add (new Xref (rs.getString(1), ds)); |
| 206 | | } |
| 207 | | } |
| 208 | | } |
| 209 | | catch (SQLException e) |
| 210 | | { |
| 211 | | throw new IDMapperException (e); |
| 212 | | } |
| 213 | | finally {pst.cleanup(); } |
| 214 | | |
| 215 | | return refs; |
| 216 | | } |
| 217 | | |
| 218 | | /** {@inheritDoc} */ |
| 219 | | public List<Xref> getCrossRefsByAttribute(String attrName, String attrValue) throws IDMapperException { |
| 220 | | // Logger.log.trace("Fetching cross references by attribute: " + attrName + " = " + attrValue); |
| 221 | | List<Xref> refs = new ArrayList<Xref>(); |
| 222 | | final QueryLifeCycle pst = qRefsByAttribute; |
| 223 | | try { |
| 224 | | pst.init(); |
| 225 | | pst.setString(1, attrName); |
| 226 | | pst.setString(2, attrValue); |
| 227 | | ResultSet r = pst.executeQuery(); |
| 228 | | while(r.next()) { |
| 229 | | Xref ref = new Xref(r.getString(1), DataSource.getBySystemCode(r.getString(2))); |
| 230 | | refs.add(ref); |
| 231 | | } |
| 232 | | } catch(SQLException e) { |
| 233 | | throw new IDMapperException (e); |
| 234 | | } |
| 235 | | finally {pst.cleanup(); } |
| 236 | | // Logger.log.trace("End fetching cross references by attribute"); |
| 237 | | return refs; |
| 238 | | } |
| 239 | | |
| 292 | | } |
| 293 | | |
| 294 | | /** |
| 295 | | * Excecutes several SQL statements to create the tables and indexes in the database the given |
| 296 | | * connection is connected to |
| 297 | | * Note: Official GDB's are created by AP, not with this code. |
| 298 | | * This is just here for testing purposes. |
| 299 | | */ |
| 300 | | public void createGdbTables() |
| 301 | | { |
| 302 | | // Logger.log.info("Info: Creating tables"); |
| 303 | | try |
| 304 | | { |
| 305 | | Statement sh = con.createStatement(); |
| 306 | | sh.execute("DROP TABLE info"); |
| 307 | | sh.execute("DROP TABLE link"); |
| 308 | | sh.execute("DROP TABLE datanode"); |
| 309 | | sh.execute("DROP TABLE attribute"); |
| 310 | | } |
| 311 | | catch(SQLException e) |
| 312 | | { |
| 313 | | // Logger.log.error("Unable to drop gdb tables (ignoring): " + e.getMessage()); |
| 314 | | } |
| 315 | | |
| 316 | | try |
| 317 | | { |
| 318 | | Statement sh = con.createStatement(); |
| 319 | | sh.execute( |
| 320 | | "CREATE TABLE " + |
| 321 | | " info " + |
| 322 | | "( schemaversion INTEGER PRIMARY KEY " + |
| 323 | | ")"); |
| 324 | | // Logger.log.info("Info table created"); |
| 325 | | sh.execute( //Add compatibility version of GDB |
| 326 | | "INSERT INTO info VALUES ( " + GDB_COMPAT_VERSION + ")"); |
| 327 | | // Logger.log.info("Version stored in info"); |
| 328 | | sh.execute( |
| 329 | | "CREATE TABLE " + |
| 330 | | " link " + |
| 331 | | " ( idLeft VARCHAR(50) NOT NULL, " + |
| 332 | | " codeLeft VARCHAR(50) NOT NULL, " + |
| 333 | | " idRight VARCHAR(50) NOT NULL, " + |
| 334 | | " codeRight VARCHAR(50) NOT NULL, " + |
| 335 | | " bridge VARCHAR(50), " + |
| 336 | | " PRIMARY KEY (idLeft, codeLeft, " + |
| 337 | | " idRight, codeRight) " + |
| 338 | | " ) "); |
| 339 | | // Logger.log.info("Link table created"); |
| 340 | | sh.execute( |
| 341 | | "CREATE TABLE " + |
| 342 | | " datanode " + |
| 343 | | " ( id VARCHAR(50), " + |
| 344 | | " code VARCHAR(50), " + |
| 345 | | " backpageText VARCHAR(800), " + |
| 346 | | " PRIMARY KEY (id, code) " + |
| 347 | | " ) "); |
| 348 | | // Logger.log.info("DataNode table created"); |
| 349 | | sh.execute( |
| 350 | | "CREATE TABLE " + |
| 351 | | " attribute " + |
| 352 | | " ( id VARCHAR(50), " + |
| 353 | | " code VARCHAR(50), " + |
| 354 | | " attrname VARCHAR(50), " + |
| 355 | | " attrvalue VARCHAR(255) " + |
| 356 | | " ) "); |
| 357 | | // Logger.log.info("Attribute table created"); |
| 358 | | } |
| 359 | | catch (SQLException e) |
| 360 | | { |
| 361 | | // Logger.log.error("while creating gdb tables: " + e.getMessage(), e); |
| 362 | | } |
| 363 | | } |
| 364 | | |
| 365 | | |
| 366 | | public static final int NO_LIMIT = 0; |
| 367 | | public static final int NO_TIMEOUT = 0; |
| 368 | | public static final int QUERY_TIMEOUT = 20; //seconds |
| 369 | | |
| 370 | | /** {@inheritDoc} */ |
| 371 | | public Set<Xref> freeSearch (String text, int limit) throws IDMapperException |
| 372 | | { |
| 373 | | Set<Xref> result = new HashSet<Xref>(); |
| 374 | | final QueryLifeCycle pst = qFreeSearch; |
| 375 | | try { |
| 376 | | pst.init(limit); |
| 377 | | pst.setString(1, "%" + text.toLowerCase() + "%"); |
| 378 | | ResultSet r = pst.executeQuery(); |
| 379 | | while(r.next()) { |
| 380 | | String id = r.getString(1); |
| 381 | | DataSource ds = DataSource.getBySystemCode(r.getString(2)); |
| 382 | | Xref ref = new Xref (id, ds); |
| 383 | | result.add (ref); |
| 384 | | } |
| 385 | | } |
| 386 | | catch (SQLException e) |
| 387 | | { |
| 388 | | throw new IDMapperException(e); |
| 389 | | } |
| 390 | | finally {pst.cleanup(); } |
| 391 | | return result; |
| 392 | | } |
| 393 | | |
| 394 | | private PreparedStatement pstGene = null; |
| 395 | | private PreparedStatement pstLink = null; |
| 396 | | private PreparedStatement pstAttr = null; |
| 397 | | |
| 398 | | /** {@inheritDoc} */ |
| 399 | | public int addGene(Xref ref, String bpText) |
| 400 | | { |
| 401 | | if (pstGene == null) throw new NullPointerException(); |
| 402 | | try |
| 403 | | { |
| 404 | | pstGene.setString(1, ref.getId()); |
| 405 | | pstGene.setString(2, ref.getDataSource().getSystemCode()); |
| 406 | | pstGene.setString(3, bpText); |
| 407 | | pstGene.executeUpdate(); |
| 408 | | } |
| 409 | | catch (SQLException e) |
| 410 | | { |
| 411 | | // Logger.log.error("" + ref, e); |
| 412 | | return 1; |
| 413 | | } |
| 414 | | return 0; |
| 415 | | } |
| 416 | | |
| 417 | | /** {@inheritDoc} */ |
| 418 | | public int addAttribute(Xref ref, String attr, String val) |
| 419 | | { |
| 420 | | try { |
| 421 | | pstAttr.setString(1, attr); |
| 422 | | pstAttr.setString(2, val); |
| 423 | | pstAttr.setString(3, ref.getId()); |
| 424 | | pstAttr.setString(4, ref.getDataSource().getSystemCode()); |
| 425 | | pstAttr.executeUpdate(); |
| 426 | | } catch (SQLException e) { |
| 427 | | // Logger.log.error(attr + "\t" + val + "\t" + ref, e); |
| 428 | | return 1; |
| 429 | | } |
| 430 | | return 0; |
| 431 | | } |
| 432 | | |
| 433 | | /** {@inheritDoc} */ |
| 434 | | public int addLink(Xref left, Xref right) |
| 435 | | { |
| 436 | | if (pstLink == null) throw new NullPointerException(); |
| 437 | | try |
| 438 | | { |
| 439 | | pstLink.setString(1, left.getId()); |
| 440 | | pstLink.setString(2, left.getDataSource().getSystemCode()); |
| 441 | | pstLink.setString(3, right.getId()); |
| 442 | | pstLink.setString(4, right.getDataSource().getSystemCode()); |
| 443 | | pstLink.executeUpdate(); |
| 444 | | } |
| 445 | | catch (SQLException e) |
| 446 | | { |
| 447 | | // Logger.log.error(left + "\t" + right , e); |
| 448 | | return 1; |
| 449 | | } |
| 450 | | return 0; |
| 451 | | } |
| 452 | | |
| 453 | | /** |
| 454 | | Create indices on the database |
| 455 | | You can call this at any time after creating the tables, |
| 456 | | but it is good to do it only after inserting all data. |
| 457 | | @throws IDMapperException on failure |
| 458 | | */ |
| 459 | | public void createGdbIndices() throws IDMapperException |
| 460 | | { |
| 461 | | try |
| 462 | | { |
| 463 | | Statement sh = con.createStatement(); |
| 464 | | sh.execute( |
| 465 | | "CREATE INDEX i_codeLeft" + |
| 466 | | " ON link(codeLeft)" |
| 467 | | ); |
| 468 | | sh.execute( |
| 469 | | "CREATE INDEX i_idRight" + |
| 470 | | " ON link(idRight)" |
| 471 | | ); |
| 472 | | sh.execute( |
| 473 | | "CREATE INDEX i_codeRight" + |
| 474 | | " ON link(codeRight)" |
| 475 | | ); |
| 476 | | sh.execute( |
| 477 | | "CREATE INDEX i_code" + |
| 478 | | " ON " + "datanode" + "(code)" |
| 479 | | ); |
| 480 | | } |
| 481 | | catch (SQLException e) |
| 482 | | { |
| 483 | | throw new IDMapperException (e); |
| 484 | | } |
| 485 | | } |
| 486 | | |
| 487 | | /** |
| 488 | | prepare for inserting genes and/or links. |
| 489 | | @throws IDMapperException on failure |
| 490 | | */ |
| 491 | | public void preInsert() throws IDMapperException |
| 492 | | { |
| 493 | | try |
| 494 | | { |
| 495 | | con.setAutoCommit(false); |
| 496 | | pstGene = con.prepareStatement( |
| 497 | | "INSERT INTO datanode " + |
| 498 | | " (id, code," + |
| 499 | | " backpageText)" + |
| 500 | | "VALUES (?, ?, ?)" |
| 501 | | ); |
| 502 | | pstLink = con.prepareStatement( |
| 503 | | "INSERT INTO link " + |
| 504 | | " (idLeft, codeLeft," + |
| 505 | | " idRight, codeRight)" + |
| 506 | | "VALUES (?, ?, ?, ?)" |
| 507 | | ); |
| 508 | | pstAttr = con.prepareStatement( |
| 509 | | "INSERT INTO attribute " + |
| 510 | | " (attrname, attrvalue, id, code)" + |
| 511 | | "VALUES (?, ?, ?, ?)" |
| 512 | | ); |
| 513 | | } |
| 514 | | catch (SQLException e) |
| 515 | | { |
| 516 | | throw new IDMapperException (e); |
| 517 | | } |
| 518 | | } |
| 519 | | |
| 520 | | /** |
| 521 | | * @return a list of data sources present in this database. |
| 522 | | @throws IDMapperException when the database is unavailable |
| 523 | | */ |
| 524 | | private Set<DataSource> getDataSources() throws IDMapperException |
| 525 | | { |
| 526 | | Set<DataSource> result = new HashSet<DataSource>(); |
| 527 | | final QueryLifeCycle pst = qDatasources; |
| 528 | | try |
| 529 | | { |
| 530 | | pst.init(); |
| 531 | | ResultSet rs = pst.executeQuery(); |
| 532 | | while (rs.next()) |
| 533 | | { |
| 534 | | DataSource ds = DataSource.getBySystemCode(rs.getString(1)); |
| 535 | | result.add (ds); |
| 536 | | } |
| 537 | | } |
| 538 | | catch (SQLException ignore) |
| 539 | | { |
| 540 | | throw new IDMapperException(ignore); |
| 541 | | } |
| 542 | | finally {pst.cleanup(); } |
| 543 | | return result; |
| 544 | | } |
| 545 | | |
| 546 | | private final IDMapperCapabilities caps; |
| 547 | | |
| 548 | | private class SimpleGdbCapabilities extends AbstractIDMapperCapabilities |
| 549 | | { |
| 550 | | /** default constructor. |
| 551 | | * @throws IDMapperException when database is not available */ |
| 552 | | public SimpleGdbCapabilities() throws IDMapperException |
| 553 | | { |
| 554 | | super (SimpleGdbImpl2.this.getDataSources(), true, |
| 555 | | SimpleGdbImpl2.this.getInfo()); |
| 556 | | } |
| 557 | | } |
| 558 | | |
| 559 | | /** |
| 560 | | * @return the capabilities of this gene database |
| 561 | | */ |
| 562 | | public IDMapperCapabilities getCapabilities() |
| 563 | | { |
| 564 | | return caps; |
| 662 | | |
| 663 | | /** |
| 664 | | * |
| 665 | | * @return true |
| 666 | | */ |
| 667 | | public boolean isFreeAttributeSearchSupported() |
| 668 | | { |
| 669 | | return true; |
| 670 | | } |
| 671 | | |
| 672 | | /** |
| 673 | | * free text search for matching symbols. |
| 674 | | * @return references that match the query |
| 675 | | * @param query The text to search for |
| 676 | | * @param attrType the attribute to look for, e.g. 'Symbol' or 'Description'. |
| 677 | | * @param limit The number of results to limit the search to |
| 678 | | * @throws IDMapperException if the mapping service is (temporarily) unavailable |
| 679 | | */ |
| 680 | | public Map<Xref, String> freeAttributeSearch (String query, String attrType, int limit) throws IDMapperException |
| 681 | | { |
| 682 | | Map<Xref, String> result = new HashMap<Xref, String>(); |
| 683 | | final QueryLifeCycle pst = (MATCH_ID.equals (attrType)) ? |
| 684 | | qIdSearchWithAttributes : qAttributeSearch; |
| 685 | | try { |
| 686 | | pst.init(limit); |
| 687 | | pst.setString(1, "%" + query.toLowerCase() + "%"); |
| 688 | | ResultSet r = pst.executeQuery(); |
| 689 | | |
| 690 | | while(r.next()) |
| 691 | | { |
| 692 | | String id = r.getString("id"); |
| 693 | | String code = r.getString("code"); |
| 694 | | String symbol = r.getString("attrValue"); |
| 695 | | result.put(new Xref (id, DataSource.getBySystemCode(code)), symbol); |
| 696 | | } |
| 697 | | } catch (SQLException e) { |
| 698 | | throw new IDMapperException (e); |
| 699 | | } |
| 700 | | finally {pst.cleanup(); } |
| 701 | | return result; |
| 702 | | } |
| 703 | | |
| 704 | | /** {@inheritDoc} */ |
| 705 | | public Set<String> getAttributeSet() throws IDMapperException |
| 706 | | { |
| 707 | | Set<String> result = new HashSet<String>(); |
| 708 | | final QueryLifeCycle pst = qAttributesSet; |
| 709 | | try |
| 710 | | { |
| 711 | | pst.init(); |
| 712 | | ResultSet rs = pst.executeQuery(); |
| 713 | | while (rs.next()) |
| 714 | | { |
| 715 | | result.add (rs.getString(1)); |
| 716 | | } |
| 717 | | } |
| 718 | | catch (SQLException ignore) |
| 719 | | { |
| 720 | | throw new IDMapperException(ignore); |
| 721 | | } |
| 722 | | finally {pst.cleanup(); } |
| 723 | | return result; |
| 724 | | } |