From 59c2827bcd682a6c78714438ef8d7aaca2ccf13b Mon Sep 17 00:00:00 2001 From: Ellen Spertus Date: Fri, 1 Jun 2018 14:54:17 -0700 Subject: Factored common code out of HBaseRowCountJob and its test into a new companion object. --- scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala | 14 +++++++++----- .../src/test/scala/sandcrawler/HBaseRowCountTest.scala | 14 +++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala index d47fe60..79ebbb1 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala @@ -16,7 +16,15 @@ class HBaseRowCountJob(args: Args) extends JobBase(args) with HBasePipeConversio val output = args("output") - val hbs = new HBaseSource( + HBaseRowCountJob.getHBaseSource + .read + .debug + .groupAll { _.size('count) } + .write(Tsv(output)) +} + +object HBaseRowCountJob { + def getHBaseSource = new HBaseSource( //"table_name", //"quorum_name:2181", "wbgrp-journal-extract-0-qa", // HBase Table Name @@ -25,8 +33,4 @@ class HBaseRowCountJob(args: Args) extends JobBase(args) with HBasePipeConversio List("file"), List(new Fields("size", "mimetype")), sourceMode = SourceMode.SCAN_ALL) - .read - .debug - .groupAll { _.size('count) } - .write(Tsv(output)) } diff --git a/scalding/src/test/scala/sandcrawler/HBaseRowCountTest.scala b/scalding/src/test/scala/sandcrawler/HBaseRowCountTest.scala index 94b3740..abb017c 100644 --- a/scalding/src/test/scala/sandcrawler/HBaseRowCountTest.scala +++ b/scalding/src/test/scala/sandcrawler/HBaseRowCountTest.scala @@ -12,6 +12,7 @@ import scala._ import com.twitter.scalding.Tsv import parallelai.spyglass.hbase.HBaseSource import parallelai.spyglass.hbase.HBaseConstants.SourceMode +import sandcrawler.HBaseRowCountJob /** * Example of how to define tests for HBaseSource @@ -39,18 +40,9 @@ class HBaseRowCountTest extends FunSpec with TupleConversions { .arg("app.conf.path", "app.conf") .arg("output", output) .arg("debug", "true") - .source[Tuple]( - new HBaseSource( - //"table_name", - //"quorum_name:2181", - "wbgrp-journal-extract-0-qa", - "mtrcs-zk1.us.archive.org:2181", - new Fields("key"), - List("file"), - List(new Fields("size", "mimetype")), - sourceMode = SourceMode.SCAN_ALL), + .source[Tuple](HBaseRowCountJob.getHBaseSource, sampleData.map(l => new Tuple(l.map(s => {new ImmutableBytesWritable(Bytes.toBytes(s))}):_*))) - .sink[Tuple](Tsv(output)) { + .sink[Tuple](Tsv(output)) { outputBuffer => it("should return the test data provided.") { -- cgit v1.2.3 From 5da6d85175992fae019e1d4d1ebeb4accfec1820 Mon Sep 17 00:00:00 2001 From: Ellen Spertus Date: Sun, 3 Jun 2018 16:31:59 -0700 Subject: Added HBaseBuilder.parseColSpecs and tests, which pass. --- .../src/main/scala/sandcrawler/HBaseBuilder.scala | 49 ++++++++++++++++++++++ .../test/scala/sandcrawler/HBaseBuilderTest.scala | 43 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 scalding/src/main/scala/sandcrawler/HBaseBuilder.scala create mode 100644 scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala diff --git a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala new file mode 100644 index 0000000..c55aef6 --- /dev/null +++ b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala @@ -0,0 +1,49 @@ +package sandcrawler + +import cascading.tuple.Fields +import parallelai.spyglass.hbase.HBaseConstants.SourceMode +import parallelai.spyglass.hbase.HBaseSource +import scala._ + +object HBaseBuilder { + // map from column families to column names + val schema = Map("f" -> List("c"), + "file" -> List("size", "mime", "cdx"), + "grobid0" -> List("status_code", "quality", "status", "tei_xml", "tei_json", "metadata"), + "match0" -> List("status", "doi", "info")) + // map from colFamily:colName -> colFamily + // Code from https://stackoverflow.com/a/50595189/6310511 + val inverseSchema = for ((k, vs) <- schema; v <- vs) yield (k + ":" + v, k) + + @throws(classOf[IllegalArgumentException]) + def parseColSpec(colSpecs: List[String]) : (List[String], List[Fields]) = { + // Verify that all column specifiers are legal. + for (colSpec <- colSpecs) { + if (!(inverseSchema contains colSpec)) { + throw new IllegalArgumentException("No such column: " + colSpec) + } + val pair = colSpec split(":") + if (colSpec.split(":").length != 2) { + throw new IllegalArgumentException("Bad column specifier " + colSpec + + " (specifiers should be family:name)") + } + } + + // Produce and return a tuple containing: + // 1. A list of column families. + // 2. A corresponding list of Fields, each containing column names. + val groupMap: Map[String, List[String]] = colSpecs.groupBy(c => (c split ":")(0)) + val families = groupMap.keys.toList + val groupedColNames : List[List[String]] = families map {fam => { + val cols = {groupMap(fam).map(v => v.split(":")(1))} + cols}} + (families, groupedColNames.map({fields => new Fields(fields : _*)})) + } + + /* + def build(table: String, server: String, colSpec: List[String], sourceMode: SourceMode, keyList: List[String]) { + val (families: List[String], fields: List[Fields]) = parseColSpec(colSpec) + new HBaseSource(table, server, new Fields("key"), families, fields, sourceMode = sourceMode, keyList = keyList) + } + */ +} diff --git a/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala b/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala new file mode 100644 index 0000000..b45751d --- /dev/null +++ b/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala @@ -0,0 +1,43 @@ +package example + +import cascading.tuple.Fields +import org.scalatest._ +import sandcrawler.HBaseBuilder + +class HBaseBuilderTest extends FlatSpec with Matchers { + "parseColSpec()" should "work on legal nontrivial input" in { + val (fams, fields) = HBaseBuilder.parseColSpec(List("file:size", "file:cdx", "match0:status")) + fams should have length 2 + fields should have length 2 + val fileIndex = fams.indexOf("file") + fileIndex should not be -1 + fields(fileIndex) should be (new Fields("size", "cdx")) + val match0Index = fams.indexOf("match0") + match0Index should not be -1 + fields(match0Index) should be (new Fields("status")) + } + + it should "work on empty input" in { + val (fams, fields) = HBaseBuilder.parseColSpec(List()) + fams should have length 0 + fields should have length 0 + } + + it should "throw IllegalArgumentException on malformed input" in { + a [IllegalArgumentException] should be thrownBy { + HBaseBuilder.parseColSpec(List("file_size")) + } + } + + it should "throw IllegalArgumentException on nonexistent family" in { + a [IllegalArgumentException] should be thrownBy { + HBaseBuilder.parseColSpec(List("foo:bar")) + } + } + + it should "throw IllegalArgumentException on nonexistent column" in { + a [IllegalArgumentException] should be thrownBy { + HBaseBuilder.parseColSpec(List("file:bar")) + } + } +} -- cgit v1.2.3 From a9fe176a305b474050727d80c1f42941ecf1a2dc Mon Sep 17 00:00:00 2001 From: Ellen Spertus Date: Sun, 3 Jun 2018 16:54:08 -0700 Subject: Added HBaseBuilder.build() and had HBaseRowCountJob call it. --- scalding/src/main/scala/sandcrawler/HBaseBuilder.scala | 6 ++---- scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala | 10 +++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala index c55aef6..35aeb46 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala @@ -40,10 +40,8 @@ object HBaseBuilder { (families, groupedColNames.map({fields => new Fields(fields : _*)})) } - /* - def build(table: String, server: String, colSpec: List[String], sourceMode: SourceMode, keyList: List[String]) { - val (families: List[String], fields: List[Fields]) = parseColSpec(colSpec) + def build(table: String, server: String, colSpec: List[String], sourceMode: SourceMode, keyList: List[String] = List("key")) = { + val (families, fields) = parseColSpec(colSpec) new HBaseSource(table, server, new Fields("key"), families, fields, sourceMode = sourceMode, keyList = keyList) } - */ } diff --git a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala index 79ebbb1..9d074ab 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala @@ -24,13 +24,9 @@ class HBaseRowCountJob(args: Args) extends JobBase(args) with HBasePipeConversio } object HBaseRowCountJob { - def getHBaseSource = new HBaseSource( - //"table_name", - //"quorum_name:2181", + def getHBaseSource = HBaseBuilder.build( "wbgrp-journal-extract-0-qa", // HBase Table Name "mtrcs-zk1.us.archive.org:2181", // HBase Zookeeper server (to get runtime config info; can be array?) - new Fields("key"), - List("file"), - List(new Fields("size", "mimetype")), - sourceMode = SourceMode.SCAN_ALL) + List("file:size", "file:mime"), + SourceMode.SCAN_ALL) } -- cgit v1.2.3 From f2a0d6fe40be3a9b282a4cc0ea0ebdb85810762b Mon Sep 17 00:00:00 2001 From: Ellen Spertus Date: Sun, 3 Jun 2018 17:16:35 -0700 Subject: Changed interface to HBaseBuilder.parseColSpec. --- scalding/src/main/scala/sandcrawler/HBaseBuilder.scala | 8 ++++++-- scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala | 2 +- scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala | 10 +++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala index 35aeb46..eb108a2 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala @@ -15,8 +15,12 @@ object HBaseBuilder { // Code from https://stackoverflow.com/a/50595189/6310511 val inverseSchema = for ((k, vs) <- schema; v <- vs) yield (k + ":" + v, k) + // The argument should be a string with a comma-separated list of family:column, + // such as "f:c, file:size". Spaces after the comma are optional. @throws(classOf[IllegalArgumentException]) - def parseColSpec(colSpecs: List[String]) : (List[String], List[Fields]) = { + def parseColSpec(colSpec: String) : (List[String], List[Fields]) = { + val colSpecs = (if (colSpec.trim.length == 0) List() else colSpec.split(", *").toList) + // Verify that all column specifiers are legal. for (colSpec <- colSpecs) { if (!(inverseSchema contains colSpec)) { @@ -40,7 +44,7 @@ object HBaseBuilder { (families, groupedColNames.map({fields => new Fields(fields : _*)})) } - def build(table: String, server: String, colSpec: List[String], sourceMode: SourceMode, keyList: List[String] = List("key")) = { + def build(table: String, server: String, colSpec: String, sourceMode: SourceMode, keyList: List[String] = List("key")) = { val (families, fields) = parseColSpec(colSpec) new HBaseSource(table, server, new Fields("key"), families, fields, sourceMode = sourceMode, keyList = keyList) } diff --git a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala index 9d074ab..041df4d 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala @@ -27,6 +27,6 @@ object HBaseRowCountJob { def getHBaseSource = HBaseBuilder.build( "wbgrp-journal-extract-0-qa", // HBase Table Name "mtrcs-zk1.us.archive.org:2181", // HBase Zookeeper server (to get runtime config info; can be array?) - List("file:size", "file:mime"), + "file:size, file:mime", SourceMode.SCAN_ALL) } diff --git a/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala b/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala index b45751d..8392100 100644 --- a/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala +++ b/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala @@ -6,7 +6,7 @@ import sandcrawler.HBaseBuilder class HBaseBuilderTest extends FlatSpec with Matchers { "parseColSpec()" should "work on legal nontrivial input" in { - val (fams, fields) = HBaseBuilder.parseColSpec(List("file:size", "file:cdx", "match0:status")) + val (fams, fields) = HBaseBuilder.parseColSpec("file:size, file:cdx, match0:status") fams should have length 2 fields should have length 2 val fileIndex = fams.indexOf("file") @@ -18,26 +18,26 @@ class HBaseBuilderTest extends FlatSpec with Matchers { } it should "work on empty input" in { - val (fams, fields) = HBaseBuilder.parseColSpec(List()) + val (fams, fields) = HBaseBuilder.parseColSpec("") fams should have length 0 fields should have length 0 } it should "throw IllegalArgumentException on malformed input" in { a [IllegalArgumentException] should be thrownBy { - HBaseBuilder.parseColSpec(List("file_size")) + HBaseBuilder.parseColSpec("file_size") } } it should "throw IllegalArgumentException on nonexistent family" in { a [IllegalArgumentException] should be thrownBy { - HBaseBuilder.parseColSpec(List("foo:bar")) + HBaseBuilder.parseColSpec("foo:bar") } } it should "throw IllegalArgumentException on nonexistent column" in { a [IllegalArgumentException] should be thrownBy { - HBaseBuilder.parseColSpec(List("file:bar")) + HBaseBuilder.parseColSpec("file:bar") } } } -- cgit v1.2.3 From 9a18da82738c16d3066e02e886ee84e04156889f Mon Sep 17 00:00:00 2001 From: Ellen Spertus Date: Mon, 4 Jun 2018 14:45:26 -0700 Subject: Made changes suggested in merge request review. - Changed inverseSchema from Map to List, eliminating incorrect comment. - Changing format of argument to HBaseBuilder.build from String to List[String]. --- scalding/src/main/scala/sandcrawler/HBaseBuilder.scala | 13 ++++--------- scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala | 2 +- scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala | 10 +++++----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala index eb108a2..87cc0cb 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseBuilder.scala @@ -11,16 +11,11 @@ object HBaseBuilder { "file" -> List("size", "mime", "cdx"), "grobid0" -> List("status_code", "quality", "status", "tei_xml", "tei_json", "metadata"), "match0" -> List("status", "doi", "info")) - // map from colFamily:colName -> colFamily - // Code from https://stackoverflow.com/a/50595189/6310511 - val inverseSchema = for ((k, vs) <- schema; v <- vs) yield (k + ":" + v, k) + val inverseSchema = {for ((k,vs) <- schema; v <-vs) yield (k + ":" + v)}.toList - // The argument should be a string with a comma-separated list of family:column, - // such as "f:c, file:size". Spaces after the comma are optional. + // The argument should be a comma-separated list of family:column, such as "f:c, file:size". @throws(classOf[IllegalArgumentException]) - def parseColSpec(colSpec: String) : (List[String], List[Fields]) = { - val colSpecs = (if (colSpec.trim.length == 0) List() else colSpec.split(", *").toList) - + def parseColSpec(colSpecs: List[String]) : (List[String], List[Fields]) = { // Verify that all column specifiers are legal. for (colSpec <- colSpecs) { if (!(inverseSchema contains colSpec)) { @@ -44,7 +39,7 @@ object HBaseBuilder { (families, groupedColNames.map({fields => new Fields(fields : _*)})) } - def build(table: String, server: String, colSpec: String, sourceMode: SourceMode, keyList: List[String] = List("key")) = { + def build(table: String, server: String, colSpec: List[String], sourceMode: SourceMode, keyList: List[String] = List("key")) = { val (families, fields) = parseColSpec(colSpec) new HBaseSource(table, server, new Fields("key"), families, fields, sourceMode = sourceMode, keyList = keyList) } diff --git a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala index 041df4d..9d074ab 100644 --- a/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala +++ b/scalding/src/main/scala/sandcrawler/HBaseRowCountJob.scala @@ -27,6 +27,6 @@ object HBaseRowCountJob { def getHBaseSource = HBaseBuilder.build( "wbgrp-journal-extract-0-qa", // HBase Table Name "mtrcs-zk1.us.archive.org:2181", // HBase Zookeeper server (to get runtime config info; can be array?) - "file:size, file:mime", + List("file:size", "file:mime"), SourceMode.SCAN_ALL) } diff --git a/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala b/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala index 8392100..b45751d 100644 --- a/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala +++ b/scalding/src/test/scala/sandcrawler/HBaseBuilderTest.scala @@ -6,7 +6,7 @@ import sandcrawler.HBaseBuilder class HBaseBuilderTest extends FlatSpec with Matchers { "parseColSpec()" should "work on legal nontrivial input" in { - val (fams, fields) = HBaseBuilder.parseColSpec("file:size, file:cdx, match0:status") + val (fams, fields) = HBaseBuilder.parseColSpec(List("file:size", "file:cdx", "match0:status")) fams should have length 2 fields should have length 2 val fileIndex = fams.indexOf("file") @@ -18,26 +18,26 @@ class HBaseBuilderTest extends FlatSpec with Matchers { } it should "work on empty input" in { - val (fams, fields) = HBaseBuilder.parseColSpec("") + val (fams, fields) = HBaseBuilder.parseColSpec(List()) fams should have length 0 fields should have length 0 } it should "throw IllegalArgumentException on malformed input" in { a [IllegalArgumentException] should be thrownBy { - HBaseBuilder.parseColSpec("file_size") + HBaseBuilder.parseColSpec(List("file_size")) } } it should "throw IllegalArgumentException on nonexistent family" in { a [IllegalArgumentException] should be thrownBy { - HBaseBuilder.parseColSpec("foo:bar") + HBaseBuilder.parseColSpec(List("foo:bar")) } } it should "throw IllegalArgumentException on nonexistent column" in { a [IllegalArgumentException] should be thrownBy { - HBaseBuilder.parseColSpec("file:bar") + HBaseBuilder.parseColSpec(List("file:bar")) } } } -- cgit v1.2.3