Skip to content
This repository was archived by the owner on Jun 14, 2024. It is now read-only.

Commit 50bc56a

Browse files
remove arrangement
1 parent 336728d commit 50bc56a

2 files changed

Lines changed: 81 additions & 12 deletions

File tree

src/main/scala/com/microsoft/hyperspace/index/IndexLogEntry.scala

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.apache.spark.sql.types.{DataType, StructType}
3131

3232
import com.microsoft.hyperspace.{BuildInfo, HyperspaceException}
3333
import com.microsoft.hyperspace.actions.Constants
34+
import com.microsoft.hyperspace.index.Content.recFilesApply
3435
import com.microsoft.hyperspace.util.PathUtils
3536

3637
// IndexLogEntry-specific fingerprint to be temporarily used where fingerprint is not defined.
@@ -45,27 +46,17 @@ case class Content(root: Directory, fingerprint: NoOpFingerprint = NoOpFingerpri
4546
@JsonIgnore
4647
lazy val files: Seq[Path] = {
4748
// Recursively find files from directory tree.
48-
rec(new Path(root.name), root, (f, prefix) => new Path(prefix, f.name))
49+
recFilesApply(new Path(root.name), root, (f, prefix) => new Path(prefix, f.name))
4950
}
5051

5152
@JsonIgnore
5253
lazy val fileInfos: Set[FileInfo] = {
53-
rec(
54+
recFilesApply(
5455
new Path(root.name),
5556
root,
5657
(f, prefix) =>
5758
FileInfo(new Path(prefix, f.name).toString, f.size, f.modifiedTime, f.id)).toSet
5859
}
59-
60-
private def rec[T](
61-
prefixPath: Path,
62-
directory: Directory,
63-
func: (FileInfo, Path) => T): Seq[T] = {
64-
val files = directory.files.map(f => func(f, prefixPath))
65-
files ++ directory.subDirs.flatMap { dir =>
66-
rec(new Path(prefixPath, dir.name), dir, func)
67-
}
68-
}
6960
}
7061

7162
object Content {
@@ -111,6 +102,43 @@ object Content {
111102
None
112103
}
113104
}
105+
106+
/**
107+
* Apply `func` to each file in directory recursively.
108+
*
109+
* @param prefixPath Root prefix
110+
* @param directory Root directory
111+
* @param func Function which would apply to current prefix and file
112+
* @tparam T
113+
* @return Result list of applying function to all files
114+
*/
115+
def recFilesApply[T](
116+
prefixPath: Path,
117+
directory: Directory,
118+
func: (FileInfo, Path) => T): Seq[T] = {
119+
@tailrec
120+
def recAcc[A](
121+
dirMap: List[(Path, Seq[Directory])],
122+
func: (FileInfo, Path) => A,
123+
acc: Seq[A] = Seq.empty): Seq[A] = {
124+
dirMap match {
125+
case Nil => acc
126+
case (curPrefixPath, curDirs) :: otherDirs =>
127+
val curAcc = for {
128+
dir <- curDirs
129+
file <- dir.files
130+
} yield func(file, new Path(curPrefixPath, dir.name))
131+
132+
val newLevels = curDirs
133+
.filter(_.subDirs.nonEmpty)
134+
.map(dir => (new Path(curPrefixPath, dir.name), dir.subDirs))
135+
136+
recAcc(otherDirs ++ newLevels, func, curAcc ++ acc)
137+
}
138+
}
139+
140+
recAcc(List(prefixPath -> Seq(directory)), func)
141+
}
114142
}
115143

116144
/**

src/test/scala/com/microsoft/hyperspace/index/IndexLogEntryTest.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,47 @@ class IndexLogEntryTest extends SparkFunSuite with SQLHelper with BeforeAndAfter
242242
assert(actual.sourceFilesSizeInBytes == 200L)
243243
}
244244

245+
test("Content.recFilesApply returns a result list of applying function to all files.") {
246+
val directory = Directory(
247+
"file:/",
248+
files = Seq(FileInfo("f0", 0, 0, UNKNOWN_FILE_ID)),
249+
subDirs = Seq(
250+
Directory(
251+
"a",
252+
files =
253+
Seq(FileInfo("f1", 0, 0, UNKNOWN_FILE_ID), FileInfo("f2", 0, 0, UNKNOWN_FILE_ID)),
254+
subDirs = Seq(
255+
Directory(
256+
"b",
257+
files =
258+
Seq(FileInfo("f3", 0, 0, UNKNOWN_FILE_ID), FileInfo("f4", 0, 0, UNKNOWN_FILE_ID)),
259+
subDirs = Seq(Directory("c"))),
260+
Directory("d")))))
261+
262+
def theFunction: (FileInfo, Path) => Path = (f, prefix) => new Path(prefix, f.name)
263+
264+
val res = Content.recFilesApply(new Path("file:/"), directory, theFunction)
265+
266+
val expected =
267+
Seq("file:/f0", "file:/a/f1", "file:/a/f2", "file:/a/b/f3", "file:/a/b/f4")
268+
.map(new Path(_))
269+
.toSet
270+
271+
val actual = res.toSet
272+
assert(actual.equals(expected))
273+
}
274+
275+
test("Content.recFilesApply returns empty list for directories without files.") {
276+
val directory = Directory("file:/")
277+
278+
val res = Content.recFilesApply(
279+
new Path("file:/"),
280+
directory,
281+
(f, prefix) => new Path(prefix, f.name))
282+
283+
assert(res.isEmpty)
284+
}
285+
245286
test("Content.files api lists all files from Content object.") {
246287
val content = Content(Directory("file:/", subDirs =
247288
Seq(

0 commit comments

Comments
 (0)