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
 *    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 java.util.Vector;
34
35
/** a utility class that represents a single term in a query */
36 2068 jones
public class QueryTerm
37
{
38
39 1831 tao
    private boolean casesensitive = false;
40 2068 jones
41 1831 tao
    private String searchmode = null;
42 2068 jones
43 1831 tao
    private String value = null;
44 2068 jones
45 1831 tao
    private String pathexpr = null;
46 2068 jones
47 1831 tao
    private boolean percentageSymbol = false;
48 2068 jones
49 1831 tao
    private int countPercentageSearchItem = 0;
50
51
    /**
52 2068 jones
     * Construct a new instance of a query term for a free text search (using
53
     * the value only)
54 2357 sgarg
     *
55 2068 jones
     * @param casesensitive
56
     *            flag indicating whether case is used to match
57
     * @param searchmode
58
     *            determines what kind of substring match is performed (one of
59
     *            starts-with|ends-with|contains|matches-exactly)
60
     * @param value
61
     *            the text value to match
62 1831 tao
     */
63 2068 jones
    public QueryTerm(boolean casesensitive, String searchmode, String value)
64
    {
65
        this.casesensitive = casesensitive;
66
        this.searchmode = searchmode;
67
        this.value = value;
68 1831 tao
    }
69
70
    /**
71
     * Construct a new instance of a query term for a structured search
72
     * (matching the value only for those nodes in the pathexpr)
73 2357 sgarg
     *
74 2068 jones
     * @param casesensitive
75
     *            flag indicating whether case is used to match
76
     * @param searchmode
77
     *            determines what kind of substring match is performed (one of
78
     *            starts-with|ends-with|contains|matches-exactly)
79
     * @param value
80
     *            the text value to match
81
     * @param pathexpr
82
     *            the hierarchical path to the nodes to be searched
83 1831 tao
     */
84 2068 jones
    public QueryTerm(boolean casesensitive, String searchmode, String value,
85
            String pathexpr)
86
    {
87
        this(casesensitive, searchmode, value);
88
        this.pathexpr = pathexpr;
89 1831 tao
    }
90
91
    /** determine if the QueryTerm is case sensitive */
92 2068 jones
    public boolean isCaseSensitive()
93
    {
94
        return casesensitive;
95 1831 tao
    }
96
97
    /** get the searchmode parameter */
98 2068 jones
    public String getSearchMode()
99
    {
100
        return searchmode;
101 1831 tao
    }
102 2068 jones
103 1831 tao
    /** get the Value parameter */
104 2068 jones
    public String getValue()
105
    {
106
        return value;
107 1831 tao
    }
108
109
    /** get the path expression parameter */
110 2068 jones
    public String getPathExpression()
111
    {
112
        return pathexpr;
113 1831 tao
    }
114 2068 jones
115
    /** get the percentage count for one query term */
116 1831 tao
    public int getPercentageSymbolCount()
117
    {
118 2068 jones
        return countPercentageSearchItem;
119 1831 tao
    }
120
121
    /**
122
     * create a SQL serialization of the query that this instance represents
123
     */
124 2068 jones
    public String printSQL(boolean useXMLIndex)
125
    {
126
        StringBuffer self = new StringBuffer();
127 1831 tao
128 2068 jones
        // Uppercase the search string if case match is not important
129
        String casevalue = null;
130
        String nodedataterm = null;
131 2654 sgarg
        boolean notEqual = false;
132 2068 jones
        if (casesensitive) {
133
            nodedataterm = "nodedata";
134
            casevalue = value;
135
        } else {
136
            nodedataterm = "UPPER(nodedata)";
137
            casevalue = value.toUpperCase();
138
        }
139 1831 tao
140 2068 jones
        // Add appropriate wildcards to search string
141
        String searchexpr = null;
142
        if (searchmode.equals("starts-with")) {
143
            searchexpr = nodedataterm + " LIKE '" + casevalue + "%' ";
144
        } else if (searchmode.equals("ends-with")) {
145
            searchexpr = nodedataterm + " LIKE '%" + casevalue + "' ";
146
        } else if (searchmode.equals("contains")) {
147
            if (!casevalue.equals("%")) {
148
                searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' ";
149
            } else {
150
                searchexpr = nodedataterm + " LIKE '" + casevalue + "' ";
151
                // find percentage symbol
152
                percentageSymbol = true;
153
            }
154
        } else if (searchmode.equals("not-contains")) {
155 2654 sgarg
        	notEqual = true;
156
            searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' ";
157 2068 jones
        } else if (searchmode.equals("equals")) {
158
            searchexpr = nodedataterm + " = '" + casevalue + "' ";
159
        } else if (searchmode.equals("isnot-equal")) {
160 2654 sgarg
        	notEqual = true;
161
            searchexpr = nodedataterm + " = '" + casevalue + "' ";
162 1831 tao
        } else {
163 2068 jones
            String oper = null;
164
            if (searchmode.equals("greater-than")) {
165
                oper = ">";
166 2357 sgarg
                nodedataterm = "nodedatanumerical";
167 2068 jones
            } else if (searchmode.equals("greater-than-equals")) {
168
                oper = ">=";
169 2357 sgarg
                nodedataterm = "nodedatanumerical";
170 2068 jones
            } else if (searchmode.equals("less-than")) {
171
                oper = "<";
172 2357 sgarg
                nodedataterm = "nodedatanumerical";
173 2068 jones
            } else if (searchmode.equals("less-than-equals")) {
174
                oper = "<=";
175 2357 sgarg
                nodedataterm = "nodedatanumerical";
176 2068 jones
            } else {
177
                System.out
178
                        .println("NOT expected case. NOT recognized operator: "
179
                                + searchmode);
180
                return null;
181
            }
182
            try {
183
                // it is number; numeric comparison
184
                // but we need to make sure there is no string in node data
185
                searchexpr = nodedataterm + " " + oper + " "
186 2357 sgarg
                        + new Double(casevalue) + " ";
187 2068 jones
            } catch (NumberFormatException nfe) {
188
                // these are characters; character comparison
189
                searchexpr = nodedataterm + " " + oper + " '" + casevalue
190
                        + "' ";
191
            }
192 1831 tao
        }
193
194 2068 jones
195 2522 sgarg
        // to check xml_path_index can be used
196
        boolean usePathIndex = false;
197
198
        // if pathexpr has been specified in metacat.properties for indexing
199
        if(pathexpr != null){
200
            if(MetaCatUtil.pathsForIndexing.contains(pathexpr)){
201
                usePathIndex = true;
202
            }
203
        }
204
205
        if(usePathIndex){
206
            // using xml_path_index table.....
207 2654 sgarg
        	if(notEqual == true){
208
        		self.append("SELECT DISTINCT docid from xml_path_index WHERE");
209
        		self.append(" docid NOT IN (Select docid FROM xml_path_index WHERE ");
210
        		self.append(searchexpr);
211
        		self.append("AND path LIKE '" + pathexpr + "') ");
212
        	} else {
213
        		self.append("SELECT DISTINCT docid FROM xml_path_index WHERE ");
214
        		self.append(searchexpr);
215
        		self.append("AND path LIKE '" + pathexpr + "' ");
216
        	}
217 2522 sgarg
218
        } else {
219
            // using xml_nodes and xml_index tables
220
221 2654 sgarg
        	if(notEqual == true){
222
        		self.append("SELECT DISTINCT docid from xml_nodes WHERE");
223
        		self.append(" docid NOT IN (Select docid FROM xml_nodes WHERE ");
224
        	} else {
225 2693 sgarg
        		self.append("(SELECT DISTINCT docid FROM xml_nodes WHERE ");
226 2654 sgarg
        	}
227
        	self.append(searchexpr);
228
229 2522 sgarg
            if (pathexpr != null) {
230
231
                // use XML Index
232
                if (useXMLIndex) {
233
                    if (!hasAttributeInPath(pathexpr)) {
234
                        // without attributes in path
235
                        self.append("AND parentnodeid IN ");
236 2654 sgarg
                    } else {
237 2522 sgarg
                        // has a attribute in path
238
                        String attributeName = QuerySpecification
239 2068 jones
                            .getAttributeName(pathexpr);
240 2522 sgarg
                        self.append(
241
                            "AND nodetype LIKE 'ATTRIBUTE' AND nodename LIKE '"
242
                            + attributeName + "' ");
243
                        // and the path expression includes element content other than
244
                        // just './' or '../'
245
                        if ( (!pathexpr.startsWith(QuerySpecification.
246
                            ATTRIBUTESYMBOL)) &&
247
                            (!pathexpr.startsWith("./" +
248
                                                  QuerySpecification.ATTRIBUTESYMBOL)) &&
249
                            (!pathexpr.startsWith("../" +
250
                                                  QuerySpecification.ATTRIBUTESYMBOL))) {
251 2068 jones
252 2522 sgarg
                            self.append("AND parentnodeid IN ");
253
                            pathexpr = QuerySpecification
254
                                .newPathExpressionWithOutAttribute(pathexpr);
255
                        }
256 2459 cjones
                    }
257 2522 sgarg
                    self.append(
258
                        "(SELECT nodeid FROM xml_index WHERE path LIKE "
259
                        + "'" + pathexpr + "') ");
260 2068 jones
                }
261 2522 sgarg
                else {
262
                    // without using XML Index; using nested statements instead
263
                    self.append("AND parentnodeid IN ");
264
                    self.append(useNestedStatements(pathexpr));
265
                }
266 2068 jones
            }
267 2522 sgarg
            else if ( (value.trim()).equals("%")) {
268
                //if pathexpr is null and search value is %, is a
269
                // percentageSearchItem
270
                // the count number will be increase one
271
                countPercentageSearchItem++;
272 2068 jones
273 2522 sgarg
            }
274 2654 sgarg
            self.append(") ");
275 1831 tao
        }
276
277 2068 jones
        return self.toString();
278 1831 tao
    }
279 2068 jones
280
    /** A method to judge if a path have attribute */
281 1831 tao
    private boolean hasAttributeInPath(String path)
282
    {
283 2068 jones
        if (path.indexOf(QuerySpecification.ATTRIBUTESYMBOL) != -1) {
284
            return true;
285
        } else {
286
            return false;
287
        }
288 1831 tao
    }
289 2068 jones
290
    /*
291
     * Constraint the query with @pathexp without using the XML Index, but
292
     * nested SQL statements instead. The query migth be slower.
293 1831 tao
     */
294 2069 jones
    public static String useNestedStatements(String pathexpr)
295 1831 tao
    {
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
}