package water.api; import dontweave.gson.*; import java.util.*; import water.*; import water.fvec.Frame; import water.fvec.Vec; public class StoreView extends Request { public static final int MAX_VIEW = 1000000; protected Str _filter = new Str(FILTER, ""); protected final Int _offset = new Int(OFFSET, 0, 0, Integer.MAX_VALUE); protected final Int _view = new Int(VIEW, 20, 0, MAX_VIEW); @Override protected Response serve() { JsonObject result = new JsonObject(); // get the offset index final int offset = _offset.value(); final int view = _view.value(); final String filter = _filter.value(); // Gather some keys that pass all filters H2O.KeySnapshot ks = H2O.KeySnapshot.globalSnapshot(); if(filter != null) ks = ks.filter(new H2O.KVFilter() { @Override public boolean filter(H2O.KeyInfo k) { return k._key.toString().indexOf(filter) != -1; } }); final H2O.KeyInfo [] kinfos = ks._keyInfos; if( ks._keyInfos.length > offset+view ) result.addProperty(Constants.MORE,true); // Now build the result JSON with all available keys final H2O cloud = H2O.CLOUD; // Current eldest Cloud JsonArray ary = new JsonArray(); int len = Math.min(kinfos.length,offset+view); for( int i=offset; i<len; i++ ) { Value val = DKV.get(kinfos[i]._key); if( val != null ) ary.add(formatKeyRow(cloud,kinfos[i]._key,val)); } result.add(KEYS,ary); result.addProperty(NUM_KEYS, len-offset); result.addProperty(CLOUD_NAME, H2O.NAME); result.addProperty(NODE_NAME, H2O.SELF.toString()); Response r = Response.done(result); r.addHeader( "<form class='well form-inline' action='StoreView.html'>" + " <input type='text' class='input-small span10' placeholder='filter' " + " name='filter' id='filter' value='"+_filter.value()+"' maxlength='512'>" + " <button type='submit' class='btn btn-primary'>Filter keys!</button>" + "</form>"); r.setBuilder(KEYS, new PaginatedTable(argumentsToJson(),offset,view,kinfos.length,false)); r.setBuilder(KEYS+"."+KEY, new KeyCellBuilder()); r.setBuilder(KEYS+".col_0", new KeyMinAvgMaxBuilder()); r.setBuilder(KEYS+".col_1", new KeyMinAvgMaxBuilder()); r.setBuilder(KEYS+".col_2", new KeyMinAvgMaxBuilder()); r.setBuilder(KEYS+".col_3", new KeyMinAvgMaxBuilder()); r.setBuilder(KEYS+".col_4", new KeyMinAvgMaxBuilder()); r.setBuilder(MORE, new HideBuilder()); return r; } static private String noNaN( double d ) { return (Double.isNaN(d) || Double.isInfinite(d)) ? "" : Double.toString(d); } // Used by tests public String setAndServe(String offset) { _offset.reset(); _offset.check(null,offset); _view .reset(); _view .check(null,"20"); _filter.reset(); return new Gson().toJson(serve()._response); } private JsonObject formatKeyRow(H2O cloud, Key key, Value val) { JsonObject result = new JsonObject(); result.addProperty(KEY, key.toString()); result.addProperty(VALUE_SIZE,val.length()); JsonObject mt = new JsonObject(); JsonObject jcols[] = new JsonObject[]{mt,mt,mt,mt,mt}; long rows = -1; int cols = -1; String str = ""; if(val.isFrame()){ Frame fr = val.get(); rows = fr.numRows(); cols = fr.numCols(); result.addProperty(ROWS,rows); // exact rows result.addProperty(COLS,cols); // exact cols for( int i = 0; i < jcols.length; ++i ) { JsonObject col = new JsonObject(); if (i < cols) { Vec v = fr.vecs()[i]; col.addProperty(HEADER,fr._names[i]); if( !v.isEnum()) { col.addProperty(MIN , noNaN(v.min() )); col.addProperty(MEAN, noNaN(v.mean())); col.addProperty(MAX , noNaN(v.max() )); } else if( v.domain().length > 0 ) { int max = v.domain().length; col.addProperty(MIN , v.domain()[0]); col.addProperty(MEAN, v.domain()[max/2]); col.addProperty(MAX , v.domain()[max-1]); } } jcols[i] = col; } } /* // Whatever this is trying to do, it's not helping. // I think this is trying to decode data files that have been POSTed, but instead it's just // corrupting StoreView output. // Maybe turn this on again once it understands how to disambiguate different data types. // // Tom if( rows == -1 ) { byte [] bits = Utils.getFirstUnzipedBytes(val); PSetupGuess sguess = ParseDataset.guessSetup(bits); if(sguess != null && sguess.valid() && sguess._data != null && sguess._data.length >= 4 && sguess._setup._ncols > 0 ) { // Able to parse sanely? int zipped_len = val.getFirstBytes().length; double bytes_per_row = (double) zipped_len / sguess._data.length; rows = (long) (val.length() / bytes_per_row); cols = sguess._setup._ncols; result.addProperty(ROWS, "~" + rows); result.addProperty(COLS, cols); final int len = sguess._data.length; for( int i=0; i<Math.min(cols,jcols.length); i++ ) { JsonObject col = new JsonObject(); if(len > 0 && i < sguess._data[0].length) col.addProperty(HEADER,sguess._data[0][i]); // First 4 rows, including labels if(len > 1 && i < sguess._data[1].length) col.addProperty(MIN ,sguess._data[1][i]); // as MIN/MEAN/MAX if(len > 2 && i < sguess._data[2].length) col.addProperty(MEAN ,sguess._data[2][i]); if(len > 3 && i < sguess._data[3].length) col.addProperty(MAX ,sguess._data[3][i]); jcols[i] = col; } } else { result.addProperty(ROWS,""); // no rows result.addProperty(COLS,""); } // Now the first 100 bytes of Value as a String StringBuilder sb = new StringBuilder(); byte[] b = bits; // Unzipped bits, if any int newlines=0; int len = Math.min(b.length,100); for( int i=0; i<len; i++ ) { byte c = b[i]; if( c == '&' ) sb.append("&"); else if( c == '<' ) sb.append("<"); else if( c == '>' ) sb.append(">"); else if( c == '\r' ) ; // ignore windows crlf else if( c == '\n' ) { // newline if( ++newlines >= 4 ) break; // limit to 4 lines visually sb.append("<br>"); // visual newline } else if( c == ',' && i+1<len && b[i+1]!=' ' ) sb.append(", "); else if( c < 32 ) sb.append('?'); else sb.append((char)c); } if( val.length() > len ) sb.append("..."); str = sb.toString(); } */ // Instead of the brokenness above, just paste in the empty string. result.addProperty(ROWS,""); result.addProperty(COLS,""); for( int i=0; i<jcols.length; i++ ) result.add("col_"+i,jcols[i]); result.addProperty(VALUE,str); // VALUE last in the JSON return result; } }