Make cross() function work for all columns (#2456)
* fix #1950 * migrate from join to lookup * reformat
This commit is contained in:
parent
cff88e6267
commit
9ad3b1080f
@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2010, Google Inc.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.refine;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import com.google.refine.expr.ExpressionUtils;
|
|
||||||
import com.google.refine.expr.HasFieldsListImpl;
|
|
||||||
import com.google.refine.expr.WrappedRow;
|
|
||||||
import com.google.refine.model.Column;
|
|
||||||
import com.google.refine.model.Project;
|
|
||||||
import com.google.refine.model.Row;
|
|
||||||
import com.google.refine.util.JoinException;
|
|
||||||
|
|
||||||
public class InterProjectModel {
|
|
||||||
static public class ProjectJoin {
|
|
||||||
final public long fromProjectID;
|
|
||||||
final public String fromProjectColumnName;
|
|
||||||
final public long toProjectID;
|
|
||||||
final public String toProjectColumnName;
|
|
||||||
|
|
||||||
final public Map<Object, List<Integer>> valueToRowIndices =
|
|
||||||
new HashMap<Object, List<Integer>>();
|
|
||||||
|
|
||||||
ProjectJoin(
|
|
||||||
long fromProjectID,
|
|
||||||
String fromProjectColumnName,
|
|
||||||
long toProjectID,
|
|
||||||
String toProjectColumnName
|
|
||||||
) {
|
|
||||||
this.fromProjectID = fromProjectID;
|
|
||||||
this.fromProjectColumnName = fromProjectColumnName;
|
|
||||||
this.toProjectID = toProjectID;
|
|
||||||
this.toProjectColumnName = toProjectColumnName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HasFieldsListImpl getRows(Object value) {
|
|
||||||
if (ExpressionUtils.isNonBlankData(value) && valueToRowIndices.containsKey(value)) {
|
|
||||||
Project toProject = ProjectManager.singleton.getProject(toProjectID);
|
|
||||||
if (toProject != null) {
|
|
||||||
HasFieldsListImpl rows = new HasFieldsListImpl();
|
|
||||||
for (Integer r : valueToRowIndices.get(value)) {
|
|
||||||
Row row = toProject.rows.get(r);
|
|
||||||
rows.add(new WrappedRow(toProject, r, row));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, ProjectJoin> _joins = new HashMap<String, ProjectJoin>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the ProjectJoin based on combination key, return the cached one from the HashMap if already computed
|
|
||||||
*
|
|
||||||
* @param fromProject
|
|
||||||
* @param fromColumn
|
|
||||||
* @param toProject
|
|
||||||
* @param toColumn
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public ProjectJoin getJoin(Long fromProject, String fromColumn, Long toProject, String toColumn) throws JoinException {
|
|
||||||
String key = fromProject + ";" + fromColumn + ";" + toProject + ";" + toColumn;
|
|
||||||
if (!_joins.containsKey(key)) {
|
|
||||||
ProjectJoin join = new ProjectJoin(
|
|
||||||
fromProject,
|
|
||||||
fromColumn,
|
|
||||||
toProject,
|
|
||||||
toColumn
|
|
||||||
);
|
|
||||||
|
|
||||||
computeJoin(join);
|
|
||||||
|
|
||||||
synchronized (_joins) {
|
|
||||||
_joins.put(key, join);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _joins.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flushJoinsInvolvingProject(long projectID) {
|
|
||||||
synchronized (_joins) {
|
|
||||||
for (Iterator<Entry<String, ProjectJoin>> it = _joins.entrySet().iterator(); it.hasNext();) {
|
|
||||||
Entry<String, ProjectJoin> entry = it.next();
|
|
||||||
ProjectJoin join = entry.getValue();
|
|
||||||
if (join.fromProjectID == projectID || join.toProjectID == projectID) {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flushJoinsInvolvingProjectColumn(long projectID, String columnName) {
|
|
||||||
synchronized (_joins) {
|
|
||||||
for (Iterator<Entry<String, ProjectJoin>> it = _joins.entrySet().iterator(); it.hasNext();) {
|
|
||||||
Entry<String, ProjectJoin> entry = it.next();
|
|
||||||
ProjectJoin join = entry.getValue();
|
|
||||||
if (join.fromProjectID == projectID && join.fromProjectColumnName.equals(columnName) ||
|
|
||||||
join.toProjectID == projectID && join.toProjectColumnName.equals(columnName)) {
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void computeJoin(ProjectJoin join) throws JoinException {
|
|
||||||
if (join.fromProjectID < 0 || join.toProjectID < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Project fromProject = ProjectManager.singleton.getProject(join.fromProjectID);
|
|
||||||
ProjectMetadata fromProjectMD = ProjectManager.singleton.getProjectMetadata(join.fromProjectID);
|
|
||||||
Project toProject = ProjectManager.singleton.getProject(join.toProjectID);
|
|
||||||
ProjectMetadata toProjectMD = ProjectManager.singleton.getProjectMetadata(join.toProjectID);
|
|
||||||
|
|
||||||
// split this test to check each one and throw an appropriate error
|
|
||||||
if (fromProject == null || toProject == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Column fromColumn = fromProject.columnModel.getColumnByName(join.fromProjectColumnName);
|
|
||||||
Column toColumn = toProject.columnModel.getColumnByName(join.toProjectColumnName);
|
|
||||||
if (fromColumn == null) {
|
|
||||||
throw new JoinException("Unable to find column " + join.fromProjectColumnName + " in project " + fromProjectMD.getName());
|
|
||||||
}
|
|
||||||
if (toColumn == null) {
|
|
||||||
throw new JoinException("Unable to find column " + join.toProjectColumnName + " in project " + toProjectMD.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Row fromRow : fromProject.rows) {
|
|
||||||
Object value = fromRow.getCellValue(fromColumn.getCellIndex());
|
|
||||||
if (ExpressionUtils.isNonBlankData(value) && !join.valueToRowIndices.containsKey(value)) {
|
|
||||||
join.valueToRowIndices.put(value, new ArrayList<Integer>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = toProject.rows.size();
|
|
||||||
for (int r = 0; r < count; r++) {
|
|
||||||
Row toRow = toProject.rows.get(r);
|
|
||||||
|
|
||||||
Object value = toRow.getCellValue(toColumn.getCellIndex());
|
|
||||||
if (ExpressionUtils.isNonBlankData(value) && join.valueToRowIndices.containsKey(value)) {
|
|
||||||
join.valueToRowIndices.get(value).add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
124
main/src/com/google/refine/LookupCacheManager.java
Normal file
124
main/src/com/google/refine/LookupCacheManager.java
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package com.google.refine;
|
||||||
|
|
||||||
|
import com.google.refine.expr.ExpressionUtils;
|
||||||
|
import com.google.refine.expr.HasFieldsListImpl;
|
||||||
|
import com.google.refine.expr.WrappedRow;
|
||||||
|
import com.google.refine.model.Column;
|
||||||
|
import com.google.refine.model.Project;
|
||||||
|
import com.google.refine.model.Row;
|
||||||
|
import com.google.refine.util.LookupException;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage the cache of project's lookups.
|
||||||
|
*
|
||||||
|
* @author Lu Liu
|
||||||
|
*/
|
||||||
|
public class LookupCacheManager {
|
||||||
|
|
||||||
|
protected final Map<String, ProjectLookup> _lookups = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the ProjectLookup based on combination key,
|
||||||
|
* returns the cached one from the HashMap if already computed.
|
||||||
|
*
|
||||||
|
* @param targetProject the project to look up
|
||||||
|
* @param targetColumn the column of the target project to look up
|
||||||
|
* @return a {@link ProjectLookup} instance of the lookup result
|
||||||
|
*/
|
||||||
|
public ProjectLookup getLookup(long targetProject, String targetColumn) throws LookupException {
|
||||||
|
String key = targetProject + ";" + targetColumn;
|
||||||
|
if (!_lookups.containsKey(key)) {
|
||||||
|
ProjectLookup lookup = new ProjectLookup(targetProject, targetColumn);
|
||||||
|
computeLookup(lookup);
|
||||||
|
|
||||||
|
synchronized (_lookups) {
|
||||||
|
_lookups.put(key, lookup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _lookups.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushLookupsInvolvingProject(long projectID) {
|
||||||
|
synchronized (_lookups) {
|
||||||
|
for (Iterator<Map.Entry<String, ProjectLookup>> it = _lookups.entrySet().iterator(); it.hasNext(); ) {
|
||||||
|
Map.Entry<String, ProjectLookup> entry = it.next();
|
||||||
|
ProjectLookup lookup = entry.getValue();
|
||||||
|
if (lookup.targetProjectID == projectID) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushLookupsInvolvingProjectColumn(long projectID, String columnName) {
|
||||||
|
synchronized (_lookups) {
|
||||||
|
for (Iterator<Map.Entry<String, ProjectLookup>> it = _lookups.entrySet().iterator(); it.hasNext(); ) {
|
||||||
|
Map.Entry<String, ProjectLookup> entry = it.next();
|
||||||
|
ProjectLookup lookup = entry.getValue();
|
||||||
|
if (lookup.targetProjectID == projectID && lookup.targetColumnName.equals(columnName)) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void computeLookup(ProjectLookup lookup) throws LookupException {
|
||||||
|
if (lookup.targetProjectID < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Project targetProject = ProjectManager.singleton.getProject(lookup.targetProjectID);
|
||||||
|
ProjectMetadata targetProjectMetadata = ProjectManager.singleton.getProjectMetadata(lookup.targetProjectID);
|
||||||
|
if (targetProject == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Column targetColumn = targetProject.columnModel.getColumnByName(lookup.targetColumnName);
|
||||||
|
if (targetColumn == null) {
|
||||||
|
throw new LookupException("Unable to find column " + lookup.targetColumnName + " in project " + targetProjectMetadata.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't use for-each here, because we'll need the row index when creating WrappedRow
|
||||||
|
int count = targetProject.rows.size();
|
||||||
|
for (int r = 0; r < count; r++) {
|
||||||
|
Row targetRow = targetProject.rows.get(r);
|
||||||
|
Object value = targetRow.getCellValue(targetColumn.getCellIndex());
|
||||||
|
if (ExpressionUtils.isNonBlankData(value)) {
|
||||||
|
lookup.valueToRowIndices.putIfAbsent(value, new ArrayList<>());
|
||||||
|
lookup.valueToRowIndices.get(value).add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class ProjectLookup {
|
||||||
|
|
||||||
|
final public long targetProjectID;
|
||||||
|
final public String targetColumnName;
|
||||||
|
|
||||||
|
final public Map<Object, List<Integer>> valueToRowIndices = new HashMap<>();
|
||||||
|
|
||||||
|
ProjectLookup(long targetProjectID, String targetColumnName) {
|
||||||
|
this.targetProjectID = targetProjectID;
|
||||||
|
this.targetColumnName = targetColumnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HasFieldsListImpl getRows(Object value) {
|
||||||
|
if (ExpressionUtils.isNonBlankData(value) && valueToRowIndices.containsKey(value)) {
|
||||||
|
Project targetProject = ProjectManager.singleton.getProject(targetProjectID);
|
||||||
|
if (targetProject != null) {
|
||||||
|
HasFieldsListImpl rows = new HasFieldsListImpl();
|
||||||
|
for (Integer r : valueToRowIndices.get(value)) {
|
||||||
|
Row row = targetProject.rows.get(r);
|
||||||
|
rows.add(new WrappedRow(targetProject, r, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -84,9 +84,9 @@ public abstract class ProjectManager {
|
|||||||
final static Logger logger = LoggerFactory.getLogger("ProjectManager");
|
final static Logger logger = LoggerFactory.getLogger("ProjectManager");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What caches the joins between projects.
|
* What caches the lookups of projects.
|
||||||
*/
|
*/
|
||||||
transient protected InterProjectModel _interProjectModel = new InterProjectModel();
|
transient protected LookupCacheManager _lookupCacheManager = new LookupCacheManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag for heavy operations like creating or importing projects. Workspace saves are skipped while it's set.
|
* Flag for heavy operations like creating or importing projects. Workspace saves are skipped while it's set.
|
||||||
@ -345,14 +345,13 @@ public abstract class ProjectManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the InterProjectModel from memory
|
* Gets the LookupCacheManager from memory
|
||||||
*/
|
*/
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public InterProjectModel getInterProjectModel() {
|
public LookupCacheManager getLookupCacheManager() {
|
||||||
return _interProjectModel;
|
return _lookupCacheManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the project metadata from memory
|
* Gets the project metadata from memory
|
||||||
* Requires that the metadata has already been loaded from the data store
|
* Requires that the metadata has already been loaded from the data store
|
||||||
|
@ -35,73 +35,62 @@ package com.google.refine.expr.functions;
|
|||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import com.google.refine.InterProjectModel.ProjectJoin;
|
import com.google.refine.LookupCacheManager.ProjectLookup;
|
||||||
import com.google.refine.ProjectManager;
|
import com.google.refine.ProjectManager;
|
||||||
import com.google.refine.expr.EvalError;
|
import com.google.refine.expr.EvalError;
|
||||||
import com.google.refine.expr.WrappedCell;
|
import com.google.refine.expr.WrappedCell;
|
||||||
import com.google.refine.grel.ControlFunctionRegistry;
|
import com.google.refine.grel.ControlFunctionRegistry;
|
||||||
import com.google.refine.grel.Function;
|
import com.google.refine.grel.Function;
|
||||||
import com.google.refine.model.Project;
|
|
||||||
import com.google.refine.util.GetProjectIDException;
|
import com.google.refine.util.GetProjectIDException;
|
||||||
import com.google.refine.util.JoinException;
|
import com.google.refine.util.LookupException;
|
||||||
|
|
||||||
public class Cross implements Function {
|
public class Cross implements Function {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Properties bindings, Object[] args) {
|
public Object call(Properties bindings, Object[] args) {
|
||||||
if (args.length == 3) {
|
if (args.length == 3) {
|
||||||
// 1st argument can take either value or cell(for backward compatibility)
|
// 1st argument can take either value or cell(for backward compatibility)
|
||||||
Object v = args[0];
|
Object v = args[0];
|
||||||
Object toProjectName = args[1];
|
Object targetProjectName = args[1];
|
||||||
Object toColumnName = args[2];
|
Object targetColumnName = args[2];
|
||||||
Long toProjectID;
|
long targetProjectID;
|
||||||
ProjectJoin join;
|
ProjectLookup lookup;
|
||||||
|
|
||||||
if (v != null &&
|
if (v != null &&
|
||||||
( v instanceof String || v instanceof WrappedCell ) &&
|
(v instanceof String || v instanceof WrappedCell) &&
|
||||||
toProjectName != null && toProjectName instanceof String &&
|
targetProjectName != null && targetProjectName instanceof String &&
|
||||||
toColumnName != null && toColumnName instanceof String) {
|
targetColumnName != null && targetColumnName instanceof String) {
|
||||||
try {
|
try {
|
||||||
toProjectID = ProjectManager.singleton.getProjectID((String) toProjectName);
|
targetProjectID = ProjectManager.singleton.getProjectID((String) targetProjectName);
|
||||||
} catch (GetProjectIDException e){
|
} catch (GetProjectIDException e) {
|
||||||
return new EvalError(e.getMessage());
|
return new EvalError(e.getMessage());
|
||||||
}
|
}
|
||||||
// add a try/catch here - error should bubble up from getInterProjectModel.computeJoin once that's modified
|
|
||||||
try {
|
try {
|
||||||
join = ProjectManager.singleton.getInterProjectModel().getJoin(
|
lookup = ProjectManager.singleton.getLookupCacheManager().getLookup(targetProjectID, (String) targetColumnName);
|
||||||
// getJoin(Long fromProject, String fromColumn, Long toProject, String toColumn) {
|
} catch (LookupException e) {
|
||||||
// source project name
|
|
||||||
(Long) ((Project) bindings.get("project")).id,
|
|
||||||
// source column name
|
|
||||||
(String) bindings.get("columnName"),
|
|
||||||
// target project name
|
|
||||||
toProjectID,
|
|
||||||
// target column name
|
|
||||||
(String) toColumnName
|
|
||||||
);
|
|
||||||
} catch (JoinException e) {
|
|
||||||
return new EvalError(e.getMessage());
|
return new EvalError(e.getMessage());
|
||||||
}
|
}
|
||||||
if(v instanceof String) {
|
if (v instanceof String) {
|
||||||
return join.getRows(v);
|
return lookup.getRows(v);
|
||||||
} else {
|
} else {
|
||||||
return join.getRows(((WrappedCell) v).cell.value);
|
return lookup.getRows(((WrappedCell) v).cell.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new EvalError(ControlFunctionRegistry.getFunctionName(this) + " expects a string or cell, a project name to join with, and a column name in that project");
|
return new EvalError(ControlFunctionRegistry.getFunctionName(this) + " expects a string or cell, a project name to join with, and a column name in that project");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "join with another project by column";
|
return "join with another project by column";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getParams() {
|
public String getParams() {
|
||||||
return "cell c or string value, string projectName, string columnName";
|
return "cell c or string value, string projectName, string columnName";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getReturns() {
|
public String getReturns() {
|
||||||
return "array";
|
return "array";
|
||||||
|
@ -45,7 +45,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.refine.InterProjectModel;
|
|
||||||
import com.google.refine.model.recon.ReconConfig;
|
import com.google.refine.model.recon.ReconConfig;
|
||||||
import com.google.refine.util.ParsingUtilities;
|
import com.google.refine.util.ParsingUtilities;
|
||||||
|
|
||||||
@ -122,8 +121,8 @@ public class Column {
|
|||||||
* <p>
|
* <p>
|
||||||
* If you are modifying something that requires this to be called, you
|
* If you are modifying something that requires this to be called, you
|
||||||
* probably also need to call
|
* probably also need to call
|
||||||
* {@link InterProjectModel#flushJoinsInvolvingProjectColumn(long, String)}.
|
* {@link com.google.refine.LookupCacheManager#flushLookupsInvolvingProjectColumn(long, String)}
|
||||||
* e.g. ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, column.getName())
|
* e.g. ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, column.getName())
|
||||||
*/
|
*/
|
||||||
public void clearPrecomputes() {
|
public void clearPrecomputes() {
|
||||||
if (_precomputes != null) {
|
if (_precomputes != null) {
|
||||||
|
@ -105,7 +105,7 @@ public class Project {
|
|||||||
logger.warn("Error signaling overlay model before disposing", e);
|
logger.warn("Error signaling overlay model before disposing", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProject(this.id);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProject(this.id);
|
||||||
// The rest of the project should get garbage collected when we return.
|
// The rest of the project should get garbage collected when we return.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class CellChange implements Change {
|
|||||||
|
|
||||||
Column column = project.columnModel.getColumnByCellIndex(cellIndex);
|
Column column = project.columnModel.getColumnByCellIndex(cellIndex);
|
||||||
column.clearPrecomputes();
|
column.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, column.getName());
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, column.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -73,7 +73,7 @@ public class CellChange implements Change {
|
|||||||
|
|
||||||
Column column = project.columnModel.getColumnByCellIndex(cellIndex);
|
Column column = project.columnModel.getColumnByCellIndex(cellIndex);
|
||||||
column.clearPrecomputes();
|
column.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, column.getName());
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, column.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -55,7 +55,7 @@ public class ColumnRenameChange extends ColumnChange {
|
|||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
synchronized (project) {
|
synchronized (project) {
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _oldColumnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _oldColumnName);
|
||||||
project.columnModel.getColumnByName(_oldColumnName).setName(_newColumnName);
|
project.columnModel.getColumnByName(_oldColumnName).setName(_newColumnName);
|
||||||
project.columnModel.update();
|
project.columnModel.update();
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ public class ColumnRenameChange extends ColumnChange {
|
|||||||
@Override
|
@Override
|
||||||
public void revert(Project project) {
|
public void revert(Project project) {
|
||||||
synchronized (project) {
|
synchronized (project) {
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _newColumnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _newColumnName);
|
||||||
project.columnModel.getColumnByName(_newColumnName).setName(_oldColumnName);
|
project.columnModel.getColumnByName(_newColumnName).setName(_oldColumnName);
|
||||||
project.columnModel.update();
|
project.columnModel.update();
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ public class ColumnSplitChange implements Change {
|
|||||||
project.columnModel.allocateNewCellIndex();
|
project.columnModel.allocateNewCellIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _columnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _columnName);
|
||||||
_column = project.columnModel.getColumnByName(_columnName);
|
_column = project.columnModel.getColumnByName(_columnName);
|
||||||
_columnIndex = project.columnModel.getColumnIndexByName(_columnName);
|
_columnIndex = project.columnModel.getColumnIndexByName(_columnName);
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ public class ColumnSplitChange implements Change {
|
|||||||
|
|
||||||
for (int i = 0; i < _columnNames.size(); i++) {
|
for (int i = 0; i < _columnNames.size(); i++) {
|
||||||
project.columnModel.columns.remove(_columnIndex + 1);
|
project.columnModel.columns.remove(_columnIndex + 1);
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _columnNames.get(i));
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _columnNames.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
project.columnModel.columnGroups.clear();
|
project.columnModel.columnGroups.clear();
|
||||||
|
@ -94,7 +94,7 @@ public class MassCellChange implements Change {
|
|||||||
if (_commonColumnName != null) {
|
if (_commonColumnName != null) {
|
||||||
Column column = project.columnModel.getColumnByName(_commonColumnName);
|
Column column = project.columnModel.getColumnByName(_commonColumnName);
|
||||||
column.clearPrecomputes();
|
column.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _commonColumnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _commonColumnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_updateRowContextDependencies) {
|
if (_updateRowContextDependencies) {
|
||||||
@ -115,7 +115,7 @@ public class MassCellChange implements Change {
|
|||||||
if (_commonColumnName != null) {
|
if (_commonColumnName != null) {
|
||||||
Column column = project.columnModel.getColumnByName(_commonColumnName);
|
Column column = project.columnModel.getColumnByName(_commonColumnName);
|
||||||
column.clearPrecomputes();
|
column.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _commonColumnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _commonColumnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_updateRowContextDependencies) {
|
if (_updateRowContextDependencies) {
|
||||||
|
@ -82,7 +82,7 @@ public class MassReconChange implements Change {
|
|||||||
// skip the flushing if already done
|
// skip the flushing if already done
|
||||||
String columnName = project.columnModel.getColumnByCellIndex(c).getName();
|
String columnName = project.columnModel.getColumnByCellIndex(c).getName();
|
||||||
if (!flushedColumn.contains(columnName)) {
|
if (!flushedColumn.contains(columnName)) {
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id,
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id,
|
||||||
columnName);
|
columnName);
|
||||||
flushedColumn.add(columnName);
|
flushedColumn.add(columnName);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ public class MassRowChange implements Change {
|
|||||||
project.rows.addAll(_newRows);
|
project.rows.addAll(_newRows);
|
||||||
|
|
||||||
project.columnModel.clearPrecomputes();
|
project.columnModel.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProject(project.id);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProject(project.id);
|
||||||
|
|
||||||
project.update();
|
project.update();
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public class MassRowColumnChange implements Change {
|
|||||||
project.rows.clear();
|
project.rows.clear();
|
||||||
project.rows.addAll(_newRows);
|
project.rows.addAll(_newRows);
|
||||||
|
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProject(project.id);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProject(project.id);
|
||||||
|
|
||||||
project.update();
|
project.update();
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ public class MassRowColumnChange implements Change {
|
|||||||
project.rows.clear();
|
project.rows.clear();
|
||||||
project.rows.addAll(_oldRows);
|
project.rows.addAll(_oldRows);
|
||||||
|
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProject(project.id);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProject(project.id);
|
||||||
|
|
||||||
project.update();
|
project.update();
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ public class ReconChange extends MassCellChange {
|
|||||||
column.setReconStats(_newReconStats);
|
column.setReconStats(_newReconStats);
|
||||||
|
|
||||||
column.clearPrecomputes();
|
column.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _commonColumnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _commonColumnName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ public class ReconChange extends MassCellChange {
|
|||||||
column.setReconStats(_oldReconStats);
|
column.setReconStats(_oldReconStats);
|
||||||
|
|
||||||
column.clearPrecomputes();
|
column.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProjectColumn(project.id, _commonColumnName);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProjectColumn(project.id, _commonColumnName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ public class RowRemovalChange implements Change {
|
|||||||
}
|
}
|
||||||
|
|
||||||
project.columnModel.clearPrecomputes();
|
project.columnModel.clearPrecomputes();
|
||||||
ProjectManager.singleton.getInterProjectModel().flushJoinsInvolvingProject(project.id);
|
ProjectManager.singleton.getLookupCacheManager().flushLookupsInvolvingProject(project.id);
|
||||||
|
|
||||||
project.update();
|
project.update();
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019, Owen Stephens
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* The name of the contributor may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.refine.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when there is an error on a project Join.
|
|
||||||
*/
|
|
||||||
public class JoinException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1553348086L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default JoinException format exception.
|
|
||||||
*/
|
|
||||||
public JoinException() { super(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JoinException exception.
|
|
||||||
*
|
|
||||||
* @param message error message
|
|
||||||
*/
|
|
||||||
public JoinException(String message) { super(message); }
|
|
||||||
}
|
|
19
main/src/com/google/refine/util/LookupException.java
Normal file
19
main/src/com/google/refine/util/LookupException.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.google.refine.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when there is an error on a project lookup.
|
||||||
|
*/
|
||||||
|
public class LookupException extends Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default LookupException format exception.
|
||||||
|
*/
|
||||||
|
public LookupException() { super(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LookupException exception.
|
||||||
|
*
|
||||||
|
* @param message error message
|
||||||
|
*/
|
||||||
|
public LookupException(String message) { super(message); }
|
||||||
|
}
|
@ -133,7 +133,29 @@ public class CrossTests extends RefineTest {
|
|||||||
Assert.assertEquals(((EvalError) invoke("cross", "mary", projectName, nonExistentColumn)).message,
|
Assert.assertEquals(((EvalError) invoke("cross", "mary", projectName, nonExistentColumn)).message,
|
||||||
"Unable to find column " + nonExistentColumn + " in project " + projectName);
|
"Unable to find column " + nonExistentColumn + " in project " + projectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void crossFunctionSameColumnTest() throws Exception {
|
||||||
|
Project project = (Project) bindings.get("project");
|
||||||
|
Cell c = project.rows.get(0).cells.get(1);
|
||||||
|
WrappedCell lookup = new WrappedCell(project, "recipient", c);
|
||||||
|
Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", lookup, "My Address Book", "friend")).get(0)).row);
|
||||||
|
String address = row.getCell(1).value.toString();
|
||||||
|
Assert.assertEquals(address, "50 Broadway Ave.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
// cross result shouldn't depend on the based column in "bindings" when the first argument is a WrappedCell instance
|
||||||
|
public void crossFunctionDifferentColumnTest() throws Exception {
|
||||||
|
Project project = (Project) bindings.get("project");
|
||||||
|
bindings.put("columnName", "gift"); // change the based column
|
||||||
|
Cell c = project.rows.get(0).cells.get(1);
|
||||||
|
WrappedCell lookup = new WrappedCell(project, "recipient", c);
|
||||||
|
Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", lookup, "My Address Book", "friend")).get(0)).row);
|
||||||
|
String address = row.getCell(1).value.toString();
|
||||||
|
Assert.assertEquals(address, "50 Broadway Ave.");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void crossFunctionOneToOneTest() throws Exception {
|
public void crossFunctionOneToOneTest() throws Exception {
|
||||||
Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", "mary", "My Address Book", "friend")).get(0)).row);
|
Row row = ((Row)((WrappedRow) ((HasFieldsListImpl) invoke("cross", "mary", "My Address Book", "friend")).get(0)).row);
|
||||||
|
Loading…
Reference in New Issue
Block a user