Project

General

Profile

« Previous | Next » 

Revision 1832

Added by Jing Tao about 21 years ago

Revied the class.

View differences:

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