Project

General

Profile

« Previous | Next » 

Revision 1831

Added by Jing Tao about 21 years ago

New class will be used in metacat ecogrid impl.

View differences:

src/edu/ucsb/nceas/metacat/QueryGroup.java
1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents a structured query, and can be 
4
 *             constructed from an XML serialization conforming to 
5
 *             pathquery.dtd. The printSQL() method can be used to print 
6
 *             a SQL serialization of the query.
7
 *  Copyright: 2000 Regents of the University of California and the
8
 *             National Center for Ecological Analysis and Synthesis
9
 *    Authors: Matt Jones
10
 *    Release: @release@
11
 *
12
 *   '$Author$'
13
 *     '$Date$'
14
 * '$Revision$'
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 2 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
 */
30

  
31
package edu.ucsb.nceas.metacat;
32

  
33
import edu.ucsb.nceas.dbadapter.*;
34

  
35
import java.io.*;
36
import java.util.Hashtable;
37
import java.util.Stack;
38
import java.util.Vector;
39
import java.util.Enumeration;
40

  
41
 /** a utility class that represents a group of terms in a query */
42
public  class QueryGroup {
43
    private String operator = null;  // indicates how query terms are combined
44
    private Vector children = null;  // the list of query terms and groups
45
    private int countPercentageSearchItem = 0;
46
    /** 
47
     * construct a new QueryGroup 
48
     *
49
     * @param operator the boolean conector used to connect query terms 
50
     *                    in this query group
51
     */
52
    public QueryGroup(String operator) {
53
      this.operator = operator;
54
      children = new Vector();
55
    }
56

  
57
    /** 
58
     * Add a child QueryGroup to this QueryGroup
59
     *
60
     * @param qgroup the query group to be added to the list of terms
61
     */
62
    public void addChild(QueryGroup qgroup) {
63
      children.add((Object)qgroup); 
64
    }
65

  
66
    /**
67
     * Add a child QueryTerm to this QueryGroup
68
     *
69
     * @param qterm the query term to be added to the list of terms
70
     */
71
    public void addChild(QueryTerm qterm) {
72
      children.add((Object)qterm); 
73
    }
74

  
75
    /**
76
     * Retrieve an Enumeration of query terms for this QueryGroup
77
     */
78
    public Enumeration getChildren() {
79
      return children.elements();
80
    }
81
    
82
    public int getPercentageSymbolCount()
83
    {
84
      return countPercentageSearchItem;
85
    }
86
   
87
    /**
88
     * create a SQL serialization of the query that this instance represents
89
     */
90
    public String printSQL(boolean useXMLIndex) {
91
      StringBuffer self = new StringBuffer();
92
      boolean first = true;
93

  
94
      self.append("(");
95

  
96
      Enumeration en= getChildren();
97
      while (en.hasMoreElements()) {
98
        Object qobject = en.nextElement();
99
        if (first) {
100
          first = false;
101
        } else {
102
          self.append(" " + operator + " ");
103
        }
104
        if (qobject instanceof QueryGroup) {
105
          QueryGroup qg = (QueryGroup)qobject;
106
          self.append(qg.printSQL(useXMLIndex));
107
          // count percerntage number
108
          int count = qg.getPercentageSymbolCount();
109
          countPercentageSearchItem = countPercentageSearchItem + count;
110
        } else if (qobject instanceof QueryTerm) {
111
           QueryTerm qt = (QueryTerm)qobject;
112
           self.append(qt.printSQL(useXMLIndex));
113
           // count percerntage number
114
           int count = qt.getPercentageSymbolCount();
115
           countPercentageSearchItem = countPercentageSearchItem + count;
116
        } else {
117
          System.err.println("qobject wrong type: fatal error");
118
        }
119
      }
120
      self.append(") \n");
121
      return self.toString();
122
    }
123

  
124
    /**
125
     * create a String description of the query that this instance represents.
126
     * This should become a way to get the XML serialization of the query.
127
     */
128
    public String toString() {
129
      StringBuffer self = new StringBuffer();
130

  
131
      self.append("  (Query group operator=" + operator + "\n");
132
      Enumeration en= getChildren();
133
      while (en.hasMoreElements()) {
134
        Object qobject = en.nextElement();
135
        self.append(qobject);
136
      }
137
      self.append("  )\n");
138
      return self.toString();
139
    }
140
  }
0 141

  
src/edu/ucsb/nceas/metacat/QueryTerm.java
1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents a structured query, and can be 
4
 *             constructed from an XML serialization conforming to 
5
 *             pathquery.dtd. The printSQL() method can be used to print 
6
 *             a SQL serialization of the query.
7
 *  Copyright: 2000 Regents of the University of California and the
8
 *             National Center for Ecological Analysis and Synthesis
9
 *    Authors: Matt Jones
10
 *    Release: @release@
11
 *
12
 *   '$Author$'
13
 *     '$Date$'
14
 * '$Revision$'
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 2 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
 */
30

  
31
package edu.ucsb.nceas.metacat;
32

  
33
import edu.ucsb.nceas.dbadapter.*;
34

  
35
import java.io.*;
36
import java.util.Hashtable;
37
import java.util.Stack;
38
import java.util.Vector;
39
import java.util.Enumeration;
40

  
41
/** a utility class that represents a single term in a query */
42
public class QueryTerm {
43
    private boolean casesensitive = false;
44
    private String searchmode = null;
45
    private String value = null;
46
    private String pathexpr = null;
47
    private boolean percentageSymbol = false;
48
    private int countPercentageSearchItem = 0;
49
   
50

  
51
    /**
52
     * Construct a new instance of a query term for a free text search
53
     * (using the value only)
54
     *
55
     * @param casesensitive flag indicating whether case is used to match
56
     * @param searchmode determines what kind of substring match is performed
57
     *        (one of starts-with|ends-with|contains|matches-exactly)
58
     * @param value the text value to match
59
     */
60
    public QueryTerm(boolean casesensitive, String searchmode, 
61
                     String value) {
62
      this.casesensitive = casesensitive;
63
      this.searchmode = searchmode;
64
      this.value = value;
65
    }
66

  
67
    /**
68
     * Construct a new instance of a query term for a structured search
69
     * (matching the value only for those nodes in the pathexpr)
70
     *
71
     * @param casesensitive flag indicating whether case is used to match
72
     * @param searchmode determines what kind of substring match is performed
73
     *        (one of starts-with|ends-with|contains|matches-exactly)
74
     * @param value the text value to match
75
     * @param pathexpr the hierarchical path to the nodes to be searched
76
     */
77
    public QueryTerm(boolean casesensitive, String searchmode, 
78
                     String value, String pathexpr) {
79
      this(casesensitive, searchmode, value);
80
      this.pathexpr = pathexpr;
81
    }
82

  
83
    /** determine if the QueryTerm is case sensitive */
84
    public boolean isCaseSensitive() {
85
      return casesensitive;
86
    }
87

  
88
    /** get the searchmode parameter */
89
    public String getSearchMode() {
90
      return searchmode;
91
    }
92
 
93
    /** get the Value parameter */
94
    public String getValue() {
95
      return value;
96
    }
97

  
98
    /** get the path expression parameter */
99
    public String getPathExpression() {
100
      return pathexpr;
101
    }
102
    
103
    /** get the percentage count for one query term*/
104
    public int getPercentageSymbolCount()
105
    {
106
      return countPercentageSearchItem;
107
    }
108

  
109
    /**
110
     * create a SQL serialization of the query that this instance represents
111
     */
112
    public String printSQL(boolean useXMLIndex) {
113
      StringBuffer self = new StringBuffer();
114

  
115
      // Uppercase the search string if case match is not important
116
      String casevalue = null;
117
      String nodedataterm = null;
118

  
119
      if (casesensitive) {
120
        nodedataterm = "nodedata";
121
        casevalue = value;
122
      } else {
123
        nodedataterm = "UPPER(nodedata)";
124
        casevalue = value.toUpperCase();
125
      }
126

  
127
      // Add appropriate wildcards to search string
128
      //String searchvalue = null;
129
      String searchexpr = null;
130
      if (searchmode.equals("starts-with")) {
131
        //searchvalue = casevalue + "%";
132
        searchexpr = nodedataterm + " LIKE '" + casevalue + "%' ";
133
      } else if (searchmode.equals("ends-with")) {
134
        //searchvalue = "%" + casevalue;
135
        searchexpr = nodedataterm + " LIKE '%" + casevalue + "' ";
136
      } else if (searchmode.equals("contains")) {
137
        //searchvalue = "%" + casevalue + "%";
138
        if (!casevalue.equals("%"))
139
        {
140
          searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' ";
141
        }
142
        else
143
        {
144
          searchexpr = nodedataterm + " LIKE '" + casevalue + "' ";
145
          // find percentage symbol
146
          percentageSymbol = true;
147
        }
148
      } else if (searchmode.equals("equals")) {
149
        //searchvalue = casevalue;
150
        searchexpr = nodedataterm + " = '" + casevalue + "' ";
151
      } else if (searchmode.equals("isnot-equal")) {
152
        //searchvalue = casevalue;
153
        searchexpr = nodedataterm + " != '" + casevalue + "' ";
154
      } else { 
155
        //searchvalue = casevalue;
156
        String oper = null;
157
        if (searchmode.equals("greater-than")) {
158
          oper = ">";
159
        } else if (searchmode.equals("greater-than-equals")) {
160
          oper = ">=";
161
        } else if (searchmode.equals("less-than")) {
162
          oper = "<";
163
        } else if (searchmode.equals("less-than-equals")) {
164
          oper = "<=";
165
        } else {
166
          System.out.println("NOT expected case. NOT recognized operator: " +
167
                             searchmode);
168
          return null;
169
        }
170
        try {
171
          // it is number; numeric comparison
172
          // but we need to make sure there is no string in node data
173
          String getRidOfString = " AND UPPER(nodedata) = LOWER(nodedata)" +
174
                                  " AND LTRIM(nodedata) != ' ' " +
175
                                  " AND nodedata IS NOT NULL ";
176
          searchexpr = nodedataterm + " " + oper + " " +
177
                       new Double(casevalue) + " "+getRidOfString;          
178
        } catch (NumberFormatException nfe) {
179
          // these are characters; character comparison
180
          searchexpr = nodedataterm + " " + oper + " '" + casevalue + "' ";
181
        }
182
      }
183

  
184
      self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
185
      //self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
186
      self.append(searchexpr);
187
      if (pathexpr != null) 
188
      {
189
        
190
        // use XML Index
191
        if ( useXMLIndex ) 
192
        {
193
          if (!hasAttributeInPath(pathexpr))
194
          {
195
            // without attributes in path
196
            self.append("AND parentnodeid IN ");
197
          }
198
          else
199
          {
200
            // has a attribute in path
201
            String attributeName = QuerySpecification.getAttributeName(pathexpr);
202
            self.append("AND nodetype LIKE 'ATTRIBUTE' AND nodename LIKE '"+
203
                        attributeName + "' ");
204
            self.append("AND parentnodeid IN ");
205
            pathexpr = 
206
                 QuerySpecification.newPathExpressionWithOutAttribute(pathexpr);
207
            
208
          } 
209
          self.append("(SELECT nodeid FROM xml_index WHERE path LIKE " + 
210
                      "'" +  pathexpr + "') " );
211
        } 
212
        else 
213
        {
214
          // without using XML Index; using nested statements instead
215
          self.append("AND parentnodeid IN ");
216
          self.append(useNestedStatements(pathexpr));
217
        }
218
      }
219
      else if ((value.trim()).equals("%"))
220
      {
221
        //if pathexpr is null and search value is %, is a percentageSearchItem
222
        // the count number will be increase one
223
        countPercentageSearchItem++;
224
        
225
       }
226

  
227
      return self.toString();
228
    }
229
    
230
    /* A method to judge if a path have attribute */
231
    private boolean hasAttributeInPath(String path)
232
    {
233
      if (path.indexOf(QuerySpecification.ATTRIBUTESYMBOL)!=-1)
234
      {
235
        return true;
236
      }
237
      else
238
      {
239
        return false;
240
      }
241
    }
242
    
243
   
244
    
245
    /* 
246
     * Constraint the query with @pathexp without using the XML Index,
247
     * but nested SQL statements instead. The query migth be slower.
248
     */
249
    private String useNestedStatements(String pathexpr)
250
    {
251
      StringBuffer nestedStmts = new StringBuffer();
252
      Vector nodes = new Vector();
253
      String path = pathexpr;
254
      int inx = 0;
255

  
256
      do {
257
        inx = path.lastIndexOf("/");
258

  
259
        nodes.addElement(path.substring(inx+1));
260
        path = path.substring(0, Math.abs(inx));
261
      } while ( inx > 0 );
262
      
263
      // nested statements
264
      int i = 0;
265
      for (i = 0; i < nodes.size()-1; i++) {
266
        nestedStmts.append("(SELECT nodeid FROM xml_nodes" + 
267
                           " WHERE nodename LIKE '" +
268
                             (String)nodes.elementAt(i) + "'" +
269
                           " AND parentnodeid IN ");
270
      }
271
      // for the last statement: it is without " AND parentnodeid IN "
272
      nestedStmts.append("(SELECT nodeid FROM xml_nodes" + 
273
                         " WHERE nodename LIKE '" +
274
                         (String)nodes.elementAt(i) + "'" );
275
      // node.size() number of closing brackets
276
      for (i = 0; i < nodes.size(); i++) {
277
        nestedStmts.append(")");
278
      }
279

  
280

  
281

  
282
      return nestedStmts.toString();
283
    }
284

  
285
    /**
286
     * create a String description of the query that this instance represents.
287
     * This should become a way to get the XML serialization of the query.
288
     */
289
    public String toString() {
290

  
291
      return this.printSQL(true);
292
    }
293
  }
0 294

  

Also available in: Unified diff