Project

General

Profile

1 155 jones
/**
2 203 jones
 *  '$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 349 jones
 *    Release: @release@
12 155 jones
 *
13 203 jones
 *   '$Author$'
14
 *     '$Date$'
15
 * '$Revision$'
16 155 jones
 */
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 172 jones
 * 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 155 jones
 */
35
public class DBQuery {
36
37
  private Connection	conn = null;
38 172 jones
  private String	parserName = null;
39 155 jones
40
  /**
41
   * the main routine used to test the DBQuery utility.
42 184 jones
   * <p>
43
   * Usage: java DBQuery <xmlfile>
44 155 jones
   *
45 170 jones
   * @param xmlfile the filename of the xml file containing the query
46 155 jones
   */
47
  static public void main(String[] args) {
48
49 184 jones
     if (args.length < 1)
50 155 jones
     {
51
        System.err.println("Wrong number of arguments!!!");
52 184 jones
        System.err.println("USAGE: java DBQuery <xmlfile>");
53 155 jones
        return;
54
     } else {
55
        try {
56
57 170 jones
          String xmlfile  = args[0];
58 155 jones
59
          // Open a connection to the database
60 184 jones
          MetaCatUtil   util = new MetaCatUtil();
61
          Connection dbconn = util.openDBConnection();
62 172 jones
63 170 jones
          // Execute the query
64 184 jones
          DBQuery queryobj = new DBQuery(dbconn, util.getOption("saxparser"));
65 170 jones
          FileReader xml = new FileReader(new File(xmlfile));
66 155 jones
          Hashtable nodelist = null;
67 181 jones
          nodelist = queryobj.findDocuments(xml);
68 155 jones
69 172 jones
          // Print the reulting document listing
70 155 jones
          StringBuffer result = new StringBuffer();
71
          String document = null;
72 170 jones
          String docid = null;
73 155 jones
          result.append("<?xml version=\"1.0\"?>\n");
74 296 higgins
          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 155 jones
          Enumeration doclist = nodelist.keys();
78
          while (doclist.hasMoreElements()) {
79 170 jones
            docid = (String)doclist.nextElement();
80 155 jones
            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 172 jones
   * @param parserName the fully qualified name of a Java class implementing
104 185 jones
   *                   the org.xml.sax.XMLReader interface
105 155 jones
   */
106 172 jones
  public DBQuery( Connection conn, String parserName )
107 155 jones
                  throws IOException,
108
                         SQLException,
109 172 jones
                         ClassNotFoundException {
110 155 jones
    this.conn = conn;
111 172 jones
    this.parserName = parserName;
112 155 jones
  }
113
114
  /**
115
   * routine to search the elements and attributes looking to match query
116
   *
117 178 jones
   * @param xmlquery the xml serialization of the query (@see pathquery.dtd)
118 155 jones
   */
119 178 jones
  public Hashtable findDocuments(Reader xmlquery) {
120 155 jones
      Hashtable	 docListResult = new Hashtable();
121
      PreparedStatement pstmt;
122 170 jones
      String docid = null;
123 155 jones
      String docname = null;
124
      String doctype = null;
125
      String doctitle = null;
126
      StringBuffer document = null;
127
128
      try {
129 172 jones
        // Get the XML query and covert it into a SQL statment
130 178 jones
        QuerySpecification qspec = new QuerySpecification(xmlquery,
131 172 jones
                                   parserName);
132 180 jones
        //System.out.println(qspec.printSQL());
133 172 jones
        pstmt = conn.prepareStatement( qspec.printSQL() );
134 155 jones
135 172 jones
        // Execute the SQL query using the JDBC connection
136 155 jones
        pstmt.execute();
137
        ResultSet rs = pstmt.getResultSet();
138
        boolean tableHasRows = rs.next();
139
        while (tableHasRows) {
140 170 jones
          docid = rs.getString(1);
141 155 jones
          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 170 jones
          docListResult.put(docid,(String)document.toString());
159 155 jones
160
          // Advance to the next record in the cursor
161
          tableHasRows = rs.next();
162
        }
163
        pstmt.close();
164
      } catch (SQLException e) {
165 180 jones
        System.err.println("Error getting id: " + e.getMessage());
166 170 jones
      } catch (IOException ioe) {
167
        System.err.println("Error printing qspec:");
168
        System.err.println(ioe.getMessage());
169 155 jones
      }
170
    return docListResult;
171
  }
172 342 berkley
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 350 berkley
  {
184
    StringBuffer query = new StringBuffer();
185 342 berkley
    Enumeration elements;
186
    Enumeration keys;
187
    Object nextkey;
188
    Object nextelement;
189 350 berkley
    //add the xml headers
190
    query.append("<?xml version=\"1.0\"?>\n");
191 342 berkley
    query.append("<pathquery version=\"1.0\"><meta_file_id>");
192 350 berkley
193 342 berkley
    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 350 berkley
203 342 berkley
    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 350 berkley
    //if you don't limit the query by doctype, then it just creates
214
    //an empty returndoctype tag.
215 342 berkley
    if (!doctype.equals("any") &&
216
        !doctype.equals("ANY") &&
217
        !doctype.equals("") )
218
    {
219
       query.append("<returndoctype>");
220
       query.append(doctype).append("</returndoctype>");
221
    }
222 350 berkley
    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 342 berkley
    		        ((String[])params.get("operator"))[0] + "\">");
232 350 berkley
    }
233
    else
234
    { //the default operator is UNION
235
      query.append("<querygroup operator=\"UNION\">");
236
    }
237 342 berkley
238
239
    //anyfield is a special case because it does a
240
    //free text search.  It does not have a <pathexpr>
241 350 berkley
    //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 342 berkley
    {
245 350 berkley
    	 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 342 berkley
    }
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 350 berkley
                     //add the query value
278 342 berkley
    		             ((String[])nextelement)[0] +
279
    		             "</value><pathexpr>" +
280 350 berkley
    		             //add the path to query by
281 342 berkley
                     nextkey.toString() +
282
                     "</pathexpr></queryterm>");
283
    	}
284
    }
285
    query.append("</querygroup></pathquery>");
286 350 berkley
    //append on the end of the xml and return the result as a string
287 342 berkley
    return query.toString();
288
  }
289
290 181 jones
  /**
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 350 berkley
     //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 181 jones
     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 155 jones
}
340 203 jones
341
/**
342
 * '$Log$
343 350 berkley
 * '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 349 jones
 * 'Revision 1.11  2000/08/11 18:26:07  berkley
348
 * 'added createSQuery
349
 * '
350 342 berkley
 * 'Revision 1.10  2000/07/26 20:40:41  higgins
351
 * 'no message
352
 * '
353 296 higgins
 * '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 203 jones
 * '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
 */