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-17 09:02:34 -0700 (Thu, 17 Aug 2000) $'
15
 * '$Revision: 372 $'
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
   */
180
  public static String createSQuery(Hashtable params)
181
  { 
182
    StringBuffer query = new StringBuffer();
183
    Enumeration elements;
184
    Enumeration keys;
185
    String doctype = null;
186
    String casesensitive = null;
187
    String searchmode = null;
188
    Object nextkey;
189
    Object nextelement;
190
    //add the xml headers
191
    query.append("<?xml version=\"1.0\"?>\n");
192
    query.append("<pathquery version=\"1.0\"><meta_file_id>");
193
    
194
    if(params.containsKey("meta_file_id"))
195
    {
196
      query.append( ((String[])params.get("meta_file_id"))[0]);
197
    	query.append("</meta_file_id>");
198
    }
199
    else
200
    {
201
      query.append("unspecified</meta_file_id>");
202
    }
203
    
204
    query.append("<querytitle>");
205
    if(params.containsKey("querytitle"))
206
    {
207
      query.append(((String[])params.get("querytitle"))[0]);
208
    	query.append("</querytitle>");
209
    }
210
    else
211
    {
212
    	query.append("unspecified</querytitle>");
213
    }
214
    
215
    if(params.containsKey("doctype"))
216
    {
217
      doctype = ((String[])params.get("doctype"))[0]; 
218
    }
219
    else
220
    {
221
      doctype = "ANY";  
222
    }
223
    
224
    //if you don't limit the query by doctype, then it just creates
225
    //an empty returndoctype tag.
226
    if (!doctype.equals("any") && 
227
        !doctype.equals("ANY") &&
228
        !doctype.equals("") ) 
229
    {
230
       query.append("<returndoctype>");
231
       query.append(doctype).append("</returndoctype>");
232
    }
233
    else
234
    { 
235
      query.append("<returndoctype></returndoctype>");
236
    }
237
    
238
    //allows the dynamic switching of boolean operators
239
    if(params.containsKey("operator"))
240
    {
241
      query.append("<querygroup operator=\"" + 
242
    		        ((String[])params.get("operator"))[0] + "\">");
243
    }
244
    else
245
    { //the default operator is UNION
246
      query.append("<querygroup operator=\"UNION\">"); 
247
    }
248
    		
249
    if(params.containsKey("casesensitive"))
250
    {
251
      casesensitive = ((String[])params.get("casesensitive"))[0]; 
252
    }
253
    else
254
    {
255
      casesensitive = "false"; 
256
    }
257
    
258
    if(params.containsKey("searchmode"))
259
    {
260
      searchmode = ((String[])params.get("searchmode"))[0]; 
261
    }
262
    else
263
    {
264
      searchmode = "contains"; 
265
    }
266
    		
267
    //anyfield is a special case because it does a 
268
    //free text search.  It does not have a <pathexpr>
269
    //tag.  This allows for a free text search within the structured
270
    //query.  This is useful if the INTERSECT operator is used.
271
    if(params.containsKey("anyfield"))
272
    {
273
       String[] anyfield = ((String[])params.get("anyfield"));
274
       //allow for more than one value for anyfield
275
       for(int i=0; i<anyfield.length; i++)
276
       {
277
         if(!anyfield[i].equals(""))
278
         {
279
           query.append("<queryterm casesensitive=\"" + casesensitive + 
280
                        "\" " + "searchmode=\"" + searchmode + "\"><value>" +
281
    			              anyfield[i] +
282
    			              "</value></queryterm>"); 
283
         }
284
       }
285
    }
286
    		
287
    //this while loop finds the rest of the parameters
288
    //and attempts to query for the field specified
289
    //by the parameter.
290
    elements = params.elements();
291
    keys = params.keys();
292
    while(keys.hasMoreElements() && elements.hasMoreElements())
293
    {
294
      nextkey = keys.nextElement();
295
    	nextelement = elements.nextElement();
296

    
297
    	//make sure we aren't querying for any of these
298
    	//parameters since the are already in the query
299
      //in one form or another.
300
    	if(!nextkey.toString().equals("doctype") && 
301
    		 !nextkey.toString().equals("action")  &&
302
    		 !nextkey.toString().equals("qformat") && 
303
    		 !nextkey.toString().equals("anyfield") &&
304
    		 !nextkey.toString().equals("operator") )
305
    	{
306
        //allow for more than value per field name
307
        for(int i=0; i<((String[])nextelement).length; i++)
308
        {
309
          if(!((String[])nextelement)[i].equals(""))
310
          {
311
            query.append("<queryterm casesensitive=\"" + casesensitive +"\" " + 
312
    				             "searchmode=\"" + searchmode + "\">" +
313
    		                 "<value>" +
314
                         //add the query value
315
    		                 ((String[])nextelement)[i] +
316
    		                 "</value><pathexpr>" +
317
    		                 //add the path to query by 
318
                         nextkey.toString() + 
319
                         "</pathexpr></queryterm>");
320
          }
321
        }
322
    	}
323
    }
324
    query.append("</querygroup></pathquery>");
325
    //append on the end of the xml and return the result as a string
326
    return query.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
   * @param doctype the type of documents to include in the result set -- use
336
   *        "any" or "ANY" for unfiltered result sets
337
   */
338
   public static String createQuery(String value, String doctype) {
339
     StringBuffer xmlquery = new StringBuffer();
340
     xmlquery.append("<?xml version=\"1.0\"?>\n");
341
     xmlquery.append("<pathquery version=\"1.0\">");
342
     xmlquery.append("<meta_file_id>Unspecified</meta_file_id>");
343
     xmlquery.append("<querytitle>Unspecified</querytitle>");
344

    
345
     if (!doctype.equals("any") && !doctype.equals("ANY")) {
346
       xmlquery.append("<returndoctype>");
347
       xmlquery.append(doctype).append("</returndoctype>");
348
     }
349

    
350
     xmlquery.append("<querygroup operator=\"UNION\">");
351
     //chad added - 8/14
352
     //the if statement allows a query to gracefully handle a null 
353
     //query.  Without this if a nullpointerException is thrown.
354
     if(!value.equals(""))
355
     {
356
       xmlquery.append("<queryterm casesensitive=\"false\" ");
357
       xmlquery.append("searchmode=\"contains\">");
358
       xmlquery.append("<value>").append(value).append("</value>");
359
       xmlquery.append("</queryterm>");
360
     }
361
     xmlquery.append("</querygroup>");
362
     xmlquery.append("</pathquery>");
363

    
364
     
365
     return (xmlquery.toString());
366
   }
367

    
368
  /**
369
   * format a simple free-text value query as an XML document that conforms
370
   * to the pathquery.dtd and is appropriate for submission to the DBQuery
371
   * structured query engine
372
   *
373
   * @param value the text string to search for in the xml catalog
374
   */
375
   public static String createQuery(String value) {
376
     return createQuery(value, "any");
377
   }
378
}
379

    
380
/**
381
 * '$Log$
382
 * 'Revision 1.13  2000/08/14 21:26:12  berkley
383
 * 'Added createSQuery() to handle structured queries of an arbitrary number of parameters.  Also modified createQuery() to handle a null query in a graceful manner.
384
 * '
385
 * 'Revision 1.12  2000/08/14 20:53:33  jones
386
 * 'Added "release" keyword to all metacat source files so that the release
387
 * 'number will be evident in software distributions.
388
 * '
389
 * 'Revision 1.11  2000/08/11 18:26:07  berkley
390
 * 'added createSQuery
391
 * '
392
 * 'Revision 1.10  2000/07/26 20:40:41  higgins
393
 * 'no message
394
 * '
395
 * 'Revision 1.9  2000/06/26 10:35:04  jones
396
 * 'Merged in substantial changes to DBWriter and associated classes and to
397
 * 'the MetaCatServlet in order to accomodate the new UPDATE and DELETE
398
 * 'functions.  The command line tools and the parameters for the
399
 * 'servlet have changed substantially.
400
 * '
401
 * 'Revision 1.8.2.2  2000/06/25 23:38:16  jones
402
 * 'Added RCSfile keyword
403
 * '
404
 * 'Revision 1.8.2.1  2000/06/25 23:34:17  jones
405
 * 'Changed documentation formatting, added log entries at bottom of source files
406
 * ''
407
 */
(8-8/27)