More server-side scaffolding for faceted browsing.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@11 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
23b9e313b8
commit
e0365f45c8
@ -16,7 +16,9 @@ public class ConjunctiveFilteredRows implements FilteredRows {
|
||||
|
||||
@Override
|
||||
public void accept(Project project, RowVisitor visitor) {
|
||||
for (Row row : project.rows) {
|
||||
for (int i = 0; i < project.rows.size(); i++) {
|
||||
Row row = project.rows.get(i);
|
||||
|
||||
boolean ok = true;
|
||||
for (RowFilter rowFilter : _rowFilters) {
|
||||
if (!rowFilter.filterRow(row)) {
|
||||
@ -26,7 +28,7 @@ public class ConjunctiveFilteredRows implements FilteredRows {
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
visitor.visit(row);
|
||||
visitor.visit(i, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,15 @@ import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridlock.browsing.facets.Facet;
|
||||
import com.metaweb.gridlock.browsing.facets.ListFacet;
|
||||
import com.metaweb.gridlock.model.Project;
|
||||
|
||||
public class Engine {
|
||||
protected List<Facet> facets = new LinkedList<Facet>();
|
||||
protected Project _project;
|
||||
protected List<Facet> _facets = new LinkedList<Facet>();
|
||||
|
||||
public Engine(Project project) {
|
||||
_project = project;
|
||||
}
|
||||
|
||||
public FilteredRows getAllFilteredRows() {
|
||||
return getFilteredRows(null);
|
||||
@ -21,7 +27,7 @@ public class Engine {
|
||||
|
||||
public FilteredRows getFilteredRows(Facet except) {
|
||||
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows();
|
||||
for (Facet facet : facets) {
|
||||
for (Facet facet : _facets) {
|
||||
if (facet != except) {
|
||||
cfr.add(facet.getRowFilter());
|
||||
}
|
||||
@ -32,8 +38,8 @@ public class Engine {
|
||||
public JSONObject getJSON(Properties options) throws JSONException {
|
||||
JSONObject o = new JSONObject();
|
||||
|
||||
List<JSONObject> a = new ArrayList<JSONObject>(facets.size());
|
||||
for (Facet facet : facets) {
|
||||
List<JSONObject> a = new ArrayList<JSONObject>(_facets.size());
|
||||
for (Facet facet : _facets) {
|
||||
a.add(facet.getJSON(options));
|
||||
}
|
||||
o.put("facets", a);
|
||||
@ -41,7 +47,7 @@ public class Engine {
|
||||
return o;
|
||||
}
|
||||
|
||||
public void initializeFromJSON(JSONObject o) throws JSONException {
|
||||
public void initializeFromJSON(JSONObject o) throws Exception {
|
||||
JSONArray a = o.getJSONArray("facets");
|
||||
int length = a.length();
|
||||
|
||||
@ -56,16 +62,16 @@ public class Engine {
|
||||
|
||||
if (facet != null) {
|
||||
facet.initializeFromJSON(fo);
|
||||
facets.add(facet);
|
||||
_facets.add(facet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void computeFacets() throws JSONException {
|
||||
for (Facet facet : facets) {
|
||||
for (Facet facet : _facets) {
|
||||
FilteredRows filteredRows = getFilteredRows(facet);
|
||||
|
||||
facet.computeChoices(filteredRows);
|
||||
facet.computeChoices(_project, filteredRows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,6 @@ package com.metaweb.gridlock.browsing;
|
||||
import com.metaweb.gridlock.model.Row;
|
||||
|
||||
public interface RowVisitor {
|
||||
public void visit(Row row);
|
||||
public void visit(int rowIndex, Row row);
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package com.metaweb.gridlock.browsing.accessors;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class DecoratedValue {
|
||||
final public Object value;
|
||||
final public String label;
|
||||
@ -8,4 +13,13 @@ public class DecoratedValue {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public JSONObject getJSON(Properties options) throws JSONException {
|
||||
JSONObject o = new JSONObject();
|
||||
|
||||
o.put("v", value);
|
||||
o.put("l", label);
|
||||
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class CellAccessorNominalRowGrouper implements RowVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Row row) {
|
||||
public void visit(int rowIndex, Row row) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
@ -35,10 +35,10 @@ public class CellAccessorNominalRowGrouper implements RowVisitor {
|
||||
new DecoratedValue(value, value.toString());
|
||||
|
||||
Object v = dValue.value;
|
||||
if (choices.containsKey(value)) {
|
||||
choices.get(value).count++;
|
||||
if (choices.containsKey(v)) {
|
||||
choices.get(v).count++;
|
||||
} else {
|
||||
NominalFacetChoice choice = new NominalFacetChoice(dValue, v);
|
||||
NominalFacetChoice choice = new NominalFacetChoice(dValue);
|
||||
choice.count = 1;
|
||||
|
||||
choices.put(v, choice);
|
||||
|
@ -0,0 +1,50 @@
|
||||
package com.metaweb.gridlock.browsing.facets;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.metaweb.gridlock.browsing.RowVisitor;
|
||||
import com.metaweb.gridlock.browsing.accessors.DecoratedValue;
|
||||
import com.metaweb.gridlock.expr.Evaluable;
|
||||
import com.metaweb.gridlock.model.Cell;
|
||||
import com.metaweb.gridlock.model.Row;
|
||||
|
||||
public class ExpressionNominalRowGrouper implements RowVisitor {
|
||||
final protected Evaluable _evaluable;
|
||||
final protected int _cellIndex;
|
||||
|
||||
final public Map<Object, NominalFacetChoice> choices = new HashMap<Object, NominalFacetChoice>();
|
||||
|
||||
public ExpressionNominalRowGrouper(Evaluable evaluable, int cellIndex) {
|
||||
_evaluable = evaluable;
|
||||
_cellIndex = cellIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int rowIndex, Row row) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
Properties bindings = new Properties();
|
||||
|
||||
bindings.put("this", cell);
|
||||
bindings.put("value", cell.value);
|
||||
|
||||
Object value = _evaluable.evaluate(bindings);
|
||||
if (value != null) {
|
||||
DecoratedValue dValue = new DecoratedValue(value, value.toString());
|
||||
|
||||
if (choices.containsKey(value)) {
|
||||
choices.get(value).count++;
|
||||
} else {
|
||||
NominalFacetChoice choice = new NominalFacetChoice(dValue);
|
||||
choice.count = 1;
|
||||
|
||||
choices.put(value, choice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,13 +7,14 @@ import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridlock.browsing.FilteredRows;
|
||||
import com.metaweb.gridlock.browsing.filters.RowFilter;
|
||||
import com.metaweb.gridlock.model.Project;
|
||||
|
||||
public interface Facet {
|
||||
public RowFilter getRowFilter();
|
||||
|
||||
public void computeChoices(FilteredRows filteredRows);
|
||||
public void computeChoices(Project project, FilteredRows filteredRows);
|
||||
|
||||
public JSONObject getJSON(Properties options) throws JSONException;
|
||||
|
||||
public void initializeFromJSON(JSONObject o) throws JSONException;
|
||||
public void initializeFromJSON(JSONObject o) throws Exception;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.metaweb.gridlock.browsing.facets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
@ -9,37 +10,91 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridlock.browsing.FilteredRows;
|
||||
import com.metaweb.gridlock.browsing.accessors.DecoratedValue;
|
||||
import com.metaweb.gridlock.browsing.filters.ExpressionEqualRowFilter;
|
||||
import com.metaweb.gridlock.browsing.filters.RowFilter;
|
||||
import com.metaweb.gridlock.expr.Evaluable;
|
||||
import com.metaweb.gridlock.expr.Parser;
|
||||
import com.metaweb.gridlock.model.Project;
|
||||
|
||||
public class ListFacet implements Facet {
|
||||
final protected List<Object> _choices = new LinkedList<Object>();
|
||||
protected List<NominalFacetChoice> _selection = new LinkedList<NominalFacetChoice>();
|
||||
protected List<NominalFacetChoice> _choices = new LinkedList<NominalFacetChoice>();
|
||||
|
||||
@Override
|
||||
public JSONObject getJSON(Properties options) throws JSONException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
protected String _name;
|
||||
protected String _expression;
|
||||
protected int _cellIndex;
|
||||
protected Evaluable _eval;
|
||||
|
||||
public ListFacet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeFromJSON(JSONObject o) throws JSONException {
|
||||
JSONArray a = o.getJSONArray("choices");
|
||||
public JSONObject getJSON(Properties options) throws JSONException {
|
||||
JSONObject o = new JSONObject();
|
||||
|
||||
o.put("name", _name);
|
||||
o.put("expression", _expression);
|
||||
o.put("cellIndex", _cellIndex);
|
||||
|
||||
List<JSONObject> a = new ArrayList<JSONObject>(_choices.size());
|
||||
for (NominalFacetChoice choice : _choices) {
|
||||
a.add(choice.getJSON(options));
|
||||
}
|
||||
o.put("choices", a);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeFromJSON(JSONObject o) throws Exception {
|
||||
_name = o.getString("name");
|
||||
_expression = o.getString("expression");
|
||||
_cellIndex = o.getInt("cellIndex");
|
||||
|
||||
_eval = new Parser(_expression).getExpression();
|
||||
_selection.clear();
|
||||
|
||||
JSONArray a = o.getJSONArray("selection");
|
||||
int length = a.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject oc = a.getJSONObject(i);
|
||||
JSONObject ocv = oc.getJSONObject("v");
|
||||
|
||||
DecoratedValue decoratedValue = new DecoratedValue(
|
||||
ocv.get("v"), ocv.getString("l"));
|
||||
|
||||
NominalFacetChoice nominalFacetChoice = new NominalFacetChoice(decoratedValue);
|
||||
|
||||
nominalFacetChoice.count = oc.getInt("c");
|
||||
nominalFacetChoice.selected = oc.getBoolean("s");
|
||||
|
||||
_selection.add(nominalFacetChoice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowFilter getRowFilter() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return new ExpressionEqualRowFilter(_eval, _cellIndex, createMatches());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeChoices(FilteredRows filteredRows) {
|
||||
// TODO Auto-generated method stub
|
||||
public void computeChoices(Project project, FilteredRows filteredRows) {
|
||||
ExpressionNominalRowGrouper grouper =
|
||||
new ExpressionNominalRowGrouper(_eval, _cellIndex);
|
||||
|
||||
filteredRows.accept(project, grouper);
|
||||
|
||||
_choices.clear();
|
||||
_choices.addAll(grouper.choices.values());
|
||||
}
|
||||
|
||||
protected Object[] createMatches() {
|
||||
Object[] a = new Object[_choices.size()];
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
a[i] = _choices.get(i).decoratedValue.value;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,28 @@
|
||||
package com.metaweb.gridlock.browsing.facets;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridlock.browsing.accessors.DecoratedValue;
|
||||
|
||||
public class NominalFacetChoice {
|
||||
final public DecoratedValue decoratedValue;
|
||||
final public Object value;
|
||||
public int count;
|
||||
public boolean selected;
|
||||
|
||||
public NominalFacetChoice(DecoratedValue decoratedValue, Object value) {
|
||||
public NominalFacetChoice(DecoratedValue decoratedValue) {
|
||||
this.decoratedValue = decoratedValue;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public JSONObject getJSON(Properties options) throws JSONException {
|
||||
JSONObject o = new JSONObject();
|
||||
|
||||
o.put("v", decoratedValue.getJSON(options));
|
||||
o.put("c", count);
|
||||
o.put("s", selected);
|
||||
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
package com.metaweb.gridlock.browsing.filters;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.metaweb.gridlock.expr.Evaluable;
|
||||
import com.metaweb.gridlock.model.Cell;
|
||||
import com.metaweb.gridlock.model.Row;
|
||||
|
||||
public class ExpressionEqualRowFilter implements RowFilter {
|
||||
final protected Evaluable _evaluable;
|
||||
final protected int _cellIndex;
|
||||
final protected Object[] _matches;
|
||||
|
||||
public ExpressionEqualRowFilter(Evaluable evaluable, int cellIndex, Object[] matches) {
|
||||
_evaluable = evaluable;
|
||||
_cellIndex = cellIndex;
|
||||
_matches = matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterRow(Row row) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
Properties bindings = new Properties();
|
||||
|
||||
bindings.put("this", cell);
|
||||
bindings.put("value", cell.value);
|
||||
|
||||
Object value = _evaluable.evaluate(bindings);
|
||||
if (value != null) {
|
||||
for (Object match : _matches) {
|
||||
if (match.equals(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import com.metaweb.gridlock.ProjectManager;
|
||||
import com.metaweb.gridlock.browsing.Engine;
|
||||
import com.metaweb.gridlock.model.Project;
|
||||
import com.oreilly.servlet.multipart.FilePart;
|
||||
import com.oreilly.servlet.multipart.MultipartParser;
|
||||
@ -150,4 +151,18 @@ public abstract class Command {
|
||||
JSONObject o = (JSONObject) t.nextValue();
|
||||
return o;
|
||||
}
|
||||
|
||||
protected Engine getEngine(HttpServletRequest request, Project project) throws Exception {
|
||||
Properties properties = new Properties();
|
||||
readFileUpload(request, properties);
|
||||
|
||||
Engine engine = new Engine(project);
|
||||
if (properties.containsKey("engine")) {
|
||||
String json = properties.getProperty("engine");
|
||||
JSONObject o = jsonStringToObject(json);
|
||||
|
||||
engine.initializeFromJSON(o);
|
||||
}
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridlock.browsing.Engine;
|
||||
import com.metaweb.gridlock.browsing.FilteredRows;
|
||||
import com.metaweb.gridlock.browsing.RowVisitor;
|
||||
import com.metaweb.gridlock.model.Project;
|
||||
import com.metaweb.gridlock.model.Row;
|
||||
|
||||
@ -20,21 +23,41 @@ public class GetRowsCommand extends Command {
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
|
||||
try {
|
||||
Project project = getProject(request);
|
||||
Engine engine = getEngine(request, project);
|
||||
|
||||
int start = Math.min(project.rows.size(), Math.max(0, getIntegerParameter(request, "start", 0)));
|
||||
int limit = Math.min(project.rows.size() - start, Math.max(0, getIntegerParameter(request, "limit", 20)));
|
||||
Properties options = new Properties();
|
||||
|
||||
try {
|
||||
JSONObject o = new JSONObject();
|
||||
|
||||
List<JSONObject> a = new ArrayList<JSONObject>(limit);
|
||||
try {
|
||||
for (int r = start; r < start + limit && r < project.rows.size(); r++) {
|
||||
Row row = project.rows.get(r);
|
||||
FilteredRows filteredRows = engine.getAllFilteredRows();
|
||||
RowAccumulator acc = new RowAccumulator(start, limit) {
|
||||
List<JSONObject> list;
|
||||
Properties options;
|
||||
|
||||
a.add(row.getJSON(options));
|
||||
public RowAccumulator init(List<JSONObject> list, Properties options) {
|
||||
this.list = list;
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void internalVisit(Row row) {
|
||||
try {
|
||||
list.add(row.getJSON(options));
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
}
|
||||
}.init(a, options);
|
||||
|
||||
filteredRows.accept(project, acc);
|
||||
|
||||
o.put("filtered", acc.total);
|
||||
} catch (JSONException e) {
|
||||
respondException(response, e);
|
||||
}
|
||||
@ -44,8 +67,31 @@ public class GetRowsCommand extends Command {
|
||||
o.put("total", project.rows.size());
|
||||
|
||||
respondJSON(response, o);
|
||||
} catch (JSONException e) {
|
||||
} catch (Exception e) {
|
||||
respondException(response, e);
|
||||
}
|
||||
}
|
||||
|
||||
static protected class RowAccumulator implements RowVisitor {
|
||||
final public int start;
|
||||
final public int limit;
|
||||
|
||||
public int total;
|
||||
|
||||
public RowAccumulator(int start, int limit) {
|
||||
this.start = start;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int rowIndex, Row row) {
|
||||
if (total >= start && total < start + limit) {
|
||||
internalVisit(row);
|
||||
}
|
||||
total++;
|
||||
}
|
||||
|
||||
protected void internalVisit(Row row) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user