Added support in protograph for specifying several column names per cell-as nodes.

Started to add support for conditional links in protograph. The UI is not hooked up with.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@1136 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-08-05 08:29:34 +00:00
parent b8ad56c6db
commit 5cb3f924f6
13 changed files with 194 additions and 102 deletions

View File

@ -0,0 +1,31 @@
package com.google.gridworks.protograph;
import com.google.gridworks.model.Column;
import com.google.gridworks.model.Project;
import com.google.gridworks.model.Row;
public class BooleanColumnCondition implements Condition {
final public String columnName;
public BooleanColumnCondition(String columnName) {
this.columnName = columnName;
}
@Override
public boolean test(Project project, int rowIndex, Row row) {
Column column = project.columnModel.getColumnByName(columnName);
if (column != null) {
Object o = row.getCellValue(column.getCellIndex());
if (o != null) {
if (o instanceof Boolean) {
return ((Boolean) o).booleanValue();
} else {
return Boolean.parseBoolean(o.toString());
}
}
}
return false;
}
}

View File

@ -6,14 +6,11 @@ import org.json.JSONException;
import org.json.JSONWriter; import org.json.JSONWriter;
public class CellKeyNode extends CellNode { public class CellKeyNode extends CellNode {
final public FreebaseTopic namespace; final public FreebaseTopic namespace;
public CellKeyNode( public CellKeyNode(
String columnName, FreebaseTopic namespace
FreebaseTopic namespace
) { ) {
super(columnName);
this.namespace = namespace; this.namespace = namespace;
} }
@ -22,7 +19,14 @@ public class CellKeyNode extends CellNode {
writer.object(); writer.object();
writer.key("nodeType"); writer.value("cell-as-key"); writer.key("nodeType"); writer.value("cell-as-key");
writer.key("columnName"); writer.value(columnName);
writer.key("columnNames");
writer.array();
for (String name : columnNames) {
writer.value(name);
}
writer.endArray();
writer.key("namespace"); namespace.write(writer, options); writer.key("namespace"); namespace.write(writer, options);
writer.endObject(); writer.endObject();
} }

View File

@ -1,11 +1,8 @@
package com.google.gridworks.protograph; package com.google.gridworks.protograph;
import java.util.LinkedList;
import java.util.List;
abstract public class CellNode implements Node { abstract public class CellNode implements Node {
final public String columnName; final public List<String> columnNames = new LinkedList<String>();
public CellNode(
String columnName
) {
this.columnName = columnName;
}
} }

View File

@ -12,11 +12,8 @@ public class CellTopicNode extends CellNode implements NodeWithLinks {
final public List<Link> links = new LinkedList<Link>(); final public List<Link> links = new LinkedList<Link>();
public CellTopicNode( public CellTopicNode(
String columnName, FreebaseType type
FreebaseType type
) { ) {
super(columnName);
this.type = type; this.type = type;
} }
@ -25,7 +22,12 @@ public class CellTopicNode extends CellNode implements NodeWithLinks {
writer.object(); writer.object();
writer.key("nodeType"); writer.value("cell-as-topic"); writer.key("nodeType"); writer.value("cell-as-topic");
writer.key("columnName"); writer.value(columnName); writer.key("columnNames");
writer.array();
for (String name : columnNames) {
writer.value(name);
}
writer.endArray();
if (type != null) { if (type != null) {
writer.key("type"); type.write(writer, options); writer.key("type"); type.write(writer, options);
} }

View File

@ -10,12 +10,9 @@ public class CellValueNode extends CellNode {
final public String lang; final public String lang;
public CellValueNode( public CellValueNode(
String columnName, String valueType,
String valueType,
String lang String lang
) { ) {
super(columnName);
this.valueType = valueType; this.valueType = valueType;
this.lang = lang; this.lang = lang;
} }
@ -25,7 +22,12 @@ public class CellValueNode extends CellNode {
writer.object(); writer.object();
writer.key("nodeType"); writer.value("cell-as-value"); writer.key("nodeType"); writer.value("cell-as-value");
writer.key("columnName"); writer.value(columnName); writer.key("columnNames");
writer.array();
for (String name : columnNames) {
writer.value(name);
}
writer.endArray();
writer.key("valueType"); writer.value(valueType); writer.key("valueType"); writer.value(valueType);
writer.key("lang"); writer.value(lang); writer.key("lang"); writer.value(lang);
writer.endObject(); writer.endObject();

View File

@ -0,0 +1,8 @@
package com.google.gridworks.protograph;
import com.google.gridworks.model.Project;
import com.google.gridworks.model.Row;
public interface Condition {
public boolean test(Project project, int rowIndex, Row row);
}

View File

@ -10,11 +10,13 @@ import com.google.gridworks.Jsonizable;
public class Link implements Jsonizable { public class Link implements Jsonizable {
final public FreebaseProperty property; final public FreebaseProperty property;
final public Node target; final public Node target;
final public Condition condition;
final public boolean load; final public boolean load;
public Link(FreebaseProperty property, Node target, boolean load) { public Link(FreebaseProperty property, Node target, Condition condition, boolean load) {
this.property = property; this.property = property;
this.target = target; this.target = target;
this.condition = condition;
this.load = load; this.load = load;
} }

View File

@ -45,27 +45,34 @@ public class Protograph implements OverlayModel {
String nodeType = o.getString("nodeType"); String nodeType = o.getString("nodeType");
if (nodeType.startsWith("cell-as-")) { if (nodeType.startsWith("cell-as-")) {
String columnName = o.getString("columnName");
if ("cell-as-topic".equals(nodeType)) { if ("cell-as-topic".equals(nodeType)) {
if (o.has("type")) { if (o.has("type")) {
node = new CellTopicNode( node = new CellTopicNode(
columnName,
reconstructType(o.getJSONObject("type")) reconstructType(o.getJSONObject("type"))
); );
} }
} else if ("cell-as-value".equals(nodeType)) { } else if ("cell-as-value".equals(nodeType)) {
node = new CellValueNode( node = new CellValueNode(
columnName,
o.getString("valueType"), o.getString("valueType"),
o.getString("lang") o.getString("lang")
); );
} else if ("cell-as-key".equals(nodeType)) { } else if ("cell-as-key".equals(nodeType)) {
node = new CellKeyNode( node = new CellKeyNode(
columnName,
reconstructTopic(o.getJSONObject("namespace")) reconstructTopic(o.getJSONObject("namespace"))
); );
} }
if (o.has("columnName") && !o.isNull("columnName")) {
((CellNode) node).columnNames.add(o.getString("columnName"));
}
if (o.has("columnNames") && !o.isNull("columnNames")) {
JSONArray columnNames = o.getJSONArray("columnNames");
int count = columnNames.length();
for (int c = 0; c < count; c++) {
((CellNode) node).columnNames.add(columnNames.getString(c));
}
}
} else if ("topic".equals(nodeType)) { } else if ("topic".equals(nodeType)) {
node = new FreebaseTopicNode(reconstructTopic(o.getJSONObject("topic"))); node = new FreebaseTopicNode(reconstructTopic(o.getJSONObject("topic")));
} else if ("value".equals(nodeType)) { } else if ("value".equals(nodeType)) {
@ -86,11 +93,20 @@ public class Protograph implements OverlayModel {
for (int j = 0; j < linkCount; j++) { for (int j = 0; j < linkCount; j++) {
JSONObject oLink = links.getJSONObject(j); JSONObject oLink = links.getJSONObject(j);
Condition condition = null;
if (oLink.has("condition") && !oLink.isNull("condition")) {
JSONObject oCondition = oLink.getJSONObject("condition");
if (oCondition.has("columnName") && !oCondition.isNull("columnName")) {
condition = new BooleanColumnCondition(oCondition.getString("columnName"));
}
}
node2.addLink(new Link( node2.addLink(new Link(
reconstructProperty(oLink.getJSONObject("property")), reconstructProperty(oLink.getJSONObject("property")),
oLink.has("target") && !oLink.isNull("target") ? oLink.has("target") && !oLink.isNull("target") ?
reconstructNode(oLink.getJSONObject("target")) : null, reconstructNode(oLink.getJSONObject("target")) : null,
condition,
oLink.has("load") && !oLink.isNull("load") ? oLink.has("load") && !oLink.isNull("load") ?
oLink.getBoolean("load") : true oLink.getBoolean("load") : true
)); ));

View File

@ -261,7 +261,9 @@ public class MqlwriteLikeTransposedNodeFactory implements TransposedNodeFactory
TransposedNode parentNode, TransposedNode parentNode,
Link link, Link link,
CellNode node, CellNode node,
int rowIndex, Cell cell) { int rowIndex,
int cellIndex,
Cell cell) {
JsonTransposedNode tnode = null; JsonTransposedNode tnode = null;
if (node instanceof CellTopicNode) { if (node instanceof CellTopicNode) {

View File

@ -21,6 +21,7 @@ public interface TransposedNodeFactory {
Link link, Link link,
CellNode node, CellNode node,
int rowIndex, int rowIndex,
int cellIndex,
Cell cell Cell cell
); );

View File

@ -97,84 +97,88 @@ public class Transposer {
Node node, Node node,
Context context Context context
) { ) {
TransposedNode tnode = null; List<TransposedNode> tnodes = new LinkedList<TransposedNode>();
TransposedNode parentNode = context.parent == null ? null : context.parent.transposedNode; TransposedNode parentNode = context.parent == null ? null : context.parent.transposedNode;
Link link = context.parent == null ? null : context.link; Link link = context.parent == null ? null : context.link;
if (node instanceof CellNode) { if (node instanceof CellNode) {
CellNode node2 = (CellNode) node; CellNode node2 = (CellNode) node;
Column column = project.columnModel.getColumnByName(node2.columnName); for (String columnName : node2.columnNames) {
if (column != null) { Column column = project.columnModel.getColumnByName(columnName);
Cell cell = row.getCell(column.getCellIndex()); if (column != null) {
if (cell != null && ExpressionUtils.isNonBlankData(cell.value)) { int cellIndex = column.getCellIndex();
if (node2 instanceof CellTopicNode &&
(cell.recon == null || cell.recon.judgment == Judgment.None)) { Cell cell = row.getCell(cellIndex);
if (cell != null && ExpressionUtils.isNonBlankData(cell.value)) {
if (node2 instanceof CellTopicNode &&
(cell.recon == null || cell.recon.judgment == Judgment.None)) {
return;
}
context.count++;
if (context.limit > 0 && context.count > context.limit) {
return; return;
}
tnodes.add(nodeFactory.transposeCellNode(
parentNode,
link,
node2,
rowIndex,
cellIndex,
cell
));
} }
context.count++;
if (context.limit > 0 && context.count > context.limit) {
return;
}
tnode = nodeFactory.transposeCellNode(
parentNode,
link,
node2,
rowIndex,
cell
);
} }
} }
} else { } else {
if (node instanceof AnonymousNode) { if (node instanceof AnonymousNode) {
tnode = nodeFactory.transposeAnonymousNode( tnodes.add(nodeFactory.transposeAnonymousNode(
parentNode, parentNode,
link, link,
(AnonymousNode) node, (AnonymousNode) node,
rowIndex rowIndex
); ));
} else if (node instanceof FreebaseTopicNode) { } else if (node instanceof FreebaseTopicNode) {
tnode = nodeFactory.transposeTopicNode( tnodes.add(nodeFactory.transposeTopicNode(
parentNode, parentNode,
link, link,
(FreebaseTopicNode) node, (FreebaseTopicNode) node,
rowIndex rowIndex
); ));
} else if (node instanceof ValueNode) { } else if (node instanceof ValueNode) {
tnode = nodeFactory.transposeValueNode( tnodes.add(nodeFactory.transposeValueNode(
parentNode, parentNode,
link, link,
(ValueNode) node, (ValueNode) node,
rowIndex rowIndex
); ));
} }
} }
if (tnode != null) { if (node instanceof NodeWithLinks) {
context.transposedNode = tnode;
context.nullifySubContextNodes();
} /*
else, previous rows might have set the context transposed node already,
and we simply inherit that transposed node.
*/
if (node instanceof NodeWithLinks && context.transposedNode != null) {
NodeWithLinks node2 = (NodeWithLinks) node; NodeWithLinks node2 = (NodeWithLinks) node;
int linkCount = node2.getLinkCount(); int linkCount = node2.getLinkCount();
for (int i = 0; i < linkCount; i++) { for (int i = 0; i < linkCount; i++) {
descend( Link link2 = node2.getLink(i);
project, if (link2.condition == null || link2.condition.test(project, rowIndex, row)) {
protograph, for (TransposedNode tnode : tnodes) {
nodeFactory, context.transposedNode = tnode;
rowIndex, context.nullifySubContextNodes();
row,
node2.getLink(i).getTarget(), descend(
context.subContexts.get(i) project,
); protograph,
nodeFactory,
rowIndex,
row,
link2.getTarget(),
context.subContexts.get(i)
);
}
}
} }
} }
} }

View File

@ -352,17 +352,19 @@ public class TripleLoaderTransposedNodeFactory implements TransposedNodeFactory
if (newTopicVars.containsKey(cell.recon.id)) { if (newTopicVars.containsKey(cell.recon.id)) {
id = newTopicVars.get(cell.recon.id); id = newTopicVars.get(cell.recon.id);
} else { } else {
long var = 0; Column column = project.columnModel.getColumnByCellIndex(cellIndex);
if (varPool.containsKey(node.columnName)) { String columnName = column.getName();
var = varPool.get(node.columnName);
}
varPool.put(node.columnName, var + 1);
id = "$" + node.columnName.replaceAll("\\W+", "_") + "_" + var; long var = 0;
if (varPool.containsKey(columnName)) {
var = varPool.get(columnName);
}
varPool.put(columnName, var + 1);
id = "$" + columnName.replaceAll("\\W+", "_") + "_" + var;
String typeID = node.type.id; String typeID = node.type.id;
Column column = project.columnModel.getColumnByName(node.columnName);
ReconConfig reconConfig = column.getReconConfig(); ReconConfig reconConfig = column.getReconConfig();
if (reconConfig instanceof StandardReconConfig) { if (reconConfig instanceof StandardReconConfig) {
typeID = ((StandardReconConfig) reconConfig).typeID; typeID = ((StandardReconConfig) reconConfig).typeID;
@ -510,14 +512,12 @@ public class TripleLoaderTransposedNodeFactory implements TransposedNodeFactory
TransposedNode parentNode, TransposedNode parentNode,
Link link, Link link,
CellNode node, CellNode node,
int rowIndex, int rowIndex,
int cellIndex,
Cell cell) { Cell cell) {
WritingTransposedNode parentNode2 = (WritingTransposedNode) parentNode; WritingTransposedNode parentNode2 = (WritingTransposedNode) parentNode;
Column column = project.columnModel.getColumnByName(node.columnName);
int cellIndex = column != null ? column.getCellIndex() : -1;
WritingTransposedNode tnode = null; WritingTransposedNode tnode = null;
if (node instanceof CellTopicNode) { if (node instanceof CellTopicNode) {
if (cell.recon != null && if (cell.recon != null &&

View File

@ -3,6 +3,11 @@ SchemaAlignmentDialog.UINode = function(dialog, node, table, options) {
this._node = node; this._node = node;
this._options = options; this._options = options;
if ("columnName" in this._node) {
this._node.columnNames = [ this._node.columnName ];
delete this._node.columnName;
}
this._linkUIs = []; this._linkUIs = [];
this._detailsRendered = false; this._detailsRendered = false;
@ -74,13 +79,19 @@ SchemaAlignmentDialog.UINode.prototype._renderMain = function() {
this._node.nodeType == "cell-as-value" || this._node.nodeType == "cell-as-value" ||
this._node.nodeType == "cell-as-key") { this._node.nodeType == "cell-as-key") {
if ("columnName" in this._node) { if ("columnNames" in this._node) {
a.html(" cell"); for (var c = 0; c < this._node.columnNames.length; c++) {
if (c > 0) {
$('<span>').text(", ").appendTo(a);
}
$('<span>')
.text(this._node.columnNames[c])
.addClass("schema-alignment-node-column")
.appendTo(a);
}
$('<span></span>') $('<span>').text(this._node.columnNames.length > 1 ? " cells" : " cell").appendTo(a);
.text(this._node.columnName)
.addClass("schema-alignment-node-column")
.prependTo(a);
} else { } else {
a.html(this._options.mustBeCellTopic ? "Which column?" : "Configure..."); a.html(this._options.mustBeCellTopic ? "Which column?" : "Configure...");
} }
@ -229,7 +240,7 @@ SchemaAlignmentDialog.UINode.prototype._showColumnPopupMenu = function(elmt) {
label: columns[index].name, label: columns[index].name,
click: function() { click: function() {
self._node.nodeType = "cell-as-topic"; self._node.nodeType = "cell-as-topic";
self._node.columnName = columns[index].name; self._node.columnNames = [ columns[index].name ];
self._showExpandable(); self._showExpandable();
self._renderMain(); self._renderMain();
} }
@ -398,11 +409,18 @@ SchemaAlignmentDialog.UINode.prototype._showNodeConfigDialog = function() {
.attr("cellpadding", "0") .attr("cellpadding", "0")
.appendTo(elmts.divColumns)[0]; .appendTo(elmts.divColumns)[0];
var columnMap = {};
if ("columnNames" in self._node) {
for (var i = 0; i < self._node.columnNames.length; i++) {
columnMap[self._node.columnNames[i]] = true;
}
}
var makeColumnChoice = function(column, columnIndex) { var makeColumnChoice = function(column, columnIndex) {
var tr = tableColumns.insertRow(tableColumns.rows.length); var tr = tableColumns.insertRow(tableColumns.rows.length);
var radio = $('<input />') var radio = $('<input />')
.attr("type", "radio") .attr("type", "checkbox")
.attr("value", column.name) .attr("value", column.name)
.attr("name", "schema-align-node-dialog-column") .attr("name", "schema-align-node-dialog-column")
.appendTo(tr.insertCell(0)) .appendTo(tr.insertCell(0))
@ -420,9 +438,7 @@ SchemaAlignmentDialog.UINode.prototype._showNodeConfigDialog = function() {
} }
}); });
if ((!("columnName" in self._node) || !self._node.columnName) && columnIndex === 0) { if (column.name in columnMap) {
radio.attr("checked", "true");
} else if (column.name == self._node.columnName) {
radio.attr("checked", "true"); radio.attr("checked", "true");
} }
@ -539,7 +555,14 @@ SchemaAlignmentDialog.UINode.prototype._showNodeConfigDialog = function() {
}; };
if (node.nodeType == "cell-as") { if (node.nodeType == "cell-as") {
node.nodeType = $("input[name='schema-align-node-dialog-node-subtype']:checked")[0].value; node.nodeType = $("input[name='schema-align-node-dialog-node-subtype']:checked")[0].value;
node.columnName = $("input[name='schema-align-node-dialog-column']:checked")[0].value; node.columnNames = $("input[name='schema-align-node-dialog-column']:checked").map(function() {
return this.getAttribute("value");
}).get();
if (node.columnNames.length == 0) {
alert("You must select at least one column.");
return null;
}
if (node.nodeType == "cell-as-topic") { if (node.nodeType == "cell-as-topic") {
node.createForNoReconMatch = elmts.radioNodeTypeCellAsTopicCreate[0].checked; node.createForNoReconMatch = elmts.radioNodeTypeCellAsTopicCreate[0].checked;
@ -631,14 +654,14 @@ SchemaAlignmentDialog.UINode.prototype.getJSON = function() {
var getLinks = false; var getLinks = false;
if (this._node.nodeType.match(/^cell-as-/)) { if (this._node.nodeType.match(/^cell-as-/)) {
if (!("columnName" in this._node) || !this._node.columnName) { if (!("columnNames" in this._node) || !this._node.columnNames) {
return null; return null;
} }
if (this._node.nodeType == "cell-as-topic") { if (this._node.nodeType == "cell-as-topic") {
result = { result = {
nodeType: this._node.nodeType, nodeType: this._node.nodeType,
columnName: this._node.columnName, columnNames: this._node.columnNames,
type: "type" in this._node ? cloneDeep(this._node.type) : { "id" : "/common/topic", "name" : "Topic", "cvt" : false }, type: "type" in this._node ? cloneDeep(this._node.type) : { "id" : "/common/topic", "name" : "Topic", "cvt" : false },
createForNoReconMatch: "createForNoReconMatch" in this._node ? this._node.createForNoReconMatch : true createForNoReconMatch: "createForNoReconMatch" in this._node ? this._node.createForNoReconMatch : true
}; };
@ -646,7 +669,7 @@ SchemaAlignmentDialog.UINode.prototype.getJSON = function() {
} else if (this._node.nodeType == "cell-as-value") { } else if (this._node.nodeType == "cell-as-value") {
result = { result = {
nodeType: this._node.nodeType, nodeType: this._node.nodeType,
columnName: this._node.columnName, columnNames: this._node.columnNames,
valueType: "valueType" in this._node ? this._node.valueType : "/type/text", valueType: "valueType" in this._node ? this._node.valueType : "/type/text",
lang: "lang" in this._node ? this._node.lang : "/lang/en" lang: "lang" in this._node ? this._node.lang : "/lang/en"
}; };
@ -656,7 +679,7 @@ SchemaAlignmentDialog.UINode.prototype.getJSON = function() {
} }
result = { result = {
nodeType: this._node.nodeType, nodeType: this._node.nodeType,
columnName: this._node.columnName, columnNames: this._node.columnNames,
type: cloneDeep(this._node.namespace) type: cloneDeep(this._node.namespace)
}; };
} }