Fixed issue 58: Meta facet

git-svn-id: http://google-refine.googlecode.com/svn/trunk@848 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-05-24 19:04:55 +00:00
parent 433a047fb6
commit c6827fe242
6 changed files with 161 additions and 27 deletions

View File

@ -15,6 +15,8 @@ Fixes:
- Issue 46: "Array literals in GEL"
- Issue 55: "Use stable sorting for text facets sorted by count"
- Issue 53: "Moving the cursor inside the Text Filter box by clicking"
- Issue 58: "Meta facet"
Supported by the function facetCount()
Features:
- Row/record sorting (Issue 32)

View File

@ -1,5 +1,6 @@
package com.metaweb.gridworks.browsing.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@ -166,35 +167,45 @@ public class ExpressionNominalValueGrouper implements RowVisitor, RecordVisitor
@Override
public Object eval(Project project, int rowIndex, Row row, Properties bindings) {
Object value = evalRow(project, rowIndex, row, bindings);
if (value != null) {
if (value.getClass().isArray()) {
Object[] a = (Object[]) value;
for (int i = 0; i < a.length; i++) {
a[i] = getValueCount(a[i]);
}
return a;
} else if (value instanceof Collection<?>) {
List<Object> list = ExpressionUtils.toObjectList(value);
int count = list.size();
for (int i = 0; i < count; i++) {
list.set(i, getValueCount(list.get(i)));
}
return list;
}
}
return getValueCount(value);
return getChoiceValueCountMultiple(value);
}
protected Integer getValueCount(Object value) {
if (value == null) {
return blankCount;
} else if (ExpressionUtils.isError(value)) {
return errorCount;
} else {
return choices.get(value).count;
}
}
};
}
public Object getChoiceValueCountMultiple(Object value) {
if (value != null) {
if (value.getClass().isArray()) {
Object[] choiceValues = (Object[]) value;
List<Integer> counts = new ArrayList<Integer>(choiceValues.length);
for (int i = 0; i < choiceValues.length; i++) {
counts.add(getChoiceValueCount(choiceValues[i]));
}
return counts;
} else if (value instanceof Collection<?>) {
List<Object> choiceValues = ExpressionUtils.toObjectList(value);
List<Integer> counts = new ArrayList<Integer>(choiceValues.size());
int count = choiceValues.size();
for (int i = 0; i < count; i++) {
counts.add(getChoiceValueCount(choiceValues.get(i)));
}
return counts;
}
}
return getChoiceValueCount(value);
}
public Integer getChoiceValueCount(Object choiceValue) {
if (ExpressionUtils.isError(choiceValue)) {
return errorCount;
} else if (ExpressionUtils.isNonBlankData(choiceValue)) {
IndexedNominalFacetChoice choice = choices.get(choiceValue);
return choice != null ? choice.count : 0;
} else {
return blankCount;
}
}
}

View File

@ -0,0 +1,64 @@
package com.metaweb.gridworks.expr.functions;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.browsing.Engine;
import com.metaweb.gridworks.browsing.util.ExpressionNominalValueGrouper;
import com.metaweb.gridworks.expr.EvalError;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.MetaParser;
import com.metaweb.gridworks.expr.ParsingException;
import com.metaweb.gridworks.gel.ControlFunctionRegistry;
import com.metaweb.gridworks.gel.Function;
import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project;
public class FacetCount implements Function {
public Object call(Properties bindings, Object[] args) {
if (args.length == 3 && args[1] instanceof String && args[2] instanceof String) {
Object choiceValue = args[0]; // choice value to look up
String facetExpression = (String) args[1];
String columnName = (String) args[2];
Project project = (Project) bindings.get("project");
Column column = project.columnModel.getColumnByName(columnName);
if (column == null) {
return new EvalError("No such column named " + columnName);
}
String key = "nominal-bin:" + facetExpression;
ExpressionNominalValueGrouper grouper = (ExpressionNominalValueGrouper) column.getPrecompute(key);
if (grouper == null) {
try {
Evaluable eval = MetaParser.parse(facetExpression);
Engine engine = new Engine(project);
grouper = new ExpressionNominalValueGrouper(eval, columnName, column.getCellIndex());
engine.getAllRows().accept(project, grouper);
column.setPrecompute(key, grouper);
} catch (ParsingException e) {
return new EvalError("Error parsing facet expression " + facetExpression);
}
}
return grouper.getChoiceValueCountMultiple(choiceValue);
}
return new EvalError(ControlFunctionRegistry.getFunctionName(this) +
" expects a choice value, an expression as a string, and a column name");
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value("Returns the facet count corresponding to the given choice value");
writer.key("params"); writer.value("choiceValue, string facetExpression, string columnName");
writer.key("returns"); writer.value("number");
writer.endObject();
}
}

View File

@ -6,6 +6,7 @@ import java.util.Set;
import java.util.Map.Entry;
import com.metaweb.gridworks.expr.functions.Cross;
import com.metaweb.gridworks.expr.functions.FacetCount;
import com.metaweb.gridworks.expr.functions.Get;
import com.metaweb.gridworks.expr.functions.Length;
import com.metaweb.gridworks.expr.functions.Slice;
@ -175,6 +176,8 @@ public class ControlFunctionRegistry {
registerFunction("cross", new Cross());
registerFunction("facetCount", new FacetCount());
registerControl("if", new If());
registerControl("with", new With());
registerControl("forEach", new ForEach());

View File

@ -245,6 +245,9 @@ ListFacet.prototype._update = function(resetScroll) {
this._elmts.bodyInnerDiv.empty().append(
$('<div>').text(this._data.error).addClass("facet-body-message"));
if (this._data.error == "Too many choices") {
this._renderBodyControls();
}
return;
}
@ -324,6 +327,7 @@ ListFacet.prototype._update = function(resetScroll) {
}
this._elmts.bodyInnerDiv.html(html.join(''));
this._renderBodyControls();
this._elmts.bodyInnerDiv[0].scrollTop = scrollTop;
var getChoice = function(elmt) {
@ -398,6 +402,52 @@ ListFacet.prototype._update = function(resetScroll) {
window.setTimeout(wireEvents, 100);
};
ListFacet.prototype._renderBodyControls = function() {
var self = this;
var bodyControls = $('<div>')
.addClass("facet-body-controls")
.appendTo(this._elmts.bodyInnerDiv);
$('<a>')
.text("facet by choice counts")
.attr("href", "javascript:{}")
.addClass("action")
.appendTo(bodyControls)
.click(function() {
ui.browsingEngine.addFacet(
"range",
{
"name" : self._config.columnName,
"columnName" : self._config.columnName,
"expression" : self._getMetaExpression(),
"mode" : "range"
},
{
}
);
});
};
ListFacet.prototype._getMetaExpression = function() {
var expression = this._config.expression;
var language = "gel:";
var colon = expression.indexOf(":");
if (colon > 0) {
var l = expression.substring(0, colon + 1);
if (l == "gel:" || l == "jython:" || l == "clojure:") {
expression = expression.substring(colon + 1);
language = l;
}
}
return language + 'facetCount(' + [
expression,
JSON.stringify(this._config.expression),
JSON.stringify(this._config.columnName)
].join(', ') + ')';
}
ListFacet.prototype._doEdit = function() {
new ClusteringDialog(this._config.columnName, this._config.expression);
};

View File

@ -137,6 +137,10 @@ a.facet-title-remove:hover {
height: 100%;
overflow: auto;
}
.facet-body-controls {
margin: 0.5em 0;
text-align: center;
}
.facet-choice {
padding: 2px 5px;