root/trunk/org.bridgedb.rdb/src/org/bridgedb/rdb/SimpleGdbImpl3.java @ 308

Revision 308, 17.7 KB (checked in by martijn, 7 months ago)

Big project Reogranization:
- all modules are named with their main package name,

e.g. "picr" becomes org.bridgedb.webservice.picr,
and the jar will be org.bridgedb.webservice.picr.jar

- webservice renamed to org.bridgedb.server
- corelib is split in org.bridgedb, org.bridgedb.rdb,

org.bridgedb.webservice.biomart and org.bridgedb.webservice.bridgerest

  • Property svn:eol-style set to native
Line 
1// BridgeDb,
2// An abstraction layer for identifer mapping services, both local and online.
3// Copyright 2006-2009 BridgeDb developers
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17package org.bridgedb.rdb;
18
19import java.sql.Connection;
20import java.sql.PreparedStatement;
21import java.sql.ResultSet;
22import java.sql.ResultSetMetaData;
23import java.sql.SQLException;
24import java.sql.Statement;
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32
33import org.bridgedb.AbstractIDMapperCapabilities;
34import org.bridgedb.DataSource;
35import org.bridgedb.IDMapperCapabilities;
36import org.bridgedb.IDMapperException;
37import org.bridgedb.Xref;
38
39/** {@inheritDoc} */
40class SimpleGdbImpl3 extends SimpleGdb
41{               
42        private static final int GDB_COMPAT_VERSION = 3; //Preferred schema version
43       
44        private final SimpleGdb.LazyPst pstDatasources = new SimpleGdb.LazyPst(
45                        "SELECT codeRight FROM link GROUP BY codeRight"
46                );
47        private final SimpleGdb.LazyPst pstInfo = new SimpleGdb.LazyPst(
48                        "SELECT * FROM info"
49                );
50        private final SimpleGdb.LazyPst pstXrefExists = new SimpleGdb.LazyPst(
51                        "SELECT id FROM " + "datanode" + " WHERE " +
52                        "id = ? AND code = ?"
53                );     
54        private final SimpleGdb.LazyPst pstAttribute = new SimpleGdb.LazyPst(
55                        "SELECT attrvalue FROM attribute " +
56                        " WHERE id = ? AND code = ? AND attrname = ?"
57                );
58        private final SimpleGdb.LazyPst pstAllAttributes = new SimpleGdb.LazyPst(
59                        "SELECT attrname, attrvalue FROM attribute " +
60                        " WHERE id = ? AND code = ?"
61                );
62        private final SimpleGdb.LazyPst pstAttributesSet = new SimpleGdb.LazyPst(
63                        "SELECT attrname FROM attribute GROUP BY attrname"
64                );
65        private final SimpleGdb.LazyPst pstCrossRefs = new SimpleGdb.LazyPst (
66                        "SELECT dest.idRight, dest.codeRight FROM link AS src JOIN link AS dest " +
67                        "ON src.idLeft = dest.idLeft and src.codeLeft = dest.codeLeft " +
68                        "WHERE src.idRight = ? AND src.codeRight = ?"
69                );
70        private final SimpleGdb.LazyPst pstCrossRefsWithCode = new SimpleGdb.LazyPst (
71                        "SELECT dest.idRight, dest.codeRight FROM link AS src JOIN link AS dest " +
72                        "ON src.idLeft = dest.idLeft and src.codeLeft = dest.codeLeft " +
73                        "WHERE src.idRight = ? AND src.codeRight = ? AND dest.codeRight = ?"
74                );
75        private final SimpleGdb.LazyPst pstRefsByAttribute = new SimpleGdb.LazyPst (
76                        "SELECT datanode.id, datanode.code FROM datanode " +
77                        " LEFT JOIN attribute ON attribute.code = datanode.code AND attribute.id = datanode.id " +
78                        "WHERE attrName = ? AND attrValue = ?"
79                );
80        private final SimpleGdb.LazyPst pstFreeSearch = new SimpleGdb.LazyPst (
81                        "SELECT id, code FROM datanode WHERE " +
82                        "LOWER(ID) LIKE ?"
83                );
84        private final SimpleGdb.LazyPst pstAttributeSearch = new SimpleGdb.LazyPst (
85                        "SELECT id, code, attrvalue FROM attribute WHERE " +
86                        "attrname = 'Symbol' AND LOWER(attrvalue) LIKE ?"
87                );
88        private final SimpleGdb.LazyPst pstIdSearchWithAttributes = new SimpleGdb.LazyPst (
89                        "SELECT id, code, attrvalue FROM attribute WHERE " +
90                        "attrname = 'Symbol' AND LOWER(ID) LIKE ?"
91                );
92       
93        /** {@inheritDoc} */
94        public boolean xrefExists(Xref xref) throws IDMapperException
95        {
96                try 
97                {
98                        PreparedStatement pst = pstXrefExists.getPreparedStatement();
99                        pst.setString(1, xref.getId());
100                        pst.setString(2, xref.getDataSource().getSystemCode());
101                        ResultSet r = pst.executeQuery();
102
103                        while(r.next())
104                        {
105                                return true;
106                        }
107                }
108                catch (SQLException e)
109                {
110                        throw new IDMapperException (e);
111                }
112                return false;
113        }
114
115        /** {@inheritDoc} */
116        public Set<Xref> mapID (Xref idc, DataSource... resultDs) throws IDMapperException
117        {
118                Set<Xref> refs = new HashSet<Xref>();
119               
120                try
121                {
122                        PreparedStatement pst;
123                        if (resultDs.length != 1)
124                        {
125                                pst = pstCrossRefs.getPreparedStatement();
126                        }
127                        else
128                        {
129                                pst = pstCrossRefsWithCode.getPreparedStatement();
130                                pst.setString(3, resultDs[0].getSystemCode());
131                        }
132                       
133                        pst.setString(1, idc.getId());
134                        pst.setString(2, idc.getDataSource().getSystemCode());
135
136                        Set<DataSource> dsFilter = new HashSet<DataSource>(Arrays.asList(resultDs));
137
138                        ResultSet rs = pst.executeQuery();
139                        while (rs.next())
140                        {
141                                DataSource ds = DataSource.getBySystemCode(rs.getString(2));
142                                if (resultDs.length == 0 || dsFilter.contains(ds))
143                                {
144                                        refs.add (new Xref (rs.getString(1), ds));
145                                }
146                        }
147                }
148                catch (SQLException e)
149                {
150                        throw new IDMapperException (e);
151                }
152               
153                return refs;
154        }
155
156        /** {@inheritDoc} */
157        public List<Xref> getCrossRefsByAttribute(String attrName, String attrValue) throws IDMapperException {
158//              Logger.log.trace("Fetching cross references by attribute: " + attrName + " = " + attrValue);
159                List<Xref> refs = new ArrayList<Xref>();
160
161                try {
162                        PreparedStatement pst = pstRefsByAttribute.getPreparedStatement();
163                        pst.setString(1, attrName);
164                        pst.setString(2, attrValue);
165                        ResultSet r = pst.executeQuery();
166                        while(r.next()) {
167                                Xref ref = new Xref(r.getString(1), DataSource.getBySystemCode(r.getString(2)));
168                                refs.add(ref);
169                        }
170                } catch(SQLException e) {
171                        throw new IDMapperException (e);
172                }
173//              Logger.log.trace("End fetching cross references by attribute");
174                return refs;
175        }
176
177        /**
178         * Opens a connection to the Gene Database located in the given file.
179         * A new instance of this class is created automatically.
180         * @param dbName The file containing the Gene Database.
181         * @param con An existing java SQL connection
182         * @param props PROP_RECREATE if you want to create a new database (possibly overwriting an existing one)
183         *      or PROP_NONE if you want to connect read-only
184         * @throws IDMapperException when the database could not be created or connected to
185         */
186        public SimpleGdbImpl3(String dbName, Connection con, int props) throws IDMapperException
187        {
188                super(con);
189                if(dbName == null) throw new NullPointerException();
190
191                this.dbName = dbName;
192
193                if ((props & DBConnector.PROP_RECREATE) == 0)
194                {
195                        try
196                        {
197                                con.setReadOnly(true);
198                        }
199                        catch (SQLException e)
200                        {
201                                throw new IDMapperException (e);
202                        }
203                        checkSchemaVersion();
204                }
205               
206                caps = new SimpleGdbCapabilities();
207        }
208       
209        /**
210         * look at the info table of the current database to determine the schema version.
211         * @throws IDMapperException when looking up the schema version failed
212         */
213        private void checkSchemaVersion() throws IDMapperException
214        {
215                int version = 0;
216                try 
217                {
218                        ResultSet r = con.createStatement().executeQuery("SELECT schemaversion FROM info");
219                        if(r.next()) version = r.getInt(1);
220                }
221                catch (SQLException e)
222                {
223                        //Ignore, older db's don't even have schema version
224                }
225                if(version != GDB_COMPAT_VERSION)
226                {
227                        throw new IDMapperException ("Implementation and schema version mismatch");
228                }
229        }
230
231        /**
232         * Excecutes several SQL statements to create the tables and indexes in the database the given
233         * connection is connected to
234         * Note: Official GDB's are created by AP, not with this code.
235         * This is just here for testing purposes.
236         */
237        public void createGdbTables()
238        {
239//              Logger.log.info("Info:  Creating tables");
240                try 
241                {
242                        Statement sh = con.createStatement();
243                        sh.execute("DROP TABLE info");
244                        sh.execute("DROP TABLE link");
245                        sh.execute("DROP TABLE datanode");
246                        sh.execute("DROP TABLE attribute");
247                }
248                catch(SQLException e)
249                {
250//                      Logger.log.error("Unable to drop gdb tables (ignoring): " + e.getMessage());
251                }
252
253                try
254                {
255                        Statement sh = con.createStatement();
256                        sh.execute(
257                                        "CREATE TABLE                                   " +
258                                        "               info                                                    " +
259                                        "(        schemaversion INTEGER PRIMARY KEY             " +
260                        ")");
261//                      Logger.log.info("Info table created");
262                        sh.execute( //Add compatibility version of GDB
263                                        "INSERT INTO info VALUES ( " + GDB_COMPAT_VERSION + ")");
264//                      Logger.log.info("Version stored in info");
265                        sh.execute(
266                                        "CREATE TABLE                                   " +
267                                        "               link                                                    " +
268                                        " (   idLeft VARCHAR(50) NOT NULL,              " +
269                                        "     codeLeft VARCHAR(50) NOT NULL,    " +
270                                        "     idRight VARCHAR(50) NOT NULL,             " +
271                                        "     codeRight VARCHAR(50) NOT NULL,   " +
272                                        "     bridge VARCHAR(50),                               " +
273                                        "     PRIMARY KEY (idLeft, codeLeft,    " +
274                                        "               idRight, codeRight)                     " +
275                                        " )                                                                             ");
276//                      Logger.log.info("Link table created");
277                        sh.execute(
278                                        "CREATE TABLE                                   " +
279                                        "               datanode                                                " +
280                                        " (   id VARCHAR(50),                                   " +
281                                        "     code VARCHAR(50)                                  " +
282                                        "     PRIMARY KEY (id, code)                    " +
283                                        " )                                                                             ");
284//                      Logger.log.info("DataNode table created");
285                        sh.execute(
286                                        "CREATE TABLE                                                   " +
287                                        "               attribute                                               " +
288                                        " (   id VARCHAR(50),                                   " +
289                                        "     code VARCHAR(50),                                 " +
290                                        "     attrname VARCHAR(50),                             " +
291                                        "         attrvalue VARCHAR(255)                        " +
292                                        " )                                                                             ");
293//                      Logger.log.info("Attribute table created");
294                }
295                catch (SQLException e)
296                {
297//                      Logger.log.error("while creating gdb tables: " + e.getMessage(), e);
298                }
299        }
300
301       
302        public static final int NO_LIMIT = 0;
303        public static final int NO_TIMEOUT = 0;
304        public static final int QUERY_TIMEOUT = 20; //seconds
305
306        /** {@inheritDoc} */
307        public Set<Xref> freeSearch (String text, int limit) throws IDMapperException
308        {               
309                Set<Xref> result = new HashSet<Xref>();
310                try {
311                        PreparedStatement ps1 = pstFreeSearch.getPreparedStatement();
312                        ps1.setQueryTimeout(QUERY_TIMEOUT);
313                        if(limit > NO_LIMIT)
314                        {
315                                ps1.setMaxRows(limit);
316                        }
317
318                        ps1.setString(1, "%" + text.toLowerCase() + "%");
319                        ResultSet r = ps1.executeQuery();
320                        while(r.next()) {
321                                String id = r.getString(1);
322                                DataSource ds = DataSource.getBySystemCode(r.getString(2));
323                                Xref ref = new Xref (id, ds);
324                                result.add (ref);
325                        }                       
326                }
327                catch (SQLException e)
328                {
329                        throw new IDMapperException(e);
330                }
331                return result;
332        }
333       
334    private PreparedStatement pstGene = null;
335    private PreparedStatement pstLink = null;
336    private PreparedStatement pstAttr = null;
337
338        /** {@inheritDoc} */
339        public int addGene(Xref ref, String bpText)
340        {
341                //TODO: bpText is unused
342        if (pstGene == null) throw new NullPointerException();
343                try 
344                {
345                        pstGene.setString(1, ref.getId());
346                        pstGene.setString(2, ref.getDataSource().getSystemCode());
347                        pstGene.executeUpdate();
348                }
349                catch (SQLException e)
350                {
351//                      Logger.log.error("" + ref, e);
352                        return 1;
353                }
354                return 0;
355    }
356   
357        /** {@inheritDoc} */
358    public int addAttribute(Xref ref, String attr, String val)
359    {
360        try {
361                pstAttr.setString(1, attr);
362                        pstAttr.setString(2, val);
363                        pstAttr.setString(3, ref.getId());
364                        pstAttr.setString(4, ref.getDataSource().getSystemCode());
365                        pstAttr.executeUpdate();
366                } catch (SQLException e) {
367//                      Logger.log.error(attr + "\t" + val + "\t" + ref, e);
368                        return 1;
369                }
370                return 0;
371    }
372
373        /** {@inheritDoc} */
374    public int addLink(Xref left, Xref right)
375    {
376        if (pstLink == null) throw new NullPointerException();
377        try 
378        {
379                        pstLink.setString(1, left.getId());
380                        pstLink.setString(2, left.getDataSource().getSystemCode());
381                        pstLink.setString(3, right.getId());
382                        pstLink.setString(4, right.getDataSource().getSystemCode());
383                        pstLink.executeUpdate();
384                }
385                catch (SQLException e)
386                {
387//                      Logger.log.error(left + "\t" + right , e);
388                        return 1;
389                }
390                return 0;
391        }
392
393        /**
394           Create indices on the database
395           You can call this at any time after creating the tables,
396           but it is good to do it only after inserting all data.
397           @throws IDMapperException on failure
398         */
399        public void createGdbIndices() throws IDMapperException
400        {
401                try
402                {
403                        Statement sh = con.createStatement();
404                        sh.execute(
405                                        "CREATE INDEX i_codeLeft" +
406                                        " ON link(codeLeft)"
407                        );
408                        sh.execute(
409                                        "CREATE INDEX i_idRight" +
410                                        " ON link(idRight)"
411                        );
412                        sh.execute(
413                                        "CREATE INDEX i_codeRight" +
414                                        " ON link(codeRight)"
415                        );
416                        sh.execute(
417                                        "CREATE INDEX i_code" +
418                                        " ON " + "datanode" + "(code)"
419                        );
420                }
421                catch (SQLException e)
422                {
423                        throw new IDMapperException (e);
424                }
425        }
426
427        /**
428           prepare for inserting genes and/or links.
429           @throws IDMapperException on failure
430         */
431        public void preInsert() throws IDMapperException
432        {
433                try
434                {
435                        con.setAutoCommit(false);
436                        pstGene = con.prepareStatement(
437                                "INSERT INTO datanode " +
438                                "       (id, code)" +
439                                "VALUES (?, ?)"
440                        );
441                        pstLink = con.prepareStatement(
442                                "INSERT INTO link " +
443                                "       (idLeft, codeLeft," +
444                                "        idRight, codeRight)" +
445                                "VALUES (?, ?, ?, ?)"
446                        );
447                        pstAttr = con.prepareStatement(
448                                        "INSERT INTO attribute " +
449                                        "       (attrname, attrvalue, id, code)" +
450                                        "VALUES (?, ?, ?, ?)"
451                                        );
452                }
453                catch (SQLException e)
454                {
455                        throw new IDMapperException (e);
456                }
457        }
458
459        /**
460         * Read the info table and return as properties.
461         * @return a map where keys are column names and values are the fields in the first row.
462         * @throws IDMapperException when the database became unavailable
463         */
464        private Map<String, String> getInfo() throws IDMapperException
465        {
466                Map<String, String> result = new HashMap<String, String>();
467                try
468                {
469                        PreparedStatement pst = pstInfo.getPreparedStatement();
470                        ResultSet rs = pst.executeQuery();
471                       
472                        if (rs.next())
473                        {
474                                ResultSetMetaData rsmd = rs.getMetaData();
475                                for (int i = 1; i <= rsmd.getColumnCount(); ++i)
476                                {
477                                        String key = rsmd.getColumnName(i);
478                                        String val = rs.getString(i);
479                                        result.put (key, val);
480                                }
481                        }
482                }
483                catch (SQLException ex)
484                {
485                        throw new IDMapperException (ex);
486                }
487               
488                return result;
489        }
490       
491        /**
492         * @return a list of data sources present in this database.
493           @throws IDMapperException when the database is unavailable
494         */
495        private Set<DataSource> getDataSources() throws IDMapperException
496        {
497                Set<DataSource> result = new HashSet<DataSource>();
498        try
499        {
500                PreparedStatement pst = pstDatasources.getPreparedStatement();
501                ResultSet rs = pst.executeQuery();
502                while (rs.next())
503                {
504                        DataSource ds = DataSource.getBySystemCode(rs.getString(1));
505                        result.add (ds);
506                }
507        }
508        catch (SQLException ignore)
509        {
510                throw new IDMapperException(ignore);
511        }
512        return result;
513        }
514       
515        private final IDMapperCapabilities caps;
516
517        private class SimpleGdbCapabilities extends AbstractIDMapperCapabilities
518        {
519                /** default constructor.
520                 * @throws IDMapperException when database is not available */
521                public SimpleGdbCapabilities() throws IDMapperException
522                {
523                        super (SimpleGdbImpl3.this.getDataSources(), true,
524                                SimpleGdbImpl3.this.getInfo());
525                }
526        }
527
528        /**
529         * @return the capabilities of this gene database
530         */
531        public IDMapperCapabilities getCapabilities()
532        {
533                return caps;
534        }
535
536        /** {@inheritDoc} */
537        public Set<String> getAttributes(Xref ref, String attrname)
538                        throws IDMapperException
539        {
540                Set<String> result = new HashSet<String>();
541                try {
542                        PreparedStatement pst = pstAttribute.getPreparedStatement();
543                        pst.setString (1, ref.getId());
544                        pst.setString (2, ref.getDataSource().getSystemCode());
545                        pst.setString (3, attrname);
546                        ResultSet r = pst.executeQuery();
547                        if (r.next())
548                        {
549                                result.add (r.getString(1));
550                        }
551                        return result;
552                } catch (SQLException e) { throw new IDMapperException (e); } // Database unavailable
553        }
554
555        /** {@inheritDoc} */
556        public Map<String, Set<String>> getAttributes(Xref ref)
557                        throws IDMapperException
558        {
559                Map<String, Set<String>> result = new HashMap<String, Set<String>>();                           
560                try {
561                        PreparedStatement pst = pstAllAttributes.getPreparedStatement();
562                        pst.setString (1, ref.getId());
563                        pst.setString (2, ref.getDataSource().getSystemCode());
564                        ResultSet r = pst.executeQuery();
565                        if (r.next())
566                        {
567                                String key = r.getString(1);
568                                String value = r.getString(2);
569                                if (result.containsKey (key))
570                                {
571                                        result.get(key).add (value);
572                                }
573                                else
574                                {
575                                        Set<String> valueSet = new HashSet<String>();
576                                        valueSet.add (value);
577                                        result.put (key, valueSet);
578                                }
579                        }
580                        return result;
581                } catch (SQLException e) { throw new IDMapperException ("Xref:" + ref, e); } // Database unavailable
582        }
583
584        /**
585         *
586         * @return true
587         */
588        public boolean isFreeAttributeSearchSupported()
589        {
590                return true;
591        }
592       
593        /**
594         * free text search for matching symbols.
595         * @return references that match the query
596         * @param query The text to search for
597         * @param attrType the attribute to look for, e.g. 'Symbol' or 'Description'.
598         * @param limit The number of results to limit the search to
599         * @throws IDMapperException if the mapping service is (temporarily) unavailable
600         */
601        public Map<Xref, String> freeAttributeSearch (String query, String attrType, int limit) throws IDMapperException
602        {
603                Map<Xref, String> result = new HashMap<Xref, String>();
604                try {
605                        PreparedStatement pst = (MATCH_ID.equals (attrType)) ?
606                                        pstIdSearchWithAttributes.getPreparedStatement() : pstAttributeSearch.getPreparedStatement();
607                        pst.setQueryTimeout(QUERY_TIMEOUT);
608                        if(limit > NO_LIMIT) pst.setMaxRows(limit);
609                        pst.setString(1, "%" + query.toLowerCase() + "%");
610                        ResultSet r = pst.executeQuery();
611
612                        while(r.next())
613                        {
614                                String id = r.getString("id");
615                                String code = r.getString("code");
616                                String symbol = r.getString("attrValue");
617                                result.put(new Xref (id, DataSource.getBySystemCode(code)), symbol);
618                        }
619                } catch (SQLException e) {
620                        throw new IDMapperException (e);
621                }
622                return result;         
623        }
624
625        /** {@inheritDoc} */
626        public Set<String> getAttributeSet() throws IDMapperException
627        {
628                Set<String> result = new HashSet<String>();
629        try
630        {
631                PreparedStatement pst = pstAttributesSet.getPreparedStatement();
632                ResultSet rs = pst.executeQuery();
633                while (rs.next())
634                {
635                        result.add (rs.getString(1));
636                }
637        }
638        catch (SQLException ignore)
639        {
640                throw new IDMapperException(ignore);
641        }
642        return result;
643        }
644
645}
Note: See TracBrowser for help on using the browser.