Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that searches a relational DB for elements and 
4
 *             attributes that have free text matches a query string,
5
 *             or structured query matches to a path specified node in the 
6
 *             XML hierarchy.  It returns a result set consisting of the 
7
 *             document ID for each document that satisfies the query
8
 *  Copyright: 2000 Regents of the University of California and the
9
 *             National Center for Ecological Analysis and Synthesis
10
 *    Authors: Matt Jones
11
 *    Release: @release@
12
 *
13
 *   '$Author: berkley $'
14
 *     '$Date: 2000-08-14 14:26:12 -0700 (Mon, 14 Aug 2000) $'
15
 * '$Revision: 350 $'
16
 */
17

    
18
package edu.ucsb.nceas.metacat;
19

    
20
import java.io.*;
21
import java.net.URL;
22
import java.net.MalformedURLException;
23
import java.sql.*;
24
import java.util.Stack;
25
import java.util.Hashtable;
26
import java.util.Enumeration;
27

    
28
/** 
29
 * A Class that searches a relational DB for elements and 
30
 * attributes that have free text matches a query string,
31
 * or structured query matches to a path specified node in the 
32
 * XML hierarchy.  It returns a result set consisting of the 
33
 * document ID for each document that satisfies the query
34
 */
35
public class DBQuery {
36

    
37
  private Connection	conn = null;
38
  private String	parserName = null;
39

    
40
  /**
41
   * the main routine used to test the DBQuery utility.
42
   * <p>
43
   * Usage: java DBQuery <xmlfile>
44
   *
45
   * @param xmlfile the filename of the xml file containing the query
46
   */
47
  static public void main(String[] args) {
48
     
49
     if (args.length < 1)
50
     {
51
        System.err.println("Wrong number of arguments!!!");
52
        System.err.println("USAGE: java DBQuery <xmlfile>");
53
        return;
54
     } else {
55
        try {
56
                    
57
          String xmlfile  = args[0];
58

    
59
          // Open a connection to the database
60
          MetaCatUtil   util = new MetaCatUtil();
61
          Connection dbconn = util.openDBConnection();
62

    
63
          // Execute the query
64
          DBQuery queryobj = new DBQuery(dbconn, util.getOption("saxparser"));
65
          FileReader xml = new FileReader(new File(xmlfile));
66
          Hashtable nodelist = null;
67
          nodelist = queryobj.findDocuments(xml);
68

    
69
          // Print the reulting document listing
70
          StringBuffer result = new StringBuffer();
71
          String document = null;
72
          String docid = null;
73
          result.append("<?xml version=\"1.0\"?>\n");
74
          result.append("<resultset>\n"); 
75
  // following line removed by Dan Higgins to avoid insertion of query XML inside returned XML doc
76
  //        result.append("  <query>" + xmlfile + "</query>\n");
77
          Enumeration doclist = nodelist.keys(); 
78
          while (doclist.hasMoreElements()) {
79
            docid = (String)doclist.nextElement();
80
            document = (String)nodelist.get(docid);
81
            result.append("  <document>\n    " + document + 
82
                          "\n  </document>\n");
83
          }
84
          result.append("</resultset>\n");
85

    
86
          System.out.println(result);
87

    
88
        } catch (Exception e) {
89
          System.err.println("EXCEPTION HANDLING REQUIRED");
90
          System.err.println(e.getMessage());
91
          e.printStackTrace(System.err);
92
        }
93
     }
94
  }
95
  
96
  /**
97
   * construct an instance of the DBQuery class 
98
   *
99
   * <p>Generally, one would call the findDocuments() routine after creating 
100
   * an instance to specify the search query</p>
101
   *
102
   * @param conn the JDBC connection that we use for the query
103
   * @param parserName the fully qualified name of a Java class implementing
104
   *                   the org.xml.sax.XMLReader interface
105
   */
106
  public DBQuery( Connection conn, String parserName ) 
107
                  throws IOException, 
108
                         SQLException, 
109
                         ClassNotFoundException {
110
    this.conn = conn;
111
    this.parserName = parserName;
112
  }
113
  
114
  /** 
115
   * routine to search the elements and attributes looking to match query
116
   *
117
   * @param xmlquery the xml serialization of the query (@see pathquery.dtd)
118
   */
119
  public Hashtable findDocuments(Reader xmlquery) {
120
      Hashtable	 docListResult = new Hashtable();
121
      PreparedStatement pstmt;
122
      String docid = null;
123
      String docname = null;
124
      String doctype = null;
125
      String doctitle = null;
126
      StringBuffer document = null; 
127

    
128
      try {
129
        // Get the XML query and covert it into a SQL statment
130
        QuerySpecification qspec = new QuerySpecification(xmlquery, 
131
                                   parserName);
132
        //System.out.println(qspec.printSQL());
133
        pstmt = conn.prepareStatement( qspec.printSQL() );
134

    
135
        // Execute the SQL query using the JDBC connection
136
        pstmt.execute();
137
        ResultSet rs = pstmt.getResultSet();
138
        boolean tableHasRows = rs.next();
139
        while (tableHasRows) {
140
          docid = rs.getString(1);
141
          docname = rs.getString(2);
142
          doctype = rs.getString(3);
143
          doctitle = rs.getString(4);
144

    
145
          document = new StringBuffer();
146
          document.append("<docid>").append(docid).append("</docid>");
147
          if (docname != null) {
148
            document.append("<docname>" + docname + "</docname>");
149
          }
150
          if (doctype != null) {
151
            document.append("<doctype>" + doctype + "</doctype>");
152
          }
153
          if (doctitle != null) {
154
            document.append("<doctitle>" + doctitle + "</doctitle>");
155
          }
156

    
157
          // Store the document id and the root node id
158
          docListResult.put(docid,(String)document.toString());
159

    
160
          // Advance to the next record in the cursor
161
          tableHasRows = rs.next();
162
        }
163
        pstmt.close();
164
      } catch (SQLException e) {
165
        System.err.println("Error getting id: " + e.getMessage());
166
      } catch (IOException ioe) {
167
        System.err.println("Error printing qspec:");
168
        System.err.println(ioe.getMessage());
169
      }
170
    return docListResult;
171
  }
172
  
173
  /**
174
   * format a structured query as an XML document that conforms
175
   * to the pathquery.dtd and is appropriate for submission to the DBQuery
176
   * structured query engine
177
   *
178
   * @param params The list of parameters that  should be included in the query
179
   * @param doctype the type of documents to include in the result set -- use
180
   *        "any" or "ANY" for unfiltered result sets
181
   */
182
  public static String createSQuery(Hashtable params, String doctype)
183
  { 
184
    StringBuffer query = new StringBuffer();
185
    Enumeration elements;
186
    Enumeration keys;
187
    Object nextkey;
188
    Object nextelement;
189
    //add the xml headers
190
    query.append("<?xml version=\"1.0\"?>\n");
191
    query.append("<pathquery version=\"1.0\"><meta_file_id>");
192
    
193
    if(params.containsKey("meta_file_id"))
194
    {
195
      query.append( ((String[])params.get("meta_file_id"))[0]);
196
    	query.append("</meta_file_id>");
197
    }
198
    else
199
    {
200
      query.append("unspecified</meta_file_id>");
201
    }
202
    
203
    query.append("<querytitle>");
204
    if(params.containsKey("querytitle"))
205
    {
206
      query.append(((String[])params.get("querytitle"))[0]);
207
    	query.append("</querytitle>");
208
    }
209
    else
210
    {
211
    	query.append("unspecified</querytitle>");
212
    }
213
    //if you don't limit the query by doctype, then it just creates
214
    //an empty returndoctype tag.
215
    if (!doctype.equals("any") && 
216
        !doctype.equals("ANY") &&
217
        !doctype.equals("") ) 
218
    {
219
       query.append("<returndoctype>");
220
       query.append(doctype).append("</returndoctype>");
221
    }
222
    else
223
    { 
224
      query.append("<returndoctype></returndoctype>");
225
    }
226
    
227
    //allows the dynamic switching of boolean operators
228
    if(params.containsKey("operator"))
229
    {
230
      query.append("<querygroup operator=\"" + 
231
    		        ((String[])params.get("operator"))[0] + "\">");
232
    }
233
    else
234
    { //the default operator is UNION
235
      query.append("<querygroup operator=\"UNION\">"); 
236
    }
237
    		
238
    		
239
    //anyfield is a special case because it does a 
240
    //free text search.  It does not have a <pathexpr>
241
    //tag.  This allows for a free text search within the structured
242
    //query.  This is useful if the INTERSECT operator is used.
243
    if(params.containsKey("anyfield"))
244
    {
245
    	 if(!((String[])params.get("anyfield"))[0].equals(""))
246
       {
247
         query.append("<queryterm casesensitive=\"false\" " +
248
    			            "searchmode=\"contains\"><value>" +
249
    			            ((String[])params.get("anyfield"))[0] +
250
    			            "</value></queryterm>");
251
       }
252
    }
253
    		
254
    //this while loop finds the rest of the parameters
255
    //and attempts to query for the field specified
256
    //by the parameter.
257
    elements = params.elements();
258
    keys = params.keys();
259
    while(keys.hasMoreElements() && elements.hasMoreElements())
260
    {
261
      nextkey = keys.nextElement();
262
    	nextelement = elements.nextElement();
263
    		
264
    	//make sure we aren't querying for any of these
265
    	//parameters since the are already in the query
266
      //in one form or another.
267
    	if(!nextkey.toString().equals("doctype") && 
268
    		 !nextkey.toString().equals("action")  &&
269
    		 !nextkey.toString().equals("qformat") && 
270
    		 !nextkey.toString().equals("anyfield") &&
271
    		 !nextkey.toString().equals("operator") &&
272
    		 !(((String[])nextelement)[0].equals("")) )
273
    	{
274
        query.append("<queryterm casesensitive=\"false\" " + 
275
    				         "searchmode=\"contains\">" +
276
    		             "<value>" +
277
                     //add the query value
278
    		             ((String[])nextelement)[0] +
279
    		             "</value><pathexpr>" +
280
    		             //add the path to query by 
281
                     nextkey.toString() + 
282
                     "</pathexpr></queryterm>");
283
    	}
284
    }
285
    query.append("</querygroup></pathquery>");
286
    //append on the end of the xml and return the result as a string
287
    return query.toString();
288
  }
289
  
290
  /**
291
   * format a simple free-text value query as an XML document that conforms
292
   * to the pathquery.dtd and is appropriate for submission to the DBQuery
293
   * structured query engine
294
   *
295
   * @param value the text string to search for in the xml catalog
296
   * @param doctype the type of documents to include in the result set -- use
297
   *        "any" or "ANY" for unfiltered result sets
298
   */
299
   public static String createQuery(String value, String doctype) {
300
     StringBuffer xmlquery = new StringBuffer();
301
     xmlquery.append("<?xml version=\"1.0\"?>\n");
302
     xmlquery.append("<pathquery version=\"1.0\">");
303
     xmlquery.append("<meta_file_id>Unspecified</meta_file_id>");
304
     xmlquery.append("<querytitle>Unspecified</querytitle>");
305

    
306
     if (!doctype.equals("any") && !doctype.equals("ANY")) {
307
       xmlquery.append("<returndoctype>");
308
       xmlquery.append(doctype).append("</returndoctype>");
309
     }
310

    
311
     xmlquery.append("<querygroup operator=\"UNION\">");
312
     //chad added - 8/14
313
     //the if statement allows a query to gracefully handle a null 
314
     //query.  Without this if a nullpointerException is thrown.
315
     if(!value.equals(""))
316
     {
317
       xmlquery.append("<queryterm casesensitive=\"false\" ");
318
       xmlquery.append("searchmode=\"contains\">");
319
       xmlquery.append("<value>").append(value).append("</value>");
320
       xmlquery.append("</queryterm>");
321
     }
322
     xmlquery.append("</querygroup>");
323
     xmlquery.append("</pathquery>");
324

    
325
     
326
     return (xmlquery.toString());
327
   }
328

    
329
  /**
330
   * format a simple free-text value query as an XML document that conforms
331
   * to the pathquery.dtd and is appropriate for submission to the DBQuery
332
   * structured query engine
333
   *
334
   * @param value the text string to search for in the xml catalog
335
   */
336
   public static String createQuery(String value) {
337
     return createQuery(value, "any");
338
   }
339
}
340

    
341
/**
342
 * '$Log$
343
 * 'Revision 1.12  2000/08/14 20:53:33  jones
344
 * 'Added "release" keyword to all metacat source files so that the release
345
 * 'number will be evident in software distributions.
346
 * '
347
 * 'Revision 1.11  2000/08/11 18:26:07  berkley
348
 * 'added createSQuery
349
 * '
350
 * 'Revision 1.10  2000/07/26 20:40:41  higgins
351
 * 'no message
352
 * '
353
 * 'Revision 1.9  2000/06/26 10:35:04  jones
354
 * 'Merged in substantial changes to DBWriter and associated classes and to
355
 * 'the MetaCatServlet in order to accomodate the new UPDATE and DELETE
356
 * 'functions.  The command line tools and the parameters for the
357
 * 'servlet have changed substantially.
358
 * '
359
 * 'Revision 1.8.2.2  2000/06/25 23:38:16  jones
360
 * 'Added RCSfile keyword
361
 * '
362
 * 'Revision 1.8.2.1  2000/06/25 23:34:17  jones
363
 * 'Changed documentation formatting, added log entries at bottom of source files
364
 * ''
365
 */
(8-8/27)