Patch the identifiers (source/target) in the relations, refinements #131

Merged
claudio.atzori merged 6 commits from fct_project_id_replacement into master 2021-07-30 11:07:54 +02:00
4 changed files with 198 additions and 162 deletions
Showing only changes of commit a6a38cca9e - Show all commits

View File

@ -1,10 +1,12 @@
package eu.dnetlib.dhp.oa.graph.raw; package eu.dnetlib.dhp.oa.graph.raw;
import com.fasterxml.jackson.databind.ObjectMapper; import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkSession;
import eu.dnetlib.dhp.application.ArgumentApplicationParser;
import eu.dnetlib.dhp.oa.graph.dump.Utils; import java.io.FileNotFoundException;
import eu.dnetlib.dhp.oa.graph.raw.common.RelationIdMapping; import java.util.Objects;
import eu.dnetlib.dhp.schema.oaf.Relation; import java.util.Optional;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.spark.SparkConf; import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FilterFunction; import org.apache.spark.api.java.function.FilterFunction;
@ -15,104 +17,111 @@ import org.apache.spark.sql.SaveMode;
import org.apache.spark.sql.SparkSession; import org.apache.spark.sql.SparkSession;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.dnetlib.dhp.application.ArgumentApplicationParser;
import eu.dnetlib.dhp.oa.graph.dump.Utils;
import eu.dnetlib.dhp.oa.graph.raw.common.RelationIdMapping;
import eu.dnetlib.dhp.schema.oaf.Relation;
import scala.Tuple2; import scala.Tuple2;
import java.io.FileNotFoundException;
import java.util.Objects;
import java.util.Optional;
import static eu.dnetlib.dhp.common.SparkSessionSupport.runWithSparkSession;
public class PatchRelationsApplication { public class PatchRelationsApplication {
private static final Logger log = LoggerFactory.getLogger(PatchRelationsApplication.class); private static final Logger log = LoggerFactory.getLogger(PatchRelationsApplication.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static void main(final String[] args) throws Exception { public static void main(final String[] args) throws Exception {
final ArgumentApplicationParser parser = new ArgumentApplicationParser( final ArgumentApplicationParser parser = new ArgumentApplicationParser(
IOUtils IOUtils
.toString( .toString(
Optional.ofNullable( Optional
PatchRelationsApplication.class .ofNullable(
.getResourceAsStream( PatchRelationsApplication.class
"/eu/dnetlib/dhp/oa/graph/patch_relations_parameters.json")) .getResourceAsStream(
.orElseThrow(FileNotFoundException::new) "/eu/dnetlib/dhp/oa/graph/patch_relations_parameters.json"))
)); .orElseThrow(FileNotFoundException::new)));
parser.parseArgument(args); parser.parseArgument(args);
final Boolean isSparkSessionManaged = Optional final Boolean isSparkSessionManaged = Optional
.ofNullable(parser.get("isSparkSessionManaged")) .ofNullable(parser.get("isSparkSessionManaged"))
.map(Boolean::valueOf) .map(Boolean::valueOf)
.orElse(Boolean.TRUE); .orElse(Boolean.TRUE);
log.info("isSparkSessionManaged: {}", isSparkSessionManaged); log.info("isSparkSessionManaged: {}", isSparkSessionManaged);
final String graphBasePath = parser.get("graphBasePath"); final String graphBasePath = parser.get("graphBasePath");
log.info("graphBasePath: {}", graphBasePath); log.info("graphBasePath: {}", graphBasePath);
final String workingDir = parser.get("workingDir"); final String workingDir = parser.get("workingDir");
log.info("workingDir: {}", workingDir); log.info("workingDir: {}", workingDir);
final String idMappingPath = parser.get("idMappingPath"); final String idMappingPath = parser.get("idMappingPath");
log.info("idMappingPath: {}", idMappingPath); log.info("idMappingPath: {}", idMappingPath);
final SparkConf conf = new SparkConf(); final SparkConf conf = new SparkConf();
runWithSparkSession( runWithSparkSession(
conf, conf,
isSparkSessionManaged, isSparkSessionManaged,
spark -> patchRelations(spark, graphBasePath, workingDir, idMappingPath)); spark -> patchRelations(spark, graphBasePath, workingDir, idMappingPath));
} }
/** /**
* Substitutes the identifiers (source/target) from the set of relations part of the graphBasePath included in the * Substitutes the identifiers (source/target) from the set of relations part of the graphBasePath included in the
* mapping provided by the dataset stored on idMappingPath, using workingDir as intermediate storage location. * mapping provided by the dataset stored on idMappingPath, using workingDir as intermediate storage location.
* *
* @param spark the SparkSession * @param spark the SparkSession
* @param graphBasePath base graph path providing the set of relations to patch * @param graphBasePath base graph path providing the set of relations to patch
* @param workingDir intermediate storage location * @param workingDir intermediate storage location
* @param idMappingPath dataset providing the old -> new identifier mapping * @param idMappingPath dataset providing the old -> new identifier mapping
*/ */
private static void patchRelations(final SparkSession spark, final String graphBasePath, final String workingDir, final String idMappingPath) { private static void patchRelations(final SparkSession spark, final String graphBasePath, final String workingDir,
final String idMappingPath) {
final String relationPath = graphBasePath + "/relation"; final String relationPath = graphBasePath + "/relation";
final Dataset<Relation> rels = Utils.readPath(spark, relationPath, Relation.class); final Dataset<Relation> rels = Utils.readPath(spark, relationPath, Relation.class);
final Dataset<RelationIdMapping> idMapping = Utils.readPath(spark, idMappingPath, RelationIdMapping.class); final Dataset<RelationIdMapping> idMapping = Utils.readPath(spark, idMappingPath, RelationIdMapping.class);
log.info("relations: {}", rels.count()); log.info("relations: {}", rels.count());
log.info("idMapping: {}", idMapping.count()); log.info("idMapping: {}", idMapping.count());
rels final Dataset<Relation> bySource = rels
.joinWith(idMapping, rels.col("source").equalTo(idMapping.col("oldId")), "left") .joinWith(idMapping, rels.col("source").equalTo(idMapping.col("oldId")), "left")
.map((MapFunction<Tuple2<Relation, RelationIdMapping>, Relation>) t -> { .map((MapFunction<Tuple2<Relation, RelationIdMapping>, Relation>) t -> {
final Relation r = t._1(); final Relation r = t._1();
Optional.ofNullable(t._2()) Optional
.map(RelationIdMapping::getNewId) .ofNullable(t._2())
.ifPresent(r::setSource); .map(RelationIdMapping::getNewId)
return r; .ifPresent(r::setSource);
}, Encoders.bean(Relation.class)) return r;
.joinWith(idMapping, rels.col("target").equalTo(idMapping.col("oldId")), "left") }, Encoders.bean(Relation.class));
.map((MapFunction<Tuple2<Relation, RelationIdMapping>, Relation>) t -> {
final Relation r = t._1();
Optional.ofNullable(t._2())
.map(RelationIdMapping::getNewId)
.ifPresent(r::setTarget);
return r;
}, Encoders.bean(Relation.class))
.map(
(MapFunction<Relation, String>) OBJECT_MAPPER::writeValueAsString,
Encoders.STRING())
.write()
.mode(SaveMode.Overwrite)
.option("compression", "gzip")
.text(workingDir);
spark.read().textFile(workingDir) bySource
.write() .joinWith(idMapping, bySource.col("target").equalTo(idMapping.col("oldId")), "left")
.mode(SaveMode.Overwrite) .map((MapFunction<Tuple2<Relation, RelationIdMapping>, Relation>) t -> {
.option("compression", "gzip") final Relation r = t._1();
.text(relationPath); Optional
} .ofNullable(t._2())
.map(RelationIdMapping::getNewId)
.ifPresent(r::setTarget);
return r;
}, Encoders.bean(Relation.class))
.map(
(MapFunction<Relation, String>) OBJECT_MAPPER::writeValueAsString,
Encoders.STRING())
.write()
.mode(SaveMode.Overwrite)
.option("compression", "gzip")
.text(workingDir);
spark
.read()
.textFile(workingDir)
.write()
.mode(SaveMode.Overwrite)
.option("compression", "gzip")
.text(relationPath);
}
} }

View File

@ -1,24 +1,25 @@
package eu.dnetlib.dhp.oa.graph.raw.common; package eu.dnetlib.dhp.oa.graph.raw.common;
public class RelationIdMapping { public class RelationIdMapping {
private String oldId; private String oldId;
private String newId; private String newId;
public String getOldId() { public String getOldId() {
return oldId; return oldId;
} }
public void setOldId(final String oldId) { public void setOldId(final String oldId) {
this.oldId = oldId; this.oldId = oldId;
} }
public String getNewId() { public String getNewId() {
return newId; return newId;
} }
public void setNewId(final String newId) { public void setNewId(final String newId) {
this.newId = newId; this.newId = newId;
} }
} }

View File

@ -1,89 +1,115 @@
package eu.dnetlib.dhp.oa.graph.raw; package eu.dnetlib.dhp.oa.graph.raw;
import com.fasterxml.jackson.databind.ObjectMapper; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.spark.SparkConf;
import org.apache.spark.sql.SparkSession;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Optional; import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.MapFunction;
import org.apache.spark.sql.Encoders;
import org.apache.spark.sql.SparkSession;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.dnetlib.dhp.schema.oaf.Relation;
public class PatchRelationApplicationTest { public class PatchRelationApplicationTest {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static final String ID_MAPPING_PATH = "map/id_mapping.json"; public static final String ID_MAPPING_PATH = "map/id_mapping.json";
private static SparkSession spark; private static SparkSession spark;
private static Path workingDir; private static Path workingDir;
private static final Logger log = LoggerFactory.getLogger(PatchRelationApplicationTest.class); private static final Logger log = LoggerFactory.getLogger(PatchRelationApplicationTest.class);
@BeforeAll @BeforeAll
public static void beforeAll() throws IOException { public static void beforeAll() throws IOException {
workingDir = Files workingDir = Files
.createTempDirectory(PatchRelationApplicationTest.class.getSimpleName()); .createTempDirectory(PatchRelationApplicationTest.class.getSimpleName());
log.info("using work dir {}", workingDir); log.info("using work dir {}", workingDir);
SparkConf conf = new SparkConf(); SparkConf conf = new SparkConf();
conf.setAppName(PatchRelationApplicationTest.class.getSimpleName()); conf.setAppName(PatchRelationApplicationTest.class.getSimpleName());
conf.setMaster("local[*]"); conf.setMaster("local[*]");
conf.set("spark.driver.host", "localhost"); conf.set("spark.driver.host", "localhost");
conf.set("hive.metastore.local", "true"); conf.set("hive.metastore.local", "true");
conf.set("spark.ui.enabled", "false"); conf.set("spark.ui.enabled", "false");
conf.set("spark.sql.warehouse.dir", workingDir.toString()); conf.set("spark.sql.warehouse.dir", workingDir.toString());
conf.set("hive.metastore.warehouse.dir", workingDir.resolve("warehouse").toString()); conf.set("hive.metastore.warehouse.dir", workingDir.resolve("warehouse").toString());
spark = SparkSession spark = SparkSession
.builder() .builder()
.appName(PatchRelationApplicationTest.class.getSimpleName()) .appName(PatchRelationApplicationTest.class.getSimpleName())
.config(conf) .config(conf)
.getOrCreate(); .getOrCreate();
FileUtils.copyInputStreamToFile( FileUtils
PatchRelationApplicationTest.class.getResourceAsStream("id_mapping.json"), .copyInputStreamToFile(
workingDir.resolve(ID_MAPPING_PATH).toFile() PatchRelationApplicationTest.class.getResourceAsStream("id_mapping.json"),
); workingDir.resolve(ID_MAPPING_PATH).toFile());
FileUtils.copyInputStreamToFile( FileUtils
PatchRelationApplicationTest.class.getResourceAsStream("relations_to_patch.json"), .copyInputStreamToFile(
workingDir.resolve("graphBasePath/relation/rels.json").toFile() PatchRelationApplicationTest.class.getResourceAsStream("relations_to_patch.json"),
); workingDir.resolve("graphBasePath/relation/rels.json").toFile());
} }
@BeforeEach @AfterAll
public void setUp() throws IOException { public static void afterAll() throws IOException {
FileUtils.deleteDirectory(workingDir.toFile());
spark.stop();
}
@Test
public void testPatchRelationApplication() throws Exception {
} final String graphBasePath = workingDir.toString() + "/graphBasePath";
PatchRelationsApplication.main(new String[] {
"-isSparkSessionManaged", Boolean.FALSE.toString(),
"-graphBasePath", graphBasePath,
"-workingDir", workingDir.toString() + "/workingDir",
"-idMappingPath", workingDir.toString() + "/" + ID_MAPPING_PATH
});
@AfterAll final List<Relation> rels = spark
public static void afterAll() throws IOException { .read()
FileUtils.deleteDirectory(workingDir.toFile()); .textFile(graphBasePath + "/relation")
spark.stop(); .map(
} (MapFunction<String, Relation>) s -> OBJECT_MAPPER.readValue(s, Relation.class),
Encoders.bean(Relation.class))
.collectAsList();
@Test assertEquals(6, rels.size());
public void testPatchRelationApplication() throws Exception {
PatchRelationsApplication.main(new String[] { assertEquals(0, getCount(rels, "1a"), "should be patched to 1b");
"-isSparkSessionManaged", Boolean.FALSE.toString(), assertEquals(0, getCount(rels, "2a"), "should be patched to 2b");
"-graphBasePath", workingDir.toString() + "/graphBasePath",
"-workingDir", workingDir.toString() + "/workingDir",
"-idMappingPath", workingDir.toString() + "/" + ID_MAPPING_PATH
});
} assertEquals(2, getCount(rels, "10a"), "not included in patching");
assertEquals(2, getCount(rels, "20a"), "not included in patching");
assertEquals(2, getCount(rels, "15a"), "not included in patching");
assertEquals(2, getCount(rels, "25a"), "not included in patching");
assertEquals(2, getCount(rels, "1b"), "patched from 1a");
assertEquals(2, getCount(rels, "2b"), "patched from 2a");
}
private long getCount(List<Relation> rels, final String id) {
return rels.stream().filter(r -> r.getSource().equals(id) || r.getTarget().equals(id)).count();
}
} }