Revision 1832
Added by Jing Tao about 21 years ago
src/edu/ucsb/nceas/metacat/QuerySpecification.java | ||
---|---|---|
81 | 81 |
private String accNumberSeparator = null; |
82 | 82 |
private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter; |
83 | 83 |
|
84 |
private int countPercentageSearchItem = 0; |
|
84 |
|
|
85 | 85 |
private boolean percentageSearch = false; |
86 | 86 |
|
87 | 87 |
private String userName = null; |
... | ... | |
483 | 483 |
{ |
484 | 484 |
return query; |
485 | 485 |
} |
486 |
|
|
487 |
/** |
|
488 |
* set the querygroup |
|
489 |
*/ |
|
490 |
public void setQueryGroup(QueryGroup group) |
|
491 |
{ |
|
492 |
query = group; |
|
493 |
} |
|
486 | 494 |
|
487 | 495 |
/** |
488 | 496 |
* Set up the SAX parser for reading the XML serialized query |
... | ... | |
705 | 713 |
|
706 | 714 |
// if there is only one percentage search item, this query is a percentage |
707 | 715 |
// search query |
708 |
if (countPercentageSearchItem ==1) |
|
716 |
MetaCatUtil.debugMessage("percentage number: " + |
|
717 |
query.getPercentageSymbolCount(), 35); |
|
718 |
if (query.getPercentageSymbolCount() ==1 ) |
|
709 | 719 |
{ |
710 |
|
|
720 |
MetaCatUtil.debugMessage("it is a percentage search", 30); |
|
711 | 721 |
percentageSearch =true; |
712 | 722 |
} |
713 | 723 |
|
... | ... | |
1013 | 1023 |
return attributeName; |
1014 | 1024 |
} |
1015 | 1025 |
|
1016 |
/** a utility class that represents a group of terms in a query */ |
|
1017 |
private class QueryGroup { |
|
1018 |
private String operator = null; // indicates how query terms are combined |
|
1019 |
private Vector children = null; // the list of query terms and groups |
|
1020 |
|
|
1021 |
/** |
|
1022 |
* construct a new QueryGroup |
|
1023 |
* |
|
1024 |
* @param operator the boolean conector used to connect query terms |
|
1025 |
* in this query group |
|
1026 |
*/ |
|
1027 |
public QueryGroup(String operator) { |
|
1028 |
this.operator = operator; |
|
1029 |
children = new Vector(); |
|
1030 |
} |
|
1031 |
|
|
1032 |
/** |
|
1033 |
* Add a child QueryGroup to this QueryGroup |
|
1034 |
* |
|
1035 |
* @param qgroup the query group to be added to the list of terms |
|
1036 |
*/ |
|
1037 |
public void addChild(QueryGroup qgroup) { |
|
1038 |
children.add((Object)qgroup); |
|
1039 |
} |
|
1040 |
|
|
1041 |
/** |
|
1042 |
* Add a child QueryTerm to this QueryGroup |
|
1043 |
* |
|
1044 |
* @param qterm the query term to be added to the list of terms |
|
1045 |
*/ |
|
1046 |
public void addChild(QueryTerm qterm) { |
|
1047 |
children.add((Object)qterm); |
|
1048 |
} |
|
1049 |
|
|
1050 |
/** |
|
1051 |
* Retrieve an Enumeration of query terms for this QueryGroup |
|
1052 |
*/ |
|
1053 |
public Enumeration getChildren() { |
|
1054 |
return children.elements(); |
|
1055 |
} |
|
1056 |
|
|
1057 |
/** |
|
1058 |
* create a SQL serialization of the query that this instance represents |
|
1059 |
*/ |
|
1060 |
public String printSQL(boolean useXMLIndex) { |
|
1061 |
StringBuffer self = new StringBuffer(); |
|
1062 |
boolean first = true; |
|
1063 |
|
|
1064 |
self.append("("); |
|
1065 |
|
|
1066 |
Enumeration en= getChildren(); |
|
1067 |
while (en.hasMoreElements()) { |
|
1068 |
Object qobject = en.nextElement(); |
|
1069 |
if (first) { |
|
1070 |
first = false; |
|
1071 |
} else { |
|
1072 |
self.append(" " + operator + " "); |
|
1073 |
} |
|
1074 |
if (qobject instanceof QueryGroup) { |
|
1075 |
QueryGroup qg = (QueryGroup)qobject; |
|
1076 |
self.append(qg.printSQL(useXMLIndex)); |
|
1077 |
} else if (qobject instanceof QueryTerm) { |
|
1078 |
QueryTerm qt = (QueryTerm)qobject; |
|
1079 |
self.append(qt.printSQL(useXMLIndex)); |
|
1080 |
} else { |
|
1081 |
System.err.println("qobject wrong type: fatal error"); |
|
1082 |
} |
|
1083 |
} |
|
1084 |
self.append(") \n"); |
|
1085 |
return self.toString(); |
|
1086 |
} |
|
1087 |
|
|
1088 |
/** |
|
1089 |
* create a String description of the query that this instance represents. |
|
1090 |
* This should become a way to get the XML serialization of the query. |
|
1091 |
*/ |
|
1092 |
public String toString() { |
|
1093 |
StringBuffer self = new StringBuffer(); |
|
1094 |
|
|
1095 |
self.append(" (Query group operator=" + operator + "\n"); |
|
1096 |
Enumeration en= getChildren(); |
|
1097 |
while (en.hasMoreElements()) { |
|
1098 |
Object qobject = en.nextElement(); |
|
1099 |
self.append(qobject); |
|
1100 |
} |
|
1101 |
self.append(" )\n"); |
|
1102 |
return self.toString(); |
|
1103 |
} |
|
1104 |
} |
|
1105 |
|
|
1106 |
/** a utility class that represents a single term in a query */ |
|
1107 |
private class QueryTerm { |
|
1108 |
private boolean casesensitive = false; |
|
1109 |
private String searchmode = null; |
|
1110 |
private String value = null; |
|
1111 |
private String pathexpr = null; |
|
1112 |
private boolean percentageSymbol = false; |
|
1113 |
|
|
1114 |
|
|
1115 |
/** |
|
1116 |
* Construct a new instance of a query term for a free text search |
|
1117 |
* (using the value only) |
|
1118 |
* |
|
1119 |
* @param casesensitive flag indicating whether case is used to match |
|
1120 |
* @param searchmode determines what kind of substring match is performed |
|
1121 |
* (one of starts-with|ends-with|contains|matches-exactly) |
|
1122 |
* @param value the text value to match |
|
1123 |
*/ |
|
1124 |
public QueryTerm(boolean casesensitive, String searchmode, |
|
1125 |
String value) { |
|
1126 |
this.casesensitive = casesensitive; |
|
1127 |
this.searchmode = searchmode; |
|
1128 |
this.value = value; |
|
1129 |
} |
|
1130 |
|
|
1131 |
/** |
|
1132 |
* Construct a new instance of a query term for a structured search |
|
1133 |
* (matching the value only for those nodes in the pathexpr) |
|
1134 |
* |
|
1135 |
* @param casesensitive flag indicating whether case is used to match |
|
1136 |
* @param searchmode determines what kind of substring match is performed |
|
1137 |
* (one of starts-with|ends-with|contains|matches-exactly) |
|
1138 |
* @param value the text value to match |
|
1139 |
* @param pathexpr the hierarchical path to the nodes to be searched |
|
1140 |
*/ |
|
1141 |
public QueryTerm(boolean casesensitive, String searchmode, |
|
1142 |
String value, String pathexpr) { |
|
1143 |
this(casesensitive, searchmode, value); |
|
1144 |
this.pathexpr = pathexpr; |
|
1145 |
} |
|
1146 |
|
|
1147 |
/** determine if the QueryTerm is case sensitive */ |
|
1148 |
public boolean isCaseSensitive() { |
|
1149 |
return casesensitive; |
|
1150 |
} |
|
1151 |
|
|
1152 |
/** get the searchmode parameter */ |
|
1153 |
public String getSearchMode() { |
|
1154 |
return searchmode; |
|
1155 |
} |
|
1156 | 1026 |
|
1157 |
/** get the Value parameter */ |
|
1158 |
public String getValue() { |
|
1159 |
return value; |
|
1160 |
} |
|
1161 |
|
|
1162 |
/** get the path expression parameter */ |
|
1163 |
public String getPathExpression() { |
|
1164 |
return pathexpr; |
|
1165 |
} |
|
1166 |
|
|
1167 |
/** |
|
1168 |
* create a SQL serialization of the query that this instance represents |
|
1169 |
*/ |
|
1170 |
public String printSQL(boolean useXMLIndex) { |
|
1171 |
StringBuffer self = new StringBuffer(); |
|
1172 |
|
|
1173 |
// Uppercase the search string if case match is not important |
|
1174 |
String casevalue = null; |
|
1175 |
String nodedataterm = null; |
|
1176 |
|
|
1177 |
if (casesensitive) { |
|
1178 |
nodedataterm = "nodedata"; |
|
1179 |
casevalue = value; |
|
1180 |
} else { |
|
1181 |
nodedataterm = "UPPER(nodedata)"; |
|
1182 |
casevalue = value.toUpperCase(); |
|
1183 |
} |
|
1184 |
|
|
1185 |
// Add appropriate wildcards to search string |
|
1186 |
//String searchvalue = null; |
|
1187 |
String searchexpr = null; |
|
1188 |
if (searchmode.equals("starts-with")) { |
|
1189 |
//searchvalue = casevalue + "%"; |
|
1190 |
searchexpr = nodedataterm + " LIKE '" + casevalue + "%' "; |
|
1191 |
} else if (searchmode.equals("ends-with")) { |
|
1192 |
//searchvalue = "%" + casevalue; |
|
1193 |
searchexpr = nodedataterm + " LIKE '%" + casevalue + "' "; |
|
1194 |
} else if (searchmode.equals("contains")) { |
|
1195 |
//searchvalue = "%" + casevalue + "%"; |
|
1196 |
if (!casevalue.equals("%")) |
|
1197 |
{ |
|
1198 |
searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' "; |
|
1199 |
} |
|
1200 |
else |
|
1201 |
{ |
|
1202 |
searchexpr = nodedataterm + " LIKE '" + casevalue + "' "; |
|
1203 |
// find percentage symbol |
|
1204 |
percentageSymbol = true; |
|
1205 |
} |
|
1206 |
} else if (searchmode.equals("equals")) { |
|
1207 |
//searchvalue = casevalue; |
|
1208 |
searchexpr = nodedataterm + " = '" + casevalue + "' "; |
|
1209 |
} else if (searchmode.equals("isnot-equal")) { |
|
1210 |
//searchvalue = casevalue; |
|
1211 |
searchexpr = nodedataterm + " != '" + casevalue + "' "; |
|
1212 |
} else { |
|
1213 |
//searchvalue = casevalue; |
|
1214 |
String oper = null; |
|
1215 |
if (searchmode.equals("greater-than")) { |
|
1216 |
oper = ">"; |
|
1217 |
} else if (searchmode.equals("greater-than-equals")) { |
|
1218 |
oper = ">="; |
|
1219 |
} else if (searchmode.equals("less-than")) { |
|
1220 |
oper = "<"; |
|
1221 |
} else if (searchmode.equals("less-than-equals")) { |
|
1222 |
oper = "<="; |
|
1223 |
} else { |
|
1224 |
System.out.println("NOT expected case. NOT recognized operator: " + |
|
1225 |
searchmode); |
|
1226 |
return null; |
|
1227 |
} |
|
1228 |
try { |
|
1229 |
// it is number; numeric comparison |
|
1230 |
// but we need to make sure there is no string in node data |
|
1231 |
String getRidOfString = " AND UPPER(nodedata) = LOWER(nodedata)" + |
|
1232 |
" AND LTRIM(nodedata) != ' ' " + |
|
1233 |
" AND nodedata IS NOT NULL "; |
|
1234 |
searchexpr = nodedataterm + " " + oper + " " + |
|
1235 |
new Double(casevalue) + " "+getRidOfString; |
|
1236 |
} catch (NumberFormatException nfe) { |
|
1237 |
// these are characters; character comparison |
|
1238 |
searchexpr = nodedataterm + " " + oper + " '" + casevalue + "' "; |
|
1239 |
} |
|
1240 |
} |
|
1241 |
|
|
1242 |
self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n"); |
|
1243 |
//self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' "); |
|
1244 |
self.append(searchexpr); |
|
1245 |
|
|
1246 |
if (pathexpr != null) |
|
1247 |
{ |
|
1248 |
|
|
1249 |
// use XML Index |
|
1250 |
if ( useXMLIndex ) |
|
1251 |
{ |
|
1252 |
if (!hasAttributeInPath(pathexpr)) |
|
1253 |
{ |
|
1254 |
// without attributes in path |
|
1255 |
self.append("AND parentnodeid IN "); |
|
1256 |
} |
|
1257 |
else |
|
1258 |
{ |
|
1259 |
// has a attribute in path |
|
1260 |
String attributeName = getAttributeName(pathexpr); |
|
1261 |
self.append("AND nodetype LIKE 'ATTRIBUTE' AND nodename LIKE '"+ |
|
1262 |
attributeName + "' "); |
|
1263 |
self.append("AND parentnodeid IN "); |
|
1264 |
pathexpr = newPathExpressionWithOutAttribute(pathexpr); |
|
1265 |
|
|
1266 |
} |
|
1267 |
self.append("(SELECT nodeid FROM xml_index WHERE path LIKE " + |
|
1268 |
"'" + pathexpr + "') " ); |
|
1269 |
} |
|
1270 |
else |
|
1271 |
{ |
|
1272 |
// without using XML Index; using nested statements instead |
|
1273 |
self.append("AND parentnodeid IN "); |
|
1274 |
self.append(useNestedStatements(pathexpr)); |
|
1275 |
} |
|
1276 |
} |
|
1277 |
else |
|
1278 |
{ |
|
1279 |
//if pathexpr is null and search value is %, is a percentageSearchItem |
|
1280 |
// the count number will be increase one |
|
1281 |
countPercentageSearchItem++; |
|
1282 |
|
|
1283 |
} |
|
1284 |
|
|
1285 |
return self.toString(); |
|
1286 |
} |
|
1287 |
|
|
1288 |
/* A method to judge if a path have attribute */ |
|
1289 |
private boolean hasAttributeInPath(String path) |
|
1290 |
{ |
|
1291 |
if (path.indexOf(ATTRIBUTESYMBOL)!=-1) |
|
1292 |
{ |
|
1293 |
return true; |
|
1294 |
} |
|
1295 |
else |
|
1296 |
{ |
|
1297 |
return false; |
|
1298 |
} |
|
1299 |
} |
|
1300 |
|
|
1301 |
|
|
1302 |
|
|
1303 |
/* |
|
1304 |
* Constraint the query with @pathexp without using the XML Index, |
|
1305 |
* but nested SQL statements instead. The query migth be slower. |
|
1306 |
*/ |
|
1307 |
private String useNestedStatements(String pathexpr) |
|
1308 |
{ |
|
1309 |
StringBuffer nestedStmts = new StringBuffer(); |
|
1310 |
Vector nodes = new Vector(); |
|
1311 |
String path = pathexpr; |
|
1312 |
int inx = 0; |
|
1313 |
|
|
1314 |
do { |
|
1315 |
inx = path.lastIndexOf("/"); |
|
1316 |
|
|
1317 |
nodes.addElement(path.substring(inx+1)); |
|
1318 |
path = path.substring(0, Math.abs(inx)); |
|
1319 |
} while ( inx > 0 ); |
|
1320 |
|
|
1321 |
// nested statements |
|
1322 |
int i = 0; |
|
1323 |
for (i = 0; i < nodes.size()-1; i++) { |
|
1324 |
nestedStmts.append("(SELECT nodeid FROM xml_nodes" + |
|
1325 |
" WHERE nodename LIKE '" + |
|
1326 |
(String)nodes.elementAt(i) + "'" + |
|
1327 |
" AND parentnodeid IN "); |
|
1328 |
} |
|
1329 |
// for the last statement: it is without " AND parentnodeid IN " |
|
1330 |
nestedStmts.append("(SELECT nodeid FROM xml_nodes" + |
|
1331 |
" WHERE nodename LIKE '" + |
|
1332 |
(String)nodes.elementAt(i) + "'" ); |
|
1333 |
// node.size() number of closing brackets |
|
1334 |
for (i = 0; i < nodes.size(); i++) { |
|
1335 |
nestedStmts.append(")"); |
|
1336 |
} |
|
1337 |
|
|
1338 |
|
|
1339 |
|
|
1340 |
return nestedStmts.toString(); |
|
1341 |
} |
|
1342 |
|
|
1343 |
/** |
|
1344 |
* create a String description of the query that this instance represents. |
|
1345 |
* This should become a way to get the XML serialization of the query. |
|
1346 |
*/ |
|
1347 |
public String toString() { |
|
1348 |
|
|
1349 |
return this.printSQL(true); |
|
1350 |
} |
|
1351 |
} |
|
1352 | 1027 |
} |
Also available in: Unified diff
Revied the class.