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;
public class CellKeyNode extends CellNode {
final public FreebaseTopic namespace;
final public FreebaseTopic namespace;
public CellKeyNode(
String columnName,
FreebaseTopic namespace
FreebaseTopic namespace
) {
super(columnName);
this.namespace = namespace;
}
@ -22,7 +19,14 @@ public class CellKeyNode extends CellNode {
writer.object();
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.endObject();
}

View File

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

View File

@ -12,11 +12,8 @@ public class CellTopicNode extends CellNode implements NodeWithLinks {
final public List<Link> links = new LinkedList<Link>();
public CellTopicNode(
String columnName,
FreebaseType type
FreebaseType type
) {
super(columnName);
this.type = type;
}
@ -25,7 +22,12 @@ public class CellTopicNode extends CellNode implements NodeWithLinks {
writer.object();
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) {
writer.key("type"); type.write(writer, options);
}

View File

@ -10,12 +10,9 @@ public class CellValueNode extends CellNode {
final public String lang;
public CellValueNode(
String columnName,
String valueType,
String valueType,
String lang
) {
super(columnName);
this.valueType = valueType;
this.lang = lang;
}
@ -25,7 +22,12 @@ public class CellValueNode extends CellNode {
writer.object();
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("lang"); writer.value(lang);
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 {
final public FreebaseProperty property;
final public Node target;
final public Condition condition;
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.target = target;
this.condition = condition;
this.load = load;
}

View File

@ -45,27 +45,34 @@ public class Protograph implements OverlayModel {
String nodeType = o.getString("nodeType");
if (nodeType.startsWith("cell-as-")) {
String columnName = o.getString("columnName");
if ("cell-as-topic".equals(nodeType)) {
if (o.has("type")) {
node = new CellTopicNode(
columnName,
reconstructType(o.getJSONObject("type"))
);
}
} else if ("cell-as-value".equals(nodeType)) {
node = new CellValueNode(
columnName,
o.getString("valueType"),
o.getString("lang")
);
} else if ("cell-as-key".equals(nodeType)) {
node = new CellKeyNode(
columnName,
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)) {
node = new FreebaseTopicNode(reconstructTopic(o.getJSONObject("topic")));
} else if ("value".equals(nodeType)) {
@ -86,11 +93,20 @@ public class Protograph implements OverlayModel {
for (int j = 0; j < linkCount; 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(
reconstructProperty(oLink.getJSONObject("property")),
oLink.has("target") && !oLink.isNull("target") ?
reconstructNode(oLink.getJSONObject("target")) : null,
condition,
oLink.has("load") && !oLink.isNull("load") ?
oLink.getBoolean("load") : true
));

View File

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

View File

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

View File

@ -97,84 +97,88 @@ public class Transposer {
Node node,
Context context
) {
TransposedNode tnode = null;
List<TransposedNode> tnodes = new LinkedList<TransposedNode>();
TransposedNode parentNode = context.parent == null ? null : context.parent.transposedNode;
Link link = context.parent == null ? null : context.link;
if (node instanceof CellNode) {
CellNode node2 = (CellNode) node;
Column column = project.columnModel.getColumnByName(node2.columnName);
if (column != null) {
Cell cell = row.getCell(column.getCellIndex());
if (cell != null && ExpressionUtils.isNonBlankData(cell.value)) {
if (node2 instanceof CellTopicNode &&
(cell.recon == null || cell.recon.judgment == Judgment.None)) {
for (String columnName : node2.columnNames) {
Column column = project.columnModel.getColumnByName(columnName);
if (column != null) {
int cellIndex = column.getCellIndex();
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;
}
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 {
if (node instanceof AnonymousNode) {
tnode = nodeFactory.transposeAnonymousNode(
tnodes.add(nodeFactory.transposeAnonymousNode(
parentNode,
link,
(AnonymousNode) node,
rowIndex
);
));
} else if (node instanceof FreebaseTopicNode) {
tnode = nodeFactory.transposeTopicNode(
tnodes.add(nodeFactory.transposeTopicNode(
parentNode,
link,
(FreebaseTopicNode) node,
rowIndex
);
));
} else if (node instanceof ValueNode) {
tnode = nodeFactory.transposeValueNode(
tnodes.add(nodeFactory.transposeValueNode(
parentNode,
link,
(ValueNode) node,
rowIndex
);
));
}
}
if (tnode != null) {
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) {
if (node instanceof NodeWithLinks) {
NodeWithLinks node2 = (NodeWithLinks) node;
int linkCount = node2.getLinkCount();
for (int i = 0; i < linkCount; i++) {
descend(
project,
protograph,
nodeFactory,
rowIndex,
row,
node2.getLink(i).getTarget(),
context.subContexts.get(i)
);
Link link2 = node2.getLink(i);
if (link2.condition == null || link2.condition.test(project, rowIndex, row)) {
for (TransposedNode tnode : tnodes) {
context.transposedNode = tnode;
context.nullifySubContextNodes();
descend(
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)) {
id = newTopicVars.get(cell.recon.id);
} else {
long var = 0;
if (varPool.containsKey(node.columnName)) {
var = varPool.get(node.columnName);
}
varPool.put(node.columnName, var + 1);
Column column = project.columnModel.getColumnByCellIndex(cellIndex);
String columnName = column.getName();
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;
Column column = project.columnModel.getColumnByName(node.columnName);
ReconConfig reconConfig = column.getReconConfig();
if (reconConfig instanceof StandardReconConfig) {
typeID = ((StandardReconConfig) reconConfig).typeID;
@ -510,14 +512,12 @@ public class TripleLoaderTransposedNodeFactory implements TransposedNodeFactory
TransposedNode parentNode,
Link link,
CellNode node,
int rowIndex,
int rowIndex,
int cellIndex,
Cell cell) {
WritingTransposedNode parentNode2 = (WritingTransposedNode) parentNode;
Column column = project.columnModel.getColumnByName(node.columnName);
int cellIndex = column != null ? column.getCellIndex() : -1;
WritingTransposedNode tnode = null;
if (node instanceof CellTopicNode) {
if (cell.recon != null &&

View File

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