Commit 1861f94c authored by Jonas Waeber's avatar Jonas Waeber
Browse files

Add institution name retrieval.

Add date range for timeperiod.
Add metadata language values
parent 0a712448
Pipeline #20596 passed with stages
in 5 minutes and 21 seconds
......@@ -34,7 +34,7 @@ ext {
dependencies {
compile group: 'org.elasticsearch.client', name: 'elasticsearch-rest-high-level-client', version: '7.6.1'
implementation 'org.memobase:memobase-service-utilities:2.0.0'
implementation 'org.memobase:memobase-service-utilities:2.0.5'
// Logging Framework
implementation "org.apache.logging.log4j:log4j-api:${log4jV}"
......
......@@ -5,7 +5,8 @@ metadata:
namespace: memobase
data:
APPLICATION_ID: "{{ .Values.deploymentName }}-app"
ELASTIC_INDEX: "{{ .Values.elasticIndex }}"
DOCUMENTS_INDEX: "{{ .Values.documentsIndex }}"
INSTITUTION_INDEX: "{{ .Values.institutionIndex }}"
MEDIA_SERVER_URL: "{{ .Values.mediaServerUrl }}"
TOPIC_IN: "{{ .Values.inputTopic }}"
TOPIC_OUT: "{{ .Values.outputTopic }}"
......
......@@ -7,7 +7,8 @@ deploymentName: search-doc-service
kafkaConfigs: prod-kafka-bootstrap-servers
elasticConfigs: prod-elastic-configs
elasticIndex: documents-v17
documentsIndex: documents-v17
institutionIndex: institutions-v1
outputTopic: search-doc-output-documents
inputTopic: search-doc-input-documents
reportingTopic: postprocessing-reporting
......
......@@ -35,7 +35,8 @@ class App {
SettingsProps.mediaUrl,
SettingsProps.elasticHost,
SettingsProps.elasticPort,
SettingsProps.elasticIndex
SettingsProps.documentsIndex,
SettingsProps.institutionIndex
),
file,
useStreamsConfig = true
......
......@@ -19,16 +19,17 @@
package org.memobase
import com.beust.klaxon.JsonObject
import java.lang.NumberFormatException
import org.apache.logging.log4j.LogManager
import org.memobase.helpers.Date
import org.memobase.helpers.ElasticSearchWrapper
import org.memobase.helpers.Extract
import org.memobase.helpers.KEYS
import org.memobase.model.FacetContainer
import org.memobase.model.IntegerRange
import org.memobase.model.LanguageContainer
import org.memobase.model.RecordSetSearchDoc
import org.memobase.model.Schema
import org.memobase.model.IntegerRange
class RecordSetSearchDocBuilder(private val elasticSearchWrapper: ElasticSearchWrapper) {
......@@ -37,22 +38,41 @@ class RecordSetSearchDocBuilder(private val elasticSearchWrapper: ElasticSearchW
fun transform(key: String, input: Map<String, JsonObject>): Schema {
val recordSet =
input["recordSet"] ?: throw InvalidInputException("No recordSet entity found in message $key.")
val identifiers = mutableListOf<JsonObject>()
val metadataLanguages = mutableListOf<JsonObject>()
input.values.forEach {
when {
it[KEYS.ricoType] == KEYS.IdentifierType.main -> {
identifiers.add(it)
it[KEYS.ricoType] == KEYS.LanguageType.metadata -> {
metadataLanguages.add(it)
}
}
}
val name = extractLanguageContainer(recordSet[KEYS.title], "NoNameFound")
val description = extractLanguageContainer(recordSet[KEYS.descriptiveNote], "NoDescriptionFound")
val id = Extract.extractIdValue(identifiers, KEYS.IdentifierType.main) ?: "NoIdentifierFound"
val institution = recordSet[KEYS.heldBy] as String?
if (institution != null) {
// TODO:
val dates = Extract.identifiers(recordSet[KEYS.isAssociatedWithDate]).mapNotNull {
input[it]
}.map {
it[KEYS.normalizedDateValue] as String
}
val date = if (dates.isNotEmpty()) {
try {
val splitDate = dates[0].split("/")
if (splitDate.size == 2) {
IntegerRange(splitDate[0].toInt(), splitDate[1].toInt())
}
else
IntegerRange(splitDate[0].toInt(), splitDate[0].toInt())
} catch (ex: NumberFormatException) {
IntegerRange(3000, 3001)
}
} else {
IntegerRange(3000, 3001)
}
val uri = recordSet[KEYS.entityId] as String
val id = uri.substringAfterLast("/")
val institution = recordSet[KEYS.heldBy] as String
val institutionId = institution.substringAfterLast("/")
return RecordSetSearchDoc(
recordSetId = id,
......@@ -67,8 +87,8 @@ class RecordSetSearchDocBuilder(private val elasticSearchWrapper: ElasticSearchW
}
},
scopeAndContent = description,
periodOfTimeAsYear = IntegerRange(1920, 2020),
institution = FacetContainer(LanguageContainer.placeholder("NoNameInstitution"), filter = institution, facet = emptyList()),
periodOfTimeAsYear = date,
institution = elasticSearchWrapper.getInstitutionName(institutionId),
supportedByMemoriav = recordSet[KEYS.sponsoredBy] != null,
name = name,
......@@ -76,7 +96,18 @@ class RecordSetSearchDocBuilder(private val elasticSearchWrapper: ElasticSearchW
keyVisualLink = recordSet[KEYS.wikidataImage].let { if (it != null) it as String else "NoKeyVisualLinkDefined" },
numberOfDocuments = elasticSearchWrapper.countNumberOfDocuments(id),
lastUpdatedDate = Date.now,
languageOfMetadata = FacetContainer(LanguageContainer.placeholder("Deutsch"), filter = null, facet = emptyList())
languageOfMetadata = metadataLanguages.map {
FacetContainer(
extractLanguageContainer(it[KEYS.name], "NoMetadataLanguageSet"),
it[KEYS.sameAs].let { wikidataUri ->
when (wikidataUri) {
is String -> wikidataUri.substringAfterLast("/")
else -> null
}
},
emptyList()
)
}
)
}
......
......@@ -53,7 +53,8 @@ class Service(settings: SettingsLoader) {
private val host = appSettings.getProperty(SettingsProps.elasticHost)
private val port = appSettings.getProperty(SettingsProps.elasticPort).toInt()
private val documentsIndex = appSettings.getProperty(SettingsProps.elasticIndex)
private val documentsIndex = appSettings.getProperty(SettingsProps.documentsIndex)
private val institutionIndex = appSettings.getProperty(SettingsProps.institutionIndex)
private val client: RestHighLevelClient = connect()
private fun connect(): RestHighLevelClient {
......@@ -65,12 +66,14 @@ class Service(settings: SettingsLoader) {
)
val indexExists = c.indices().exists(GetIndexRequest(documentsIndex), RequestOptions.DEFAULT)
val aliasExists = c.indices().existsAlias(GetAliasesRequest(documentsIndex), RequestOptions.DEFAULT)
val institutionIndexExists = c.indices().exists(GetIndexRequest(institutionIndex), RequestOptions.DEFAULT)
val institutionIndexAliasExists = c.indices().existsAlias(GetAliasesRequest(institutionIndex), RequestOptions.DEFAULT)
if (!indexExists && !aliasExists) {
log.error("Could not find the index or alias defined in the configuration: $documentsIndex.")
if (!indexExists && !aliasExists && !institutionIndexExists && !institutionIndexAliasExists) {
log.error("Could not find the indices or aliases defined in the configuration: $documentsIndex, $institutionIndex.")
exitProcess(1)
} else {
log.info("Successfully connected to index $documentsIndex. Ready to query.")
log.info("Successfully connected to indices $documentsIndex and $institutionIndex. Ready to query.")
c
}
} catch (ex: ElasticsearchException) {
......
......@@ -22,6 +22,7 @@ import com.beust.klaxon.KlaxonException
import java.util.Properties
import org.apache.logging.log4j.LogManager
import org.elasticsearch.ElasticsearchException
import org.elasticsearch.action.get.GetRequest
import org.elasticsearch.action.search.ClearScrollRequest
import org.elasticsearch.action.search.SearchRequest
import org.elasticsearch.action.search.SearchScrollRequest
......@@ -33,6 +34,8 @@ import org.elasticsearch.index.query.QueryBuilders.termQuery
import org.elasticsearch.search.Scroll
import org.elasticsearch.search.builder.SearchSourceBuilder
import org.memobase.model.FacetContainer
import org.memobase.model.LanguageContainer
import org.memobase.model.LanguageContainer.Companion
/**
......@@ -45,7 +48,8 @@ class ElasticSearchWrapper(
private val translationMappers: TranslationMappers
) {
private val log = LogManager.getLogger("ElasticSearchWrapper")
private val documentsIndex = settings.getProperty(KEYS.SettingsProps.elasticIndex)
private val documentsIndex = settings.getProperty(KEYS.SettingsProps.documentsIndex)
private val institutionIndex = settings.getProperty(KEYS.SettingsProps.institutionIndex)
private val klaxon = Klaxon()
......@@ -143,4 +147,38 @@ class ElasticSearchWrapper(
emptyList()
}
}
fun getInstitutionName(identifier: String): FacetContainer {
return try {
log.info("Attempting to retrieve institution record.")
val request = GetRequest(institutionIndex, identifier)
val response = client.get(request, RequestOptions.DEFAULT)
if (response.isExists) {
log.info("Successfully retrieved institution name.")
FacetContainer(
LanguageContainer.fromMap(response.sourceAsMap.getValue("name")),
identifier,
emptyList()
)
} else {
log.error("Could not find institution $identifier in index $institutionIndex.")
FacetContainer(
LanguageContainer.EMPTY,
identifier,
emptyList()
)
}
} catch (ex: ElasticsearchException) {
log.error(ex.detailedMessage)
FacetContainer(
LanguageContainer.EMPTY,
identifier,
emptyList()
)
}
}
}
\ No newline at end of file
......@@ -19,6 +19,10 @@
package org.memobase.helpers
object KEYS {
object LanguageType {
const val metadata = "metadata"
}
object SettingsProps {
const val accessTermLabelsPath = "accessTermLabelsPath"
const val reuseStatementLabelsPath = "reuseStatementLabelsPath"
......@@ -27,8 +31,13 @@ object KEYS {
const val institutionTypeLabelsPath = "institutionTypeLabelsPath"
const val elasticHost = "elastic.host"
const val elasticPort = "elastic.port"
const val elasticIndex = "elastic.index"
const val documentsIndex = "elastic.documentsIndex"
const val institutionIndex = "elastic.institutionIndex"
}
const val isAssociatedWithDate = "isAssociatedWithDate"
const val normalizedDateValue = "normalizedDateValue"
const val sameAs = "sameAs"
const val entityId = "@id"
const val atType = "@type"
......
......@@ -45,6 +45,20 @@ data class LanguageContainer(
fun placeholder(placeholder: String): LanguageContainer {
return LanguageContainer(listOf(placeholder), listOf(placeholder), listOf(placeholder), listOf(placeholder))
}
fun fromMap(map: Any): LanguageContainer {
return when (map) {
is Map<*, *> -> {
LanguageContainer(
map["de"] as List<String>,
map["fr"] as List<String>,
map["it"] as List<String>,
map["un"] as List<String>
)
}
else -> EMPTY
}
}
}
fun toList(): List<String> {
......
......@@ -31,7 +31,7 @@ data class RecordSetSearchDoc(
// Facets
val documentType: List<FacetContainer>,
val supportedByMemoriav: Boolean,
val languageOfMetadata: FacetContainer,
val languageOfMetadata: List<FacetContainer>,
val institution: FacetContainer,
val periodOfTimeAsYear: IntegerRange,
......@@ -57,11 +57,11 @@ data class RecordSetSearchDoc(
facet = emptyList()
)),
supportedByMemoriav = true,
languageOfMetadata = FacetContainer(
languageOfMetadata = listOf(FacetContainer(
LanguageContainer.placeholder("TEST LANGUAGE"),
filter = "",
facet = emptyList()
),
)),
institution = FacetContainer(
LanguageContainer.placeholder("TEST INSTITUTION"),
filter = "INSTITUTION_IDENTIFIER",
......
......@@ -2,7 +2,8 @@ app:
elastic:
host: ${ELASTIC_HOST:?system}
port: ${ELASTIC_PORT:?system}
index: ${ELASTIC_INDEX:?system}
documentsIndex: ${DOCUMENTS_INDEX:?system}
institutionIndex: ${INSTITUTION_INDEX:?system}
media:
url: ${MEDIA_SERVER_URL:?system}
institutionTypeLabelsPath: "/configs/institution_types/labels.csv"
......
......@@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertAll
import org.memobase.helpers.ElasticSearchWrapper
import org.memobase.helpers.KEYS
import org.memobase.model.FacetContainer
import org.memobase.model.LanguageContainer
......@@ -28,7 +29,7 @@ class TestElasticSearchWrapper {
private val host = "localhost"
private val port = 8080
private val documentsIndex = "documents-v17"
private val institutionIndex = "institutions-v1"
private val client: RestHighLevelClient = connect()
......@@ -62,6 +63,35 @@ class TestElasticSearchWrapper {
}
}
@Test
@Disabled
fun `test get institution name`() {
val props = Properties()
props.setProperty(KEYS.SettingsProps.documentsIndex, documentsIndex)
props.setProperty(KEYS.SettingsProps.institutionIndex, institutionIndex)
val wrapper = ElasticSearchWrapper(props, client, TestUtilities.translationMappers)
val result = wrapper.getInstitutionName("aag")
assertAll("",
{
assertThat(result)
.isEqualTo(
FacetContainer(
LanguageContainer(
listOf("Staatsarchiv des Kantons Aargau"),
listOf("Archives de l’Etat Argovie"),
listOf("Archivio cantonale del Argovia"),
emptyList()
),
"aag",
emptyList()
)
)
}
)
}
/**
* This test can only be run locally. Create a tunnel to
* ssh -L 8080:mb-es1:8080 swissbib@mb-es1.memobase.unibas.ch
......@@ -71,7 +101,8 @@ class TestElasticSearchWrapper {
@Disabled
fun `test getDocumentTypesFromRecords`() {
val props = Properties()
props.setProperty("elastic.index", documentsIndex)
props.setProperty(KEYS.SettingsProps.documentsIndex, documentsIndex)
props.setProperty(KEYS.SettingsProps.institutionIndex, institutionIndex)
val wrapper = ElasticSearchWrapper(props, client, TestUtilities.translationMappers)
......@@ -83,7 +114,8 @@ class TestElasticSearchWrapper {
},
{
assertThat(results[0])
.isEqualTo(FacetContainer(
.isEqualTo(
FacetContainer(
LanguageContainer(
listOf("Fotografie"),
listOf("Photographie"),
......@@ -92,7 +124,8 @@ class TestElasticSearchWrapper {
),
"Foto",
emptyList()
))
)
)
}
)
}
......
......@@ -30,9 +30,6 @@ class TestInstitutionSearchDoc {
return File("$dataPath/$fileName").readText(Charset.defaultCharset())
}
private val dateRegex = Regex("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}")
@Test
@Disabled
fun `test institution search doc with production es client`() {
......@@ -110,8 +107,8 @@ class TestInstitutionSearchDoc {
val reportValue = reader.readValue(report.value(), Report::class.java)
val key = record.key()
val value = record.value().replace(dateRegex, "2020")
val resultValue = readFile("output.json").replace(dateRegex, "2020")
val value = record.value().replace(TestUtilities.dateRegex, "2020")
val resultValue = readFile("output.json").replace(TestUtilities.dateRegex, "2020")
assertAll("",
{
......
......@@ -32,8 +32,6 @@ class TestRecordSetSearchDoc {
return File("$dataPath/$fileName").readText(Charset.defaultCharset())
}
private val dateRegex = Regex("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}")
@Test
fun `test create default record set `() {
val searchDoc = RecordSetSearchDoc.DEFAULT
......@@ -48,21 +46,36 @@ class TestRecordSetSearchDoc {
val data = readFile("input1.json")
val wrapper = mockk<ElasticSearchWrapper>()
every { wrapper.countNumberOfDocuments("FSS-HM") } returns 102
every { wrapper.getDocumentTypesFromRecords("FSS-HM", "recordSet.facet") } returns listOf(FacetContainer(LanguageContainer(listOf("Fotographie"), listOf("Photographie"), listOf("Fotografia"), emptyList()), null, emptyList()))
every { wrapper.countNumberOfDocuments("sap-021") } returns 102
every { wrapper.getDocumentTypesFromRecords("sap-021", "recordSet.facet") } returns listOf(FacetContainer(LanguageContainer(listOf("Fotographie"), listOf("Photographie"), listOf("Fotografia"), emptyList()), null, emptyList()))
every { wrapper.getInstitutionName("sap") } returns FacetContainer(
LanguageContainer(
listOf("Staatsarchiv des Kantons Aargau"),
listOf("Archives de l’Etat Argovie"),
listOf("Archivio cantonale del Argovia"),
emptyList()
),
"sap",
emptyList()
)
val input = JSON.unpack(JSON.parse(data))
val searchDocBuilder = RecordSetSearchDocBuilder(wrapper)
val result = searchDocBuilder.transform("FSS-HM", input)
val result = searchDocBuilder.transform("https://memobase.ch/recordSet/sap-021", input)
result as RecordSetSearchDoc
val resultString = result.toJson().replace(TestUtilities.dateRegex, "2020")
val targetString = readFile("output1.json").replace(TestUtilities.dateRegex, "2020")
assertAll("",
{
assertThat(result.id).isEqualTo("FSS-HM")
assertThat(result.id).isEqualTo("sap-021")
},
{}
{
assertThat(resultString).isEqualTo(
targetString
)
}
)
}
......@@ -108,8 +121,8 @@ class TestRecordSetSearchDoc {
val reportValue = reader.readValue(report.value(), Report::class.java)
val key = record.key()
val value = record.value().replace(dateRegex, "2020")
val resultValue = readFile("output1.json").replace(dateRegex, "2020")
val value = record.value().replace(TestUtilities.dateRegex, "2020")
val resultValue = readFile("output1.json").replace(TestUtilities.dateRegex, "2020")
assertAll("",
{
......
......@@ -22,6 +22,8 @@ object TestUtilities {
private const val documentTypePath = "src/test/resources/configs/document-type-labels.csv"
private const val reuseStatementPath = "src/test/resources/configs/reuse-statement-labels.csv"
val dateRegex = Regex("(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{2,3})")
const val mediaUrl = "https://media.memobase.k8s.unibas.ch/memo/"
val translationMappers =
......
{"name":{"de":["TEST_RECORD_SET"],"fr":["TEST_RECORD_SET"],"it":["TEST_RECORD_SET"],"un":["TEST_RECORD_SET"]},"isPublished":false,"documentType":[{"name":{"de":["TEST DOCUMENT TYPE"],"fr":["TEST DOCUMENT TYPE"],"it":["TEST DOCUMENT TYPE"],"un":["TEST DOCUMENT TYPE"]}}],"supportedByMemoriav":true,"languageOfMetadata":{"name":{"de":["TEST LANGUAGE"],"fr":["TEST LANGUAGE"],"it":["TEST LANGUAGE"],"un":["TEST LANGUAGE"]}},"institution":{"name":{"de":["TEST INSTITUTION"],"fr":["TEST INSTITUTION"],"it":["TEST INSTITUTION"],"un":["TEST INSTITUTION"]},"filter":"INSTITUTION_IDENTIFIER"},"periodOfTimeAsYear":{"gte":2000,"lte":2020},"lastUpdatedDate":"2020-11-20T10:29:01.128","scopeAndContent":{"de":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],"fr":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],"it":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],"un":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."]},"keyVisualLink":"https://mb-wf1.memobase.unibas.ch/sites/default/files/styles/teaser/public/2020-10/1.jpg?itok=5ncVBnVQ","numberOfDocuments":100,"id":"NoRecordSetId"}
\ No newline at end of file
{"name":{"de":["TEST_RECORD_SET"],"fr":["TEST_RECORD_SET"],"it":["TEST_RECORD_SET"],"un":["TEST_RECORD_SET"]},"isPublished":false,"documentType":[{"name":{"de":["TEST DOCUMENT TYPE"],"fr":["TEST DOCUMENT TYPE"],"it":["TEST DOCUMENT TYPE"],"un":["TEST DOCUMENT TYPE"]}}],"supportedByMemoriav":true,"languageOfMetadata":[{"name":{"de":["TEST LANGUAGE"],"fr":["TEST LANGUAGE"],"it":["TEST LANGUAGE"],"un":["TEST LANGUAGE"]}}],"institution":{"name":{"de":["TEST INSTITUTION"],"fr":["TEST INSTITUTION"],"it":["TEST INSTITUTION"],"un":["TEST INSTITUTION"]},"filter":"INSTITUTION_IDENTIFIER"},"periodOfTimeAsYear":{"gte":2000,"lte":2020},"lastUpdatedDate":"2020-11-20T10:29:01.128","scopeAndContent":{"de":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],"fr":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],"it":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],"un":["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."]},"keyVisualLink":"https://mb-wf1.memobase.unibas.ch/sites/default/files/styles/teaser/public/2020-10/1.jpg?itok=5ncVBnVQ","numberOfDocuments":100,"id":"NoRecordSetId"}
\ No newline at end of file
{
"@graph": [
{
"@id": "https://memobase.ch/recordSet/FSS-HM",
"@type": "https://www.ica.org/standards/RiC/ontology#RecordSet",
"eventType": "Create",
"http://memobase.ch/internal/isPublished": true,
"P60451": "https://memobase.ch/institution/mrv",
"descriptiveNote": [
{
"@language": "fr",
"@value": "<p>Originale Fotografien und Fotomontagen von Herbert Matter (1907–1984), der mit seinen modernen Reportage- und Sachaufnahmen, kombiniert mit Typografie, die Schweizer Werbegrafik der Zwischenkriegszeit wesentlich prägte.</p>"
"@id": "_:b0",
"@type": "https://www.ica.org/standards/RiC/ontology#Identifier",
"identifier": "Tanzarchiv-36560",
"type": "oldMemobase"
},
{
"@language": "it",
"@value": "<p>Originale Fotografien und Fotomontagen von Herbert Matter (1907–1984), der mit seinen modernen Reportage- und Sachaufnahmen, kombiniert mit Typografie, die Schweizer Werbegrafik der Zwischenkriegszeit wesentlich prägte.</p>"
"@id": "_:b1",
"@type": "https://www.ica.org/standards/RiC/ontology#DateRange",
"normalizedDateValue": "1983/1985"
},
{
"@id": "_:b2",
"@type": "https://www.ica.org/standards/RiC/ontology#Title",
"title": [
{
"@language": "de",
"@value": "<p>Originale Fotografien und Fotomontagen von Herbert Matter (1907–1984), der mit seinen modernen Reportage- und Sachaufnahmen, kombiniert mit Typografie, die Schweizer Werbegrafik der Zwischenkriegszeit wesentlich prägte.</p>"
"@value": "Videobestand Marie-Jane Otth"
},
{
"@language": "fr",
"@value": "Videobestand Marie-Jane Otth"
},
{
"@language": "it",
"@value": "Videobestand Marie-Jane Otth"
}
],
"hasTitle": "https://memobase.ch/recordSet/FSS-HM#genid8569dfe0-6f6c-4a78-84f6-83923c27cd28",
"heldBy": "https://memobase.ch/institution/FSS",
"identifiedBy": "https://memobase.ch/recordSet/FSS-HM#genid5b358a96-bae3-4690-9e92-d42e878f8edb",
"title": [
"type": "main"
},
{
"@language": "it",
"@value": "Fotosammlung Herbert Matter"
"@id": "_:b3",
"@type": "https://www.ica.org/standards/RiC/ontology#Language",
"sameAs": "http://www.wikidata.org/entity/Q188",
"name": [
{
"@language": "de",
"@value": "Deutsch"
},
{
"@language": "fr",
"@value": "Fotosammlung Herbert Matter"
"@value": "Allemand"
},
{
"@language": "de",
"@value": "Fotosammlung Herbert Matter"
"@language": "it",
"@value": "Tedesco"
}
]
],
"type": "metadata"
},
{
"@id": "https://memobase.ch/recordSet/FSS-HM#genid5b358a96-bae3-4690-9e92-d42e878f8edb",
"@id": "_:b4",
"@type": "https://www.ica.org/standards/RiC/ontology#Identifier",
"identifier": "FSS-HM",
"identifier": "sap-021",
"type": "main"
},
{
"@id": "https://memobase.ch/recordSet/FSS-HM#genid8569dfe0-6f6c-4a78-84f6-83923c27cd28",
"@type": "https://www.ica.org/standards/RiC/ontology#Title",
"@id": "https://memobase.ch/recordSet/sap-021",
"@type": "https://www.ica.org/standards/RiC/ontology#RecordSet",