Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stack filter in awesome Scala #967

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions modules/core/shared/src/main/scala/scaladex/core/model/Stack.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package scaladex.core.model

sealed trait Stack {
def name: String
def label: String = name.toLowerCase.replace(' ', '-')
def projects: Seq[Project.Reference]
override def toString: String = name
}

object Stack {
val all: Seq[Stack] = Seq(Akka, Lihaoyi, Spark, Typelevel, Zio)
val byLabel: Map[String, Stack] = all.map(s => s.label -> s).toMap

object Akka extends Stack {
override val name: String = "Akka"
override val projects: Seq[Project.Reference] = Seq("akka/akka", "akka/akka-http", "akka/alpakka").map(Project.Reference.from)
}

object Lihaoyi extends Stack {
override val name: String = "Li Haoyi"
override val projects: Seq[Project.Reference] = Seq("com-lihaoyi/ammonite", "com-liahoyi/mill", "com-lihaoyi/fastparse", "com-lihaoyi/scalatags", "com-lihaoyi/requests-scala", "com-lihaoyi/upickle", "com-lihaoyi/cask", "com-lihaoyi/mainargs", "com-lihaoyi/castor", "com-lihaoyi/geny").map(Project.Reference.from)
}

object Spark extends Stack {
override val name: String = "Spark"
override val projects: Seq[Project.Reference] = Seq("apache/spark").map(Project.Reference.from)
}

object Typelevel extends Stack {
override val name: String = "Typelevel"
override val projects: Seq[Project.Reference] = Seq("typelevel/cats", "typelevel/cats-effect", "typelevel/fs2").map(Project.Reference.from)
}

object Zio extends Stack {
override val name: String = "ZIO"
override val projects: Seq[Project.Reference] = Seq("zio/zio", "zio/zio-prelude").map(Project.Reference.from)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package scaladex.core.model.search

import scaladex.core.model.Language
import scaladex.core.model.Platform
import scaladex.core.model.Stack

final case class AwesomeParams(
languages: Seq[Language],
platforms: Seq[Platform],
stacks: Seq[Stack],
sorting: Sorting
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ final case class ProjectDocument(
updateDate: Option[Instant],
languages: Seq[Language],
platforms: Seq[Platform],
projectDependencies: Seq[Project.Reference],
inverseProjectDependencies: Int,
category: Option[Category],
formerReferences: Seq[Project.Reference],
Expand All @@ -46,6 +47,7 @@ object ProjectDocument {
None,
Seq.empty,
Seq.empty,
Seq.empty,
0,
None,
Seq.empty,
Expand All @@ -55,6 +57,7 @@ object ProjectDocument {
def apply(
project: Project,
artifacts: Seq[Artifact],
projectDependencies: Seq[Project.Reference],
inverseProjectDependencies: Int,
formerReferences: Seq[Project.Reference]
): ProjectDocument = {
Expand All @@ -69,6 +72,7 @@ object ProjectDocument {
updateDate = None,
artifacts.map(_.binaryVersion.language).distinct.sorted,
artifacts.map(_.binaryVersion.platform).distinct.sorted,
projectDependencies,
inverseProjectDependencies,
settings.category,
formerReferences,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ trait WebDatabase {
def getAllProjects(): Future[Seq[Project]]
def getProject(projectRef: Project.Reference): Future[Option[Project]]
def getFormerReferences(projectRef: Project.Reference): Future[Seq[Project.Reference]]
def getProjectDependencies(projectRef: Project.Reference): Future[Seq[Project.Reference]]
def countInverseProjectDependencies(projectRef: Project.Reference): Future[Int]
def getArtifacts(projectRef: Project.Reference): Future[Seq[Artifact]]
def getArtifactsByName(projectRef: Project.Reference, artifactName: Artifact.Name): Future[Seq[Artifact]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ class InMemoryDatabase extends SchedulerDatabase {

override def insertProjectDependencies(projectDependencies: Seq[ProjectDependency]): Future[Int] = ???

override def getProjectDependencies(projectRef: Project.Reference): Future[Seq[Project.Reference]] =
// not really implemented
Future.successful(Seq.empty)

override def countInverseProjectDependencies(projectRef: Project.Reference): Future[Int] =
// not really implemented
Future.successful(0)
Expand Down
16 changes: 14 additions & 2 deletions modules/core/shared/src/test/scala/scaladex/core/test/Values.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,13 @@ object Values {
Project.default(reference, None, Some(githubInfo), Some(settings), now = now)

val projectDocument: ProjectDocument =
ProjectDocument(project.copy(creationDate = Some(now.minus(1, ChronoUnit.MINUTES))), Seq(artifact), 0, Seq.empty)
ProjectDocument(
project.copy(creationDate = Some(now.minus(1, ChronoUnit.MINUTES))),
Seq(artifact),
Seq.empty,
0,
Seq.empty
)
}

object PlayJsonExtra {
Expand Down Expand Up @@ -193,7 +199,13 @@ object Values {
)

val projectDocument: ProjectDocument =
ProjectDocument(project.copy(creationDate = Some(now.minus(10, ChronoUnit.MINUTES))), allArtifacts, 1, Seq.empty)
ProjectDocument(
project.copy(creationDate = Some(now.minus(10, ChronoUnit.MINUTES))),
allArtifacts,
Seq.empty,
1,
Seq.empty
)
}

object CatsEffect {
Expand Down
2 changes: 1 addition & 1 deletion modules/infra/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ scaladex {
elasticsearch {
index = scaladex-dev
port = 9200
reset = false
reset = true
}
database {
user = user
Expand Down
2 changes: 1 addition & 1 deletion modules/infra/src/main/scala/scaladex/infra/Codecs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import scaladex.core.model.search.GithubInfoDocument
object Codecs {
implicit val organization: Codec[Project.Organization] = fromString(_.value, Project.Organization.apply)
implicit val repository: Codec[Project.Repository] = fromString(_.value, Project.Repository.apply)
implicit val reference: Codec[Project.Reference] = deriveCodec
implicit val reference: Codec[Project.Reference] = fromString(_.toString, Project.Reference.from)
implicit val artifactName: Codec[Artifact.Name] = fromString(_.value, Artifact.Name.apply)
implicit val instant: Codec[Instant] = fromLong[Instant](_.toEpochMilli, Instant.ofEpochMilli)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import scaladex.core.model.GithubIssue
import scaladex.core.model.Language
import scaladex.core.model.Platform
import scaladex.core.model.Project
import scaladex.core.model.Stack
import scaladex.core.model.search.AwesomeParams
import scaladex.core.model.search.Page
import scaladex.core.model.search.PageParams
Expand Down Expand Up @@ -251,10 +252,7 @@ class ElasticsearchEngine(esClient: ElasticClient, index: String)(implicit ec: E
params: AwesomeParams,
page: PageParams
): Future[Page[ProjectDocument]] = {
val query = must(
termQuery("category", category.label),
binaryVersionQuery(params.languages.map(_.label), params.platforms.map(_.label))
)
val query = awesomeQuery(category, params)
val request = search(index)
.query(gitHubStarScoring(query))
.sortBy(sortQuery(params.sorting))
Expand Down Expand Up @@ -319,7 +317,8 @@ class ElasticsearchEngine(esClient: ElasticClient, index: String)(implicit ec: E
private def awesomeQuery(category: Category, params: AwesomeParams): Query =
must(
termQuery("category", category.label),
binaryVersionQuery(params.languages.map(_.label), params.platforms.map(_.label))
binaryVersionQuery(params.languages.map(_.label), params.platforms.map(_.label)),
must(params.stacks.map(stackQuery))
)

private def filteredSearchQuery(params: SearchParams): Query =
Expand Down Expand Up @@ -368,8 +367,7 @@ class ElasticsearchEngine(esClient: ElasticClient, index: String)(implicit ec: E
.field("repository", 6)
.field("primaryTopic", 5)
.field("organization", 5)
.field("formerReferences.repository", 5)
.field("formerReferences.organization", 4)
.field("formerReferences", 4)
.field("githubInfo.description", 4)
.field("githubInfo.topics", 4)
.field("artifactNames", 2)
Expand All @@ -393,8 +391,7 @@ class ElasticsearchEngine(esClient: ElasticClient, index: String)(implicit ec: E
prefixQuery("repository", prefix).boost(6),
prefixQuery("primaryTopic", prefix).boost(5),
prefixQuery("organization", prefix).boost(5),
prefixQuery("formerReferences.repository", prefix).boost(5),
prefixQuery("formerReferences.organization", prefix).boost(4),
prefixQuery("formerReferences", prefix).boost(4),
prefixQuery("githubInfo.description", prefix).boost(4),
prefixQuery("githubInfo.topics", prefix).boost(4),
prefixQuery("artifactNames", prefix).boost(2)
Expand All @@ -421,9 +418,7 @@ class ElasticsearchEngine(esClient: ElasticClient, index: String)(implicit ec: E

private val cliQuery = termQuery("hasCli", true)

private def repositoriesQuery(
repositories: Seq[Project.Reference]
): Query =
private def repositoriesQuery(repositories: Seq[Project.Reference]): Query =
should(repositories.map(repositoryQuery))

private def repositoryQuery(repo: Project.Reference): Query =
Expand All @@ -435,6 +430,12 @@ class ElasticsearchEngine(esClient: ElasticClient, index: String)(implicit ec: E
private def binaryVersionQuery(languages: Seq[String], platforms: Seq[String]): Query =
must(languages.map(termQuery("languages", _)) ++ platforms.map(termQuery("platforms", _)))

private def stackQuery(stack: Stack): Query =
should(stack.projects.map(repositoryQuery) ++ stack.projects.map(projectDependencyQuery))

private def projectDependencyQuery(dependency: Project.Reference): Query =
termQuery("projectDependencies", dependency.toString())

private def binaryVersionQuery(binaryVersion: BinaryVersion): Query =
must(
termQuery("platforms", binaryVersion.platform.label),
Expand Down
3 changes: 3 additions & 0 deletions modules/infra/src/main/scala/scaladex/infra/SqlDatabase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ class SqlDatabase(conf: DatabaseConfig, xa: doobie.Transactor[IO]) extends Sched
override def insertProjectDependencies(projectDependencies: Seq[ProjectDependency]): Future[Int] =
run(ProjectDependenciesTable.insertOrUpdate.updateMany(projectDependencies))

override def getProjectDependencies(projectRef: Project.Reference): Future[Seq[Project.Reference]] =
run(ProjectDependenciesTable.selectByReference.to[Seq](projectRef))

override def countInverseProjectDependencies(projectRef: Project.Reference): Future[Int] =
run(ProjectDependenciesTable.countInverseDependencies.unique(projectRef))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ object ElasticsearchMapping {
dateField("creationDate"),
keywordField("languages"),
keywordField("platforms"),
keywordField("projectDependencies"),
intField("inverseProjectDependencies"),
keywordField("category"),
textField("githubInfo.description").analyzer("english"),
Expand All @@ -71,9 +72,6 @@ object ElasticsearchMapping {
keywordField("keyword").normalizer("lowercase")
),
nestedField("githubInfo.openIssues"),
objectField("formerReferences").fields(
textField("organization").analyzer("standard"),
textField("repository").analyzer("standard")
)
textField("formerReferences").analyzer("standard")
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ case class RawProjectDocument(
updateDate: Option[Instant],
languages: Seq[String],
platforms: Seq[String],
projectDependencies: Seq[Project.Reference],
inverseProjectDependencies: Int,
category: Option[String],
formerReferences: Seq[Project.Reference],
Expand All @@ -39,6 +40,7 @@ case class RawProjectDocument(
updateDate,
languages.flatMap(Language.fromLabel).sorted,
platforms.flatMap(Platform.fromLabel).sorted,
projectDependencies,
inverseProjectDependencies,
category.flatMap(Category.byLabel.get),
formerReferences,
Expand All @@ -63,6 +65,7 @@ object RawProjectDocument {
updateDate,
languages.map(_.label),
platforms.map(_.label),
projectDependencies,
inverseProjectDependencies,
category.map(_.label),
formerReferences,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ object ProjectDependenciesTable {
val countInverseDependencies: Query[Project.Reference, Int] =
selectRequest(table, Seq("COUNT(*)"), targetFields)

val selectByReference: Query[Project.Reference, Project.Reference] =
selectRequest(table, targetFields, sourceFields)

val deleteBySource: Update[Project.Reference] =
deleteRequest(table, sourceFields)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import scaladex.core.model.Language
import scaladex.core.model.MetaCategory
import scaladex.core.model.Platform
import scaladex.core.model.Scala
import scaladex.core.model.Stack
import scaladex.core.model.UserState
import scaladex.core.model.search.AwesomeParams
import scaladex.core.model.search.PageParams
Expand Down Expand Up @@ -41,13 +42,15 @@ class AwesomePages(env: Env, searchEngine: SearchEngine)(implicit ec: ExecutionC
parameters(
"languages".repeated,
"platforms".repeated,
"stacks".repeated,
"sort".?
).tmap {
case (languageParams, platformParams, sortParam) =>
case (languageParams, platformParams, stacksParams, sortParam) =>
val scalaVersions = languageParams.flatMap(Language.fromLabel).collect { case v: Scala => v }.toSeq
val platforms = platformParams.flatMap(Platform.fromLabel).toSeq
val stacks = stacksParams.flatMap(Stack.byLabel.get).toSeq
val sorting = sortParam.flatMap(Sorting.byLabel.get).getOrElse(Sorting.Relevance)
Tuple1(AwesomeParams(scalaVersions, platforms, sorting))
Tuple1(AwesomeParams(scalaVersions, platforms, stacks, sorting))
}

private def awesomeScala(user: Option[UserState], params: AwesomeParams): Future[Html] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ class SearchSynchronizer(database: WebDatabase, searchEngine: SearchEngine)(impl
private def insertDocument(project: Project, formerReferences: Seq[Project.Reference]): Future[Unit] =
for {
artifacts <- database.getArtifacts(project.reference)
projectDependencies <- database.getProjectDependencies(project.reference)
inverseProjectDependencies <- database.countInverseProjectDependencies(project.reference)
document = ProjectDocument(project, artifacts, inverseProjectDependencies, formerReferences)
document = ProjectDocument(project, artifacts, projectDependencies, inverseProjectDependencies, formerReferences)
_ <- searchEngine.insert(document)
_ <- formerReferences.mapSync(searchEngine.delete)
} yield ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import scaladex.core.model.search.ProjectDocument
@import scaladex.core.model.Scala
@import scaladex.core.model.Platform
@import scaladex.core.model.Stack
@import scaladex.core.model.search.AwesomeParams

@(
Expand Down Expand Up @@ -50,6 +51,17 @@ <h1><a class="awesome" href="/awesome">Awesome Scala</a></h1>
}
</select>
</div>

<div class="filter">
<label for="select-stack"><h3>Stack:</h3></label>
<select id="select-stack" name="stacks" class="selectpicker" multiple data-max-options="1" onchange="this.form.submit()">
@for(stack <- Stack.all) {
<option value="@stack.label" @if(params.stacks.contains(stack)){selected}>
@stack
</option>
}
</select>
</div>
</form>

<nav>
Expand Down