Skip to content

Commit

Permalink
Merge pull request #481 from AbsaOSS/feature/480-basic-search-for-ver…
Browse files Browse the repository at this point in the history
…sioned-entities

#480 Basic search for versioned menas entities
  • Loading branch information
lokm01 authored Jun 19, 2019
2 parents 8d90f55 + b15fd7b commit 44dd108
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,19 @@ abstract class VersionedModelController[C <: VersionedModel with Product with Au

import scala.concurrent.ExecutionContext.Implicits.global

@GetMapping(Array("/list"))
@GetMapping(Array("/list", "/list/{searchQuery}"))
@ResponseStatus(HttpStatus.OK)
def getList(): CompletableFuture[Seq[VersionedSummary]] = {
versionedModelService.getLatestVersions()
def getList(@PathVariable searchQuery: Optional[String]): CompletableFuture[Seq[VersionedSummary]] = {
val searchOpt: Option[String] = searchQuery
versionedModelService.getLatestVersions(searchQuery)
}


@GetMapping(Array("/searchSuggestions"))
@ResponseStatus(HttpStatus.OK)
def getSearchSuggestions(): CompletableFuture[Seq[String]] = {
versionedModelService.getSearchSuggestions()
}

@GetMapping(Array("/detail/{name}/{version}"))
@ResponseStatus(HttpStatus.OK)
def getVersionDetail(@PathVariable name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,19 @@ abstract class VersionedMongoRepository[C <: VersionedModel](mongoDb: MongoDatab
})
}

def getLatestVersions(): Future[Seq[VersionedSummary]] = {
val pipeline = Seq(filter(getNotDisabledFilter),
Aggregates.group("$name", Accumulators.max("latestVersion", "$version")))
def getDistinctNamesEnabled(): Future[Seq[String]] = {
collection.distinct[String]("name", getNotDisabledFilter).toFuture()
}

def getLatestVersions(searchQuery: Option[String] = None): Future[Seq[VersionedSummary]] = {
val searchFilter = searchQuery match {
case Some(search) => Filters.regex("name", search, "i")
case None => Filters.expr(true)
}
val pipeline = Seq(
filter(Filters.and(searchFilter, getNotDisabledFilter)),
Aggregates.group("$name", Accumulators.max("latestVersion", "$version"))
)
collection.aggregate[VersionedSummary](pipeline).toFuture()
}

Expand All @@ -65,42 +75,42 @@ abstract class VersionedMongoRepository[C <: VersionedModel](mongoDb: MongoDatab
}

def getLatestVersionValue(name: String): Future[Option[Int]] = {
val pipeline = Seq(filter(getNameFilter(name)),
val pipeline = Seq(
filter(getNameFilter(name)),
filter(getNotDisabledFilter),
Aggregates.group("$name", Accumulators.max("latestVersion", "$version")))
Aggregates.group("$name", Accumulators.max("latestVersion", "$version"))
)
collection.aggregate[VersionedSummary](pipeline).headOption().map(_.map(_.latestVersion))
}

def getAllVersions(name: String, inclDisabled: Boolean = false): Future[Seq[C]] = {
val filter = if (inclDisabled) getNameFilter(name) else getNameFilterEnabled(name)
val filter = if(inclDisabled) getNameFilter(name) else getNameFilterEnabled(name)
collection.find(filter).toFuture()
}

def create(item: C, username: String): Future[Completed] = {
super.create(item
.setCreatedInfo(username)
.setUpdatedInfo(username)
.asInstanceOf[C])
.asInstanceOf[C]
)
}

def update(username: String, updated: C): Future[C] = {
for {
latestVersion <- getLatestVersionValue(updated.name)
newVersion <- if (latestVersion.isEmpty) {
throw NotFoundException()
} else if (latestVersion.get != updated.version) {
throw EntityAlreadyExistsException(s"Entity ${updated.name} (version. ${updated.version}) already exists.")
} else {
Future.successful(latestVersion.get + 1)
}
newVersion <- if(latestVersion.isEmpty) throw new NotFoundException()
else if(latestVersion.get != updated.version) throw new EntityAlreadyExistsException(s"Entity ${updated.name} (version. ${updated.version}) already exists.")
else Future.successful(latestVersion.get + 1)
newInfo <- Future.successful(updated.setUpdatedInfo(username).setVersion(newVersion).setParent(Some(getParent(updated))).asInstanceOf[C])
res <- collection.insertOne(newInfo).toFuture()
} yield newInfo

}

def disableVersion(name: String, version: Option[Int], username: String): Future[UpdateResult] = {
collection.updateMany(getNameVersionFilter(name, version), combine(set("disabled", true),
collection.updateMany(getNameVersionFilter(name, version), combine(
set("disabled", true),
set("dateDisabled", ZonedDateTime.now()),
set("userDisabled", username))).toFuture()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ abstract class VersionedModelService[C <: VersionedModel with Product with Audit

private[services] val logger = LoggerFactory.getLogger(this.getClass)

def getLatestVersions(): Future[Seq[VersionedSummary]] = {
versionedMongoRepository.getLatestVersions()
def getLatestVersions(searchQuery: Option[String]): Future[Seq[VersionedSummary]] = {
versionedMongoRepository.getLatestVersions(searchQuery)
}

def getSearchSuggestions(): Future[Seq[String]] = {
versionedMongoRepository.getDistinctNamesEnabled()
}

def getVersion(name: String, version: Int): Future[Option[C]] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,96 @@ class DatasetRepositoryIntegrationSuite extends BaseRepositoryTest {
assert(actualStored.contains(expected))
}

"DatasetMongoRepository::getDistinctNamesEnabled" should {
"return an empty Seq" when {
"no datasets exist" in {
val actual = await(datasetMongoRepository.getDistinctNamesEnabled)
assert(actual.isEmpty)
}

"only disabled datasets exist" in {
val dataset1 = DatasetFactory.getDummyDataset(name = "dataset1", version = 1,
disabled = true, dateDisabled = Option(DatasetFactory.dummyZonedDateTime), userDisabled = Option("user"))
datasetFixture.add(dataset1)
val res = await(datasetMongoRepository.getDistinctNamesEnabled)
assert(res.isEmpty)
}
}
"return Seq with a single name" when {
"single dataset exists" in {
val dataset2 = DatasetFactory.getDummyDataset(name = "dataset2", version = 1)
datasetFixture.add(dataset2)
val res1 = await(datasetMongoRepository.getDistinctNamesEnabled)
assert(res1.toSeq == Seq("dataset2"))
}
"multiple versions of a dataset exist" in {
val dataset2 = DatasetFactory.getDummyDataset(name = "dataset2", version = 1)
val dataset3 = DatasetFactory.getDummyDataset(name = "dataset2", version = 2)
datasetFixture.add(dataset2, dataset3)
val res2 = await(datasetMongoRepository.getDistinctNamesEnabled)
assert(res2.toSeq == Seq("dataset2"))
}
}
"return distinct names" when {
"multiple datasets exist" in {
val dataset3 = DatasetFactory.getDummyDataset(name = "dataset2", version = 2)
val dataset4 = DatasetFactory.getDummyDataset(name = "dataset3", version = 1)
datasetFixture.add(dataset3, dataset4)
val res3 = await(datasetMongoRepository.getDistinctNamesEnabled)
assert(res3.toSeq.sorted == Seq("dataset2", "dataset3"))
}
}
}

"DatasetMongoRepository::getLatestVersions" should {
"return an empty Seq" when {
"no datasets exist and search query is provided" in {
val actual = await(datasetMongoRepository.getLatestVersions(Some("abc")))
assert(actual.isEmpty)
}
"only disabled dataset exists" in {
val dataset1 = DatasetFactory.getDummyDataset(name = "dataset1", version = 1,
disabled = true, dateDisabled = Option(DatasetFactory.dummyZonedDateTime), userDisabled = Option("user"))
datasetFixture.add(dataset1)
assert(await(datasetMongoRepository.getLatestVersions(Some("dataset1"))).isEmpty)
}
}
"return seq of versioned summaries matching the search query" when {
"search query is a perfect match" in {
val dataset2 = DatasetFactory.getDummyDataset(name = "dataset2", version = 1)
val dataset3 = DatasetFactory.getDummyDataset(name = "dataset2", version = 2)
val dataset4 = DatasetFactory.getDummyDataset(name = "dataset3", version = 1)
val dataset5 = DatasetFactory.getDummyDataset(name = "abc", version = 1)

datasetFixture.add(dataset2, dataset3, dataset4, dataset5)
val res1 = await(datasetMongoRepository.getLatestVersions(Some("dataset2")))
assert(res1.size == 1 && res1.head._id == "dataset2")
}
"search query is a partial match" in {
val dataset2 = DatasetFactory.getDummyDataset(name = "dataset2", version = 1)
val dataset3 = DatasetFactory.getDummyDataset(name = "dataset2", version = 2)
val dataset4 = DatasetFactory.getDummyDataset(name = "dataset3", version = 1)
val dataset5 = DatasetFactory.getDummyDataset(name = "abc", version = 1)

datasetFixture.add(dataset2, dataset3, dataset4, dataset5)
val res2 = await(datasetMongoRepository.getLatestVersions(Some("tas")))
assert(res2.size == 2 && res2.exists(_._id == "dataset2") && res2.exists(_._id == "dataset3"))
}
}
"return all datasets" when {
"search query is empty" in {
val dataset2 = DatasetFactory.getDummyDataset(name = "dataset2", version = 1)
val dataset3 = DatasetFactory.getDummyDataset(name = "dataset2", version = 2)
val dataset4 = DatasetFactory.getDummyDataset(name = "dataset3", version = 1)
val dataset5 = DatasetFactory.getDummyDataset(name = "abc", version = 1)

datasetFixture.add(dataset2, dataset3, dataset4, dataset5)
val res3 = await(datasetMongoRepository.getLatestVersions(Some("")))
assert(res3.exists(_._id == "dataset2") && res3.exists(_._id == "dataset3") && res3.exists(_._id == "abc"))
}
}
}

"DatasetMongoRepository::distinctCount" should {
"return 0" when {
"no datasets exists" in {
Expand Down
23 changes: 23 additions & 0 deletions menas/ui/components/dataset/datasetMaster.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,33 @@ sap.ui.define([
new DatasetDialogFactory(this, Fragment.load).getAdd();

this._datasetService = new DatasetService(this._model, this._eventBus)
this._searchField = this.byId("datasetSearchField")
},

list: function () {
this._datasetService.getList(this.byId("masterPage"));
this._datasetService.getSearchSuggestions(this._model, "dataset")
},

onSearch: function(oEv) {
this._datasetService.getList(this.byId("masterPage"), oEv.getSource().getValue())
},

_searchFilter: function(sValue) {
return [new sap.ui.model.Filter([
new sap.ui.model.Filter("name", function(sText) {
return (sText || "").toUpperCase().indexOf(sValue.toUpperCase()) > -1;
})], false)];
},

onSuggest: function(oEv) {
let value = oEv.getParameter("suggestValue");
let filters = [];
if (value) {
filters = this._searchFilter(value)
}
this._searchField.getBinding("suggestionItems").filter(filters);
this._searchField.suggest();
},

onPressMasterBack: function () {
Expand Down
6 changes: 6 additions & 0 deletions menas/ui/components/dataset/datasetMaster.view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
xmlns:cust="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1">
<Page id="masterPage" title="Datasets" showNavButton="true" navButtonPress="onPressMasterBack">
<content>
<SearchField id="datasetSearchField" placeholder="Search..." enableSuggestions="true" search="onSearch"
suggest="onSuggest" suggestionItems="{path: '/datasetSearchSuggestions', sorter: { path: 'name' }}">
<suggestionItems>
<SuggestionItem text="{name}"/>
</suggestionItems>
</SearchField>
<List items="{datasets>/}"
mode="SingleSelectMaster"
selectionChange="datasetSelected"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,33 @@ sap.ui.define([
new MappingTableDialogFactory(this, Fragment.load).getAdd();

this._mappingTableService = new MappingTableService(this._model, this._eventBus)
this._searchField = this.byId("mappingTableSearchField")
},

list: function () {
this._mappingTableService.getList(this.byId("masterPage"));
this._mappingTableService.getList(this.byId("masterPage"));
this._mappingTableService.getSearchSuggestions(this._model, "mappingTable")
},

onSearch: function(oEv) {
this._mappingTableService.getList(this.byId("masterPage"), oEv.getSource().getValue())
},

_searchFilter: function(sValue) {
return [new sap.ui.model.Filter([
new sap.ui.model.Filter("name", function(sText) {
return (sText || "").toUpperCase().indexOf(sValue.toUpperCase()) > -1;
})], false)];
},

onSuggest: function(oEv) {
let value = oEv.getParameter("suggestValue");
let filters = [];
if (value) {
filters = this._searchFilter(value)
}
this._searchField.getBinding("suggestionItems").filter(filters);
this._searchField.suggest();
},

onPressMasterBack: function () {
Expand Down
6 changes: 6 additions & 0 deletions menas/ui/components/mappingTable/mappingTableMaster.view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
xmlns:cust="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1">
<Page id="masterPage" title="Mapping Tables" showNavButton="true" navButtonPress="onPressMasterBack">
<content>
<SearchField id="mappingTableSearchField" placeholder="Search..." enableSuggestions="true" search="onSearch"
suggest="onSuggest" suggestionItems="{path: '/mappingTableSearchSuggestions', sorter: { path: 'name' }}">
<suggestionItems>
<SuggestionItem text="{name}"/>
</suggestionItems>
</SearchField>
<List items="{mappingTables>/}"
mode="SingleSelectMaster"
selectionChange="mappingTableSelected"
Expand Down
23 changes: 23 additions & 0 deletions menas/ui/components/schema/schemaMaster.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,33 @@ sap.ui.define([
new SchemaDialogFactory(this, Fragment.load).getAdd();

this._schemaService = new SchemaService(this._model, this._eventBus)
this._searchField = this.byId("schemaSearchField")
},

list: function () {
this._schemaService.getList(this.byId("masterPage"));
this._schemaService.getSearchSuggestions(this._model, "schema")
},

onSearch: function(oEv) {
this._schemaService.getList(this.byId("masterPage"), oEv.getSource().getValue())
},

_searchFilter: function(sValue) {
return [new sap.ui.model.Filter([
new sap.ui.model.Filter("name", function(sText) {
return (sText || "").toUpperCase().indexOf(sValue.toUpperCase()) > -1;
})], false)];
},

onSuggest: function(oEv) {
let value = oEv.getParameter("suggestValue");
let filters = [];
if (value) {
filters = this._searchFilter(value)
}
this._searchField.getBinding("suggestionItems").filter(filters);
this._searchField.suggest();
},

onPressMasterBack: function () {
Expand Down
6 changes: 6 additions & 0 deletions menas/ui/components/schema/schemaMaster.view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
xmlns:cust="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1">
<Page id="masterPage" title="Schemas" showNavButton="true" navButtonPress="onPressMasterBack">
<content>
<SearchField id="schemaSearchField" placeholder="Search..." enableSuggestions="true" search="onSearch"
suggest="onSuggest" suggestionItems="{path: '/schemaSearchSuggestions', sorter: { path: 'name' }}">
<suggestionItems>
<SuggestionItem text="{name}"/>
</suggestionItems>
</SearchField>
<List items="{schemas>/}"
mode="SingleSelectMaster"
selectionChange="schemaSelected"
Expand Down
Loading

0 comments on commit 44dd108

Please sign in to comment.