Added support for including dependent rows in row visiting. Facets still don't count them, though.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@282 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-03-12 01:06:23 +00:00
parent 7e2667ab45
commit af3cb76056
21 changed files with 125 additions and 93 deletions

View File

@ -9,10 +9,12 @@ import com.metaweb.gridworks.model.Row;
public class ConjunctiveFilteredRows implements FilteredRows { public class ConjunctiveFilteredRows implements FilteredRows {
final protected List<RowFilter> _rowFilters = new LinkedList<RowFilter>(); final protected List<RowFilter> _rowFilters = new LinkedList<RowFilter>();
final protected boolean _contextual; final protected boolean _includeContextual;
final protected boolean _includeDependent;
public ConjunctiveFilteredRows(boolean contextual) { public ConjunctiveFilteredRows(boolean includeContextual, boolean includeDependent) {
_contextual = contextual; _includeContextual = includeContextual;
_includeDependent = includeDependent;
} }
public void add(RowFilter rowFilter) { public void add(RowFilter rowFilter) {
@ -20,63 +22,66 @@ public class ConjunctiveFilteredRows implements FilteredRows {
} }
public void accept(Project project, RowVisitor visitor) { public void accept(Project project, RowVisitor visitor) {
if (_contextual) {
contextualAccept(project, visitor);
} else {
simpleAccept(project, visitor);
}
}
protected void simpleAccept(Project project, RowVisitor visitor) {
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(project, i, row)) {
ok = false;
break;
}
}
if (ok) {
visitor.visit(project, i, row, false);
}
}
}
protected void contextualAccept(Project project, RowVisitor visitor) {
int lastVisitedRow = -1; int lastVisitedRow = -1;
int lastRecordRowAccepted = -1;
for (int i = 0; i < project.rows.size(); i++) {
int c = project.rows.size();
for (int i = 0; i < c; i++) {
Row row = project.rows.get(i); Row row = project.rows.get(i);
boolean ok = true; if (checkRow(project, i, row)) {
for (RowFilter rowFilter : _rowFilters) { if (row.recordIndex >= 0) {
if (!rowFilter.filterRow(project, i, row)) { // this is a record row itself
ok = false;
break; lastRecordRowAccepted = i;
} }
visitRow(project, visitor, i, row, lastVisitedRow);
lastVisitedRow = i;
} else if (
_includeDependent &&
row.recordIndex < 0 &&
row.contextRows != null &&
row.contextRows.size() > 0) {
if (row.contextRows.get(0) == lastRecordRowAccepted) {
visitor.visit(project, i, row, false, true);
lastVisitedRow = i;
}
} }
}
if (ok) { }
if (row.contextRows != null && lastVisitedRow < i - 1) {
for (int contextRowIndex : row.contextRows) { protected void visitRow(Project project, RowVisitor visitor, int rowIndex, Row row, int lastVisitedRow) {
if (contextRowIndex > lastVisitedRow) { if (_includeContextual) {
visitor.visit( if (row.contextRows != null && lastVisitedRow < rowIndex - 1) {
project, for (int contextRowIndex : row.contextRows) {
contextRowIndex, if (contextRowIndex > lastVisitedRow) {
project.rows.get(contextRowIndex), visitor.visit(
true project,
); contextRowIndex,
lastVisitedRow = contextRowIndex; project.rows.get(contextRowIndex),
} true,
false
);
lastVisitedRow = contextRowIndex;
} }
} }
}
visitor.visit(project, i, row, false);
lastVisitedRow = i; visitor.visit(project, rowIndex, row, false, false);
} else {
visitor.visit(project, rowIndex, row, false, false);
}
}
protected boolean checkRow(Project project, int rowIndex, Row row) {
for (RowFilter rowFilter : _rowFilters) {
if (!rowFilter.filterRow(project, rowIndex, row)) {
return false;
} }
} }
return true;
} }
} }

View File

@ -20,17 +20,18 @@ import com.metaweb.gridworks.model.Project;
public class Engine implements Jsonizable { public class Engine implements Jsonizable {
protected Project _project; protected Project _project;
protected List<Facet> _facets = new LinkedList<Facet>(); protected List<Facet> _facets = new LinkedList<Facet>();
protected boolean _includeDependent;
public Engine(Project project) { public Engine(Project project) {
_project = project; _project = project;
} }
public FilteredRows getAllFilteredRows(boolean contextual) { public FilteredRows getAllFilteredRows(boolean includeContextual) {
return getFilteredRows(null, contextual); return getFilteredRows(null, includeContextual);
} }
public FilteredRows getFilteredRows(Facet except, boolean contextual) { public FilteredRows getFilteredRows(Facet except, boolean includeContextual) {
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows(contextual); ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows(includeContextual, _includeDependent);
for (Facet facet : _facets) { for (Facet facet : _facets) {
if (facet != except) { if (facet != except) {
RowFilter rowFilter = facet.getRowFilter(); RowFilter rowFilter = facet.getRowFilter();
@ -64,6 +65,10 @@ public class Engine implements Jsonizable {
_facets.add(facet); _facets.add(facet);
} }
} }
if (o.has("includeDependent") && !o.isNull("includeDependent")) {
_includeDependent = o.getBoolean("includeDependent");
}
} }
public void computeFacets() throws JSONException { public void computeFacets() throws JSONException {
@ -78,11 +83,13 @@ public class Engine implements Jsonizable {
throws JSONException { throws JSONException {
writer.object(); writer.object();
writer.key("facets"); writer.array(); writer.key("facets");
for (Facet facet : _facets) { writer.array();
facet.write(writer, options); for (Facet facet : _facets) {
} facet.write(writer, options);
writer.endArray(); }
writer.endArray();
writer.key("includeDependent"); writer.value(_includeDependent);
writer.endObject(); writer.endObject();
} }
} }

View File

@ -4,6 +4,11 @@ import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Row; import com.metaweb.gridworks.model.Row;
public interface RowVisitor { public interface RowVisitor {
public boolean visit(Project project, int rowIndex, Row row, boolean contextual); public boolean visit(
Project project,
int rowIndex, // zero-based row index
Row row,
boolean contextual, // true if this row is included because it's the context row of an included row
boolean dependent // true if this row is included because it depends on an included row
);
} }

View File

@ -25,7 +25,7 @@ public class ExpressionNominalRowGrouper implements RowVisitor {
_cellIndex = cellIndex; _cellIndex = cellIndex;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = _cellIndex < 0 ? null : row.getCell(_cellIndex); Cell cell = _cellIndex < 0 ? null : row.getCell(_cellIndex);
Properties bindings = ExpressionUtils.createBindings(project); Properties bindings = ExpressionUtils.createBindings(project);

View File

@ -28,7 +28,7 @@ public class ExpressionNumericRowBinner implements RowVisitor {
bins = new int[_index.getBins().length]; bins = new int[_index.getBins().length];
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(_cellIndex); Cell cell = row.getCell(_cellIndex);
Properties bindings = ExpressionUtils.createBindings(project); Properties bindings = ExpressionUtils.createBindings(project);

View File

@ -62,7 +62,7 @@ public class BinningClusterer extends Clusterer {
} }
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(_colindex); Cell cell = row.getCell(_colindex);
if (cell != null && cell.value != null) { if (cell != null && cell.value != null) {
String v = cell.value.toString(); String v = cell.value.toString();

View File

@ -76,7 +76,7 @@ public class kNNClusterer extends Clusterer {
} }
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.cells.get(_colindex); Cell cell = row.cells.get(_colindex);
if (cell != null && cell.value != null) { if (cell != null && cell.value != null) {
Object v = cell.value; Object v = cell.value;
@ -114,7 +114,7 @@ public class kNNClusterer extends Clusterer {
} }
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(_colindex); Cell cell = row.getCell(_colindex);
if (cell != null && cell.value != null) { if (cell != null && cell.value != null) {
Object v = cell.value; Object v = cell.value;

View File

@ -53,7 +53,7 @@ public class GetRowsCommand extends Command {
} }
@Override @Override
public boolean internalVisit(int rowIndex, Row row, boolean contextual) { public boolean internalVisit(int rowIndex, Row row, boolean contextual, boolean includeDependent) {
try { try {
if (contextual) { if (contextual) {
options.put("extra", extra); options.put("extra", extra);
@ -99,19 +99,19 @@ public class GetRowsCommand extends Command {
this.limit = limit; this.limit = limit;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
boolean r = false; boolean r = false;
if (total >= start && total < start + limit) { if (total >= start && total < start + limit) {
r = internalVisit(rowIndex, row, contextual); r = internalVisit(rowIndex, row, includeContextual, includeDependent);
} }
if (!contextual) { if (!includeContextual) {
total++; total++;
} }
return r; return r;
} }
protected boolean internalVisit(int rowIndex, Row row, boolean contextual) { protected boolean internalVisit(int rowIndex, Row row, boolean contextual, boolean includeDependent) {
return false; return false;
} }
} }

View File

@ -38,7 +38,7 @@ public class TsvExporter implements Exporter {
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean contextual, boolean includeDependent) {
boolean first = true; boolean first = true;
try { try {
for (Column column : project.columnModel.columns) { for (Column column : project.columnModel.columns) {

View File

@ -246,13 +246,17 @@ public class Project {
lastNonBlankRowsByGroup[i] = -1; lastNonBlankRowsByGroup[i] = -1;
} }
int rowCount = rows.size();
int groupCount = keyedGroups.size();
int recordIndex = 0; int recordIndex = 0;
for (int r = 0; r < rows.size(); r++) { for (int r = 0; r < rowCount; r++) {
Row row = rows.get(r); Row row = rows.get(r);
row.contextRows = null;
row.contextRowSlots = null; row.contextRowSlots = null;
row.contextCellSlots = null; row.contextCellSlots = null;
for (int g = 0; g < keyedGroups.size(); g++) { for (int g = 0; g < groupCount; g++) {
Group group = keyedGroups.get(g); Group group = keyedGroups.get(g);
if (!ExpressionUtils.isNonBlankData(row.getCellValue(group.keyCellIndex))) { if (!ExpressionUtils.isNonBlankData(row.getCellValue(group.keyCellIndex))) {
@ -296,6 +300,7 @@ public class Project {
rootKeyedGroup.cellIndices = new int[count - 1]; rootKeyedGroup.cellIndices = new int[count - 1];
rootKeyedGroup.keyCellIndex = columnModel.columns.get(columnModel.getKeyColumnIndex()).getCellIndex(); rootKeyedGroup.keyCellIndex = columnModel.columns.get(columnModel.getKeyColumnIndex()).getCellIndex();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (i < rootKeyedGroup.keyCellIndex) { if (i < rootKeyedGroup.keyCellIndex) {
rootKeyedGroup.cellIndices[i] = i; rootKeyedGroup.cellIndices[i] = i;

View File

@ -121,10 +121,10 @@ public class ColumnAdditionOperation extends EngineDependentOperation {
Properties bindings = ExpressionUtils.createBindings(project); Properties bindings = ExpressionUtils.createBindings(project);
return new RowVisitor() { return new RowVisitor() {
int cellIndex; int cellIndex;
Properties bindings; Properties bindings;
List<CellAtRow> cellsAtRows; List<CellAtRow> cellsAtRows;
Evaluable eval; Evaluable eval;
public RowVisitor init(int cellIndex, Properties bindings, List<CellAtRow> cellsAtRows, Evaluable eval) { public RowVisitor init(int cellIndex, Properties bindings, List<CellAtRow> cellsAtRows, Evaluable eval) {
this.cellIndex = cellIndex; this.cellIndex = cellIndex;
@ -134,7 +134,7 @@ public class ColumnAdditionOperation extends EngineDependentOperation {
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(cellIndex); Cell cell = row.getCell(cellIndex);
ExpressionUtils.bind(bindings, row, rowIndex, cell); ExpressionUtils.bind(bindings, row, rowIndex, cell);

View File

@ -163,7 +163,7 @@ public class MassEditOperation extends EngineDependentMassCellOperation {
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(cellIndex); Cell cell = row.getCell(cellIndex);
ExpressionUtils.bind(bindings, row, rowIndex, cell); ExpressionUtils.bind(bindings, row, rowIndex, cell);

View File

@ -69,7 +69,7 @@ public class ReconDiscardJudgmentsOperation extends EngineDependentMassCellOpera
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(cellIndex); Cell cell = row.getCell(cellIndex);
if (cell != null && cell.recon != null) { if (cell != null && cell.recon != null) {
Recon recon = cell.recon.dup(); Recon recon = cell.recon.dup();

View File

@ -159,7 +159,7 @@ public class ReconJudgeSimilarCellsOperation extends EngineDependentMassCellOper
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(_cellIndex); Cell cell = row.getCell(_cellIndex);
if (cell != null && if (cell != null &&
ExpressionUtils.isNonBlankData(cell.value) && ExpressionUtils.isNonBlankData(cell.value) &&

View File

@ -82,7 +82,7 @@ public class ReconMarkNewTopicsOperation extends EngineDependentMassCellOperatio
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(cellIndex); Cell cell = row.getCell(cellIndex);
if (cell != null) { if (cell != null) {
Recon recon = null; Recon recon = null;

View File

@ -69,7 +69,7 @@ public class ReconMatchBestCandidatesOperation extends EngineDependentMassCellOp
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
if (cellIndex < row.cells.size()) { if (cellIndex < row.cells.size()) {
Cell cell = row.cells.get(cellIndex); Cell cell = row.cells.get(cellIndex);
if (cell.recon != null) { if (cell.recon != null) {

View File

@ -103,7 +103,7 @@ public class ReconMatchSpecificTopicOperation extends EngineDependentMassCellOpe
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
if (cellIndex < row.cells.size()) { if (cellIndex < row.cells.size()) {
Cell cell = row.cells.get(cellIndex); Cell cell = row.cells.get(cellIndex);

View File

@ -170,7 +170,7 @@ public class ReconOperation extends EngineDependentOperation {
FilteredRows filteredRows = engine.getAllFilteredRows(false); FilteredRows filteredRows = engine.getAllFilteredRows(false);
filteredRows.accept(_project, new RowVisitor() { filteredRows.accept(_project, new RowVisitor() {
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
if (_cellIndex < row.cells.size()) { if (_cellIndex < row.cells.size()) {
Cell cell = row.cells.get(_cellIndex); Cell cell = row.cells.get(_cellIndex);
if (cell != null && ExpressionUtils.isNonBlankData(cell.value)) { if (cell != null && ExpressionUtils.isNonBlankData(cell.value)) {

View File

@ -77,7 +77,7 @@ public class RowStarOperation extends EngineDependentOperation {
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
if (row.starred != _starred) { if (row.starred != _starred) {
RowStarChange change = new RowStarChange(rowIndex, _starred); RowStarChange change = new RowStarChange(rowIndex, _starred);

View File

@ -118,7 +118,7 @@ public class TextTransformOperation extends EngineDependentMassCellOperation {
return this; return this;
} }
public boolean visit(Project project, int rowIndex, Row row, boolean contextual) { public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cell = row.getCell(cellIndex); Cell cell = row.getCell(cellIndex);
Object oldValue = cell != null ? cell.value : null; Object oldValue = cell != null ? cell.value : null;

View File

@ -42,7 +42,10 @@ BrowsingEngine.prototype._initializeUI = function() {
this._div.html( this._div.html(
'<div class="browsing-panel-indicator" bind="indicator"><img src="images/small-spinner.gif" /> refreshing facets ...</div>' + '<div class="browsing-panel-indicator" bind="indicator"><img src="images/small-spinner.gif" /> refreshing facets ...</div>' +
'<div class="browsing-panel-controls" bind="controls"><a href="javascript:{}" bind="refreshLink">refresh facets</a></div>' + '<div class="browsing-panel-controls" bind="controls">' +
'<input type="checkbox" bind="includeDependentRowsCheck" /> show dependent rows &bull; ' +
'<a href="javascript:{}" bind="refreshLink">refresh</a></div>' +
'</div>' +
'<ul bind="facets" class="facets-container"></ul>' '<ul bind="facets" class="facets-container"></ul>'
); );
this._elmts = DOM.bind(this._div); this._elmts = DOM.bind(this._div);
@ -54,6 +57,10 @@ BrowsingEngine.prototype._initializeUI = function() {
}); });
this._elmts.facets.disableSelection(); this._elmts.facets.disableSelection();
this._elmts.includeDependentRowsCheck.change(function() {
Gridworks.update({ engineChanged: true });
});
this._elmts.refreshLink.click(function() { self.update(); }); this._elmts.refreshLink.click(function() { self.update(); });
}; };
@ -73,7 +80,10 @@ BrowsingEngine.prototype._updateFacetOrder = function() {
}; };
BrowsingEngine.prototype.getJSON = function(keepUnrestrictedFacets) { BrowsingEngine.prototype.getJSON = function(keepUnrestrictedFacets) {
var a = { facets: [] }; var a = {
facets: [],
includeDependent: this._elmts.includeDependentRowsCheck[0].checked
};
for (var i = 0; i < this._facets.length; i++) { for (var i = 0; i < this._facets.length; i++) {
var facet = this._facets[i]; var facet = this._facets[i];
if (keepUnrestrictedFacets || facet.facet.hasSelection()) { if (keepUnrestrictedFacets || facet.facet.hasSelection()) {