Project

General

Profile

1 1831 tao
/**
2
 *  '$RCSfile$'
3 2357 sgarg
 *    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 1831 tao
 *             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
 *
11
 *   '$Author$'
12
 *     '$Date$'
13
 * '$Revision$'
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29
30
package edu.ucsb.nceas.metacat;
31
32
import java.util.Vector;
33
34
/** a utility class that represents a single term in a query */
35 2068 jones
public class QueryTerm
36
{
37
38 1831 tao
    private boolean casesensitive = false;
39 2068 jones
40 1831 tao
    private String searchmode = null;
41 2068 jones
42 1831 tao
    private String value = null;
43 2068 jones
44 1831 tao
    private String pathexpr = null;
45 2068 jones
46 1831 tao
    private boolean percentageSymbol = false;
47 2068 jones
48 1831 tao
    private int countPercentageSearchItem = 0;
49
50
    /**
51 2068 jones
     * Construct a new instance of a query term for a free text search (using
52
     * the value only)
53 2357 sgarg
     *
54 2068 jones
     * @param casesensitive
55
     *            flag indicating whether case is used to match
56
     * @param searchmode
57
     *            determines what kind of substring match is performed (one of
58
     *            starts-with|ends-with|contains|matches-exactly)
59
     * @param value
60
     *            the text value to match
61 1831 tao
     */
62 2068 jones
    public QueryTerm(boolean casesensitive, String searchmode, String value)
63
    {
64
        this.casesensitive = casesensitive;
65
        this.searchmode = searchmode;
66
        this.value = value;
67 1831 tao
    }
68
69
    /**
70
     * Construct a new instance of a query term for a structured search
71
     * (matching the value only for those nodes in the pathexpr)
72 2357 sgarg
     *
73 2068 jones
     * @param casesensitive
74
     *            flag indicating whether case is used to match
75
     * @param searchmode
76
     *            determines what kind of substring match is performed (one of
77
     *            starts-with|ends-with|contains|matches-exactly)
78
     * @param value
79
     *            the text value to match
80
     * @param pathexpr
81
     *            the hierarchical path to the nodes to be searched
82 1831 tao
     */
83 2068 jones
    public QueryTerm(boolean casesensitive, String searchmode, String value,
84
            String pathexpr)
85
    {
86
        this(casesensitive, searchmode, value);
87
        this.pathexpr = pathexpr;
88 1831 tao
    }
89
90
    /** determine if the QueryTerm is case sensitive */
91 2068 jones
    public boolean isCaseSensitive()
92
    {
93
        return casesensitive;
94 1831 tao
    }
95
96
    /** get the searchmode parameter */
97 2068 jones
    public String getSearchMode()
98
    {
99
        return searchmode;
100 1831 tao
    }
101 2068 jones
102 1831 tao
    /** get the Value parameter */
103 2068 jones
    public String getValue()
104
    {
105
        return value;
106 1831 tao
    }
107
108
    /** get the path expression parameter */
109 2068 jones
    public String getPathExpression()
110
    {
111
        return pathexpr;
112 1831 tao
    }
113 2068 jones
114
    /** get the percentage count for one query term */
115 1831 tao
    public int getPercentageSymbolCount()
116
    {
117 2068 jones
        return countPercentageSearchItem;
118 1831 tao
    }
119
120
    /**
121
     * create a SQL serialization of the query that this instance represents
122
     */
123 2068 jones
    public String printSQL(boolean useXMLIndex)
124
    {
125
        StringBuffer self = new StringBuffer();
126 1831 tao
127 2068 jones
        // Uppercase the search string if case match is not important
128
        String casevalue = null;
129
        String nodedataterm = null;
130 2654 sgarg
        boolean notEqual = false;
131 2068 jones
        if (casesensitive) {
132
            nodedataterm = "nodedata";
133
            casevalue = value;
134
        } else {
135
            nodedataterm = "UPPER(nodedata)";
136
            casevalue = value.toUpperCase();
137
        }
138 1831 tao
139 2068 jones
        // Add appropriate wildcards to search string
140
        String searchexpr = null;
141
        if (searchmode.equals("starts-with")) {
142 3053 jones
            searchexpr = nodedataterm + " LIKE '" + casevalue + "%' ";
143 2068 jones
        } else if (searchmode.equals("ends-with")) {
144 3053 jones
            searchexpr = nodedataterm + " LIKE '%" + casevalue + "' ";
145 2068 jones
        } else if (searchmode.equals("contains")) {
146
            if (!casevalue.equals("%")) {
147 3053 jones
                searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' ";
148 2068 jones
            } else {
149 3053 jones
                searchexpr = nodedataterm + " LIKE '" + casevalue + "' ";
150 2068 jones
                // find percentage symbol
151
                percentageSymbol = true;
152
            }
153
        } else if (searchmode.equals("not-contains")) {
154 2654 sgarg
        	notEqual = true;
155 3053 jones
            searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' ";
156 2068 jones
        } else if (searchmode.equals("equals")) {
157
            searchexpr = nodedataterm + " = '" + casevalue + "' ";
158
        } else if (searchmode.equals("isnot-equal")) {
159 2654 sgarg
        	notEqual = true;
160
            searchexpr = nodedataterm + " = '" + casevalue + "' ";
161 1831 tao
        } else {
162 2068 jones
            String oper = null;
163
            if (searchmode.equals("greater-than")) {
164
                oper = ">";
165 2357 sgarg
                nodedataterm = "nodedatanumerical";
166 2068 jones
            } else if (searchmode.equals("greater-than-equals")) {
167
                oper = ">=";
168 2357 sgarg
                nodedataterm = "nodedatanumerical";
169 2068 jones
            } else if (searchmode.equals("less-than")) {
170
                oper = "<";
171 2357 sgarg
                nodedataterm = "nodedatanumerical";
172 2068 jones
            } else if (searchmode.equals("less-than-equals")) {
173
                oper = "<=";
174 2357 sgarg
                nodedataterm = "nodedatanumerical";
175 2068 jones
            } else {
176
                System.out
177
                        .println("NOT expected case. NOT recognized operator: "
178
                                + searchmode);
179
                return null;
180
            }
181
            try {
182
                // it is number; numeric comparison
183
                // but we need to make sure there is no string in node data
184
                searchexpr = nodedataterm + " " + oper + " "
185 2357 sgarg
                        + new Double(casevalue) + " ";
186 2068 jones
            } catch (NumberFormatException nfe) {
187
                // these are characters; character comparison
188
                searchexpr = nodedataterm + " " + oper + " '" + casevalue
189
                        + "' ";
190
            }
191 1831 tao
        }
192
193 2068 jones
194 2522 sgarg
        // to check xml_path_index can be used
195
        boolean usePathIndex = false;
196
197
        // if pathexpr has been specified in metacat.properties for indexing
198
        if(pathexpr != null){
199
            if(MetaCatUtil.pathsForIndexing.contains(pathexpr)){
200
                usePathIndex = true;
201
            }
202
        }
203
204
        if(usePathIndex){
205
            // using xml_path_index table.....
206 2654 sgarg
        	if(notEqual == true){
207
        		self.append("SELECT DISTINCT docid from xml_path_index WHERE");
208
        		self.append(" docid NOT IN (Select docid FROM xml_path_index WHERE ");
209
        		self.append(searchexpr);
210 3053 jones
        		self.append("AND path LIKE '" + pathexpr + "') ");
211 2654 sgarg
        	} else {
212
        		self.append("SELECT DISTINCT docid FROM xml_path_index WHERE ");
213
        		self.append(searchexpr);
214 3053 jones
        		self.append("AND path LIKE '" + pathexpr + "' ");
215 2654 sgarg
        	}
216 2522 sgarg
217
        } else {
218
            // using xml_nodes and xml_index tables
219
220 2654 sgarg
        	if(notEqual == true){
221
        		self.append("SELECT DISTINCT docid from xml_nodes WHERE");
222
        		self.append(" docid NOT IN (Select docid FROM xml_nodes WHERE ");
223
        	} else {
224 2693 sgarg
        		self.append("(SELECT DISTINCT docid FROM xml_nodes WHERE ");
225 2654 sgarg
        	}
226
        	self.append(searchexpr);
227
228 2522 sgarg
            if (pathexpr != null) {
229
230
                // use XML Index
231
                if (useXMLIndex) {
232
                    if (!hasAttributeInPath(pathexpr)) {
233
                        // without attributes in path
234
                        self.append("AND parentnodeid IN ");
235 2654 sgarg
                    } else {
236 2522 sgarg
                        // has a attribute in path
237
                        String attributeName = QuerySpecification
238 2068 jones
                            .getAttributeName(pathexpr);
239 2522 sgarg
                        self.append(
240
                            "AND nodetype LIKE 'ATTRIBUTE' AND nodename LIKE '"
241
                            + attributeName + "' ");
242
                        // and the path expression includes element content other than
243
                        // just './' or '../'
244
                        if ( (!pathexpr.startsWith(QuerySpecification.
245
                            ATTRIBUTESYMBOL)) &&
246
                            (!pathexpr.startsWith("./" +
247
                                                  QuerySpecification.ATTRIBUTESYMBOL)) &&
248
                            (!pathexpr.startsWith("../" +
249
                                                  QuerySpecification.ATTRIBUTESYMBOL))) {
250 2068 jones
251 2522 sgarg
                            self.append("AND parentnodeid IN ");
252
                            pathexpr = QuerySpecification
253
                                .newPathExpressionWithOutAttribute(pathexpr);
254
                        }
255 2459 cjones
                    }
256 2522 sgarg
                    self.append(
257
                        "(SELECT nodeid FROM xml_index WHERE path LIKE "
258
                        + "'" + pathexpr + "') ");
259 2068 jones
                }
260 2522 sgarg
                else {
261
                    // without using XML Index; using nested statements instead
262
                    self.append("AND parentnodeid IN ");
263
                    self.append(useNestedStatements(pathexpr));
264
                }
265 2068 jones
            }
266 2522 sgarg
            else if ( (value.trim()).equals("%")) {
267
                //if pathexpr is null and search value is %, is a
268
                // percentageSearchItem
269
                // the count number will be increase one
270
                countPercentageSearchItem++;
271 2068 jones
272 2522 sgarg
            }
273 2654 sgarg
            self.append(") ");
274 1831 tao
        }
275
276 2068 jones
        return self.toString();
277 1831 tao
    }
278 2068 jones
279
    /** A method to judge if a path have attribute */
280 1831 tao
    private boolean hasAttributeInPath(String path)
281
    {
282 2068 jones
        if (path.indexOf(QuerySpecification.ATTRIBUTESYMBOL) != -1) {
283
            return true;
284
        } else {
285
            return false;
286
        }
287 1831 tao
    }
288 2068 jones
289
    /*
290
     * Constraint the query with @pathexp without using the XML Index, but
291
     * nested SQL statements instead. The query migth be slower.
292 1831 tao
     */
293 2069 jones
    public static String useNestedStatements(String pathexpr)
294 1831 tao
    {
295 3211 berkley
      System.out.println("pathexpr: " + pathexpr);
296 2068 jones
        StringBuffer nestedStmts = new StringBuffer();
297
        Vector nodes = new Vector();
298
        String path = pathexpr;
299
        int inx = 0;
300 1831 tao
301 2068 jones
        do {
302
            inx = path.lastIndexOf("/");
303 1831 tao
304 2068 jones
            nodes.addElement(path.substring(inx + 1));
305
            path = path.substring(0, Math.abs(inx));
306
        } while (inx > 0);
307 1831 tao
308 2068 jones
        // nested statements
309
        int i = 0;
310
        for (i = 0; i < nodes.size() - 1; i++) {
311
            nestedStmts.append("(SELECT nodeid FROM xml_nodes"
312
                    + " WHERE nodename LIKE '" + (String) nodes.elementAt(i)
313
                    + "'" + " AND parentnodeid IN ");
314
        }
315
        // for the last statement: it is without " AND parentnodeid IN "
316
        nestedStmts.append("(SELECT nodeid FROM xml_nodes"
317
                + " WHERE nodename LIKE '" + (String) nodes.elementAt(i) + "'");
318
        // node.size() number of closing brackets
319
        for (i = 0; i < nodes.size(); i++) {
320
            nestedStmts.append(")");
321
        }
322 1831 tao
323 2068 jones
        return nestedStmts.toString();
324 1831 tao
    }
325
326
    /**
327
     * create a String description of the query that this instance represents.
328
     * This should become a way to get the XML serialization of the query.
329
     */
330 2068 jones
    public String toString()
331
    {
332 1831 tao
333 2068 jones
        return this.printSQL(true);
334 1831 tao
    }
335 2068 jones
}