├── .npmrc ├── .husky ├── commit-msg └── pre-commit ├── .dockerignore ├── commitlint.config.ts ├── packages ├── catalog │ ├── src │ │ ├── @types │ │ │ └── rdf-validate-shacl │ │ │ │ └── index.d.ts │ │ ├── index.ts │ │ └── genre.ts │ ├── test │ │ ├── vitest.d.ts │ │ └── fixtures │ │ │ └── credentials │ │ │ └── credentials.jsonld │ ├── tsconfig.json │ ├── eslint.config.mjs │ ├── tsconfig.test.json │ ├── tsconfig.lib.json │ ├── vite.config.ts │ ├── catalog │ │ ├── queries │ │ │ ├── lookup │ │ │ │ ├── rtf-personen.rq │ │ │ │ ├── uitvoeringsmedium.rq │ │ │ │ ├── iconclass.rq │ │ │ │ ├── adamlink-straten.rq │ │ │ │ ├── brabantse-gebouwen.rq │ │ │ │ ├── wikidata.rq │ │ │ │ ├── geonames.rq │ │ │ │ ├── nmvw.rq │ │ │ │ ├── goudatijdmachine-straten.rq │ │ │ │ ├── muziekweb.rq │ │ │ │ ├── adamlink-adressen.rq │ │ │ │ ├── muziekschatten-klassiekewerken.rq │ │ │ │ ├── rijksmonumenten.rq │ │ │ │ ├── muziekschatten-personen.rq │ │ │ │ ├── eurovoc.rq │ │ │ │ ├── ied.rq │ │ │ │ ├── muziekschatten-onderwerpen.rq │ │ │ │ ├── gtaa.rq │ │ │ │ ├── canon-van-limburg.rq │ │ │ │ ├── poolparty.rq │ │ │ │ └── wo2thesaurus.rq │ │ │ └── search │ │ │ │ ├── rtf-personen.rq │ │ │ │ ├── brabantse-gebouwen.rq │ │ │ │ ├── mw-personengroepen.rq │ │ │ │ ├── iconclass.rq │ │ │ │ ├── adamlink-straten.rq │ │ │ │ ├── uitvoeringsmedium.rq │ │ │ │ ├── mw-genresstijlen.rq │ │ │ │ ├── nta.rq │ │ │ │ ├── wikidata-entities-all.rq │ │ │ │ ├── nmvw.rq │ │ │ │ ├── goudatijdmachine-straten.rq │ │ │ │ ├── geonames.rq │ │ │ │ ├── stcn-drukkers.rq │ │ │ │ ├── geonames-nl-be-de.rq │ │ │ │ ├── wikidata-entities-persons.rq │ │ │ │ ├── adamlink-adressen.rq │ │ │ │ ├── ied.rq │ │ │ │ ├── abr.rq │ │ │ │ ├── poolparty.rq │ │ │ │ ├── muziekschatten-klassiekewerken.rq │ │ │ │ ├── rijksmonumenten.rq │ │ │ │ ├── canon-van-limburg.rq │ │ │ │ ├── muziekschatten-onderwerpen.rq │ │ │ │ ├── wo2thesaurus.rq │ │ │ │ ├── brinkman.rq │ │ │ │ ├── cht.rq │ │ │ │ └── wikidata-entities-streets.rq │ │ └── datasets │ │ │ ├── wikidata-entities-persons.jsonld │ │ │ ├── wikidata-entities-streets.jsonld │ │ │ ├── wikidata-entities-places.jsonld │ │ │ ├── wikidata-entities-all.jsonld │ │ │ ├── adamlink-straten.jsonld │ │ │ ├── gtaa-genres.jsonld │ │ │ ├── adamlink-adressen.jsonld │ │ │ ├── stcn-drukkers.jsonld │ │ │ ├── muziekschatten-personen.jsonld │ │ │ ├── rkdartists.jsonld │ │ │ ├── gtaa-persoonsnamen.jsonld │ │ │ ├── muziekschatten-klassieke-werken.jsonld │ │ │ ├── wo2biografie.jsonld │ │ │ ├── iconclass.jsonld │ │ │ ├── gtaa-classificatie.jsonld │ │ │ ├── gtaa-geografische-namen.jsonld │ │ │ ├── homosaurus.jsonld │ │ │ ├── nta.jsonld │ │ │ ├── rijksmonumenten.jsonld │ │ │ ├── rtf-personen.jsonld │ │ │ ├── brabantse-gebouwen.jsonld │ │ │ ├── vrouwenthesaurus.jsonld │ │ │ ├── geonames.jsonld │ │ │ ├── gtaa-onderwerpen.jsonld │ │ │ ├── muziekschatten-onderwerpen.jsonld │ │ │ ├── mw-personengroepen.jsonld │ │ │ ├── gtaa-namen.jsonld │ │ │ ├── mw-genresstijlen.jsonld │ │ │ ├── uitvoeringsmedium.jsonld │ │ │ ├── geonames-nl-be-de.jsonld │ │ │ ├── goudatijdmachine-straten.jsonld │ │ │ └── brinkman.jsonld │ └── package.json ├── query │ ├── README.md │ ├── tsconfig.json │ ├── src │ │ ├── index.ts │ │ ├── config.ts │ │ ├── helpers │ │ │ ├── logger.ts │ │ │ └── logger-pino.ts │ │ ├── literal.ts │ │ ├── instrumentation.ts │ │ └── search │ │ │ └── query-mode.ts │ ├── tsconfig.lib.json │ ├── eslint.config.mjs │ ├── tsconfig.test.json │ ├── vite.config.ts │ ├── package.json │ └── test │ │ ├── fixtures │ │ └── terms.ttl │ │ ├── query.test.ts │ │ └── search │ │ └── query-mode.test.ts ├── cli │ ├── bin │ │ └── run.js │ ├── tsconfig.json │ ├── eslint.config.mjs │ ├── tsconfig.app.json │ ├── package.json │ └── src │ │ └── list.ts ├── graphql │ ├── tsconfig.json │ ├── Dockerfile │ ├── src │ │ ├── main.ts │ │ ├── config.ts │ │ └── server.ts │ ├── tsconfig.test.json │ └── tsconfig.app.json └── reconciliation │ ├── locales │ ├── en.json │ └── nl.json │ ├── src │ ├── config.ts │ ├── main.ts │ ├── score.ts │ ├── manifest.ts │ └── data-extension.ts │ ├── Dockerfile │ ├── tsconfig.json │ ├── eslint.config.mjs │ ├── test │ └── score.test.ts │ ├── tsconfig.app.json │ ├── tsconfig.test.json │ ├── vite.config.ts │ ├── json-schema │ └── data-extension-query.json │ └── README.md ├── vitest.workspace.ts ├── .prettierignore ├── docker-compose.yml ├── .prettierrc ├── tsconfig.json ├── docs ├── tests.md └── docker.md ├── CONTRIBUTING.md ├── .github ├── workflows │ ├── qa.yml │ └── dependabot-auto-merge.yml └── dependabot.yml ├── tsconfig.base.json ├── .gitignore ├── eslint.config.mjs └── package.json /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | npx --no -- commitlint --edit $1 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .github 2 | **/node_modules/ 3 | **/build/ 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx nx affected -t lint --fix 2 | npx nx format:write 3 | -------------------------------------------------------------------------------- /commitlint.config.ts: -------------------------------------------------------------------------------- 1 | export default { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /packages/catalog/src/@types/rdf-validate-shacl/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'rdf-validate-shacl'; 2 | -------------------------------------------------------------------------------- /packages/catalog/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getCatalog.js'; 2 | export * from './genre.js'; 3 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | '**/vite.config.{mjs,js,ts,mts}', 3 | '**/vitest.config.{mjs,js,ts,mts}', 4 | ]; 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | /dist 3 | /coverage 4 | /.nx/cache 5 | /.nx/workspace-data 6 | LICENSE.md 7 | -------------------------------------------------------------------------------- /packages/catalog/test/vitest.d.ts: -------------------------------------------------------------------------------- 1 | import 'vitest'; 2 | 3 | declare module 'vitest' { 4 | interface Matchers { 5 | toConform(): unknown; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/query/README.md: -------------------------------------------------------------------------------- 1 | # Network of Terms Query Engine 2 | 3 | This package exposes query and lookup functionality to the [Network of Terms](../../README.md) API packages. 4 | -------------------------------------------------------------------------------- /packages/cli/bin/run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --experimental-specifier-resolution=node 2 | import { run, flush, Errors } from '@oclif/core'; 3 | 4 | run(void 0, import.meta.url) 5 | .then(flush) 6 | .catch(Errors.handle); 7 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | services: 3 | node: 4 | image: node:lts 5 | user: node 6 | volumes: 7 | - .:/app 8 | ports: 9 | - '3123:3123' 10 | working_dir: /app 11 | command: bash 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "plugins": ["prettier-plugin-packagejson"], 4 | "overrides": [ 5 | { 6 | "files": "*.jsonld", 7 | "options": { 8 | "parser": "json" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "../catalog" 8 | }, 9 | { 10 | "path": "../query" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/graphql/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "../query" 8 | }, 9 | { 10 | "path": "../catalog" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.test.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/reconciliation/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "altLabels": "Alternative labels", 3 | "broader": "Broader terms", 4 | "narrower": "Narrower terms", 5 | "related": "Related terms", 6 | "notFound": "Not found", 7 | "view": "View at Network of Terms", 8 | "source": "Terminology source" 9 | } 10 | -------------------------------------------------------------------------------- /packages/reconciliation/locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "altLabels": "Alternatieve labels", 3 | "broader": "Bredere termen", 4 | "narrower": "Nauwere termen", 5 | "related": "Gerelateerde termen", 6 | "notFound": "Niet gevonden", 7 | "view": "Bekijk in Termennetwerk", 8 | "source": "Terminologiebron" 9 | } 10 | -------------------------------------------------------------------------------- /packages/reconciliation/src/config.ts: -------------------------------------------------------------------------------- 1 | import { envSchema } from 'env-schema'; 2 | 3 | const schema = { 4 | type: 'object', 5 | properties: { 6 | TRUST_PROXY: { 7 | type: 'boolean', 8 | default: false, 9 | }, 10 | }, 11 | }; 12 | 13 | export const config = envSchema({ 14 | schema, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/graphql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | LABEL org.opencontainers.image.source="https://github.com/netwerk-digitaal-erfgoed/network-of-terms" 3 | 4 | WORKDIR /app 5 | 6 | COPY dist/package*.json ./ 7 | RUN npm ci --omit=dev 8 | 9 | COPY dist/ ./ 10 | 11 | USER node 12 | CMD ["node", "main.js"] 13 | EXPOSE 3000 14 | -------------------------------------------------------------------------------- /packages/catalog/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "../query" 8 | }, 9 | { 10 | "path": "./tsconfig.lib.json" 11 | }, 12 | { 13 | "path": "./tsconfig.test.json" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/reconciliation/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | LABEL org.opencontainers.image.source="https://github.com/netwerk-digitaal-erfgoed/dataset-register" 3 | 4 | WORKDIR /app 5 | 6 | COPY dist/package*.json ./ 7 | RUN npm ci --omit=dev 8 | 9 | COPY dist/ ./ 10 | 11 | USER node 12 | CMD ["node", "main.js"] 13 | EXPOSE 3000 14 | -------------------------------------------------------------------------------- /packages/reconciliation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "../query" 8 | }, 9 | { 10 | "path": "../catalog" 11 | }, 12 | { 13 | "path": "./tsconfig.app.json" 14 | }, 15 | { 16 | "path": "./tsconfig.test.json" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "files": [], 4 | "references": [ 5 | { 6 | "path": "./packages/catalog" 7 | }, 8 | { 9 | "path": "./packages/query" 10 | }, 11 | { 12 | "path": "./packages/graphql" 13 | }, 14 | { 15 | "path": "./packages/reconciliation" 16 | }, 17 | { 18 | "path": "./packages/cli" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/query/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './query.js'; 2 | export * from './catalog.js'; 3 | export * from './literal.js'; 4 | export * from './lookup/lookup.js'; 5 | export * from './terms.js'; 6 | export * from './search/query-mode.js'; 7 | export * from './distributions.js'; 8 | export * from './helpers/logger.js'; 9 | 10 | import { QueryEngine } from '@comunica/query-sparql'; 11 | 12 | export const comunica = () => new QueryEngine(); 13 | -------------------------------------------------------------------------------- /packages/graphql/src/main.ts: -------------------------------------------------------------------------------- 1 | import { getCatalog } from '@netwerk-digitaal-erfgoed/network-of-terms-catalog'; 2 | import { server } from './server.js'; 3 | import { config } from './config.js'; 4 | 5 | try { 6 | const httpServer = await server( 7 | await getCatalog((config.CATALOG_PATH as string) || undefined), 8 | config, 9 | ); 10 | await httpServer.listen({ port: 3123, host: '0.0.0.0' }); 11 | } catch (err) { 12 | console.error(err); 13 | } 14 | -------------------------------------------------------------------------------- /docs/tests.md: -------------------------------------------------------------------------------- 1 | # Running the tests 2 | 3 | For simplicity and because we have integration tests (versus separate unit tests for each package) we want to combine 4 | code coverage for all tests. To run the tests, first clone the repository and install its dependencies: 5 | 6 | git clone https://github.com/netwerk-digitaal-erfgoed/network-of-terms.git 7 | cd network-of-terms 8 | npm install 9 | 10 | Then run the tests from the repository’s root directory: 11 | 12 | npm run test 13 | -------------------------------------------------------------------------------- /packages/graphql/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/vitest", 5 | "types": [ 6 | "vitest/globals", 7 | "vitest/importMeta", 8 | "vite/client", 9 | "node", 10 | "vitest" 11 | ] 12 | }, 13 | "include": [ 14 | "vite.config.ts", 15 | "vite.config.mts", 16 | "vitest.config.ts", 17 | "vitest.config.mts", 18 | "test/**/*.test.ts", 19 | "src/**/*.d.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/query/src/config.ts: -------------------------------------------------------------------------------- 1 | import { envSchema, JSONSchemaType } from 'env-schema'; 2 | 3 | const schema = { 4 | type: 'object', 5 | properties: { 6 | MAX_QUERY_TIMEOUT: { 7 | type: 'number', 8 | default: 60000, 9 | }, 10 | DEFAULT_QUERY_TIMEOUT: { 11 | type: 'number', 12 | default: 5000, 13 | }, 14 | }, 15 | }; 16 | 17 | interface Env { 18 | MAX_QUERY_TIMEOUT: number; 19 | DEFAULT_QUERY_TIMEOUT: number; 20 | } 21 | 22 | export const config: JSONSchemaType = envSchema({ 23 | schema, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/graphql/src/config.ts: -------------------------------------------------------------------------------- 1 | import { envSchema } from 'env-schema'; 2 | import path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | 5 | const schema = { 6 | type: 'object', 7 | properties: { 8 | TRUST_PROXY: { 9 | type: 'boolean', 10 | default: false, 11 | }, 12 | CATALOG_PATH: { 13 | type: 'string', 14 | default: path.join( 15 | path.dirname(fileURLToPath(import.meta.url)), 16 | 'catalog', 17 | ), 18 | }, 19 | }, 20 | }; 21 | 22 | export const config = envSchema({ 23 | schema, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/cli/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs'; 2 | 3 | export default [ 4 | ...baseConfig, 5 | { 6 | files: ['**/*.json'], 7 | rules: { 8 | '@nx/dependency-checks': [ 9 | 'error', 10 | { 11 | ignoredFiles: [ 12 | '{projectRoot}/eslint.config.{js,cjs,mjs}', 13 | '{projectRoot}/vite.config.{js,ts,mjs,mts}', 14 | ], 15 | }, 16 | ], 17 | }, 18 | languageOptions: { 19 | parser: await import('jsonc-eslint-parser'), 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/query/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": "src", 6 | "outDir": "dist", 7 | "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", 8 | "emitDeclarationOnly": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "types": ["node"] 11 | }, 12 | "include": ["src/**/*.ts"], 13 | "exclude": [ 14 | "vite.config.ts", 15 | "vite.config.mts", 16 | "vitest.config.ts", 17 | "vitest.config.mts", 18 | "test/**/*.test.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/catalog/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs'; 2 | 3 | export default [ 4 | ...baseConfig, 5 | { 6 | files: ['**/*.json'], 7 | rules: { 8 | '@nx/dependency-checks': [ 9 | 'error', 10 | { 11 | ignoredFiles: [ 12 | '{projectRoot}/eslint.config.{js,cjs,mjs}', 13 | '{projectRoot}/vite.config.{js,ts,mjs,mts}', 14 | ], 15 | }, 16 | ], 17 | }, 18 | languageOptions: { 19 | parser: await import('jsonc-eslint-parser'), 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/query/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs'; 2 | 3 | export default [ 4 | ...baseConfig, 5 | { 6 | files: ['**/*.json'], 7 | rules: { 8 | '@nx/dependency-checks': [ 9 | 'error', 10 | { 11 | ignoredFiles: [ 12 | '{projectRoot}/eslint.config.{js,cjs,mjs}', 13 | '{projectRoot}/vite.config.{js,ts,mjs,mts}', 14 | ], 15 | }, 16 | ], 17 | }, 18 | languageOptions: { 19 | parser: await import('jsonc-eslint-parser'), 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/reconciliation/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs'; 2 | 3 | export default [ 4 | ...baseConfig, 5 | { 6 | files: ['**/*.json'], 7 | rules: { 8 | '@nx/dependency-checks': [ 9 | 'error', 10 | { 11 | ignoredFiles: [ 12 | '{projectRoot}/eslint.config.{js,cjs,mjs}', 13 | '{projectRoot}/vite.config.{js,ts,mjs,mts}', 14 | ], 15 | }, 16 | ], 17 | }, 18 | languageOptions: { 19 | parser: await import('jsonc-eslint-parser'), 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/reconciliation/test/score.test.ts: -------------------------------------------------------------------------------- 1 | import { calculateMatchingScore } from '../src/score.js'; 2 | 3 | describe('Score', () => { 4 | it('ignores punctuation', () => { 5 | expect( 6 | calculateMatchingScore('pieter de hooch', ['Hooch, Pieter de']), 7 | ).toEqual(91.67); 8 | }); 9 | 10 | it('notices small differences', () => { 11 | expect(calculateMatchingScore('zagen', ['inzagen'])).toEqual(80); 12 | }); 13 | 14 | it('matches identical strings', () => { 15 | expect(calculateMatchingScore('schilderen', ['schilderen'])).toEqual(100); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "types": ["node"], 6 | "rootDir": "src", 7 | "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo" 8 | }, 9 | "include": ["src/**/*.ts"], 10 | "exclude": [ 11 | "eslint.config.js", 12 | "eslint.config.cjs", 13 | "eslint.config.mjs", 14 | "test/**/*" 15 | ], 16 | "references": [ 17 | { 18 | "path": "../catalog/tsconfig.lib.json" 19 | }, 20 | { 21 | "path": "../query/tsconfig.lib.json" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the Network of Terms 2 | 3 | First off, thank you for taking the time to contribute! 4 | 5 | ## Committing changes 6 | 7 | This repository follows [Semantic Versioning](https://semver.org). Tags and [releases](/releases) are 8 | [created automatically](.github/workflows/release.yml) based on commit messages. So please make sure to follow 9 | the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/#summary) when committing 10 | changes. 11 | 12 | ## Adding a terminology source 13 | 14 | Please see the [catalog package](packages/catalog/). 15 | -------------------------------------------------------------------------------- /packages/reconciliation/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "types": ["node"], 6 | "rootDir": "src", 7 | "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo" 8 | }, 9 | "include": ["./src/**/*", "./src/**/*.json"], 10 | "exclude": [ 11 | "eslint.config.js", 12 | "eslint.config.cjs", 13 | "eslint.config.mjs", 14 | "test/**/*" 15 | ], 16 | "references": [ 17 | { 18 | "path": "../query/tsconfig.lib.json" 19 | }, 20 | { 21 | "path": "../catalog/tsconfig.lib.json" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | name: QA 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | permissions: 10 | actions: read 11 | contents: read 12 | 13 | jobs: 14 | main: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v5 18 | with: 19 | filter: tree:0 20 | fetch-depth: 0 21 | 22 | - uses: actions/setup-node@v6 23 | with: 24 | node-version: lts/* 25 | cache: 'npm' 26 | 27 | - run: npm ci 28 | - uses: nrwl/nx-set-shas@v4 29 | 30 | - run: npx nx affected -t lint test typecheck build 31 | -------------------------------------------------------------------------------- /packages/graphql/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "types": ["node"], 6 | "rootDir": "src", 7 | "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", 8 | "emitDeclarationOnly": false 9 | }, 10 | "include": ["src/**/*.ts"], 11 | "exclude": [ 12 | "eslint.config.js", 13 | "eslint.config.cjs", 14 | "eslint.config.mjs", 15 | "test/**/*" 16 | ], 17 | "references": [ 18 | { 19 | "path": "../query/tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "../catalog/tsconfig.lib.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/query/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/vitest", 5 | "types": [ 6 | "vitest/globals", 7 | "vitest/importMeta", 8 | "vite/client", 9 | "node", 10 | "vitest" 11 | ], 12 | "forceConsistentCasingInFileNames": true 13 | }, 14 | "include": [ 15 | "vite.config.ts", 16 | "vite.config.mts", 17 | "vitest.config.ts", 18 | "vitest.config.mts", 19 | "test/**/*.ts", 20 | "src/**/*.d.ts" 21 | ], 22 | "references": [ 23 | { 24 | "path": "./tsconfig.lib.json" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/catalog/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/vitest", 5 | "types": [ 6 | "vitest/globals", 7 | "vitest/importMeta", 8 | "vite/client", 9 | "node", 10 | "vitest" 11 | ], 12 | "forceConsistentCasingInFileNames": true 13 | }, 14 | "include": [ 15 | "vite.config.ts", 16 | "vite.config.mts", 17 | "vitest.config.ts", 18 | "vitest.config.mts", 19 | "test/**/*.ts", 20 | "src/**/*.d.ts" 21 | ], 22 | "references": [ 23 | { 24 | "path": "./tsconfig.lib.json" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/reconciliation/src/main.ts: -------------------------------------------------------------------------------- 1 | import { server } from './server.js'; 2 | import { getCatalog } from '@netwerk-digitaal-erfgoed/network-of-terms-catalog'; 3 | import { config } from './config.js'; 4 | import { fileURLToPath } from 'url'; 5 | import path from 'path'; 6 | 7 | try { 8 | const catalogPath = path.join( 9 | path.dirname(fileURLToPath(import.meta.url)), 10 | 'catalog', 11 | ); 12 | const c = await getCatalog(catalogPath); 13 | console.log('catalog', c); 14 | const httpServer = await server(c, config); 15 | await httpServer.listen({ port: 3123, host: '0.0.0.0' }); 16 | } catch (err) { 17 | console.error(err); 18 | } 19 | -------------------------------------------------------------------------------- /packages/reconciliation/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/vitest", 5 | "types": [ 6 | "vitest/globals", 7 | "vitest/importMeta", 8 | "vite/client", 9 | "node", 10 | "vitest" 11 | ], 12 | "forceConsistentCasingInFileNames": true 13 | }, 14 | "include": [ 15 | "vite.config.ts", 16 | "vite.config.mts", 17 | "vitest.config.ts", 18 | "vitest.config.mts", 19 | "test/**/*.test.ts", 20 | "src/**/*.d.ts" 21 | ], 22 | "references": [ 23 | { 24 | "path": "./tsconfig.app.json" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/catalog/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": "src", 6 | "outDir": "dist", 7 | "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", 8 | "emitDeclarationOnly": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "types": ["node"] 11 | }, 12 | "include": ["src/**/*.ts"], 13 | "exclude": [ 14 | "vite.config.ts", 15 | "vite.config.mts", 16 | "vitest.config.ts", 17 | "vitest.config.mts", 18 | "test/" 19 | ], 20 | "references": [ 21 | { 22 | "path": "../query/tsconfig.lib.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: github.actor == 'dependabot[bot]' 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v2.4.0 16 | with: 17 | github-token: '${{ secrets.GITHUB_TOKEN }}' 18 | - name: Enable auto-merge for Dependabot PRs 19 | run: gh pr merge --auto --squash "$PR_URL" 20 | env: 21 | PR_URL: ${{github.event.pull_request.html_url}} 22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 23 | -------------------------------------------------------------------------------- /packages/query/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig(() => ({ 4 | root: __dirname, 5 | cacheDir: '../../node_modules/.vite/packages/query', 6 | plugins: [], 7 | test: { 8 | watch: false, 9 | globals: true, 10 | environment: 'node', 11 | include: ['test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], 12 | reporters: ['default'], 13 | coverage: { 14 | enabled: true, 15 | reporter: ['text'], 16 | provider: 'v8' as const, 17 | thresholds: { 18 | autoUpdate: true, 19 | lines: 58.24, 20 | functions: 40, 21 | branches: 96.36, 22 | statements: 58.24, 23 | }, 24 | }, 25 | }, 26 | })); 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'monthly' 7 | 8 | - package-ecosystem: 'npm' 9 | directory: '/' 10 | schedule: 11 | interval: 'monthly' 12 | ignore: 13 | - dependency-name: 'jsonld-context-parser' # Must match the version that Comunica depends on: https://github.com/comunica/comunica/blob/master/packages/actor-rdf-parse-jsonld/package.json#L48 14 | groups: 15 | comunica: 16 | patterns: 17 | - '@comunica/*' 18 | nx: 19 | patterns: 20 | - '@nx/*' 21 | - 'nx' 22 | opentelemetry: 23 | patterns: 24 | - '@opentelemetry/*' 25 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "declarationMap": true, 5 | "emitDeclarationOnly": true, 6 | "importHelpers": true, 7 | "isolatedModules": true, 8 | "lib": ["es2022"], 9 | "module": "nodenext", 10 | "moduleResolution": "nodenext", 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "noEmitOnError": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitOverride": true, 17 | "noImplicitReturns": true, 18 | "noUnusedLocals": true, 19 | "skipLibCheck": true, 20 | "strict": true, 21 | "target": "es2022", 22 | "customConditions": ["development"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/catalog/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig(() => ({ 4 | root: __dirname, 5 | cacheDir: '../../node_modules/.vite/packages/catalog', 6 | plugins: [], 7 | test: { 8 | watch: false, 9 | globals: true, 10 | environment: 'node', 11 | include: ['test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], 12 | reporters: ['default'], 13 | testTimeout: 10_000, 14 | coverage: { 15 | enabled: true, 16 | reporter: ['text'], 17 | provider: 'v8' as const, 18 | thresholds: { 19 | autoUpdate: true, 20 | lines: 90.57, 21 | functions: 85.71, 22 | branches: 100, 23 | statements: 90.57, 24 | }, 25 | }, 26 | }, 27 | })); 28 | -------------------------------------------------------------------------------- /packages/reconciliation/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig(() => ({ 4 | root: __dirname, 5 | cacheDir: '../../node_modules/.vite/packages/catalog', 6 | plugins: [], 7 | test: { 8 | watch: false, 9 | globals: true, 10 | environment: 'node', 11 | include: ['test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], 12 | reporters: ['default'], 13 | testTimeout: 10_000, 14 | coverage: { 15 | enabled: true, 16 | reporter: ['text'], 17 | provider: 'v8' as const, 18 | thresholds: { 19 | autoUpdate: true, 20 | lines: 95.47, 21 | functions: 92.85, 22 | branches: 96.42, 23 | statements: 95.47, 24 | }, 25 | }, 26 | }, 27 | })); 28 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/rtf-personen.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX schema: 3 | PREFIX skos: 4 | 5 | CONSTRUCT { 6 | ?uri a skos:Concept ; 7 | skos:prefLabel ?prefLabel ; 8 | skos:altLabel ?altLabel ; 9 | skos:scopeNote ?scopeNote ; 10 | skos:exactMatch ?exactMatch_uri . 11 | } 12 | WHERE { 13 | GRAPH { 14 | SELECT * WHERE { 15 | VALUES ?uri { ?uris } 16 | 17 | ?uri a skos:Concept ; 18 | skos:prefLabel ?prefLabel ; 19 | skos:altLabel ?altLabel . 20 | 21 | OPTIONAL { ?uri skos:scopeNote ?scopeNote } 22 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@netwerk-digitaal-erfgoed/network-of-terms-cli", 3 | "private": true, 4 | "description": "Query the Network of Terms from the command line", 5 | "keywords": [], 6 | "homepage": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms/tree/master/packages/network-of-terms-cli#readme", 7 | "bugs": { 8 | "url": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/netwerk-digitaal-erfgoed/network-of-terms.git" 13 | }, 14 | "license": "EUPL-1.2", 15 | "author": "", 16 | "type": "module", 17 | "bin": { 18 | "network-of-terms-cli": "./bin/run" 19 | }, 20 | "oclif": { 21 | "commands": "./build" 22 | }, 23 | "dependencies": { 24 | "@oclif/core": "^4.2.0", 25 | "cli-ux": "^6.0.9" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # compiled output 4 | dist 5 | tmp 6 | out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | .nx/cache 42 | .nx/workspace-data 43 | .cursor/rules/nx-rules.mdc 44 | .github/instructions/nx.instructions.md 45 | 46 | vite.config.*.timestamp* 47 | vitest.config.*.timestamp* 48 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/rtf-personen.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX schema: 3 | PREFIX skos: 4 | 5 | CONSTRUCT { 6 | ?uri a skos:Concept ; 7 | skos:prefLabel ?prefLabel ; 8 | skos:altLabel ?altLabel ; 9 | skos:scopeNote ?scopeNote ; 10 | skos:exactMatch ?exactMatch_uri . 11 | } 12 | WHERE { 13 | GRAPH { 14 | SELECT * WHERE { 15 | ?uri a skos:Concept ; 16 | skos:prefLabel ?prefLabel ; 17 | skos:altLabel ?altLabel . 18 | 19 | OPTIONAL { ?uri skos:scopeNote ?scopeNote } 20 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } 21 | 22 | FILTER(CONTAINS(LCASE(?prefLabel), LCASE(?query)) || CONTAINS(LCASE(?altLabel), LCASE(?query))) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/uitvoeringsmedium.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | PREFIX som: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept; 6 | skos:prefLabel ?prefLabel ; 7 | skos:altLabel ?altLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:exactMatch ?exactMatch_uri . 11 | ?broader_uri skos:prefLabel ?broader_prefLabel . 12 | } 13 | WHERE { 14 | VALUES ?uri { ?uris } 15 | 16 | ?uri skos:prefLabel ?prefLabel . 17 | 18 | OPTIONAL { 19 | ?uri skos:scopeNote ?scopeNote 20 | } 21 | 22 | OPTIONAL { 23 | ?uri skos:altLabel ?altLabel . 24 | } 25 | 26 | OPTIONAL { 27 | ?uri skos:broader ?broader_uri . 28 | ?broader_uri skos:prefLabel ?broader_prefLabel . 29 | } 30 | 31 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } # Has no labels. 32 | } 33 | LIMIT 1000 34 | -------------------------------------------------------------------------------- /packages/reconciliation/json-schema/data-extension-query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema#", 3 | "$id": "https://reconciliation-api.github.io/specs/draft/schemas/data-extension-query.json", 4 | "type": "object", 5 | "description": "This schema validates a data extension query", 6 | "properties": { 7 | "ids": { 8 | "type": "array", 9 | "description": "The list of entity identifiers to fetch property values from", 10 | "items": { 11 | "type": "string" 12 | } 13 | }, 14 | "properties": { 15 | "type": "array", 16 | "description": "The list of properties to fetch, with their optional configuration", 17 | "items": { 18 | "type": "object", 19 | "properties": { 20 | "id": { 21 | "type": "string" 22 | }, 23 | "settings": { 24 | "type": "object" 25 | } 26 | }, 27 | "required": ["id"] 28 | } 29 | } 30 | }, 31 | "required": ["ids", "properties"] 32 | } 33 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/iconclass.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:broader ?broader_uri ; 7 | skos:narrower ?narrower_uri ; 8 | skos:related ?related_uri . 9 | 10 | ?broader_uri skos:prefLabel ?broader_prefLabel . 11 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 12 | ?related_uri skos:prefLabel ?related_prefLabel . 13 | } 14 | WHERE { 15 | ?uri a skos:Concept ; 16 | skos:prefLabel ?prefLabel . 17 | 18 | VALUES ?uri { ?uris } 19 | 20 | OPTIONAL { 21 | ?uri skos:broader ?broader_uri . 22 | ?broader_uri skos:prefLabel ?broader_prefLabel . 23 | } 24 | OPTIONAL { 25 | ?uri skos:narrower ?narrower_uri . 26 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 27 | } 28 | OPTIONAL { 29 | ?uri skos:related ?related_uri . 30 | ?related_uri skos:prefLabel ?related_prefLabel . 31 | } 32 | } 33 | LIMIT 1000 34 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/brabantse-gebouwen.rq: -------------------------------------------------------------------------------- 1 | PREFIX schema: 2 | PREFIX skos: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept ; 6 | skos:prefLabel ?schema_name ; 7 | skos:altLabel ?schema_alternateName ; 8 | skos:hiddenLabel ?hiddenLabel; 9 | skos:scopeNote ?schema_description, ?scopeNote . 10 | } 11 | WHERE { 12 | { 13 | SELECT * WHERE { 14 | ?uri a schema:LandmarksOrHistoricalBuildings ; 15 | ?predicate ?label . 16 | VALUES ?predicate { schema:name schema:alternateName skos:hiddenLabel skos:scopeNote} 17 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 18 | } 19 | #LIMIT# 20 | } 21 | 22 | OPTIONAL { ?uri schema:name ?schema_name } 23 | OPTIONAL { ?uri skos:hiddenLabel ?hiddenLabel } 24 | OPTIONAL { ?uri skos:scopeNote ?scopeNote} 25 | OPTIONAL { ?uri schema:alternateName ?schema_alternateName } 26 | OPTIONAL { ?uri schema:description ?schema_description } 27 | } 28 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import nx from '@nx/eslint-plugin'; 2 | 3 | export default [ 4 | ...nx.configs['flat/base'], 5 | ...nx.configs['flat/typescript'], 6 | ...nx.configs['flat/javascript'], 7 | { 8 | ignores: [ 9 | '**/dist', 10 | '**/vite.config.*.timestamp*', 11 | '**/vitest.config.*.timestamp*', 12 | ], 13 | }, 14 | { 15 | files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], 16 | rules: { 17 | '@nx/enforce-module-boundaries': [ 18 | 'error', 19 | { 20 | enforceBuildableLibDependency: true, 21 | allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], 22 | depConstraints: [ 23 | { 24 | sourceTag: '*', 25 | onlyDependOnLibsWithTags: ['*'], 26 | }, 27 | ], 28 | }, 29 | ], 30 | }, 31 | }, 32 | { 33 | files: [ 34 | '**/*.ts', 35 | '**/*.tsx', 36 | '**/*.cts', 37 | '**/*.mts', 38 | '**/*.js', 39 | '**/*.jsx', 40 | '**/*.cjs', 41 | '**/*.mjs', 42 | ], 43 | // Override or add rules here 44 | rules: {}, 45 | }, 46 | ]; 47 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/mw-personengroepen.rq: -------------------------------------------------------------------------------- 1 | PREFIX owl: 2 | PREFIX rdfs: 3 | PREFIX schema: 4 | PREFIX skos: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?prefLabel ; 9 | skos:altLabel ?altLabel ; 10 | skos:hiddenLabel ?hiddenLabel ; 11 | skos:scopeNote ?schema_description ; 12 | skos:exactMatch ?exactMatch_uri . 13 | } 14 | WHERE { 15 | { 16 | SELECT DISTINCT ?uri WHERE { 17 | ?uri a schema:MusicGroup ; 18 | ?predicate ?label . 19 | VALUES ?predicate { skos:prefLabel skos:altLabel } 20 | ?label ?virtuosoQuery . 21 | } 22 | #LIMIT# 23 | } 24 | 25 | OPTIONAL { ?uri skos:prefLabel ?prefLabel } 26 | OPTIONAL { ?uri skos:altLabel ?altLabel } 27 | OPTIONAL { ?uri skos:hiddenLabel ?hiddenLabel } 28 | OPTIONAL { ?uri schema:description ?schema_description } 29 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 30 | } 31 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/adamlink-straten.rq: -------------------------------------------------------------------------------- 1 | PREFIX hg: 2 | PREFIX skos: 3 | PREFIX owl: 4 | 5 | CONSTRUCT { 6 | ?uri a skos:Concept ; 7 | skos:prefLabel ?prefLabel ; 8 | skos:altLabel ?altLabel ; 9 | skos:scopeNote ?scopeNote ; 10 | skos:exactMatch ?exactMatch_uri . 11 | } 12 | WHERE { 13 | # For example: 14 | # Jodenbreestraat: 15 | # Damrak: 16 | 17 | VALUES ?uri { ?uris } 18 | 19 | GRAPH { 20 | ?uri a hg:Street . 21 | 22 | OPTIONAL { ?uri skos:prefLabel ?prefLabel } 23 | OPTIONAL { ?uri skos:altLabel ?altLabel } 24 | OPTIONAL { 25 | ?uri hg:liesIn # Gemeente Amsterdam 26 | BIND("Straat in Gemeente Amsterdam" AS ?scopeNote) 27 | } 28 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 29 | } 30 | } 31 | LIMIT 1000 32 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/brabantse-gebouwen.rq: -------------------------------------------------------------------------------- 1 | PREFIX schema: 2 | PREFIX skos: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept ; 6 | skos:prefLabel ?schema_name ; 7 | skos:altLabel ?schema_alternateName ; 8 | skos:hiddenLabel ?hiddenLabel; 9 | skos:scopeNote ?schema_description, ?scopeNote . 10 | } 11 | WHERE { 12 | # For example: 13 | # Huize Boschlust: 14 | # Mariaklooster: 15 | # Abdij van Binderen: 16 | VALUES ?uri { ?uris } 17 | 18 | ?uri a schema:LandmarksOrHistoricalBuildings . 19 | OPTIONAL { ?uri schema:name ?schema_name } 20 | OPTIONAL { ?uri schema:alternateName ?schema_alternateName } 21 | OPTIONAL { ?uri schema:description ?schema_description } 22 | OPTIONAL { ?uri skos:hiddenLabel ?hiddenLabel } 23 | OPTIONAL { ?uri skos:scopeNote ?scopeNote} 24 | } 25 | LIMIT 1000 26 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/iconclass.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:broader ?broader_uri ; 7 | skos:narrower ?narrower_uri ; 8 | skos:related ?related_uri . 9 | 10 | ?broader_uri skos:prefLabel ?broader_prefLabel . 11 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 12 | ?related_uri skos:prefLabel ?related_prefLabel . 13 | } 14 | WHERE { 15 | { 16 | SELECT * WHERE { 17 | ?uri a skos:Concept ; 18 | skos:prefLabel ?prefLabel . 19 | FILTER(CONTAINS(LCASE(?prefLabel), LCASE(?query))) 20 | } 21 | #LIMIT# 22 | } 23 | 24 | OPTIONAL { 25 | ?uri skos:broader ?broader_uri . 26 | ?broader_uri skos:prefLabel ?broader_prefLabel . 27 | } 28 | OPTIONAL { 29 | ?uri skos:narrower ?narrower_uri . 30 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 31 | } 32 | OPTIONAL { 33 | ?uri skos:related ?related_uri . 34 | ?related_uri skos:prefLabel ?related_prefLabel . 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/wikidata.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX schema: 3 | PREFIX skos: 4 | PREFIX wdt: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?prefLabel ; 9 | skos:altLabel ?altLabel ; 10 | skos:scopeNote ?description . 11 | } 12 | WHERE { 13 | # For example: 14 | # Some entity (e.g. a painting): 15 | # Person: 16 | # Place: 17 | # Street: 18 | VALUES ?uri { ?uris } 19 | 20 | ?uri rdfs:label ?prefLabel . 21 | FILTER(LANG(?prefLabel) = "nl" || LANG(?prefLabel) = "en") 22 | 23 | OPTIONAL { 24 | ?uri skos:altLabel ?altLabel . 25 | FILTER(LANG(?altLabel) = "nl" || LANG(?altLabel) = "en") 26 | } 27 | OPTIONAL { 28 | ?uri schema:description ?description . 29 | FILTER(LANG(?description) = "nl" || LANG(?description) = "en") 30 | } 31 | } 32 | LIMIT 1000 33 | -------------------------------------------------------------------------------- /packages/reconciliation/src/score.ts: -------------------------------------------------------------------------------- 1 | import { Term } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; 2 | import stringComparison from 'string-comparison'; 3 | 4 | /** 5 | * Calculate score for term based on case-insensitive cosine similarity, ignoring punctuation, between the search string 6 | * and the term’s prefLabels and altLabels, on a scale of 0–100 percentage match. 7 | */ 8 | export const score = (searchString: string, term: Term): number => { 9 | return calculateMatchingScore( 10 | searchString.toLowerCase(), 11 | [...term.prefLabels, ...term.altLabels].map((literal) => literal.value), 12 | ); 13 | }; 14 | 15 | export const calculateMatchingScore = ( 16 | searchString: string, 17 | againstStrings: string[], 18 | ): number => { 19 | const scores = stringComparison.diceCoefficient.sortMatch( 20 | normalize(searchString), 21 | againstStrings.map(normalize), 22 | ); 23 | const maxScore = Math.max(...scores.map((score) => score.rating)); 24 | 25 | return Math.round((maxScore + Number.EPSILON) * 10000) / 100; // Return percentage match rounded to two decimals. 26 | }; 27 | 28 | const normalize = (string: string) => string.replace(/[,.]/g, ''); 29 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/adamlink-straten.rq: -------------------------------------------------------------------------------- 1 | PREFIX hg: 2 | PREFIX owl: 3 | PREFIX bif: 4 | PREFIX skos: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?prefLabel ; 9 | skos:altLabel ?altLabel ; 10 | skos:scopeNote ?scopeNote ; 11 | skos:exactMatch ?exactMatch_uri . 12 | } 13 | WHERE { 14 | GRAPH { 15 | { 16 | SELECT ?uri WHERE { 17 | ?uri a hg:Street ; 18 | ?predicate ?label . 19 | VALUES ?predicate { skos:prefLabel skos:altLabel } 20 | ?label ?virtuosoQuery . 21 | } 22 | #LIMIT# 23 | } 24 | 25 | OPTIONAL { ?uri skos:prefLabel ?prefLabel } 26 | OPTIONAL { ?uri skos:altLabel ?altLabel } 27 | OPTIONAL { 28 | ?uri hg:liesIn # Gemeente Amsterdam 29 | BIND("Straat in Gemeente Amsterdam" AS ?scopeNote) 30 | } 31 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | # Run in a development Docker container 2 | 3 | An alternative way to run the application for development purposes is in a Docker container. This is useful if you don’t 4 | want to install Node on your host machine. 5 | 6 | Start by cloning the repository: 7 | 8 | git clone https://github.com/netwerk-digitaal-erfgoed/network-of-terms.git 9 | cd network-of-terms 10 | 11 | Then start run the development container: 12 | 13 | docker compose run --service-ports --rm node 14 | 15 | And execute the commands that you find in each package’s readme (for example 16 | [GraphQL](../packages/network-of-terms-graphql/) or [Reconciliation](../packages/network-of-terms-reconciliation)): 17 | 18 | # In the container: 19 | /app # npm install 20 | /app # npx nx serve network-of-terms-graphql 21 | 22 | # or: 23 | 24 | /app # npx nx serve network-of-terms-reconciliation 25 | 26 | You can also use this method if you want to run the application on an 27 | [Apple silicon](https://support.apple.com/en-gb/HT211814) computer without emulation, as our ready-made Docker images 28 | don’t yet support M1. 29 | 30 | If you just want to run the application, please refer to our ready-made 31 | [Docker images](https://github.com/orgs/netwerk-digitaal-erfgoed/packages?repo_name=network-of-terms). 32 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/uitvoeringsmedium.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | PREFIX som: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept; 6 | skos:prefLabel ?prefLabel ; 7 | skos:altLabel ?altLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:exactMatch ?exactMatch_uri . 11 | ?broader_uri skos:prefLabel ?broader_prefLabel . 12 | } 13 | WHERE { 14 | { 15 | SELECT ?uri ?prefLabel WHERE { 16 | ?uri a skos:Concept ; 17 | skos:inScheme som:Uitvoeringsmedium ; 18 | skos:prefLabel ?prefLabel . 19 | 20 | ?uri ?predicate ?label . 21 | VALUES ?predicate { skos:prefLabel skos:altLabel } 22 | ?label ?virtuosoQuery . 23 | } 24 | GROUP BY ?uri 25 | #LIMIT# 26 | } 27 | 28 | OPTIONAL { 29 | ?uri skos:scopeNote ?scopeNote 30 | } 31 | 32 | OPTIONAL { 33 | ?uri skos:altLabel ?altLabel . 34 | } 35 | 36 | OPTIONAL { 37 | ?uri skos:broader ?broader_uri . 38 | ?broader_uri skos:prefLabel ?broader_prefLabel . 39 | } 40 | 41 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } # Has no labels. 42 | } 43 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/geonames.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | PREFIX gn: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept ; 6 | skos:prefLabel ?prefLabel_ext ; 7 | skos:altLabel ?altLabel ; 8 | skos:scopeNote ?scopeNote_en ; 9 | skos:broader ?broader . 10 | ?broader skos:prefLabel ?broader_prefLabel . 11 | } 12 | WHERE { 13 | # https://sws.geonames.org/2752375 (Koudekerke) 14 | VALUES ?uri { ?uris } 15 | 16 | ?uri a gn:Feature ; 17 | gn:countryCode ?countryCode ; 18 | gn:name ?prefLabel . 19 | 20 | BIND(CONCAT(?prefLabel," (",UCASE(?countryCode),")") as ?prefLabel_ext) 21 | 22 | OPTIONAL { 23 | ?uri gn:alternateName ?altLabel . 24 | FILTER(?altLabel != "") 25 | } 26 | 27 | OPTIONAL { 28 | ?uri ?parents ?broader . 29 | ?broader gn:name ?broader_prefLabel 30 | VALUES ?parents { gn:parentADM1 gn:parentADM2 } 31 | # filter out the circular links in the converted data 32 | FILTER(?uri != ?broader) 33 | } 34 | 35 | OPTIONAL { 36 | ?uri gn:featureCode/gn:name ?scopeNote . 37 | # scopeNote is always in English. 38 | BIND(STRLANG(?scopeNote, "en") as ?scopeNote_en) 39 | } 40 | } 41 | LIMIT 1000 42 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/mw-genresstijlen.rq: -------------------------------------------------------------------------------- 1 | PREFIX muziekweb: 2 | PREFIX owl: 3 | PREFIX rdfs: 4 | PREFIX skos: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?rdfs_label ; 9 | skos:scopeNote ?rdfs_comment ; 10 | skos:broader ?broader_uri ; 11 | skos:narrower ?narrower_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_prefLabel . 14 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 15 | } 16 | WHERE { 17 | { 18 | SELECT DISTINCT ?uri WHERE { 19 | ?uri a muziekweb:Genre ; 20 | rdfs:label ?rdfs_label . 21 | ?rdfs_label ?virtuosoQuery . 22 | } 23 | #LIMIT# 24 | } 25 | 26 | ?uri rdfs:label ?rdfs_label . 27 | 28 | OPTIONAL { 29 | ?uri skos:broader ?broader_uri . 30 | ?broader_uri rdfs:label ?broader_prefLabel 31 | } 32 | OPTIONAL { 33 | ?uri skos:narrower ?narrower_uri . 34 | ?narrower_uri rdfs:label ?narrower_prefLabel 35 | } 36 | OPTIONAL { ?uri rdfs:comment ?rdfs_comment } 37 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 38 | } 39 | 40 | -------------------------------------------------------------------------------- /packages/cli/src/list.ts: -------------------------------------------------------------------------------- 1 | import { cli } from 'cli-ux'; 2 | import { Command } from '@oclif/core'; 3 | import { 4 | Dataset, 5 | Distribution, 6 | } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; 7 | import { getCatalog } from '@netwerk-digitaal-erfgoed/network-of-terms-catalog'; 8 | 9 | type DatasetDistribution = { 10 | dataset: Dataset; 11 | distribution: Distribution; 12 | }; 13 | 14 | export class ListSourcesCommand extends Command { 15 | static description = 'List queryable sources'; 16 | 17 | private render(distributions: readonly DatasetDistribution[]): void { 18 | cli.table(distributions as DatasetDistribution[], { 19 | distributionTitle: { 20 | header: 'Source Name', 21 | get: (distribution: DatasetDistribution) => distribution.dataset.name, 22 | }, 23 | distributionId: { 24 | header: 'Source URI', 25 | get: (distribution: DatasetDistribution) => 26 | distribution.distribution.iri.toString(), 27 | }, 28 | }); 29 | } 30 | 31 | async run(): Promise { 32 | const catalog = await getCatalog(); 33 | const distributions = catalog.datasets.flatMap((dataset: Dataset) => 34 | dataset.distributions.map((distribution) => ({ 35 | dataset, 36 | distribution, 37 | })), 38 | ); 39 | 40 | this.render(distributions); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/query/src/helpers/logger.ts: -------------------------------------------------------------------------------- 1 | import Joi from 'joi'; 2 | import Pino from 'pino'; 3 | 4 | export interface GetLoggerOptions { 5 | name: string; 6 | level: string; 7 | } 8 | 9 | const schemaGetLogger = Joi.object({ 10 | name: Joi.string().required(), 11 | level: Joi.string().required(), 12 | }); 13 | 14 | const baseOptions: Pino.LoggerOptions = { 15 | base: { 16 | name: undefined, // Don't log PID and hostname 17 | }, 18 | level: 'warn', 19 | messageKey: 'message', 20 | formatters: { 21 | level(label) { 22 | return { 23 | level: label, 24 | }; 25 | }, 26 | }, 27 | }; 28 | 29 | export function getCliLogger(options: GetLoggerOptions): Pino.Logger { 30 | const args = Joi.attempt(options, schemaGetLogger); 31 | const loggerOptions = Object.assign(baseOptions, { 32 | base: { 33 | name: args.name, 34 | }, 35 | level: args.level, 36 | prettyPrint: { 37 | colorize: true, 38 | }, 39 | }); 40 | const destinationStdErr = Pino.destination(2); 41 | return Pino.pino(loggerOptions, destinationStdErr); 42 | } 43 | 44 | export function getHttpLogger(options: GetLoggerOptions): Pino.LoggerOptions { 45 | const args = Joi.attempt(options, schemaGetLogger); 46 | return Object.assign(baseOptions, { 47 | base: { 48 | name: args.name, 49 | }, 50 | level: args.level, 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /packages/query/src/literal.ts: -------------------------------------------------------------------------------- 1 | import * as RDF from '@rdfjs/types'; 2 | import { DataFactory } from 'rdf-data-factory'; 3 | 4 | const dataFactory = new DataFactory(); 5 | 6 | export function filterLiteralsByLanguage( 7 | literals: RDF.Literal[], 8 | languages: string[], 9 | ) { 10 | const preferredLanguageLiterals = literals.filter((literal) => 11 | languages.includes(literal.language), 12 | ); 13 | if (preferredLanguageLiterals.length > 0) { 14 | return preferredLanguageLiterals; 15 | } 16 | 17 | // If literal has no language tag, we assume it is in the Network of Terms’ default language, Dutch. 18 | return literals 19 | .filter((literal) => literal.language === '') 20 | .map((literal) => dataFactory.literal(literal.value, 'nl')); 21 | } 22 | 23 | /** 24 | * Return value from {@link Literal} in the given languages. 25 | * 26 | * @param literals 27 | * @param languages 28 | */ 29 | export function literalValues( 30 | literals: RDF.Literal[], 31 | languages: string[] = ['nl'], 32 | ) { 33 | const languageLiterals = filterLiteralsByLanguage(literals, languages); 34 | if (languageLiterals.length > 0) { 35 | return languageLiterals.map((literal) => literal.value); 36 | } 37 | 38 | // Fall back to English for sources that provide no Dutch labels. 39 | return filterLiteralsByLanguage(literals, ['en']).map( 40 | (literal) => literal.value, 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /packages/reconciliation/src/manifest.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Catalog, 3 | FeatureType, 4 | IRI, 5 | } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; 6 | 7 | export class ServiceManifest { 8 | public readonly versions = ['0.1', '0.2']; 9 | public readonly schemaSpace = 'http://www.w3.org/2004/02/skos/core#Concept'; 10 | public readonly defaultTypes = [ 11 | { 12 | id: this.schemaSpace, 13 | name: 'Concept', 14 | }, 15 | ]; 16 | public readonly preview; 17 | public readonly extend; 18 | 19 | constructor( 20 | public readonly name: string, 21 | public readonly identifierSpace: IRI, 22 | readonly root: string, 23 | ) { 24 | this.preview = { 25 | width: 300, 26 | height: 300, 27 | url: root + '/preview/{{id}}', 28 | }; 29 | 30 | this.extend = { 31 | propose_properties: { 32 | service_url: root, 33 | service_path: '/extend/propose', 34 | }, 35 | property_settings: [], 36 | }; 37 | } 38 | } 39 | 40 | export function findManifest( 41 | dataset: IRI, 42 | catalog: Catalog, 43 | root: string, 44 | ): ServiceManifest | undefined { 45 | const source = catalog.getDatasetByIri(dataset); 46 | if ( 47 | source && 48 | source.getSparqlDistribution()?.hasFeature(FeatureType.RECONCILIATION) 49 | ) { 50 | return new ServiceManifest(source.name.nl, source.iri, root); 51 | } 52 | 53 | return undefined; 54 | } 55 | -------------------------------------------------------------------------------- /packages/query/src/helpers/logger-pino.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@comunica/types'; 2 | import Pino from 'pino'; 3 | 4 | export interface ConstructorOptions { 5 | logger: Pino.Logger; 6 | } 7 | 8 | export class LoggerPino extends Logger { 9 | protected logger: Pino.Logger; 10 | 11 | constructor(options: ConstructorOptions) { 12 | super(); 13 | this.logger = options.logger; 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 17 | debug(message: string, data?: any): void { 18 | this.logger.debug(message, data); 19 | } 20 | 21 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 22 | error(message: string, data?: any): void { 23 | this.logger.error(message, data); 24 | } 25 | 26 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 27 | fatal(message: string, data?: any): void { 28 | this.logger.fatal(message, data); 29 | } 30 | 31 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 32 | info(message: string, data?: any): void { 33 | this.logger.info(message, data); 34 | } 35 | 36 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 37 | trace(message: string, data?: any): void { 38 | this.logger.trace(message, data); 39 | } 40 | 41 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 42 | warn(message: string, data?: any): void { 43 | this.logger.warn(message, data); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/nta.rq: -------------------------------------------------------------------------------- 1 | PREFIX owl: 2 | PREFIX rdfs: 3 | PREFIX skos: 4 | PREFIX schema: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?rdfs_label ; 9 | skos:altLabel ?schema_name ; 10 | skos:altLabel ?schema_alternateName ; 11 | skos:scopeNote ?schema_description ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 14 | } 15 | WHERE { 16 | { 17 | SELECT DISTINCT ?uri WHERE { 18 | ?uri schema:mainEntityOfPage/schema:isPartOf ; 19 | ?predicate ?label . 20 | VALUES ?predicate { rdfs:label schema:name schema:alternateName } 21 | ?label ?virtuosoQuery . 22 | } 23 | #LIMIT# 24 | } 25 | 26 | OPTIONAL { ?uri rdfs:label ?rdfs_label } 27 | OPTIONAL { ?uri schema:name ?schema_name } 28 | OPTIONAL { ?uri schema:alternateName ?schema_alternateName } 29 | OPTIONAL { ?uri schema:description ?schema_description } 30 | OPTIONAL { 31 | ?uri ?matchPredicate ?exactMatch_uri . 32 | VALUES ?matchPredicate { owl:sameAs schema:sameAs } 33 | OPTIONAL { 34 | ?exactMatch_uri rdfs:label ?exactMatch_prefLabel . 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/wikidata-entities-all.rq: -------------------------------------------------------------------------------- 1 | PREFIX bd: 2 | PREFIX mwapi: 3 | PREFIX rdfs: 4 | PREFIX schema: 5 | PREFIX skos: 6 | PREFIX vrank: 7 | PREFIX wikibase: 8 | 9 | CONSTRUCT { 10 | ?item a skos:Concept ; 11 | skos:prefLabel ?prefLabel ; 12 | skos:altLabel ?altLabel ; 13 | skos:scopeNote ?description ; 14 | vrank:simpleRank ?score . 15 | } 16 | WHERE { 17 | SERVICE wikibase:mwapi { 18 | bd:serviceParam wikibase:endpoint "www.wikidata.org" ; 19 | wikibase:api "EntitySearch" ; 20 | wikibase:limit ?limit ; 21 | mwapi:language "nl" ; 22 | mwapi:search ?query . 23 | ?item wikibase:apiOutputItem mwapi:item . 24 | ?ordinal wikibase:apiOrdinal true. 25 | } 26 | ?item rdfs:label ?prefLabel . 27 | FILTER(LANG(?prefLabel) = "nl" || LANG(?prefLabel) = "en") 28 | OPTIONAL { 29 | ?item skos:altLabel ?altLabel . 30 | FILTER(LANG(?altLabel) = "nl" || LANG(?altLabel) = "en") 31 | } 32 | OPTIONAL { 33 | ?item schema:description ?description . 34 | FILTER(LANG(?description) = "nl" || LANG(?description) = "en") 35 | } 36 | BIND(-?ordinal AS ?score) 37 | } 38 | ORDER BY ASC(?ordinal) 39 | 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "network-of-terms", 3 | "private": true, 4 | "workspaces": [ 5 | "./packages/*" 6 | ], 7 | "devDependencies": { 8 | "@commitlint/cli": "^19.8.0", 9 | "@commitlint/config-conventional": "^19.8.1", 10 | "@nx/docker": "^22.0.2", 11 | "@nx/esbuild": "^22.0.2", 12 | "@nx/eslint": "^22.0.2", 13 | "@nx/eslint-plugin": "^22.0.2", 14 | "@nx/js": "^22.0.2", 15 | "@nx/vite": "^22.0.2", 16 | "@nx/web": "22.0.2", 17 | "@rdfjs/types": "^2.0.1", 18 | "@swc-node/core": "^1.14.1", 19 | "@swc-node/register": "^1.11.1", 20 | "@types/node": "^22.15.3", 21 | "@vitest/coverage-v8": "^3.2.4", 22 | "@vitest/ui": "^3.0.0", 23 | "esbuild": "^0.25.11", 24 | "eslint": "^9.36.0", 25 | "eslint-config-prettier": "^10.1.8", 26 | "husky": "^9.1.7", 27 | "jiti": "2.4.2", 28 | "jsonc-eslint-parser": "^2.4.0", 29 | "nodaemon": "^0.0.5", 30 | "nx": "22.0.2", 31 | "prettier": "^3.6.2", 32 | "prettier-plugin-packagejson": "^2.5.19", 33 | "tslib": "^2.3.0", 34 | "typescript": "^5.8.3", 35 | "typescript-eslint": "^8.44.0", 36 | "vitest": "^3.2.4" 37 | }, 38 | "nx": { 39 | "includedScripts": [], 40 | "targets": { 41 | "local-registry": { 42 | "executor": "@nx/js:verdaccio", 43 | "options": { 44 | "port": 4873, 45 | "config": ".verdaccio/config.yml", 46 | "storage": "tmp/local-registry/storage" 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/nmvw.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_prefLabel . 13 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 14 | } 15 | WHERE { 16 | # For example: 17 | # Klassieke Balinese Periode: 18 | # Luxemburg: 19 | VALUES ?uri { ?uris } 20 | 21 | ?uri a skos:Concept . 22 | 23 | OPTIONAL { 24 | ?uri skos:prefLabel ?prefLabel . 25 | } 26 | OPTIONAL { 27 | ?uri skos:altLabel ?altLabel . 28 | } 29 | OPTIONAL { 30 | ?uri skos:hiddenLabel ?hiddenLabel . 31 | } 32 | OPTIONAL { 33 | ?uri skos:scopeNote ?scopeNote . 34 | } 35 | OPTIONAL { 36 | ?uri skos:broader ?broader_uri . 37 | ?broader_uri skos:prefLabel ?broader_prefLabel . 38 | } 39 | OPTIONAL { 40 | ?uri skos:narrower ?narrower_uri . 41 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 42 | } 43 | OPTIONAL { 44 | ?uri skos:exactMatch ?exactMatch_uri . # Has no labels. 45 | } 46 | } 47 | LIMIT 1000 48 | -------------------------------------------------------------------------------- /packages/catalog/test/fixtures/credentials/credentials.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/Classificatie", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "nl", 8 | "@value": "GTAA: classificatie" 9 | } 10 | ], 11 | "description": [ 12 | { 13 | "@language": "nl", 14 | "@value": "Classificaties voor het beschrijven van audiovisueel materiaal" 15 | } 16 | ], 17 | "genre": [ 18 | { 19 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 20 | } 21 | ], 22 | "creator": [ 23 | { 24 | "@id": "https://www.beeldengeluid.nl/", 25 | "@type": "Organization", 26 | "name": "Nederlands Instituut voor Beeld en Geluid", 27 | "alternateName": "Beeld en Geluid" 28 | } 29 | ], 30 | "url": ["http://data.beeldengeluid.nl/gtaa/"], 31 | "mainEntityOfPage": ["https://example.com/gtaa"], 32 | "inLanguage": "nl", 33 | "distribution": [ 34 | { 35 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0027", 36 | "@type": "DataDownload", 37 | "contentUrl": "https://$DATASET_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 38 | "encodingFormat": "application/sparql-query", 39 | "potentialAction": [ 40 | { 41 | "@type": ["Action", "SearchAction"], 42 | "query": "query" 43 | }, 44 | { 45 | "@type": ["Action", "FindAction"], 46 | "query": "query" 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/nmvw.rq: -------------------------------------------------------------------------------- 1 | #PREFIX bif: 2 | PREFIX skos: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept ; 6 | skos:prefLabel ?prefLabel ; 7 | skos:altLabel ?altLabel ; 8 | skos:hiddenLabel ?hiddenLabel ; 9 | skos:scopeNote ?scopeNote ; 10 | skos:broader ?broader_uri ; 11 | skos:narrower ?narrower_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_prefLabel . 14 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 15 | } 16 | WHERE { 17 | { 18 | SELECT DISTINCT ?uri { 19 | ?uri ?predicate ?label . 20 | VALUES ?predicate { skos:prefLabel skos:altLabel } 21 | ?label ?virtuosoQuery 22 | } 23 | #LIMIT# 24 | } 25 | 26 | OPTIONAL { 27 | ?uri skos:prefLabel ?prefLabel . 28 | } 29 | OPTIONAL { 30 | ?uri skos:scopeNote ?scopeNote . 31 | } 32 | OPTIONAL { 33 | ?uri skos:altLabel ?altLabel . 34 | } 35 | OPTIONAL { 36 | ?uri skos:hiddenLabel ?hiddenLabel . 37 | } 38 | OPTIONAL { 39 | ?uri skos:broader ?broader_uri . 40 | ?broader_uri skos:prefLabel ?broader_prefLabel . 41 | } 42 | OPTIONAL { 43 | ?uri skos:narrower ?narrower_uri . 44 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 45 | } 46 | OPTIONAL { 47 | ?uri skos:exactMatch ?exactMatch_uri . # Has no labels. 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/wikidata-entities-persons.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.wikidata.org#entities-persons", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Wikidata: persons" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Wikidata: personen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.wikidata.org/entity/Q180" 23 | } 24 | ], 25 | "url": ["http://www.wikidata.org/entity/"], 26 | "mainEntityOfPage": ["https://www.wikidata.org"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Persons" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Personen" 35 | } 36 | ], 37 | "inLanguage": ["en", "nl"], 38 | "distribution": [ 39 | { 40 | "@id": "https://query.wikidata.org/sparql#entities-persons", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://query.wikidata.org/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/wikidata-entities-persons.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/wikidata.rq" 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/goudatijdmachine-straten.rq: -------------------------------------------------------------------------------- 1 | PREFIX sdo: 2 | PREFIX skos: 3 | PREFIX gtm: 4 | PREFIX hg: 5 | PREFIX owl: 6 | PREFIX omeka: 7 | CONSTRUCT { 8 | ?uri a skos:Concept ; 9 | skos:prefLabel ?prefLabel ; 10 | skos:altLabel ?altLabel ; 11 | skos:scopeNote ?scopeNote ; 12 | skos:related ?related_uri ; 13 | skos:exactMatch ?exactMatch_uri . 14 | ?related_uri skos:prefLabel ?related_prefLabel . 15 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 16 | } WHERE { 17 | # For example: 18 | # Vuilsteeg: 19 | # Kapelstraat: 20 | VALUES ?uri { ?uris } 21 | ?uri a gtm:Straat ; 22 | sdo:identifier ?identifier ; 23 | sdo:name ?prefLabel . 24 | OPTIONAL { 25 | ?uri sdo:alternateName ?altLabel 26 | } 27 | BIND ( CONCAT( IF ( EXISTS { 28 | ?uri omeka:item_set 29 | }, "Verdwenen straat", "Straat" )," in Gouda") AS ?scopeNote ) 30 | OPTIONAL { 31 | ?uri hg:absorbedBy|hg:absorbed ?related_uri . 32 | ?related_uri a gtm:Straat ; 33 | sdo:name ?related_prefLabel . 34 | FILTER (?uri != ?related_uri ) 35 | } 36 | OPTIONAL { 37 | ?uri owl:sameAs ?exactMatch_uri . 38 | ?exactMatch_uri sdo:name ?exactMatch_prefLabel . 39 | FILTER(?exactMatch_uri != ?uri) 40 | } 41 | } 42 | LIMIT 100 -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/wikidata-entities-streets.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.wikidata.org#entities-streets", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Wikidata: streets in the Netherlands" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Wikidata: straten in Nederland" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.wikidata.org/entity/Q180" 23 | } 24 | ], 25 | "url": ["http://www.wikidata.org/entity/"], 26 | "mainEntityOfPage": ["https://www.wikidata.org"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Streets in the Netherlands" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Straten in Nederland" 35 | } 36 | ], 37 | "inLanguage": ["en", "nl"], 38 | "distribution": [ 39 | { 40 | "@id": "https://query.wikidata.org/sparql#entities-streets", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://query.wikidata.org/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/wikidata-entities-streets.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/wikidata.rq" 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /packages/catalog/src/genre.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IRI, 3 | StringDictionary, 4 | } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; 5 | import { QueryEngine } from '@comunica/query-sparql'; 6 | import memoize from 'memoize'; 7 | import type { Literal } from '@rdfjs/types'; 8 | 9 | const queryEngine = new QueryEngine(); 10 | const timeout = 10_000; 11 | 12 | export class Genre { 13 | constructor( 14 | public readonly iri: IRI, 15 | public readonly name: StringDictionary, 16 | ) {} 17 | } 18 | 19 | const doDereferenceGenre = async (genre: IRI): Promise => { 20 | try { 21 | const data = await queryEngine.queryBindings( 22 | `SELECT ?prefLabel WHERE { 23 | ?s a ; 24 | ?prefLabel . 25 | }`, 26 | { 27 | // We can't dereference at the actual URL because content negotiation is broken, so suffix .rdf. 28 | sources: [`${genre.toString()}.rdf`], 29 | httpTimeout: timeout, 30 | httpBodyTimeout: true, 31 | }, 32 | ); 33 | const bindings = await data.toArray(); 34 | 35 | return new Genre( 36 | genre, 37 | bindings.reduce((acc: StringDictionary, binding) => { 38 | acc[(binding.get('prefLabel') as Literal)!.language] = 39 | binding.get('prefLabel')!.value; 40 | return acc; 41 | }, {}), 42 | ); 43 | } catch (error) { 44 | console.error(error); 45 | return null; 46 | } 47 | }; 48 | 49 | export const dereferenceGenre = memoize(doDereferenceGenre, { 50 | cacheKey: String, 51 | maxAge: 86400, 52 | }); 53 | -------------------------------------------------------------------------------- /packages/query/src/instrumentation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MeterProvider, 3 | PeriodicExportingMetricReader, 4 | } from '@opentelemetry/sdk-metrics'; 5 | import { 6 | defaultResource, 7 | resourceFromAttributes, 8 | } from '@opentelemetry/resources'; 9 | import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto'; 10 | import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; 11 | import { metrics, ValueType } from '@opentelemetry/api'; 12 | 13 | const sourceQueriesHistogramName = 'queries.source'; 14 | 15 | const meterProvider = new MeterProvider({ 16 | resource: defaultResource().merge( 17 | resourceFromAttributes({ [ATTR_SERVICE_NAME]: 'network-of-terms' }), 18 | ), 19 | readers: 20 | 'test' === process.env.NODE_ENV 21 | ? [] 22 | : [ 23 | new PeriodicExportingMetricReader({ 24 | exporter: new OTLPMetricExporter(), 25 | exportIntervalMillis: 26 | (process.env.OTEL_METRIC_EXPORT_INTERVAL as unknown as number) ?? 27 | 60000, 28 | }), 29 | ], 30 | }); 31 | 32 | metrics.setGlobalMeterProvider(meterProvider); 33 | 34 | const meter = metrics.getMeter('default'); 35 | 36 | export const clientQueriesCounter = meter.createCounter( 37 | 'queries.client.counter', 38 | { 39 | description: 'Number of user queries', 40 | valueType: ValueType.INT, 41 | }, 42 | ); 43 | 44 | export const sourceQueriesHistogram = meter.createHistogram( 45 | sourceQueriesHistogramName, 46 | { 47 | description: 'Queries to terminology sources and their response times', 48 | valueType: ValueType.INT, 49 | unit: 'ms', 50 | }, 51 | ); 52 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/wikidata-entities-places.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.wikidata.org#entities-places", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Wikidata: places in the Netherlands and Belgium" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Wikidata: plaatsen in Nederland en België" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.wikidata.org/entity/Q180" 23 | } 24 | ], 25 | "url": ["http://www.wikidata.org/entity/"], 26 | "mainEntityOfPage": ["https://www.wikidata.org"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Places in the Netherlands and Belgium" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Plaatsen in Nederland en België" 35 | } 36 | ], 37 | "inLanguage": ["en", "nl"], 38 | "distribution": [ 39 | { 40 | "@id": "https://query.wikidata.org/sparql#entities-places", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://query.wikidata.org/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/wikidata-entities-places.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/wikidata.rq" 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/muziekweb.rq: -------------------------------------------------------------------------------- 1 | PREFIX muziekweb: 2 | PREFIX rdfs: 3 | PREFIX schema: 4 | PREFIX skos: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?rdfs_label ; 9 | skos:prefLabel ?prefLabel ; 10 | skos:altLabel ?altLabel ; 11 | skos:hiddenLabel ?hiddenLabel ; 12 | skos:scopeNote ?rdfs_comment ; 13 | skos:scopeNote ?schema_description ; 14 | skos:broader ?broader_uri ; 15 | skos:narrower ?narrower_uri ; 16 | skos:exactMatch ?exactMatch_uri . 17 | ?broader_uri skos:prefLabel ?broader_prefLabel . 18 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 19 | } 20 | WHERE { 21 | # For example: 22 | # Music group: 23 | # Music genre: 24 | VALUES ?uri { ?uris } 25 | 26 | ?uri a ?type . 27 | VALUES ?type { muziekweb:Genre schema:MusicGroup } . 28 | 29 | OPTIONAL { ?uri rdfs:label ?rdfs_label } 30 | OPTIONAL { ?uri skos:prefLabel ?prefLabel } 31 | OPTIONAL { ?uri skos:altLabel ?altLabel } 32 | OPTIONAL { ?uri skos:hiddenLabel ?hiddenLabel } 33 | OPTIONAL { ?uri rdfs:comment ?rdfs_comment } 34 | OPTIONAL { ?uri schema:description ?schema_description } 35 | OPTIONAL { 36 | ?uri skos:broader ?broader_uri . 37 | ?broader_uri rdfs:label ?broader_prefLabel 38 | } 39 | OPTIONAL { 40 | ?uri skos:narrower ?narrower_uri . 41 | ?narrower_uri rdfs:label ?narrower_prefLabel 42 | } 43 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 44 | } 45 | LIMIT 1000 46 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/goudatijdmachine-straten.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | PREFIX gtm: 3 | PREFIX sdo: 4 | PREFIX hg: 5 | PREFIX ql: 6 | PREFIX omeka: 7 | PREFIX owl: 8 | PREFIX rdfs: 9 | CONSTRUCT { 10 | ?piduri a skos:Concept ; 11 | skos:prefLabel ?prefLabel ; 12 | skos:altLabel ?altLabel; 13 | skos:scopeNote ?scopeNote ; 14 | skos:related ?related_uri ; 15 | skos:exactMatch ?exactMatch_uri . 16 | ?related_uri skos:prefLabel ?related_prefLabel . 17 | ?exactMatch_uri skos:prefLabel ?exactMatch_label . 18 | } WHERE { 19 | ?uri sdo:name|sdo:alternateName ?item . 20 | ?uri a gtm:Straat . 21 | ?text ql:contains-entity ?item . 22 | ?text ql:contains-word ?query . 23 | ?uri owl:sameAs ?piduri . 24 | ?uri sdo:name ?prefLabel . 25 | OPTIONAL { 26 | ?uri gtm:genoemdNaar ?genoemdNaar . 27 | } 28 | BIND ( 29 | CONCAT( 30 | IF ( EXISTS { 31 | ?uri omeka:item_set 32 | }, "Verdwenen straat", "Straat" ), 33 | IF ( BOUND(?genoemdNaar), CONCAT(" in Gouda, genoemd naar ", ?genoemdNaar )," in Gouda" ) 34 | ) 35 | AS ?scopeNote ) 36 | OPTIONAL { 37 | ?uri sdo:alternateName ?altLabel . 38 | } 39 | OPTIONAL { 40 | ?uri hg:absorbedBy|hg:absorbed ?related_uri . 41 | ?related_uri a gtm:Straat ; 42 | sdo:name ?related_prefLabel . 43 | FILTER (?uri != ?related_uri ) 44 | } 45 | OPTIONAL { 46 | ?uri rdfs:seeAlso ?exactMatch_uri . 47 | ?exactMatch_uri omeka:label ?exactMatch_label . 48 | } 49 | } -------------------------------------------------------------------------------- /packages/query/src/search/query-mode.ts: -------------------------------------------------------------------------------- 1 | export enum QueryMode { 2 | RAW = 'raw', 3 | OPTIMIZED = 'optimized', 4 | } 5 | 6 | export function queryVariants(query: string, type: QueryMode) { 7 | switch (type) { 8 | case QueryMode.RAW: 9 | return new Map([ 10 | ['query', query], 11 | ['virtuosoQuery', query], 12 | ]); 13 | case QueryMode.OPTIMIZED: 14 | return new Map([ 15 | ['query', stringQuery(query)], 16 | ['virtuosoQuery', virtuosoQuery(stringQuery(query))], 17 | ]); 18 | } 19 | } 20 | 21 | const stringQuery = (query: string) => 22 | query.toLowerCase().replace(/\s+/g, ' ').trim(); 23 | 24 | const virtuosoQuery = (query: string) => 25 | join(quote(filterStopWords(split(escape(query))))); 26 | 27 | const escape = (query: string) => query.replace(/'/g, "\\'"); 28 | 29 | const split = (query: string) => query.split(/\s+/); 30 | 31 | /** 32 | * Quote parts that are not boolean operators. 33 | */ 34 | const quote = (queryParts: string[]) => 35 | queryParts.map((part) => (isBooleanOperator(part) ? part : `'${part}'`)); 36 | 37 | const filterStopWords = (queryParts: string[]) => 38 | queryParts.filter((part) => part !== '&'); 39 | 40 | /** 41 | * Join query parts with boolean AND if they are not yet connected with a boolean. 42 | */ 43 | const join = (queryParts: string[]) => 44 | queryParts.reduce((previousValue, currentValue, currentIndex, array) => { 45 | const previous = array[currentIndex - 1]; 46 | if (!isBooleanOperator(previous) && !isBooleanOperator(currentValue)) { 47 | return `${previousValue} AND ${currentValue}`; 48 | } 49 | 50 | return `${previousValue} ${currentValue}`; 51 | }); 52 | 53 | const isBooleanOperator = (maybeBool: string) => 54 | maybeBool.toLowerCase() === 'and' || maybeBool.toLowerCase() === 'or'; 55 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/geonames.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | PREFIX gn: 3 | PREFIX text: 4 | PREFIX vrank: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?prefLabel_ext ; 9 | skos:altLabel ?altLabel ; 10 | skos:scopeNote ?scopeNote_en ; 11 | skos:broader ?broader ; 12 | vrank:simpleRank ?score . 13 | ?broader skos:prefLabel ?broader_prefLabel . 14 | } 15 | WHERE { 16 | { 17 | SELECT ?uri (MAX(?sc) as ?score) WHERE { 18 | (?uri ?sc) text:query (gn:name gn:alternateName ?query) . 19 | ?uri a gn:Feature ; 20 | gn:featureClass ?featureClass ; 21 | # Limit results to places (P), localities (L), administrative levels (A) and water surfaces (H). 22 | VALUES ?featureClass { gn:P gn:L gn:A gn:H } 23 | } 24 | GROUP BY ?uri 25 | ORDER BY DESC(?score) 26 | #LIMIT# 27 | } 28 | 29 | ?uri gn:name ?prefLabel ; 30 | gn:countryCode ?countryCode . 31 | 32 | BIND(CONCAT(?prefLabel," (",UCASE(?countryCode),")") as ?prefLabel_ext) 33 | 34 | OPTIONAL { 35 | ?uri gn:alternateName ?altLabel . 36 | FILTER(?altLabel != "") 37 | } 38 | 39 | OPTIONAL { 40 | ?uri ?parents ?broader . 41 | ?broader gn:name ?broader_prefLabel 42 | VALUES ?parents { gn:parentADM1 gn:parentADM2 } 43 | # filter out the circular links in the converted data 44 | FILTER(?uri != ?broader) 45 | } 46 | 47 | OPTIONAL { 48 | ?uri gn:featureCode/gn:name ?scopeNote . 49 | # scopeNote is always in English. 50 | BIND(STRLANG(?scopeNote, "en") as ?scopeNote_en) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/catalog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@netwerk-digitaal-erfgoed/network-of-terms-catalog", 3 | "version": "10.16.0", 4 | "description": "Catalog of Network of Terms datasets", 5 | "keywords": [ 6 | "nde" 7 | ], 8 | "homepage": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms#readme", 9 | "bugs": { 10 | "url": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms/issues" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms" 15 | }, 16 | "license": "EUPL-1.2", 17 | "author": "Netwerk Digitaal Erfgoed", 18 | "type": "module", 19 | "exports": { 20 | "./package.json": "./package.json", 21 | ".": { 22 | "types": "./dist/index.d.ts", 23 | "import": "./dist/index.js", 24 | "development": "./src/index.ts", 25 | "default": "./dist/index.js" 26 | } 27 | }, 28 | "main": "./dist/index.js", 29 | "module": "./dist/index.js", 30 | "types": "./dist/index.d.ts", 31 | "files": [ 32 | "dist/", 33 | "catalog/", 34 | "!**/*.tsbuildinfo" 35 | ], 36 | "dependencies": { 37 | "@comunica/context-entries": "^4.4.1", 38 | "@comunica/query-sparql": "^4.3.0", 39 | "@comunica/query-sparql-rdfjs": "^4.3.0", 40 | "@netwerk-digitaal-erfgoed/network-of-terms-query": "*", 41 | "@rdfjs/types": "^2.0.1", 42 | "globby": "^14.0.2", 43 | "jsonld-context-parser": "^2.4.0", 44 | "ldkit": "^2.5.1", 45 | "memoize": "^10.0.0", 46 | "rdf-parse": "^4.0.0", 47 | "rdf-stores": "^2.1.1", 48 | "tslib": "^2.3.0" 49 | }, 50 | "devDependencies": { 51 | "@comunica/types": "^4.2.0", 52 | "@types/n3": "^1.24.2", 53 | "@types/rdf-ext": "^2.5.2", 54 | "jsonld-streaming-parser": "^5.0.0", 55 | "rdf-ext": "^2.6.0", 56 | "rdf-validate-shacl": "^0.6.5" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/wikidata-entities-all.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.wikidata.org#entities-all", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Wikidata: all entities" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Wikidata: alle entiteiten" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Objecten" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 21 | }, 22 | { 23 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 24 | } 25 | ], 26 | "creator": [ 27 | { 28 | "@id": "https://www.wikidata.org/entity/Q180" 29 | } 30 | ], 31 | "url": ["http://www.wikidata.org/entity/"], 32 | "mainEntityOfPage": ["https://www.wikidata.org"], 33 | "description": [ 34 | { 35 | "@language": "en", 36 | "@value": "All entities in Wikidata, such as professions, movies or artworks" 37 | }, 38 | { 39 | "@language": "nl", 40 | "@value": "Alle entiteiten in Wikidata, zoals beroepen, films of kunstwerken" 41 | } 42 | ], 43 | "inLanguage": ["en", "nl"], 44 | "distribution": [ 45 | { 46 | "@id": "https://query.wikidata.org/sparql#entities-all", 47 | "@type": "DataDownload", 48 | "contentUrl": "https://query.wikidata.org/sparql", 49 | "encodingFormat": "application/sparql-query", 50 | "potentialAction": [ 51 | { 52 | "@type": "SearchAction", 53 | "query": "file://../queries/search/wikidata-entities-all.rq" 54 | }, 55 | { 56 | "@type": "FindAction", 57 | "query": "file://../queries/lookup/wikidata.rq" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/stcn-drukkers.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX schema: 3 | PREFIX skos: 4 | PREFIX void: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?rdfs_label ; 9 | skos:altLabel ?schema_name ; 10 | skos:altLabel ?schema_alternateName ; 11 | skos:scopeNote ?scopeNote ; 12 | skos:exactMatch ?exactMatch_uri . 13 | } 14 | WHERE { 15 | { 16 | SELECT ?uri ?rdfs_label WHERE { 17 | ?uri schema:mainEntityOfPage/schema:isPartOf ; 18 | schema:additionalType ; # Select printers. 19 | rdfs:label ?rdfs_label . 20 | 21 | ?uri ?predicate ?label . 22 | VALUES ?predicate { rdfs:label schema:name schema:alternateName } 23 | ?label ?virtuosoQuery . 24 | } 25 | #LIMIT# 26 | } 27 | # STCN defines multiple URIs for the same printer. 28 | # The distinction between these URIs is based on the place where the printer lived and worked. 29 | # See for example "Plantijn" 30 | # http://data.bibliotheken.nl/id/thes/p075556251 for Plantijn working in Leiden 31 | # http://data.bibliotheken.nl/id/thes/p338012834 for Plantijn working in Antwerp 32 | # The original source (STCN) shows also a time period, this data currently not available in the LOD version. 33 | OPTIONAL { 34 | ?uri schema:location/schema:address/schema:addressLocality ?scopeNote 35 | } 36 | OPTIONAL { ?uri schema:name ?schema_name } 37 | OPTIONAL { ?uri schema:alternateName ?schema_alternateName } 38 | OPTIONAL { 39 | ?uri schema:sameAs ?exactMatch_uri . # Has no labels. 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/adamlink-adressen.rq: -------------------------------------------------------------------------------- 1 | PREFIX hg: 2 | PREFIX skos: 3 | PREFIX owl: 4 | PREFIX roar: 5 | PREFIX schema: 6 | PREFIX bag: 7 | PREFIX rdfs: 8 | 9 | CONSTRUCT { 10 | ?uri a skos:Concept ; 11 | skos:prefLabel ?prefLabel ; 12 | skos:altLabel ?altLabel ; 13 | skos:scopeNote ?scopeNote ; 14 | skos:exactMatch ?exactMatch_uri ; 15 | rdfs:seeAlso ?seeAlso_uri . 16 | 17 | ?seeAlso_uri skos:prefLabel ?seeAlso_label . 18 | } 19 | WHERE { 20 | # For example: 21 | # Jodenbreestraat: 22 | # Damrak: 23 | 24 | VALUES ?uri { ?uris } 25 | 26 | GRAPH { 27 | ?uri a hg:Address . 28 | 29 | OPTIONAL { ?uri rdfs:label ?prefLabel } 30 | OPTIONAL { ?uri skos:altLabel ?altLabel } 31 | OPTIONAL { 32 | ?uri roar:documentedIn ?source . 33 | ?source rdfs:label ?source_name . 34 | BIND(CONCAT("Adres afkomstig van ", ?source_name) AS ?scopeNote) 35 | } 36 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 37 | OPTIONAL { 38 | ?exactMatch_uri a bag:Pand ; 39 | schema:geoContains ?geo . 40 | ?uri schema:geoContains ?geo . 41 | } # A historical address is very similar to a bag:Pand 42 | OPTIONAL { 43 | ?uri schema:geoContains ?geo . 44 | ?seeAlso_uri schema:geoContains ?geo ; 45 | skos:altLabel ?seeAlso_label . 46 | } 47 | } 48 | } 49 | LIMIT 1000 50 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/muziekschatten-klassiekewerken.rq: -------------------------------------------------------------------------------- 1 | PREFIX owl: 2 | PREFIX rdaw: 3 | PREFIX schema: 4 | PREFIX skos: 5 | PREFIX som: 6 | 7 | CONSTRUCT { 8 | ?uri a skos:Concept; 9 | skos:prefLabel ?prefLabel ; 10 | skos:altLabel ?altLabel ; 11 | skos:scopeNote ?scopeNote ; 12 | skos:broader ?broader_uri ; 13 | skos:narrower ?narrower_uri . 14 | ?broader_uri skos:prefLabel ?broader_prefLabel . 15 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 16 | } 17 | WHERE { 18 | VALUES ?uri { ?uris } 19 | 20 | ?uri a ; 21 | rdfs:label ?prefLabel . 22 | 23 | OPTIONAL { ?uri rdaw:P10086 ?altLabel . } # Varianttitel 24 | OPTIONAL { ?uri rdaw:P10219 ?eerste_datum } 25 | OPTIONAL { ?uri rdaw:P10333 ?opusnummer } 26 | OPTIONAL { ?uri rdaw:P10335 ?thematisch_nummer } 27 | OPTIONAL { 28 | ?uri rdaw:P10019 ?broader_uri . # Is part of work 29 | ?broader_uri rdfs:label ?broader_prefLabel . 30 | } 31 | OPTIONAL { 32 | ?uri rdaw:P10147 ?narrower_uri . # Has part work 33 | ?narrower_uri rdfs:label ?narrower_prefLabel . 34 | } 35 | 36 | BIND( 37 | STRLANG( 38 | CONCAT( 39 | ?prefLabel, ",", 40 | IF(STRLEN(?altLabel) > 0, CONCAT(" Varianttitel: ", ?altLabel, ","), ""), 41 | IF(BOUND(?opusnummer), CONCAT(" Opusnr.: ", ?opusnummer, ","), ""), 42 | IF(BOUND(?thematisch_nummer), CONCAT(" Thematisch nr.: ", ?thematisch_nummer, ","), ""), 43 | IF(BOUND(?eerste_datum), CONCAT(" Datum: ", ?eerste_datum), "") 44 | ), 45 | "nl" 46 | ) AS ?scopeNote 47 | ) 48 | } 49 | LIMIT 1000 50 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/geonames-nl-be-de.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | PREFIX gn: 3 | PREFIX text: 4 | PREFIX vrank: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept ; 8 | skos:prefLabel ?prefLabel_ext ; 9 | skos:altLabel ?altLabel ; 10 | skos:scopeNote ?scopeNote_en ; 11 | skos:broader ?broader ; 12 | vrank:simpleRank ?score . 13 | ?broader skos:prefLabel ?broader_prefLabel . 14 | } 15 | WHERE { 16 | { 17 | SELECT ?uri (MAX(?sc) as ?score) WHERE { 18 | (?uri ?sc) text:query (gn:name gn:alternateName ?query) . 19 | ?uri a gn:Feature ; 20 | gn:featureClass ?featureClass ; 21 | gn:countryCode ?countryCode . 22 | # Limit results to places (P), localities (L), administrative levels (A) and water surfaces (H). 23 | VALUES ?featureClass { gn:P gn:L gn:A gn:H } 24 | 25 | VALUES ?countryCode { "NL" "BE" "DE" } 26 | } 27 | GROUP BY ?uri 28 | ORDER BY DESC(?score) 29 | #LIMIT# 30 | } 31 | 32 | ?uri gn:name ?prefLabel ; 33 | gn:countryCode ?countryCode . 34 | 35 | BIND(CONCAT(?prefLabel," (",UCASE(?countryCode),")") as ?prefLabel_ext) 36 | 37 | OPTIONAL { 38 | ?uri gn:alternateName ?altLabel . 39 | FILTER(?altLabel != "") 40 | } 41 | 42 | OPTIONAL { 43 | ?uri ?parents ?broader . 44 | ?broader gn:name ?broader_prefLabel 45 | VALUES ?parents { gn:parentADM1 gn:parentADM2 } 46 | # filter out the circular links in the converted data 47 | FILTER(?uri != ?broader) 48 | } 49 | 50 | OPTIONAL { 51 | ?uri gn:featureCode/gn:name ?scopeNote . 52 | # scopeNote is always in English. 53 | BIND(STRLANG(?scopeNote, "en") as ?scopeNote_en) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/reconciliation/README.md: -------------------------------------------------------------------------------- 1 | # Network of Terms Reconciliation Service API 2 | 3 | This package is a [Reconciliation Service API](https://reconciliation-api.github.io/specs/latest/) 4 | for the [Network of Terms](http://github.com/netwerk-digitaal-erfgoed/network-of-terms). 5 | 6 | You can use it to **match and reconcile textual strings in your data with terms** from the Network of Terms. 7 | 8 | ## For users 9 | 10 | If you want to use the Reconciliation Service API, configure one or more service endpoints in your 11 | [OpenRefine](https://openrefine.org) application. An endpoint’s URL is structured like 12 | `https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{sourceUri}`. A full list of endpoints can be found in 13 | the [Network of Terms FAQ](https://termennetwerk.netwerkdigitaalerfgoed.nl/reconciliation). 14 | 15 | Note that the Network of Terms provides Reconciliation endpoints only for terminology sources that do not provide such 16 | endpoints themselves. 17 | 18 | ## For developers 19 | 20 | If you want to run the application locally, or host it yourself, you can use our 21 | [Docker image](https://github.com/netwerk-digitaal-erfgoed/network-of-terms/pkgs/container/network-of-terms-reconciliation): 22 | 23 | docker run -p 3123:3123 ghcr.io/netwerk-digitaal-erfgoed/network-of-terms-reconciliation 24 | 25 | If you want to make changes to the application, run it locally using Node (or in a 26 | [Docker container](../../docs/docker.md)): 27 | 28 | git clone https://github.com/netwerk-digitaal-erfgoed/network-of-terms.git 29 | cd network-of-terms 30 | npm install 31 | npx nx serve network-of-terms-reconciliation 32 | 33 | In both cases, Reconciliation endpoints will be available at `http://localhost:3123/reconcile/{distribution URI}`, for 34 | example http://localhost:3123/reconcile/https://data.rkd.nl/rkdartists. 35 | 36 | See [Running the tests](../../docs/tests.md) for more information about this repository’s test suite. 37 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/wikidata-entities-persons.rq: -------------------------------------------------------------------------------- 1 | PREFIX bd: 2 | PREFIX mwapi: 3 | PREFIX rdfs: 4 | PREFIX schema: 5 | PREFIX skos: 6 | PREFIX vrank: 7 | PREFIX wikibase: 8 | PREFIX wd: 9 | PREFIX wdt: 10 | 11 | CONSTRUCT { 12 | ?item a skos:Concept ; 13 | skos:prefLabel ?prefLabel ; 14 | skos:altLabel ?altLabel ; 15 | skos:scopeNote ?description ; 16 | vrank:simpleRank ?score . 17 | } 18 | WHERE { 19 | { 20 | SELECT DISTINCT ?item ?score WHERE { 21 | SERVICE wikibase:mwapi { 22 | bd:serviceParam wikibase:endpoint "www.wikidata.org" ; 23 | wikibase:api "Search" ; 24 | wikibase:limit "once" ; # Disabling continuation improves performance. 25 | mwapi:language "nl" ; 26 | mwapi:srsearch ?query . 27 | ?item wikibase:apiOutputItem mwapi:title . 28 | ?ordinal wikibase:apiOrdinal true. 29 | } 30 | 31 | BIND(-?ordinal AS ?score) 32 | # Only select instances of type "human" 33 | ?item wdt:P31 wd:Q5 . 34 | } 35 | ORDER BY ASC(?ordinal) 36 | #LIMIT# 37 | } 38 | 39 | OPTIONAL { 40 | ?item rdfs:label ?prefLabel . 41 | FILTER(LANG(?prefLabel) = "nl" || LANG(?prefLabel) = "en") 42 | } 43 | OPTIONAL { 44 | ?item skos:altLabel ?altLabel . 45 | FILTER(LANG(?altLabel) = "nl" || LANG(?altLabel) = "en") 46 | } 47 | OPTIONAL { 48 | ?item schema:description ?description . 49 | FILTER(LANG(?description) = "nl" || LANG(?description) = "en") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/rijksmonumenten.rq: -------------------------------------------------------------------------------- 1 | PREFIX ceo: 2 | PREFIX rdf: 3 | PREFIX rdfs: 4 | PREFIX ceosp: 5 | PREFIX skos: 6 | PREFIX xsd: 7 | PREFIX schema: 8 | 9 | CONSTRUCT { 10 | ?uri a skos:Concept ; 11 | skos:prefLabel ?rijksmonumentnummer_tn ; 12 | skos:altLabel ?adres_lang ; 13 | skos:scopeNote ?scopeNote ; 14 | rdfs:seeAlso ?monument_url . 15 | } 16 | WHERE { 17 | VALUES ?uri { ?uris } 18 | 19 | ?uri schema:identifier ?rijksmonumentnummer ; 20 | schema:category ?monumentaard ; 21 | schema:postalCode ?postcode ; 22 | schema:additionalType ?functie ; 23 | schema:addressLocality ?woonplaats ; 24 | schema:addressRegion ?provincie ; 25 | schema:sameAs ?monument_url . 26 | 27 | OPTIONAL { 28 | ?uri schema:name ?naam . 29 | } 30 | 31 | BIND(CONCAT("Rijksmonumentnummer ", ?rijksmonumentnummer) as ?rijksmonumentnummer_tn) 32 | 33 | # Aggregate and get the minimum address as ?adres2 34 | { 35 | SELECT ?uri (MIN(?adres) AS ?adres2) 36 | WHERE { 37 | OPTIONAL { ?uri schema:address ?adres . } 38 | } 39 | GROUP BY ?uri 40 | } 41 | 42 | BIND( 43 | CONCAT( 44 | IF(BOUND(?naam), CONCAT("Naam: ", STR(?naam), " / "), ""), 45 | "Oorspronkelijke functie: ", STR(?functie), 46 | " / Type monument: ", STR(?monumentaard) 47 | ) AS ?scopeNote 48 | ) 49 | 50 | BIND( 51 | CONCAT( 52 | IF(BOUND(?adres2), CONCAT(STR(?adres2), " "), ""), 53 | STR(?postcode), " ", STR(?woonplaats) 54 | ) AS ?adres_lang 55 | ) 56 | } 57 | LIMIT 200 58 | -------------------------------------------------------------------------------- /packages/query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@netwerk-digitaal-erfgoed/network-of-terms-query", 3 | "version": "6.2.8", 4 | "description": "Engine for querying sources in the Network of Terms", 5 | "keywords": [], 6 | "homepage": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms#readme", 7 | "bugs": { 8 | "url": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/netwerk-digitaal-erfgoed/network-of-terms.git" 13 | }, 14 | "license": "EUPL-1.2", 15 | "author": "", 16 | "type": "module", 17 | "exports": { 18 | "./package.json": "./package.json", 19 | ".": { 20 | "types": "./dist/index.d.ts", 21 | "import": "./dist/index.js", 22 | "default": "./dist/index.js" 23 | }, 24 | "./test-utils": { 25 | "types": "./src/test-utils.ts", 26 | "import": "./src/test-utils.ts", 27 | "default": "./src/test-utils.ts" 28 | } 29 | }, 30 | "main": "./dist/index.js", 31 | "types": "./dist/index.d.ts", 32 | "dependencies": { 33 | "@comunica/query-sparql": "^4.3.0", 34 | "@comunica/types": "^4.2.0", 35 | "@comunica/utils-bindings-factory": "^4.2.0", 36 | "@hapi/hoek": "^11.0.7", 37 | "@opentelemetry/api": "^1.9.0", 38 | "@opentelemetry/exporter-metrics-otlp-proto": "0.207.0", 39 | "@opentelemetry/resources": "2.2.0", 40 | "@opentelemetry/sdk-metrics": "2.2.0", 41 | "@opentelemetry/semantic-conventions": "1.37.0", 42 | "@rdfjs/types": "^2.0.1", 43 | "env-schema": "^6.0.1", 44 | "jest-dev-server": "11.0.0", 45 | "joi": "^17.13.3", 46 | "nock": "^14.0.10", 47 | "pino": "^9.7.0", 48 | "pretty-ms": "^9.2.0", 49 | "rdf-data-factory": "^2.0.2", 50 | "spawnd": "^11.0.0", 51 | "tslib": "^2.3.0" 52 | }, 53 | "devDependencies": { 54 | "@comunica/query-sparql-file": "^4.3.0", 55 | "asynciterator": "^3.9.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/query/test/fixtures/terms.ttl: -------------------------------------------------------------------------------- 1 | @prefix skos: . 2 | @prefix rdfs: . 3 | @prefix vrank: . 4 | 5 | 6 | a skos:Concept ; 7 | skos:prefLabel "Nachtwacht"@nl, "The Night Watch"@en ; 8 | skos:altLabel "Nachtwacht alt"@nl, "Night Watch alt"@en ; 9 | skos:scopeNote "One of the most famous Dutch paintings"@en ; 10 | skos:hiddenLabel "art" ; 11 | skos:broader ; 12 | skos:related , 13 | , 14 | ; 15 | skos:exactMatch ; 16 | skos:ignored "Not used by Network of Terms" ; 17 | rdfs:seeAlso ; 18 | vrank:simpleRank 18.0 . 19 | 20 | 21 | a ; 22 | skos:altLabel 23 | "painted things that can be beautiful"@en , 24 | "another altLabel"@en , 25 | "mooie geschilderde dingen"@nl , 26 | "en nog meer"@nl ; 27 | skos:related ; 28 | skos:broader . 29 | 30 | 31 | a ; 32 | skos:prefLabel "Rembrandt"@en ; 33 | vrank:simpleRank 20.5 . 34 | 35 | 36 | a skos:Concept ; 37 | skos:prefLabel "All things art"@en, "Kunstige dingen" ; # last value is fallback and has no language tag on purpose 38 | skos:altLabel "Art"@en ; 39 | skos:narrower . 40 | 41 | 42 | a skos:Concept ; 43 | skos:altLabel "Resource without prefLabel"@en . 44 | 45 | 46 | skos:prefLabel "Exact match"@en . 47 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/muziekschatten-personen.rq: -------------------------------------------------------------------------------- 1 | PREFIX owl: 2 | PREFIX rdaw: 3 | PREFIX schema: 4 | PREFIX skos: 5 | PREFIX som: 6 | 7 | CONSTRUCT { 8 | ?uri a skos:Concept; 9 | skos:prefLabel ?prefLabel ; 10 | skos:altLabel ?schema_alternateName ; 11 | skos:scopeNote ?schema_hasOccupation ; 12 | skos:exactMatch ?exactMatch_uri . 13 | } 14 | WHERE { 15 | VALUES ?uri { ?uris } 16 | 17 | ?uri a schema:Person . 18 | 19 | OPTIONAL { ?uri schema:name ?schema_name } 20 | OPTIONAL { ?uri schema:alternateName ?schema_alternateName } 21 | OPTIONAL { 22 | ?uri schema:hasOccupation ?hasOccupation . 23 | BIND(REPLACE(?hasOccupation, ";;;;;;;;;;;;", "") AS ?schema_hasOccupation) 24 | } 25 | 26 | OPTIONAL { ?uri schema:familyName ?schema_familyName } 27 | OPTIONAL { ?uri schema:givenName ?schema_givenName } 28 | BIND( 29 | COALESCE( 30 | IF(STRLEN(?schema_familyName) > 0 && STRLEN(?schema_givenName) > 0, CONCAT(?schema_familyName, ", ", ?schema_givenName), ?noName), 31 | ?schema_familyName, 32 | ?schema_givenName, 33 | ?schema_name # Fallback 34 | ) AS ?name 35 | ) 36 | OPTIONAL { ?uri som:GDAT ?som_birthYear } 37 | OPTIONAL { ?uri som:SDAT ?som_deathYear } 38 | BIND( 39 | COALESCE( 40 | IF(BOUND(?som_birthYear) && BOUND(?som_deathYear), CONCAT(?som_birthYear, "-", ?som_deathYear), ?noDate), 41 | IF(BOUND(?som_birthYear), CONCAT(?som_birthYear, "-"), ?noDate), 42 | IF(BOUND(?som_deathYear), CONCAT("-", ?som_deathYear), ?noDate), 43 | "" 44 | ) AS ?dates 45 | ) 46 | BIND(CONCAT(?name, IF(?dates != "", CONCAT(" (", ?dates, ")"), "")) AS ?prefLabel) 47 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } # Has no labels. 48 | } 49 | LIMIT 1000 50 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/adamlink-adressen.rq: -------------------------------------------------------------------------------- 1 | PREFIX hg: 2 | PREFIX owl: 3 | PREFIX bif: 4 | PREFIX skos: 5 | PREFIX roar: 6 | PREFIX rdfs: 7 | PREFIX bag: 8 | PREFIX schema: 9 | 10 | CONSTRUCT { 11 | ?uri a skos:Concept ; 12 | skos:prefLabel ?prefLabel ; 13 | skos:altLabel ?altLabel ; 14 | skos:scopeNote ?scopeNote ; 15 | skos:exactMatch ?exactMatch_uri ; 16 | rdfs:seeAlso ?seeAlso_uri . 17 | 18 | ?seeAlso_uri skos:prefLabel ?seeAlso_label . 19 | } 20 | WHERE { 21 | GRAPH { 22 | { 23 | SELECT ?uri WHERE { 24 | ?uri a hg:Address ; 25 | ?predicate ?label . 26 | VALUES ?predicate { rdfs:label skos:prefLabel skos:altLabel } 27 | ?label ?virtuosoQuery . 28 | } 29 | #LIMIT# 30 | } 31 | 32 | OPTIONAL { ?uri rdfs:label ?prefLabel } 33 | OPTIONAL { ?uri skos:altLabel ?altLabel } 34 | OPTIONAL { 35 | ?uri roar:documentedIn ?source . 36 | ?source rdfs:label ?source_name . 37 | BIND(CONCAT("Adres afkomstig van ", ?source_name) AS ?scopeNote) 38 | } 39 | OPTIONAL { ?uri owl:sameAs ?exactMatch_uri } 40 | OPTIONAL { 41 | ?exactMatch_uri a bag:Pand ; 42 | schema:geoContains ?geo . 43 | ?uri schema:geoContains ?geo . 44 | } # A historical address is very similar to a bag:Pand 45 | OPTIONAL { 46 | ?uri schema:geoContains ?geo . 47 | ?seeAlso_uri schema:geoContains ?geo ; 48 | skos:altLabel ?seeAlso_label . 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/eurovoc.rq: -------------------------------------------------------------------------------- 1 | PREFIX bif: 2 | PREFIX skos: 3 | 4 | CONSTRUCT { 5 | ?uri a skos:Concept ; 6 | skos:prefLabel ?prefLabel ; 7 | skos:altLabel ?altLabel ; 8 | skos:broader ?broader_uri ; 9 | skos:narrower ?narrower_uri ; 10 | skos:related ?related_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_prefLabel . 13 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 14 | ?related_uri skos:prefLabel ?related_prefLabel . 15 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 16 | } 17 | WHERE { 18 | #For example: 19 | # Water: http://eurovoc.europa.eu/597 20 | VALUES ?uri { ?uris } 21 | 22 | ?uri a skos:Concept . 23 | 24 | OPTIONAL { 25 | ?uri skos:prefLabel ?prefLabel . 26 | FILTER(LANG(?prefLabel) = "nl" || LANG(?prefLabel) = "en") 27 | } 28 | OPTIONAL { 29 | ?uri skos:altLabel ?altLabel . 30 | FILTER(LANG(?altLabel) = "nl" || LANG(?altLabel) = "en") 31 | } 32 | OPTIONAL { 33 | ?uri skos:broader ?broader_uri . 34 | ?broader_uri skos:prefLabel ?broader_prefLabel . 35 | FILTER(LANG(?broader_prefLabel) = "nl" || LANG(?broader_prefLabel) = "en") 36 | } 37 | OPTIONAL { 38 | ?uri skos:narrower ?narrower_uri . 39 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 40 | FILTER(LANG(?narrower_prefLabel) = "nl" || LANG(?narrower_prefLabel) = "en") 41 | } 42 | OPTIONAL { 43 | ?uri skos:related ?related_uri . 44 | ?related_uri skos:prefLabel ?related_prefLabel . 45 | FILTER(LANG(?related_prefLabel) = "nl" || LANG(?related_prefLabel) = "en") 46 | } 47 | OPTIONAL { 48 | ?uri skos:exactMatch ?exactMatch_uri . 49 | ?exactMatch_uri skos:exactMatch ?exactMatch_prefLabel . 50 | FILTER(LANG(?exactMatch_prefLabel) = "nl" || LANG(?exactMatch_prefLabel) = "en") 51 | } 52 | } 53 | LIMIT 1000 54 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/adamlink-straten.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://adamlink.nl/geo/streets/list", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Adamlink: streets in Amsterdam" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Adamlink: straten in Amsterdam" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.amsterdamtimemachine.nl" 23 | } 24 | ], 25 | "url": ["https://adamlink.nl/geo/street/"], 26 | "mainEntityOfPage": ["https://adamlink.nl"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Streets in Amsterdam" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Straten in Amsterdam" 35 | } 36 | ], 37 | "inLanguage": "nl", 38 | "distribution": [ 39 | { 40 | "@id": "https://api.lod.uba.uva.nl/datasets/ATM/ATM-KG/services/ATM-KG/sparql#adamlink-streets", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://api.lod.uba.uva.nl/datasets/ATM/ATM-KG/services/ATM-KG/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/adamlink-straten.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/adamlink-straten.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/ied.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_prefLabel . 13 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 14 | } 15 | WHERE { 16 | # For example: 17 | # Concept: 18 | # Event: 19 | # Organization: 20 | VALUES ?uri { ?uris } 21 | 22 | ?uri a skos:Concept . 23 | 24 | # Exclude terms from the 'Technische Lijsten', a concept scheme with terms that should only be used by NOB 25 | FILTER NOT EXISTS { 26 | ?uri skos:inScheme 27 | } 28 | OPTIONAL { 29 | ?uri skos:prefLabel ?prefLabel . 30 | FILTER(LANG(?prefLabel) = "nl") 31 | } 32 | OPTIONAL { 33 | ?uri skos:altLabel ?altLabel . 34 | FILTER(LANG(?altLabel) = "nl") 35 | } 36 | OPTIONAL { 37 | ?uri skos:hiddenLabel ?hiddenLabel . 38 | FILTER(LANG(?hiddenLabel) = "nl") 39 | } 40 | OPTIONAL { 41 | ?uri skos:scopeNote ?scopeNote . 42 | FILTER(LANG(?scopeNote) = "nl") 43 | } 44 | OPTIONAL { 45 | ?uri skos:broader ?broader_uri . 46 | ?broader_uri skos:prefLabel ?broader_prefLabel . 47 | FILTER(LANG(?broader_prefLabel) = "nl") 48 | } 49 | OPTIONAL { 50 | ?uri skos:narrower ?narrower_uri . 51 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 52 | FILTER(LANG(?narrower_prefLabel) = "nl") 53 | } 54 | OPTIONAL { 55 | ?uri skos:exactMatch ?exactMatch_uri . # Has no labels. 56 | } 57 | } 58 | LIMIT 1000 -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/ied.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_prefLabel . 13 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 14 | } 15 | WHERE { 16 | { 17 | SELECT * WHERE { 18 | ?uri ?predicate ?label . 19 | # Exclude terms from the 'Technische Lijsten', a concept scheme with terms that should only be used by NOB 20 | FILTER NOT EXISTS { 21 | ?uri skos:inScheme 22 | } 23 | VALUES ?predicate { skos:prefLabel skos:altLabel } 24 | FILTER(LANG(?label) = "nl") 25 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 26 | } 27 | #LIMIT# 28 | } 29 | OPTIONAL { 30 | ?uri skos:prefLabel ?prefLabel . 31 | FILTER(LANG(?prefLabel) = "nl" ) 32 | } 33 | OPTIONAL { 34 | ?uri skos:scopeNote ?scopeNote . 35 | FILTER(LANG(?scopeNote) = "nl") 36 | } 37 | OPTIONAL { 38 | ?uri skos:altLabel ?altLabel . 39 | FILTER(LANG(?altLabel) = "nl") 40 | } 41 | OPTIONAL { 42 | ?uri skos:hiddenLabel ?hiddenLabel . 43 | FILTER(LANG(?hiddenLabel) = "nl") 44 | } 45 | OPTIONAL { 46 | ?uri skos:broader ?broader_uri . 47 | ?broader_uri skos:prefLabel ?broader_prefLabel . 48 | FILTER(LANG(?broader_prefLabel) = "nl") 49 | } 50 | OPTIONAL { 51 | ?uri skos:narrower ?narrower_uri . 52 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 53 | FILTER(LANG(?narrower_prefLabel) = "nl") 54 | } 55 | OPTIONAL { 56 | ?uri skos:exactMatch ?exactMatch_uri . # Has no labels. 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/graphql/src/server.ts: -------------------------------------------------------------------------------- 1 | import fastify, { 2 | FastifyInstance, 3 | FastifyLoggerOptions, 4 | FastifyRequest, 5 | } from 'fastify'; 6 | import mercurius from 'mercurius'; 7 | import { resolvers } from './resolvers.js'; 8 | import { schema } from './schema.js'; 9 | import fastifyCors from '@fastify/cors'; 10 | import { Server } from 'http'; 11 | import { 12 | Catalog, 13 | comunica, 14 | getHttpLogger, 15 | } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; 16 | import { EnvSchemaData } from 'env-schema'; 17 | import mercuriusLogging from 'mercurius-logging'; 18 | import fastifyAccepts from '@fastify/accepts'; 19 | 20 | export async function server( 21 | catalog: Catalog, 22 | config: EnvSchemaData, 23 | ): Promise> { 24 | const logger: FastifyLoggerOptions = getHttpLogger({ 25 | name: 'http', 26 | level: 'info', 27 | }); 28 | const server = fastify({ logger, trustProxy: config.TRUST_PROXY as boolean }); 29 | await server.register(fastifyAccepts); 30 | await server.register(mercurius, { 31 | schema: schema(catalog.getLanguages()), 32 | resolvers, 33 | graphiql: true, 34 | context: async (req: FastifyRequest) => { 35 | return { 36 | catalog, 37 | // Use a fresh Comunica instance for each request to prevent Comunica's rate-limiting from delaying requests. 38 | // We need requests to fail within the user-supplied timeoutMs. 39 | comunica: comunica(), 40 | catalogLanguage: req.language(['nl', 'en']) || 'nl', 41 | }; 42 | }, 43 | }); 44 | await server.register(fastifyCors); 45 | await server.register(mercuriusLogging, { 46 | logBody: true, 47 | }); 48 | server.route({ 49 | method: 'GET', 50 | url: '/', 51 | handler: (req, reply) => { 52 | void reply.redirect('/graphiql'); 53 | }, 54 | }); 55 | 56 | // Redirect /playground to /graphiql for BC. 57 | server.route({ 58 | method: 'GET', 59 | url: '/playground', 60 | handler: (req, reply) => { 61 | void reply.redirect('/graphiql', 301); 62 | }, 63 | }); 64 | 65 | return server; 66 | } 67 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/muziekschatten-onderwerpen.rq: -------------------------------------------------------------------------------- 1 | PREFIX owl: 2 | PREFIX rdaw: 3 | PREFIX schema: 4 | PREFIX skos: 5 | PREFIX som: 6 | 7 | CONSTRUCT { 8 | ?uri a skos:Concept; 9 | skos:prefLabel ?prefLabel; 10 | skos:scopeNote ?scopeNote_nl, ?scopeNote_en; 11 | skos:broader ?broader_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_schema_name . 14 | } 15 | WHERE { 16 | VALUES ?uri { ?uris } 17 | 18 | ?uri a skos:Concept 19 | BIND( AS ?datasetUri). 20 | 21 | OPTIONAL { 22 | ?uri schema:name ?prefLabel . 23 | } 24 | OPTIONAL { 25 | ?uri skos:broader ?broader_uri . 26 | ?broader_uri schema:name ?broader_schema_name . 27 | } 28 | OPTIONAL { 29 | ?uri schema:keywords ?schema_keywords 30 | } 31 | OPTIONAL { 32 | ?uri som:DC ?dc 33 | } 34 | BIND( 35 | STRLANG( 36 | COALESCE( 37 | IF(BOUND(?schema_keywords) && BOUND(?dc), CONCAT("Trefwoorden: ", ?schema_keywords, " | ", "Dewey: ", ?dc), ?noScopeNote), 38 | IF(BOUND(?schema_keywords), CONCAT("Trefwoorden: ", ?schema_keywords), ?noScopeNote), 39 | IF(BOUND(?dc), CONCAT("Dewey: ", ?dc), ?noScopeNote) 40 | ), 41 | "nl" 42 | ) AS ?scopeNote_nl 43 | ) 44 | 45 | BIND( 46 | STRLANG( 47 | COALESCE( 48 | IF(BOUND(?schema_keywords) && BOUND(?dc), CONCAT("Keywords: ", ?schema_keywords, " | ", "Dewey: ", ?dc), ?noScopeNote), 49 | IF(BOUND(?schema_keywords), CONCAT("Keywords: ", ?schema_keywords), ?noScopeNote), 50 | IF(BOUND(?dc), CONCAT("Dewey: ", ?dc), ?noScopeNote) 51 | ), 52 | "en" 53 | ) AS ?scopeNote_en 54 | ) 55 | 56 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } # Has no labels. 57 | } 58 | LIMIT 1000 59 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/gtaa-genres.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/Genre", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GTAA: genres" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GTAA: genres" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.beeldengeluid.nl/" 23 | } 24 | ], 25 | "url": ["http://data.beeldengeluid.nl/gtaa/"], 26 | "mainEntityOfPage": [ 27 | "https://www.beeldengeluid.nl/kennis/kennisthemas/metadata/gemeenschappelijke-thesaurus-audiovisuele-archieven" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Genres for describing audiovisual material" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Genres voor het beschrijven van audiovisueel materiaal" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0028", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://$GTAA_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/gtaa.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/gtaa.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/gtaa.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:related ?related_uri ; 12 | skos:inScheme ?datasetUri ; 13 | skos:exactMatch ?exactMatch_uri . 14 | ?broader_uri skos:prefLabel ?broader_prefLabel . 15 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 16 | ?related_uri skos:prefLabel ?related_prefLabel . 17 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 18 | } 19 | WHERE { 20 | 21 | VALUES ?uri { ?uris } 22 | 23 | ?uri a skos:Concept ; 24 | skos:inScheme ?datasetUri . 25 | 26 | OPTIONAL { 27 | ?uri skos:prefLabel ?prefLabel . 28 | FILTER(LANG(?prefLabel) = "nl" ) 29 | } 30 | OPTIONAL { 31 | ?uri skos:altLabel ?altLabel . 32 | FILTER(LANG(?altLabel) = "nl") 33 | } 34 | OPTIONAL { 35 | ?uri skos:hiddenLabel ?hiddenLabel . 36 | FILTER(LANG(?hiddenLabel) = "nl") 37 | } 38 | OPTIONAL { 39 | ?uri skos:scopeNote ?scopeNote . 40 | FILTER(LANG(?scopeNote) = "nl") 41 | } 42 | OPTIONAL { 43 | ?uri skos:broader ?broader_uri . 44 | ?broader_uri skos:prefLabel ?broader_prefLabel . 45 | FILTER(LANG(?broader_prefLabel) = "nl") 46 | } 47 | OPTIONAL { 48 | ?uri skos:narrower ?narrower_uri . 49 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 50 | FILTER(LANG(?narrower_prefLabel) = "nl") 51 | } 52 | OPTIONAL { 53 | ?uri skos:related ?related_uri . 54 | ?related_uri skos:prefLabel ?related_prefLabel . 55 | FILTER(LANG(?related_prefLabel) = "nl") 56 | } 57 | OPTIONAL { 58 | ?uri skos:exactMatch ?exactMatch_uri . 59 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 60 | FILTER(LANG(?exactMatch_prefLabel) = "nl") 61 | } 62 | } 63 | LIMIT 1000 64 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/adamlink-adressen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://adamlink.nl/geo/addresses/start/", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Adamlink: historical addresses in Amsterdam" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Adamlink: historische adressen in Amsterdam" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.amsterdamtimemachine.nl" 23 | } 24 | ], 25 | "url": ["https://adamlink.nl/geo/address/"], 26 | "mainEntityOfPage": ["https://adamlink.nl"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Historical addresses in Amsterdam" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Historische adressen in Amsterdam" 35 | } 36 | ], 37 | "inLanguage": "nl", 38 | "distribution": [ 39 | { 40 | "@id": "https://api.lod.uba.uva.nl/datasets/ATM/ATM-KG/services/ATM-KG/sparql#adamlink-addresses", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://api.lod.uba.uva.nl/datasets/ATM/ATM-KG/services/ATM-KG/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/adamlink-adressen.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/adamlink-adressen.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/stcn-drukkers.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.bibliotheken.nl/id/dataset/stcn/printers", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "STCN: printers" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "STCN: drukkers" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "http://data.bibliotheken.nl/id/thes/p075301482" 23 | } 24 | ], 25 | "url": ["http://data.bibliotheken.nl/id/thes/"], 26 | "mainEntityOfPage": [ 27 | "https://data.bibliotheken.nl/KB/-/stories/STCN-persons" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Printers, a subset of Short-Title Catalogue Netherlands (STCN)" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Uitgevers, een subset van Short-Title Catalogue Netherlands (STCN)" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "http://data.bibliotheken.nl/thes/drukkers/sparql", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://data.bibliotheken.nl/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/stcn-drukkers.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/brinkman-nta-stcn.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/abr.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:related ?related_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_prefLabel . 14 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 15 | ?related_uri skos:prefLabel ?related_prefLabel . 16 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 17 | } 18 | WHERE { 19 | { 20 | SELECT DISTINCT ?uri WHERE { 21 | ?uri a skos:Concept ; 22 | skos:inScheme ; 23 | ?predicate ?label . 24 | 25 | VALUES ?predicate { skos:prefLabel skos:altLabel skos:hiddenLabel } 26 | 27 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 28 | 29 | } 30 | #LIMIT# 31 | } 32 | OPTIONAL { 33 | ?uri skos:prefLabel ?prefLabel . 34 | } 35 | OPTIONAL { 36 | ?uri skos:altLabel ?altLabel . 37 | } 38 | OPTIONAL { 39 | ?uri skos:hiddenLabel ?hiddenLabel . 40 | } 41 | OPTIONAL { 42 | ?uri skos:scopeNote ?scopeNote . 43 | } 44 | OPTIONAL { 45 | ?uri skos:definition ?scopeNote . 46 | } 47 | OPTIONAL { 48 | ?uri skos:broader ?broader_uri . 49 | ?broader_uri skos:prefLabel ?broader_prefLabel . 50 | } 51 | OPTIONAL { 52 | ?uri skos:narrower ?narrower_uri . 53 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 54 | } 55 | OPTIONAL { 56 | ?uri skos:related ?related_uri . 57 | ?related_uri skos:prefLabel ?related_prefLabel . 58 | } 59 | OPTIONAL { 60 | ?uri skos:exactMatch ?exactMatch_uri . 61 | OPTIONAL { 62 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/query/test/query.test.ts: -------------------------------------------------------------------------------- 1 | import { testCatalog } from '../src/test-utils.js'; 2 | import { QueryMode, QueryTermsService } from '../src/index.js'; 3 | import { QueryEngine } from '@comunica/query-sparql'; 4 | import { ArrayIterator } from 'asynciterator'; 5 | import type { Term } from '@rdfjs/types'; 6 | import { beforeEach, describe, expect, it, vi } from 'vitest'; 7 | 8 | const catalog = testCatalog(1000); 9 | const comunicaMock = { 10 | queryQuads: vi.fn( 11 | (_query: string, _config: object) => 12 | new ArrayIterator([], { autoStart: false }), 13 | ), 14 | }; 15 | const service = new QueryTermsService({ 16 | comunica: comunicaMock as unknown as QueryEngine, 17 | }); 18 | 19 | describe('Query', () => { 20 | beforeEach(async () => { 21 | vi.clearAllMocks(); 22 | }); 23 | it('passes dataset IRI query parameter to Comunica', async () => { 24 | const config = await query( 25 | 'https://data.netwerkdigitaalerfgoed.nl/rkd/rkdartists/sparql', 26 | ); 27 | expect(config.initialBindings.get('datasetUri')?.value).toEqual( 28 | 'https://data.rkd.nl/rkdartists', 29 | ); 30 | }); 31 | 32 | it('supports HTTP authentication', async () => { 33 | const config = await query( 34 | 'https://data.beeldengeluid.nl/id/datadownload/0026', 35 | ); 36 | 37 | // Must not contain credentials in URL... 38 | expect(config.sources[0].value).toEqual( 39 | 'https://gtaa.apis.beeldengeluid.nl/sparql', 40 | ); 41 | // ... but in separate httpAuth context element. 42 | expect(config.httpAuth).toEqual('username:password'); 43 | }); 44 | }); 45 | 46 | const query = async (iri: string) => { 47 | const dataset = catalog.getDatasetByDistributionIri(iri)!; 48 | await service.search( 49 | 'van gogh', 50 | QueryMode.OPTIMIZED, 51 | dataset, 52 | dataset.getDistributionByIri(iri)!, 53 | 10_000, 54 | 10_000, 55 | ); 56 | 57 | return comunicaMock.queryQuads.mock.calls[0][1] as { 58 | httpAuth: string; 59 | sources: [ 60 | { 61 | type: 'sparql'; 62 | value: string; 63 | }, 64 | ]; 65 | initialBindings: Map; 66 | }; 67 | }; 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/muziekschatten-personen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.muziekschatten.nl/#personen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Muziekschatten: persons" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Muziekschatten: personen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.omroepmuziek.nl" 23 | } 24 | ], 25 | "url": ["https://data.muziekschatten.nl/som/"], 26 | "mainEntityOfPage": ["https://data.muziekschatten.nl/som/"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Persons in the catalog of the sheet music collection of Stichting Omroep Muziek" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Personen in de catalogus van de bladmuziekcollectie van Stichting Omroep Muziek" 35 | } 36 | ], 37 | "inLanguage": "nl", 38 | "distribution": [ 39 | { 40 | "@id": "https://data.muziekschatten.nl/sparql/#personen", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://data.muziekschatten.nl/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/muziekschatten-personen.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/muziekschatten-personen.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/rkdartists.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.rkd.nl/rkdartists", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "RKDartists" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "RKDartists" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://rkd.nl" 23 | } 24 | ], 25 | "url": ["https://data.rkd.nl/artists/"], 26 | "mainEntityOfPage": ["https://rkd.nl/nl/explore/artists"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Biographical data of Dutch and foreign artists from the Middle Ages to the present" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Biografische gegevens van Nederlandse en buitenlandse kunstenaars van de middeleeuwen tot heden" 35 | } 36 | ], 37 | "inLanguage": ["en", "nl"], 38 | "distribution": [ 39 | { 40 | "@id": "https://api.rkd.triply.cc/datasets/rkd/RKD-SDO-Knowledge-Graph/sparql", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://api.rkd.triply.cc/datasets/rkd/RKD-SDO-Knowledge-Graph/services/Virtuoso/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/rkdartists.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/rkdartists.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/canon-van-limburg.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX schema: 3 | PREFIX skos: 4 | 5 | CONSTRUCT { 6 | ?uri a skos:Concept ; 7 | skos:prefLabel ?prefLabel ; 8 | skos:altLabel ?altLabel ; 9 | skos:scopeNote ?scopeNote ; 10 | skos:exactMatch ?exactMatch_uri ; 11 | skos:broader ?broader_uri ; 12 | skos:narrower ?narrower_uri ; 13 | skos:related ?related_uri ; 14 | skos:seeAlso ?seeAlso_uri . 15 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 16 | ?broader_uri skos:prefLabel ?broader_prefLabel . 17 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 18 | ?related_uri skos:prefLabel ?related_prefLabel . 19 | ?seeAlso_uri skos:prefLabel ?seeAlso_prefLabel . 20 | } 21 | WHERE { 22 | GRAPH { 23 | SELECT * WHERE { 24 | VALUES ?uri { ?uris } 25 | 26 | ?uri a skos:Concept ; 27 | skos:prefLabel ?prefLabel ; 28 | skos:altLabel ?altLabel . 29 | 30 | OPTIONAL { ?uri skos:scopeNote ?scopeNote } 31 | OPTIONAL { 32 | ?uri skos:exactMatch ?exactMatch_uri . 33 | OPTIONAL { ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel } 34 | } 35 | OPTIONAL { 36 | ?uri skos:broader ?broader_uri . 37 | OPTIONAL { ?broader_uri skos:prefLabel ?broader_prefLabel } 38 | } 39 | OPTIONAL { 40 | ?uri skos:narrower ?narrower_uri . 41 | OPTIONAL { ?narrower_uri skos:prefLabel ?narrower_prefLabel } 42 | } 43 | OPTIONAL { 44 | ?uri skos:related ?related_uri . 45 | OPTIONAL { ?related_uri skos:prefLabel ?related_prefLabel } 46 | } 47 | OPTIONAL { 48 | ?uri skos:seeAlso ?seeAlso_uri . 49 | OPTIONAL { ?seeAlso_uri skos:prefLabel ?seeAlso_prefLabel } 50 | } 51 | 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/gtaa-persoonsnamen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/Persoonsnamen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GTAA: personal names" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GTAA: persoonsnamen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.beeldengeluid.nl/" 23 | } 24 | ], 25 | "url": ["http://data.beeldengeluid.nl/gtaa/"], 26 | "mainEntityOfPage": [ 27 | "https://www.beeldengeluid.nl/kennis/kennisthemas/metadata/gemeenschappelijke-thesaurus-audiovisuele-archieven" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Persons for describing audiovisual material" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Personen voor het beschrijven van audiovisueel materiaal" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0026", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://$GTAA_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/gtaa.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/gtaa.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/muziekschatten-klassieke-werken.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.muziekschatten.nl/som/Klassiekewerken", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Muziekschatten: classical music works" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Muziekschatten: klassieke muziekwerken" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Objecten" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.podiumkunst.net" 23 | } 24 | ], 25 | "url": ["https://data.muziekschatten.nl/som/work/"], 26 | "mainEntityOfPage": ["https://data.muziekschatten.nl/"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Terms for describing (Dutch) classical music works" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Termen voor het beschrijven van (Nederlandse) klassieke muziekwerken" 35 | } 36 | ], 37 | "inLanguage": "nl", 38 | "distribution": [ 39 | { 40 | "@id": "https://data.muziekschatten.nl/sparql/#klassiekewerken", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://data.muziekschatten.nl/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/muziekschatten-klassiekewerken.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/muziekschatten-klassiekewerken.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/wo2biografie.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.niod.nl/WO2_biografieen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "WW2 biographies" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "WO2-biografieën" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.wo2net.nl" 23 | } 24 | ], 25 | "url": ["https://data.niod.nl/WO2_biografieen/"], 26 | "mainEntityOfPage": [ 27 | "https://www.oorlogsbronnen.nl/nieuws/project-wo2-thesaurus" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Short biographies of persons who played a key role in World War II" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Korte biografieën van personen die een sleutelrol speelden in de Tweede Wereldoorlog" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.niod.nl/PoolParty/sparql/WO2_biografieen", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://api.linkeddata.cultureelerfgoed.nl/datasets/thesauri/oorlogsbronnen-biografieen/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/poolparty.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/poolparty.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/iconclass.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://iconclass.org", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Iconclass" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Iconclass" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 21 | }, 22 | { 23 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Periodes" 24 | } 25 | ], 26 | "creator": [ 27 | { 28 | "@id": "https://henrivandewaalfoundation.org" 29 | } 30 | ], 31 | "url": ["https://iconclass.org/"], 32 | "mainEntityOfPage": ["https://iconclass.org"], 33 | "description": [ 34 | { 35 | "@language": "en", 36 | "@value": "Terms for describing the content of images" 37 | }, 38 | { 39 | "@language": "nl", 40 | "@value": "Termen voor het beschrijven van de inhoud van afbeeldingen" 41 | } 42 | ], 43 | "inLanguage": "en", 44 | "distribution": [ 45 | { 46 | "@id": "https://iconclass.org/sparql", 47 | "@type": "DataDownload", 48 | "contentUrl": "https://iconclass.org/sparql", 49 | "encodingFormat": "application/sparql-query", 50 | "potentialAction": [ 51 | { 52 | "@type": "SearchAction", 53 | "query": "file://../queries/search/iconclass.rq" 54 | }, 55 | { 56 | "@type": "FindAction", 57 | "query": "file://../queries/lookup/iconclass.rq" 58 | }, 59 | { 60 | "@type": "Action", 61 | "target": { 62 | "@type": "EntryPoint", 63 | "actionApplication": { 64 | "@id": "https://reconciliation-api.github.io/specs/latest/", 65 | "@type": "SoftwareApplication" 66 | }, 67 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 68 | } 69 | } 70 | ] 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/gtaa-classificatie.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/Classificatie", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GTAA: classification" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GTAA: classificatie" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.beeldengeluid.nl/" 23 | } 24 | ], 25 | "url": ["http://data.beeldengeluid.nl/gtaa/"], 26 | "mainEntityOfPage": [ 27 | "https://www.beeldengeluid.nl/kennis/kennisthemas/metadata/gemeenschappelijke-thesaurus-audiovisuele-archieven" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Classifications for describing audiovisual material" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Classificaties voor het beschrijven van audiovisueel materiaal" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0027", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://$GTAA_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/gtaa.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/gtaa.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/poolparty.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:related ?related_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_prefLabel . 14 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 15 | ?related_uri skos:prefLabel ?related_prefLabel . 16 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 17 | } 18 | WHERE { 19 | { 20 | SELECT DISTINCT ?uri WHERE { 21 | ?uri a skos:Concept ; 22 | ?predicate ?label . 23 | VALUES ?predicate { skos:prefLabel skos:altLabel } 24 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 25 | } 26 | #LIMIT# 27 | } 28 | OPTIONAL { 29 | ?uri skos:prefLabel ?prefLabel . 30 | } 31 | OPTIONAL { 32 | ?uri skos:altLabel ?altLabel . 33 | } 34 | OPTIONAL { 35 | ?uri skos:hiddenLabel ?hiddenLabel . 36 | } 37 | OPTIONAL { 38 | ?uri skos:scopeNote ?scopeNote . 39 | } 40 | OPTIONAL { 41 | ?uri skos:definition ?scopeNote . 42 | } 43 | OPTIONAL { 44 | ?uri skos:broader ?broader_uri . 45 | ?broader_uri skos:prefLabel ?broader_prefLabel . 46 | } 47 | OPTIONAL { 48 | ?uri skos:narrower ?narrower_uri . 49 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 50 | } 51 | OPTIONAL { 52 | ?uri skos:related ?related_uri . 53 | ?related_uri skos:prefLabel ?related_prefLabel . 54 | } 55 | OPTIONAL { 56 | ?uri skos:exactMatch ?exactMatch_uri . 57 | FILTER(!STRSTARTS(STR(?exactMatch_uri), "https://data.cultureelerfgoed.nl/term/id/rn")) 58 | FILTER(!STRSTARTS(STR(?exactMatch_uri), "https://data.cultureelerfgoed.nl/semnet")) 59 | OPTIONAL { 60 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/gtaa-geografische-namen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/GeografischeNamen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GTAA: geographical names" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GTAA: geografische namen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.beeldengeluid.nl/" 23 | } 24 | ], 25 | "url": ["http://data.beeldengeluid.nl/gtaa/"], 26 | "mainEntityOfPage": [ 27 | "https://www.beeldengeluid.nl/kennis/kennisthemas/metadata/gemeenschappelijke-thesaurus-audiovisuele-archieven" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Geographical names for describing audiovisual material" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Geografische namen voor het beschrijven van audiovisueel materiaal" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0029", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://$GTAA_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/gtaa.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/gtaa.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/homosaurus.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.ihlia.nl/homosaurus", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Homosaurus" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Homosaurus" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://ihlia.nl" 23 | } 24 | ], 25 | "url": ["https://data.ihlia.nl/homosaurus/"], 26 | "mainEntityOfPage": ["https://homosaurus.org"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Terms for describing LGBTIQ (Lesbian/Gay/Bisexual/Transgender/Intersex/Queer) publications and heritage" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Termen voor het beschrijven van LHBTIQ (Lesbisch/Homo/Biseksueel/Transgender/Intersekse/Queer)-publicaties en -erfgoed" 35 | } 36 | ], 37 | "inLanguage": ["nl", "en"], 38 | "distribution": [ 39 | { 40 | "@id": "https://data.ihlia.nl/PoolParty/sparql/homosaurus", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://api.linkeddata.cultureelerfgoed.nl/datasets/thesauri/homosaurus/services/homosaurus-virtuoso/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/poolparty.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/poolparty.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/nta.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.bibliotheken.nl/id/dataset/persons", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Dutch National Thesaurus for Author Names" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Nederlandse Thesaurus van Auteursnamen" 13 | } 14 | ], 15 | "alternateName": [ 16 | { 17 | "@language": "nl", 18 | "@value": "NTA" 19 | } 20 | ], 21 | "genre": [ 22 | { 23 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 24 | } 25 | ], 26 | "creator": [ 27 | { 28 | "@id": "http://data.bibliotheken.nl/id/thes/p075301482" 29 | } 30 | ], 31 | "url": ["http://data.bibliotheken.nl/id/thes/"], 32 | "mainEntityOfPage": ["https://data.bibliotheken.nl/KB/-/stories/NTA"], 33 | "description": [ 34 | { 35 | "@language": "en", 36 | "@value": "Names and other personal data of authors" 37 | }, 38 | { 39 | "@language": "nl", 40 | "@value": "Naams- en andere persoonsgegevens van auteurs" 41 | } 42 | ], 43 | "inLanguage": "nl", 44 | "distribution": [ 45 | { 46 | "@id": "http://data.bibliotheken.nl/thesp/sparql", 47 | "@type": "DataDownload", 48 | "contentUrl": "https://data.bibliotheken.nl/sparql", 49 | "encodingFormat": "application/sparql-query", 50 | "potentialAction": [ 51 | { 52 | "@type": "SearchAction", 53 | "query": "file://../queries/search/nta.rq" 54 | }, 55 | { 56 | "@type": "FindAction", 57 | "query": "file://../queries/lookup/brinkman-nta-stcn.rq" 58 | }, 59 | { 60 | "@type": "Action", 61 | "target": { 62 | "@type": "EntryPoint", 63 | "actionApplication": { 64 | "@id": "https://reconciliation-api.github.io/specs/latest/", 65 | "@type": "SoftwareApplication" 66 | }, 67 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 68 | } 69 | } 70 | ] 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/rijksmonumenten.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://linkeddata.cultureelerfgoed.nl/cho-kennis/id/rijksmonument/", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Rijksmonumentenregister" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Rijksmonumentenregister" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Gebouwen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.cultureelerfgoed.nl" 23 | } 24 | ], 25 | "url": [ 26 | "https://linkeddata.cultureelerfgoed.nl/cho-kennis/id/rijksmonument/" 27 | ], 28 | "mainEntityOfPage": ["https://monumentenregister.cultureelerfgoed.nl/"], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "National monuments in the Netherlands" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Rijksmonumenten" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://linkeddata.cultureelerfgoed.nl/cho-kennis/id/rijksmonument/", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://api.linkeddata.cultureelerfgoed.nl/datasets/rce/Rijksmonumenten-sdo/services/Rijksmonumenten-sdo/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/rijksmonumenten.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/rijksmonumenten.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/rtf-personen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://fryslan.regiotermen.nl/personen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Regiotermen Fryslân: Persons" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Regiotermen Fryslân: Personen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.tresoar.nl" 23 | } 24 | ], 25 | "url": ["https://fryslan.regiotermen.nl/"], 26 | "mainEntityOfPage": ["https://fryslan.regiotermen.nl/personen"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Frysian historical persons, a collection of historical figures from the Dutch region of Fryslân." 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Friese historische personen, een verzameling van historische figuren uit de Nederlandse regio Fryslân." 35 | } 36 | ], 37 | "inLanguage": "nl", 38 | "distribution": [ 39 | { 40 | "@id": "https://graph.friesland.regiotermen.nl/repositories/friese_thesaurus", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://graph.friesland.regiotermen.nl/repositories/friese_thesaurus", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/rtf-personen.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/rtf-personen.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/poolparty.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:related ?related_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_prefLabel . 14 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 15 | ?related_uri skos:prefLabel ?related_prefLabel . 16 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 17 | } 18 | WHERE { 19 | # For example: 20 | # Aardewerk: 21 | # Aardewerk: 22 | VALUES ?uri { ?uris } 23 | 24 | ?uri a skos:Concept . 25 | 26 | OPTIONAL { 27 | ?uri skos:prefLabel ?prefLabel . 28 | } 29 | OPTIONAL { 30 | ?uri skos:altLabel ?altLabel . 31 | } 32 | OPTIONAL { 33 | ?uri skos:hiddenLabel ?hiddenLabel . 34 | } 35 | OPTIONAL { 36 | ?uri skos:scopeNote ?scopeNote . 37 | } 38 | OPTIONAL { 39 | ?uri skos:definition ?scopeNote . 40 | } 41 | OPTIONAL { 42 | ?uri skos:broader ?broader_uri . 43 | ?broader_uri skos:prefLabel ?broader_prefLabel . 44 | } 45 | OPTIONAL { 46 | ?uri skos:narrower ?narrower_uri . 47 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 48 | } 49 | OPTIONAL { 50 | ?uri skos:related ?related_uri . 51 | ?related_uri skos:prefLabel ?related_prefLabel . 52 | } 53 | OPTIONAL { 54 | ?uri skos:exactMatch ?exactMatch_uri . 55 | FILTER(!STRSTARTS(STR(?exactMatch_uri), "https://data.cultureelerfgoed.nl/term/id/rn")) 56 | FILTER(!STRSTARTS(STR(?exactMatch_uri), "https://data.cultureelerfgoed.nl/semnet")) 57 | OPTIONAL { 58 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 59 | } 60 | } 61 | } 62 | LIMIT 1000 63 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/muziekschatten-klassiekewerken.rq: -------------------------------------------------------------------------------- 1 | PREFIX bif: 2 | PREFIX rdf: 3 | PREFIX rdfs: 4 | PREFIX rdaw: 5 | PREFIX skos: 6 | PREFIX schema: 7 | PREFIX som: 8 | 9 | CONSTRUCT { 10 | ?uri a skos:Concept; 11 | skos:prefLabel ?prefLabel ; 12 | skos:altLabel ?altLabel ; 13 | skos:scopeNote ?scopeNote ; 14 | skos:broader ?broader_uri ; 15 | skos:narrower ?narrower_uri . 16 | ?broader_uri skos:prefLabel ?broader_prefLabel . 17 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 18 | } 19 | WHERE { 20 | { 21 | SELECT ?uri WHERE { 22 | ?uri a ; 23 | ?predicate ?label . 24 | VALUES ?predicate { rdfs:label rdaw:P10086 rdaw:P10223 rdaw:P10333 rdaw:P10335 rdaw:P10047 } 25 | ?label ?virtuosoQuery . 26 | } 27 | #LIMIT# 28 | } 29 | 30 | ?uri rdfs:label ?prefLabel . 31 | 32 | OPTIONAL { ?uri rdaw:P10086 ?altLabel } # Varianttitel 33 | OPTIONAL { ?uri rdaw:P10219 ?eerste_datum } 34 | OPTIONAL { ?uri rdaw:P10333 ?opusnummer } 35 | OPTIONAL { ?uri rdaw:P10335 ?thematisch_nummer } 36 | OPTIONAL { 37 | ?uri rdaw:P10019 ?broader_uri . # Is part of work 38 | ?broader_uri rdfs:label ?broader_prefLabel . 39 | } 40 | OPTIONAL { 41 | ?uri rdaw:P10147 ?narrower_uri . # Has part work 42 | ?narrower_uri rdfs:label ?narrower_prefLabel . 43 | } 44 | 45 | BIND( 46 | CONCAT( 47 | ?prefLabel, ",", 48 | IF(BOUND(?altLabel), CONCAT(" Varianttitel: ", ?altLabel, ","), ""), 49 | IF(BOUND(?opusnummer), CONCAT(" Opusnr.: ", ?opusnummer, ","), ""), 50 | IF(BOUND(?thematisch_nummer), CONCAT(" Thematisch nr.: ", ?thematisch_nummer, ","), ""), 51 | IF(BOUND(?eerste_datum), CONCAT(" Datum: ", ?eerste_datum), "") 52 | ) AS ?scopeNote 53 | ) 54 | } 55 | 56 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/rijksmonumenten.rq: -------------------------------------------------------------------------------- 1 | PREFIX ceo: 2 | PREFIX rdf: 3 | PREFIX rdfs: 4 | PREFIX ceosp: 5 | PREFIX skos: 6 | PREFIX xsd: 7 | PREFIX schema: 8 | 9 | CONSTRUCT { 10 | ?uri a skos:Concept ; 11 | skos:prefLabel ?rijksmonumentnummer_tn ; 12 | skos:altLabel ?adres_lang ; 13 | skos:scopeNote ?scopeNote ; 14 | rdfs:seeAlso ?monument_url . 15 | } 16 | WHERE { 17 | { 18 | SELECT ?uri WHERE { 19 | ?uri ?predicate ?label . 20 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 21 | VALUES ?predicate { schema:addressLocality schema:identifier schema:address schema:name schema:addressRegion } 22 | } 23 | #LIMIT# 24 | } 25 | 26 | ?uri schema:identifier ?rijksmonumentnummer ; 27 | schema:category ?monumentaard ; 28 | schema:postalCode ?postcode ; 29 | schema:additionalType ?functie ; 30 | schema:addressLocality ?woonplaats ; 31 | schema:addressRegion ?provincie ; 32 | schema:sameAs ?monument_url . 33 | 34 | OPTIONAL { 35 | ?uri schema:name ?naam . 36 | } 37 | 38 | BIND(CONCAT("Rijksmonumentnummer ", ?rijksmonumentnummer) as ?rijksmonumentnummer_tn) 39 | 40 | # Aggregate and get the minimum address as ?adres2 41 | { 42 | SELECT ?uri (MIN(?adres) AS ?adres2) 43 | WHERE { 44 | OPTIONAL { ?uri schema:address ?adres . } 45 | } 46 | GROUP BY ?uri 47 | } 48 | 49 | BIND( 50 | CONCAT( 51 | IF(BOUND(?naam), CONCAT("Naam: ", STR(?naam), " / "), ""), 52 | "Oorspronkelijke functie: ", STR(?functie), 53 | " / Type monument: ", STR(?monumentaard) 54 | ) AS ?scopeNote 55 | ) 56 | 57 | BIND( 58 | CONCAT( 59 | IF(BOUND(?adres2), CONCAT(STR(?adres2), " "), ""), 60 | STR(?postcode), " ", STR(?woonplaats) 61 | ) AS ?adres_lang 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/brabantse-gebouwen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.brabantcloud.nl/gebouwen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Brabants buildings" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Brabantse gebouwen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Gebouwen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.erfgoedbrabant.nl" 23 | } 24 | ], 25 | "url": ["https://n2t.net/ark:/15052/"], 26 | "mainEntityOfPage": [ 27 | "https://www.brabantcloud.nl/page/787/brabantse-kloosters-als-termenlijst" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Buildings in the province of Gebouwen in de provincie of North Brabant, as yet with a religious function such as monasteries" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Gebouwen in de provincie Noord-Brabant, vooralsnog met een religieuze functie, zoals kloosters" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.brabantcloud.nl/gebouwen/query/", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://data.brabantcloud.nl/gebouwen/query/", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/brabantse-gebouwen.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/brabantse-gebouwen.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/vrouwenthesaurus.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://vrouwenthesaurus.org", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Women’s Thesaurus" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Vrouwenthesaurus" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://atria.nl" 23 | } 24 | ], 25 | "url": ["https://vrouwenthesaurus.org/conceptid/"], 26 | "mainEntityOfPage": ["https://vrouwenthesaurus.org/conceptid.html"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Concepts for describing information about the position and experiences of women in all their diversity, emancipation, and women’s history." 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Onderwerpen voor het beschrijven van informatie over de positie van vrouwen in al hun diversiteit, emancipatie en vrouwengeschiedenis." 35 | } 36 | ], 37 | "inLanguage": ["en", "nl"], 38 | "distribution": [ 39 | { 40 | "@id": "https://vrouwenthesaurus.org/PoolParty/sparql/conceptid", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://vrouwenthesaurus.org/PoolParty/sparql/conceptid", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/poolparty.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/poolparty.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/canon-van-limburg.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX schema: 3 | PREFIX skos: 4 | 5 | CONSTRUCT { 6 | ?uri a skos:Concept ; 7 | skos:prefLabel ?prefLabel ; 8 | skos:altLabel ?altLabel ; 9 | skos:scopeNote ?scopeNote ; 10 | skos:exactMatch ?exactMatch_uri ; 11 | skos:broader ?broader_uri ; 12 | skos:narrower ?narrower_uri ; 13 | skos:related ?related_uri ; 14 | skos:seeAlso ?seeAlso_uri . 15 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 16 | ?broader_uri skos:prefLabel ?broader_prefLabel . 17 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 18 | ?related_uri skos:prefLabel ?related_prefLabel . 19 | ?seeAlso_uri skos:prefLabel ?seeAlso_prefLabel . 20 | } 21 | WHERE { 22 | GRAPH { 23 | { 24 | SELECT ?uri ?prefLabel ?altLabel WHERE { 25 | ?uri a skos:Concept ; 26 | skos:prefLabel ?prefLabel ; 27 | skos:altLabel ?altLabel . 28 | 29 | FILTER(CONTAINS(LCASE(?prefLabel), LCASE(?query)) || CONTAINS(LCASE(?altLabel), LCASE(?query))) 30 | } 31 | #LIMIT# 32 | } 33 | 34 | OPTIONAL { ?uri skos:scopeNote ?scopeNote } 35 | OPTIONAL { 36 | ?uri skos:exactMatch ?exactMatch_uri . 37 | OPTIONAL { ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel } 38 | } 39 | OPTIONAL { 40 | ?uri skos:broader ?broader_uri . 41 | OPTIONAL { ?broader_uri skos:prefLabel ?broader_prefLabel } 42 | } 43 | OPTIONAL { 44 | ?uri skos:narrower ?narrower_uri . 45 | OPTIONAL { ?narrower_uri skos:prefLabel ?narrower_prefLabel } 46 | } 47 | OPTIONAL { 48 | ?uri skos:related ?related_uri . 49 | OPTIONAL { ?related_uri skos:prefLabel ?related_prefLabel } 50 | } 51 | OPTIONAL { 52 | ?uri skos:seeAlso ?seeAlso_uri . 53 | OPTIONAL { ?seeAlso_uri skos:prefLabel ?seeAlso_prefLabel } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/muziekschatten-onderwerpen.rq: -------------------------------------------------------------------------------- 1 | PREFIX owl: 2 | PREFIX schema: 3 | PREFIX skos: 4 | PREFIX som: 5 | 6 | CONSTRUCT { 7 | ?uri a skos:Concept; 8 | skos:prefLabel ?prefLabel; 9 | skos:scopeNote ?scopeNote_nl, ?scopeNote_en; 10 | skos:broader ?broader_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_schema_name . 13 | } 14 | WHERE { 15 | { 16 | SELECT DISTINCT ?uri WHERE { 17 | ?uri a skos:Concept ; 18 | som:BASIS "1" . 19 | ?uri ?predicate ?label . 20 | VALUES ?predicate { schema:name schema:keywords } 21 | ?label ?virtuosoQuery . 22 | } 23 | #LIMIT# 24 | } 25 | 26 | OPTIONAL { 27 | ?uri schema:name ?prefLabel . 28 | } 29 | OPTIONAL { 30 | ?uri skos:broader ?broader_uri . 31 | ?broader_uri schema:name ?broader_schema_name . 32 | } 33 | OPTIONAL { 34 | ?uri schema:keywords ?schema_keywords 35 | } 36 | OPTIONAL { 37 | ?uri som:DC ?dc 38 | } 39 | BIND( 40 | STRLANG( 41 | COALESCE( 42 | IF(BOUND(?schema_keywords) && BOUND(?dc), CONCAT("Trefwoorden: ", ?schema_keywords, " | ", "Dewey: ", ?dc), ?noScopeNote), 43 | IF(BOUND(?schema_keywords), CONCAT("Trefwoorden: ", ?schema_keywords), ?noScopeNote), 44 | IF(BOUND(?dc), CONCAT("Dewey: ", ?dc), ?noScopeNote) 45 | ), 46 | "nl" 47 | ) AS ?scopeNote_nl 48 | ) 49 | 50 | BIND( 51 | STRLANG( 52 | COALESCE( 53 | IF(BOUND(?schema_keywords) && BOUND(?dc), CONCAT("Keywords: ", ?schema_keywords, " | ", "Dewey: ", ?dc), ?noScopeNote), 54 | IF(BOUND(?schema_keywords), CONCAT("Keywords: ", ?schema_keywords), ?noScopeNote), 55 | IF(BOUND(?dc), CONCAT("Dewey: ", ?dc), ?noScopeNote) 56 | ), 57 | "en" 58 | ) AS ?scopeNote_en 59 | ) 60 | 61 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } # Has no labels. 62 | } 63 | -------------------------------------------------------------------------------- /packages/query/test/search/query-mode.test.ts: -------------------------------------------------------------------------------- 1 | import { QueryMode, queryVariants } from '../../src/index.js'; 2 | 3 | describe('Search query', () => { 4 | it('transforms simple query', () => { 5 | expect(queryVariants('test', QueryMode.OPTIMIZED)).toEqual( 6 | new Map([ 7 | ['query', 'test'], 8 | ['virtuosoQuery', "'test'"], 9 | ]), 10 | ); 11 | }); 12 | 13 | it('transforms multiple word query', () => { 14 | expect(queryVariants('Dr. H. Colijnstraat', QueryMode.OPTIMIZED)).toEqual( 15 | new Map([ 16 | ['query', 'dr. h. colijnstraat'], 17 | ['virtuosoQuery', "'dr.' AND 'h.' AND 'colijnstraat'"], 18 | ]), 19 | ); 20 | }); 21 | 22 | it('trims whitespaces', () => { 23 | expect(queryVariants(' a b c ', QueryMode.OPTIMIZED)).toEqual( 24 | new Map([ 25 | ['query', 'a b c'], 26 | ['virtuosoQuery', "'a' AND 'b' AND 'c'"], 27 | ]), 28 | ); 29 | }); 30 | 31 | it('skips already present boolean operators', () => { 32 | expect(queryVariants('a AND b c or d', QueryMode.OPTIMIZED)).toEqual( 33 | new Map([ 34 | ['query', 'a and b c or d'], 35 | ['virtuosoQuery', "'a' and 'b' AND 'c' or 'd'"], 36 | ]), 37 | ); 38 | }); 39 | 40 | it('escapes quotation marks', () => { 41 | expect( 42 | queryVariants("Rex Stewart's 'Big' Eight", QueryMode.OPTIMIZED), 43 | ).toEqual( 44 | new Map([ 45 | ['query', "rex stewart's 'big' eight"], 46 | [ 47 | 'virtuosoQuery', 48 | "'rex' AND 'stewart\\'s' AND '\\'big\\'' AND 'eight'", 49 | ], 50 | ]), 51 | ); 52 | }); 53 | 54 | it('removes stop words', () => { 55 | expect(queryVariants('Sammy Dowds & Leslie', QueryMode.OPTIMIZED)).toEqual( 56 | new Map([ 57 | ['query', 'sammy dowds & leslie'], 58 | ['virtuosoQuery', "'sammy' AND 'dowds' AND 'leslie'"], 59 | ]), 60 | ); 61 | }); 62 | 63 | it('keeps raw queries unchanged', () => { 64 | expect(queryVariants("Rex Stewart's Big Eight", QueryMode.RAW)).toEqual( 65 | new Map([ 66 | ['query', "Rex Stewart's Big Eight"], 67 | ['virtuosoQuery', "Rex Stewart's Big Eight"], 68 | ]), 69 | ); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/geonames.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.geonames.org", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GeoNames: global geographical names" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GeoNames: geografische namen wereldwijd" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.geonames.org/team.html" 23 | } 24 | ], 25 | "url": ["https://sws.geonames.org/"], 26 | "mainEntityOfPage": ["https://www.geonames.org/about.html"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Selection of geographical names such as places, administrative divisions (municipalities, provinces) and water bodies (rivers, streams, lakes etc.)" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Selectie van geografische namen zoals plaatsen, administratieve eenheden (gemeentes, provincies) en waterlichamen (rivieren, beken, meren etc.)" 35 | } 36 | ], 37 | "inLanguage": "en", 38 | "distribution": [ 39 | { 40 | "@id": "https://geonames.netwerkdigitaalerfgoed.nl", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://geonames.netwerkdigitaalerfgoed.nl/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/geonames.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/geonames.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/gtaa-onderwerpen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/Onderwerpen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GTAA: subjects" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GTAA: onderwerpen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 21 | } 22 | ], 23 | "creator": [ 24 | { 25 | "@id": "https://www.beeldengeluid.nl/" 26 | } 27 | ], 28 | "url": ["http://data.beeldengeluid.nl/gtaa/"], 29 | "mainEntityOfPage": [ 30 | "https://www.beeldengeluid.nl/kennis/kennisthemas/metadata/gemeenschappelijke-thesaurus-audiovisuele-archieven" 31 | ], 32 | "description": [ 33 | { 34 | "@language": "en", 35 | "@value": "Subjects for describing audiovisual material" 36 | }, 37 | { 38 | "@language": "nl", 39 | "@value": "Onderwerpen voor het beschrijven van audiovisueel materiaal" 40 | } 41 | ], 42 | "inLanguage": "nl", 43 | "distribution": [ 44 | { 45 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0031", 46 | "@type": "DataDownload", 47 | "contentUrl": "https://$GTAA_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 48 | "encodingFormat": "application/sparql-query", 49 | "potentialAction": [ 50 | { 51 | "@type": "SearchAction", 52 | "query": "file://../queries/search/gtaa.rq" 53 | }, 54 | { 55 | "@type": "FindAction", 56 | "query": "file://../queries/lookup/gtaa.rq" 57 | }, 58 | { 59 | "@type": "Action", 60 | "target": { 61 | "@type": "EntryPoint", 62 | "actionApplication": { 63 | "@id": "https://reconciliation-api.github.io/specs/latest/", 64 | "@type": "SoftwareApplication" 65 | }, 66 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/wo2thesaurus.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_prefLabel . 13 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 14 | } 15 | WHERE { 16 | { 17 | SELECT ?uri WHERE { 18 | ?uri ?predicate ?label . 19 | # Exclude terms that are *only* in 'Technische Lijsten', but keep terms that are also in other schemes 20 | FILTER NOT EXISTS { 21 | ?uri skos:inScheme . 22 | FILTER NOT EXISTS { 23 | ?uri skos:inScheme ?otherScheme . 24 | FILTER(?otherScheme != ) 25 | } 26 | } 27 | VALUES ?predicate { skos:prefLabel skos:altLabel } 28 | FILTER(LANG(?label) = "nl") 29 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 30 | } 31 | 32 | #LIMIT# 33 | } 34 | OPTIONAL { 35 | ?uri skos:prefLabel ?prefLabel . 36 | FILTER(LANG(?prefLabel) = "nl" ) 37 | } 38 | OPTIONAL { 39 | ?uri skos:scopeNote ?scopeNote . 40 | FILTER(LANG(?scopeNote) = "nl") 41 | } 42 | OPTIONAL { 43 | ?uri skos:altLabel ?altLabel . 44 | FILTER(LANG(?altLabel) = "nl") 45 | } 46 | OPTIONAL { 47 | ?uri skos:hiddenLabel ?hiddenLabel . 48 | FILTER(LANG(?hiddenLabel) = "nl") 49 | } 50 | OPTIONAL { 51 | ?uri skos:broader ?broader_uri . 52 | ?broader_uri skos:prefLabel ?broader_prefLabel . 53 | FILTER(LANG(?broader_prefLabel) = "nl") 54 | } 55 | OPTIONAL { 56 | ?uri skos:narrower ?narrower_uri . 57 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 58 | FILTER(LANG(?narrower_prefLabel) = "nl") 59 | } 60 | OPTIONAL { 61 | ?uri skos:exactMatch ?exactMatch_uri . # Has no labels. 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/muziekschatten-onderwerpen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.muziekschatten.nl/#onderwerpen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Muziekschatten: subjects" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Muziekschatten: onderwerpen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 21 | } 22 | ], 23 | "creator": [ 24 | { 25 | "@id": "https://www.omroepmuziek.nl" 26 | } 27 | ], 28 | "url": ["https://data.muziekschatten.nl/som/d"], 29 | "mainEntityOfPage": ["https://data.muziekschatten.nl/"], 30 | "description": [ 31 | { 32 | "@language": "en", 33 | "@value": "Subjects in the catalog of the sheet music collection of Stichting Omroep Muziek" 34 | }, 35 | { 36 | "@language": "nl", 37 | "@value": "Onderwerpen in de catalogus van de bladmuziekcollectie van Stichting Omroep Muziek" 38 | } 39 | ], 40 | "inLanguage": "nl", 41 | "distribution": [ 42 | { 43 | "@id": "https://data.muziekschatten.nl/sparql/#onderwerpen", 44 | "@type": "DataDownload", 45 | "contentUrl": "https://data.muziekschatten.nl/sparql", 46 | "encodingFormat": "application/sparql-query", 47 | "potentialAction": [ 48 | { 49 | "@type": "SearchAction", 50 | "query": "file://../queries/search/muziekschatten-onderwerpen.rq" 51 | }, 52 | { 53 | "@type": "FindAction", 54 | "query": "file://../queries/lookup/muziekschatten-onderwerpen.rq" 55 | }, 56 | { 57 | "@type": "Action", 58 | "target": { 59 | "@type": "EntryPoint", 60 | "actionApplication": { 61 | "@id": "https://reconciliation-api.github.io/specs/latest/", 62 | "@type": "SoftwareApplication" 63 | }, 64 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 65 | } 66 | } 67 | ] 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/mw-personengroepen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.muziekweb.nl/MuziekwebOrganization/Muziekweb#mw-personengroepen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Music: persons and groups" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Muziek: personen en groepen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.muziekweb.nl" 23 | } 24 | ], 25 | "url": ["https://data.muziekweb.nl/Link/"], 26 | "mainEntityOfPage": [ 27 | "https://data.muziekweb.nl/MuziekwebOrganization/Muziekweb" 28 | ], 29 | "description": [ 30 | { 31 | "@language": "en", 32 | "@value": "Musical artists, both individuals and groups, in the collection of Muziekweb" 33 | }, 34 | { 35 | "@language": "nl", 36 | "@value": "Artiesten, zowel personen als groepen, in de collectie van Muziekweb" 37 | } 38 | ], 39 | "inLanguage": "nl", 40 | "distribution": [ 41 | { 42 | "@id": "https://data.muziekweb.nl/MuziekwebOrganization/Muziekweb/sparql/Muziekweb#mw-personengroepen", 43 | "@type": "DataDownload", 44 | "contentUrl": "https://api.data.muziekweb.nl/datasets/MuziekwebOrganization/Muziekweb/services/Muziekweb/sparql", 45 | "encodingFormat": "application/sparql-query", 46 | "potentialAction": [ 47 | { 48 | "@type": "SearchAction", 49 | "query": "file://../queries/search/mw-personengroepen.rq" 50 | }, 51 | { 52 | "@type": "FindAction", 53 | "query": "file://../queries/lookup/muziekweb.rq" 54 | }, 55 | { 56 | "@type": "Action", 57 | "target": { 58 | "@type": "EntryPoint", 59 | "actionApplication": { 60 | "@id": "https://reconciliation-api.github.io/specs/latest/", 61 | "@type": "SoftwareApplication" 62 | }, 63 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 64 | } 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/gtaa-namen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.beeldengeluid.nl/gtaa/Namen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GTAA: names" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GTAA: namen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Personen" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Actoren-en-organismen" 21 | } 22 | ], 23 | "creator": [ 24 | { 25 | "@id": "https://www.beeldengeluid.nl/" 26 | } 27 | ], 28 | "url": ["http://data.beeldengeluid.nl/gtaa"], 29 | "mainEntityOfPage": [ 30 | "https://www.beeldengeluid.nl/kennis/kennisthemas/metadata/gemeenschappelijke-thesaurus-audiovisuele-archieven" 31 | ], 32 | "description": [ 33 | { 34 | "@language": "en", 35 | "@value": "Various types of proper names for describing audiovisual material" 36 | }, 37 | { 38 | "@language": "nl", 39 | "@value": "Diverse soorten eigennamen voor het beschrijven van audiovisueel materiaal" 40 | } 41 | ], 42 | "inLanguage": "nl", 43 | "distribution": [ 44 | { 45 | "@id": "https://data.beeldengeluid.nl/id/datadownload/0030", 46 | "@type": "DataDownload", 47 | "contentUrl": "https://$GTAA_CREDENTIALS@gtaa.apis.beeldengeluid.nl/sparql", 48 | "encodingFormat": "application/sparql-query", 49 | "potentialAction": [ 50 | { 51 | "@type": "SearchAction", 52 | "query": "file://../queries/search/gtaa.rq" 53 | }, 54 | { 55 | "@type": "FindAction", 56 | "query": "file://../queries/lookup/gtaa.rq" 57 | }, 58 | { 59 | "@type": "Action", 60 | "target": { 61 | "@type": "EntryPoint", 62 | "actionApplication": { 63 | "@id": "https://reconciliation-api.github.io/specs/latest/", 64 | "@type": "SoftwareApplication" 65 | }, 66 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/lookup/wo2thesaurus.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:exactMatch ?exactMatch_uri . 12 | ?broader_uri skos:prefLabel ?broader_prefLabel . 13 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 14 | } 15 | WHERE { 16 | # For example: 17 | # Concept: 18 | # Event: 19 | # Organization: 20 | VALUES ?uri { ?uris } 21 | 22 | ?uri a skos:Concept . 23 | 24 | # Exclude terms from the 'Technische Lijsten', a concept scheme with terms that should only be used by NOB 25 | # Include terms that are both in "Technische lijsten" and other conceptScheme's 26 | FILTER NOT EXISTS { 27 | ?uri skos:inScheme . 28 | FILTER NOT EXISTS { 29 | ?uri skos:inScheme ?otherScheme . 30 | FILTER(?otherScheme != ) 31 | } 32 | } 33 | 34 | OPTIONAL { 35 | ?uri skos:prefLabel ?prefLabel . 36 | FILTER(LANG(?prefLabel) = "nl") 37 | } 38 | OPTIONAL { 39 | ?uri skos:altLabel ?altLabel . 40 | FILTER(LANG(?altLabel) = "nl") 41 | } 42 | OPTIONAL { 43 | ?uri skos:hiddenLabel ?hiddenLabel . 44 | FILTER(LANG(?hiddenLabel) = "nl") 45 | } 46 | OPTIONAL { 47 | ?uri skos:scopeNote ?scopeNote . 48 | FILTER(LANG(?scopeNote) = "nl") 49 | } 50 | OPTIONAL { 51 | ?uri skos:broader ?broader_uri . 52 | ?broader_uri skos:prefLabel ?broader_prefLabel . 53 | FILTER(LANG(?broader_prefLabel) = "nl") 54 | } 55 | OPTIONAL { 56 | ?uri skos:narrower ?narrower_uri . 57 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 58 | FILTER(LANG(?narrower_prefLabel) = "nl") 59 | } 60 | OPTIONAL { 61 | ?uri skos:exactMatch ?exactMatch_uri . # Has no labels. 62 | } 63 | } 64 | LIMIT 1000 65 | -------------------------------------------------------------------------------- /packages/reconciliation/src/data-extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IRI, 3 | LookupService, 4 | Term, 5 | } from '@netwerk-digitaal-erfgoed/network-of-terms-query'; 6 | 7 | export type DataExtensionQuery = { 8 | ids: string[]; 9 | properties: { id: string }[]; 10 | }; 11 | 12 | export const dataExtensionProperties: { 13 | id: 'prefLabels' | 'altLabels' | 'scopeNotes'; 14 | name: string; 15 | }[] = [ 16 | { 17 | id: 'prefLabels', 18 | name: 'prefLabels', 19 | }, 20 | { 21 | id: 'altLabels', 22 | name: 'altLabels', 23 | }, 24 | { 25 | id: 'scopeNotes', 26 | name: 'scopeNotes', 27 | }, 28 | ]; 29 | 30 | export async function extendQuery( 31 | terms: IRI[], 32 | lookupService: LookupService, 33 | language: string, 34 | ) { 35 | const lookupResults = (await lookupService.lookup(terms, 10000)).filter( 36 | (lookupResult) => lookupResult.result instanceof Term, 37 | ); 38 | 39 | const futureSpecResult = { 40 | meta: dataExtensionProperties, 41 | rows: lookupResults.map((lookupResult) => ({ 42 | id: lookupResult.uri.toString(), 43 | properties: dataExtensionProperties.map((property) => ({ 44 | id: property.id, 45 | values: (lookupResult.result as Term)[property.id] 46 | .filter((literal) => literal.language === language) 47 | .map((literal) => ({ 48 | str: literal.value, 49 | })), 50 | })), 51 | })), 52 | }; 53 | 54 | return downcastToReconciliationSpecV0_2(futureSpecResult); 55 | } 56 | 57 | const downcastToReconciliationSpecV0_2 = ( 58 | futureSpecResult: DataExtensionResult, 59 | ) => ({ 60 | meta: futureSpecResult.meta, 61 | rows: futureSpecResult.rows.reduce( 62 | (acc: { [key: string]: { [key: string]: { str: string }[] } }, current) => { 63 | acc[current.id] = current.properties.reduce( 64 | (acc: { [key: string]: { str: string }[] }, current) => { 65 | acc[current.id] = current.values; 66 | return acc; 67 | }, 68 | {}, 69 | ); 70 | return acc; 71 | }, 72 | {}, 73 | ), 74 | }); 75 | 76 | export type DataExtensionResult = { 77 | meta: { id: string; name: string }[]; 78 | rows: { 79 | id: string; 80 | properties: { 81 | id: string; 82 | values: { str: string }[]; 83 | }[]; 84 | }[]; 85 | }; 86 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/brinkman.rq: -------------------------------------------------------------------------------- 1 | # Do not remove this prefix - KB data uses old version of SKOS 2 | PREFIX skos: 3 | PREFIX bif: 4 | 5 | CONSTRUCT { 6 | ?uri a skos:Concept ; 7 | skos:prefLabel ?prefLabel ; 8 | skos:altLabel ?altLabel ; 9 | skos:hiddenLabel ?hiddenLabel ; 10 | skos:scopeNote ?scopeNote ; 11 | skos:broader ?broader_uri ; 12 | skos:narrower ?narrower_uri ; 13 | skos:related ?related_uri ; 14 | skos:exactMatch ?exactMatch_uri . 15 | ?broader_uri skos:prefLabel ?broader_prefLabel . 16 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 17 | ?related_uri skos:prefLabel ?related_prefLabel . 18 | } 19 | WHERE { 20 | { 21 | SELECT ?uri WHERE { 22 | ?uri a skos:Concept ; 23 | skos:inScheme ; 24 | ?predicate ?label . 25 | VALUES ?predicate { skos:prefLabel skos:altLabel } 26 | FILTER(LANG(?label) = "nl") 27 | ?label ?virtuosoQuery . 28 | } 29 | #LIMIT# 30 | } 31 | 32 | OPTIONAL { 33 | ?uri skos:prefLabel ?prefLabel . 34 | FILTER(LANG(?prefLabel) = "nl" ) 35 | } 36 | OPTIONAL { 37 | ?uri skos:altLabel ?altLabel . 38 | FILTER(LANG(?altLabel) = "nl") 39 | } 40 | OPTIONAL { 41 | ?uri skos:hiddenLabel ?hiddenLabel . 42 | FILTER(LANG(?hiddenLabel) = "nl") 43 | } 44 | OPTIONAL { 45 | ?uri skos:scopeNote ?scopeNote . 46 | FILTER(LANG(?scopeNote) = "nl") 47 | } 48 | OPTIONAL { 49 | ?uri skos:broader ?broader_uri . 50 | ?broader_uri skos:prefLabel ?broader_prefLabel . 51 | FILTER(LANG(?broader_prefLabel) = "nl") 52 | } 53 | OPTIONAL { 54 | ?uri skos:narrower ?narrower_uri . 55 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 56 | FILTER(LANG(?narrower_prefLabel) = "nl") 57 | } 58 | OPTIONAL { 59 | ?uri skos:related ?related_uri . 60 | ?related_uri skos:prefLabel ?related_prefLabel . 61 | FILTER(LANG(?related_prefLabel) = "nl") 62 | } 63 | OPTIONAL { ?uri skos:exactMatch ?exactMatch_uri } # Has no labels. 64 | } 65 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/mw-genresstijlen.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.muziekweb.nl/MuziekwebOrganization/Muziekweb#mw-genresstijlen", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Music: genres and styles" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Muziek: genres en stijlen" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Stijlen" 21 | } 22 | ], 23 | "creator": [ 24 | { 25 | "@id": "https://www.muziekweb.nl" 26 | } 27 | ], 28 | "url": ["https://data.muziekweb.nl/Link/"], 29 | "mainEntityOfPage": [ 30 | "https://data.muziekweb.nl/MuziekwebOrganization/Muziekweb" 31 | ], 32 | "description": [ 33 | { 34 | "@language": "en", 35 | "@value": "Genres and styles in the collection of Muziekweb" 36 | }, 37 | { 38 | "@language": "nl", 39 | "@value": "Genres en stijlen in de collectie van Muziekweb" 40 | } 41 | ], 42 | "inLanguage": "nl", 43 | "distribution": [ 44 | { 45 | "@id": "https://data.muziekweb.nl/MuziekwebOrganization/Muziekweb/sparql/Muziekweb#mw-genresstijlen", 46 | "@type": "DataDownload", 47 | "contentUrl": "https://api.data.muziekweb.nl/datasets/MuziekwebOrganization/Muziekweb/services/Muziekweb/sparql", 48 | "encodingFormat": "application/sparql-query", 49 | "potentialAction": [ 50 | { 51 | "@type": "SearchAction", 52 | "query": "file://../queries/search/mw-genresstijlen.rq" 53 | }, 54 | { 55 | "@type": "FindAction", 56 | "query": "file://../queries/lookup/muziekweb.rq" 57 | }, 58 | { 59 | "@type": "Action", 60 | "target": { 61 | "@type": "EntryPoint", 62 | "actionApplication": { 63 | "@id": "https://reconciliation-api.github.io/specs/latest/", 64 | "@type": "SoftwareApplication" 65 | }, 66 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/uitvoeringsmedium.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://data.muziekschatten.nl/som/Uitvoeringsmedium", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Uitvoeringsmedium" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Uitvoeringsmedium" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Objecten" 18 | }, 19 | { 20 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Abstracte-begrippen" 21 | } 22 | ], 23 | "creator": [ 24 | { 25 | "@id": "https://www.podiumkunst.net" 26 | } 27 | ], 28 | "url": ["https://data.muziekschatten.nl/som/um"], 29 | "mainEntityOfPage": ["https://data.muziekschatten.nl/som/"], 30 | "description": [ 31 | { 32 | "@language": "en", 33 | "@value": "Terms for describing musical instruments, vocal parts and ensembles (in single and multiple formation)" 34 | }, 35 | { 36 | "@language": "nl", 37 | "@value": "Termen voor het beschrijven van muziekinstrumenten, zangstemmen en ensembles (in enkelvoudige en meervoudige bezetting)" 38 | } 39 | ], 40 | "inLanguage": ["en", "nl"], 41 | "distribution": [ 42 | { 43 | "@id": "https://data.muziekschatten.nl/sparql/#uitvoeringsmedium", 44 | "@type": "DataDownload", 45 | "contentUrl": "https://data.muziekschatten.nl/sparql", 46 | "encodingFormat": "application/sparql-query", 47 | "potentialAction": [ 48 | { 49 | "@type": "SearchAction", 50 | "query": "file://../queries/search/uitvoeringsmedium.rq" 51 | }, 52 | { 53 | "@type": "FindAction", 54 | "query": "file://../queries/lookup/uitvoeringsmedium.rq" 55 | }, 56 | { 57 | "@type": "Action", 58 | "target": { 59 | "@type": "EntryPoint", 60 | "actionApplication": { 61 | "@id": "https://reconciliation-api.github.io/specs/latest/", 62 | "@type": "SoftwareApplication" 63 | }, 64 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 65 | } 66 | } 67 | ] 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/geonames-nl-be-de.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.geonames.org#nl-be-de", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "GeoNames: geographical names in The Netherlands, Belgium and Germany" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "GeoNames: geografische namen in Nederland, België en Duitsland" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.geonames.org/team.html" 23 | } 24 | ], 25 | "url": ["https://sws.geonames.org/"], 26 | "mainEntityOfPage": ["https://www.geonames.org/about.html"], 27 | "description": [ 28 | { 29 | "@language": "en", 30 | "@value": "Selection of geographical names such as places, administrative divisions (municipalities, provinces) and water bodies (rivers, streams, lakes etc.)" 31 | }, 32 | { 33 | "@language": "nl", 34 | "@value": "Selectie van geografische namen zoals plaatsen, administratieve eenheden (gemeentes, provincies) en waterlichamen (rivieren, beken, meren etc.)" 35 | } 36 | ], 37 | "inLanguage": "en", 38 | "distribution": [ 39 | { 40 | "@id": "https://geonames.netwerkdigitaalerfgoed.nl#nl-be-de", 41 | "@type": "DataDownload", 42 | "contentUrl": "https://geonames.netwerkdigitaalerfgoed.nl/sparql", 43 | "encodingFormat": "application/sparql-query", 44 | "potentialAction": [ 45 | { 46 | "@type": "SearchAction", 47 | "query": "file://../queries/search/geonames-nl-be-de.rq" 48 | }, 49 | { 50 | "@type": "FindAction", 51 | "query": "file://../queries/lookup/geonames.rq" 52 | }, 53 | { 54 | "@type": "Action", 55 | "target": { 56 | "@type": "EntryPoint", 57 | "actionApplication": { 58 | "@id": "https://reconciliation-api.github.io/specs/latest/", 59 | "@type": "SoftwareApplication" 60 | }, 61 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 62 | } 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/cht.rq: -------------------------------------------------------------------------------- 1 | PREFIX skos: 2 | 3 | CONSTRUCT { 4 | ?uri a skos:Concept ; 5 | skos:prefLabel ?prefLabel ; 6 | skos:altLabel ?altLabel ; 7 | skos:hiddenLabel ?hiddenLabel ; 8 | skos:scopeNote ?scopeNote ; 9 | skos:broader ?broader_uri ; 10 | skos:narrower ?narrower_uri ; 11 | skos:related ?related_uri ; 12 | skos:exactMatch ?exactMatch_uri . 13 | ?broader_uri skos:prefLabel ?broader_prefLabel . 14 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 15 | ?related_uri skos:prefLabel ?related_prefLabel . 16 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 17 | } 18 | WHERE { 19 | { 20 | SELECT DISTINCT ?uri WHERE { 21 | ?uri a skos:Concept ; 22 | skos:inScheme ; 23 | ?predicate ?label . 24 | 25 | VALUES ?predicate { skos:prefLabel skos:altLabel skos:hiddenLabel } 26 | 27 | FILTER(CONTAINS(LCASE(?label), LCASE(?query))) 28 | 29 | } 30 | #LIMIT# 31 | } 32 | OPTIONAL { 33 | ?uri skos:prefLabel ?prefLabel . 34 | } 35 | OPTIONAL { 36 | ?uri skos:altLabel ?altLabel . 37 | } 38 | OPTIONAL { 39 | ?uri skos:hiddenLabel ?hiddenLabel . 40 | } 41 | OPTIONAL { 42 | ?uri skos:scopeNote ?scopeNote . 43 | } 44 | OPTIONAL { 45 | ?uri skos:definition ?scopeNote . 46 | } 47 | OPTIONAL { 48 | ?uri skos:broader ?broader_uri . 49 | ?broader_uri skos:prefLabel ?broader_prefLabel . 50 | } 51 | OPTIONAL { 52 | ?uri skos:narrower ?narrower_uri . 53 | ?narrower_uri skos:prefLabel ?narrower_prefLabel . 54 | } 55 | OPTIONAL { 56 | ?uri skos:related ?related_uri . 57 | ?related_uri skos:prefLabel ?related_prefLabel . 58 | } 59 | OPTIONAL { 60 | ?uri skos:exactMatch ?exactMatch_uri . 61 | FILTER(!STRSTARTS(STR(?exactMatch_uri), "https://data.cultureelerfgoed.nl/term/id/rn")) 62 | FILTER(!STRSTARTS(STR(?exactMatch_uri), "https://data.cultureelerfgoed.nl/semnet")) 63 | OPTIONAL { 64 | ?exactMatch_uri skos:prefLabel ?exactMatch_prefLabel . 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/goudatijdmachine-straten.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "https://www.goudatijdmachine.nl/id/straten", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Gouda streets" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Goudse straten" 13 | } 14 | ], 15 | "genre": [ 16 | { 17 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Locaties" 18 | } 19 | ], 20 | "creator": [ 21 | { 22 | "@id": "https://www.goudatijdmachine.nl", 23 | "@type": "Organization", 24 | "name": [ 25 | { 26 | "@language": "en", 27 | "@value": "Gouda Time Machine" 28 | }, 29 | { 30 | "@language": "nl", 31 | "@value": "Gouda Tijdmachine" 32 | } 33 | ], 34 | "alternateName": "GTM" 35 | } 36 | ], 37 | "url": ["https://n2t.net/ark:/60537/"], 38 | "mainEntityOfPage": [ 39 | "https://www.goudatijdmachine.nl/omeka/s/data/page/straten" 40 | ], 41 | "description": [ 42 | { 43 | "@language": "en", 44 | "@value": "Streets in Gouda" 45 | }, 46 | { 47 | "@language": "nl", 48 | "@value": "Straten in Gouda" 49 | } 50 | ], 51 | "inLanguage": "nl", 52 | "distribution": [ 53 | { 54 | "@id": "https://www.goudatijdmachine.nl/id/straten#streets", 55 | "@type": "DataDownload", 56 | "contentUrl": "https://www.goudatijdmachine.nl/sparql11", 57 | "encodingFormat": "application/sparql-query", 58 | "potentialAction": [ 59 | { 60 | "@type": "SearchAction", 61 | "query": "file://../queries/search/goudatijdmachine-straten.rq" 62 | }, 63 | { 64 | "@type": "FindAction", 65 | "query": "file://../queries/lookup/goudatijdmachine-straten.rq" 66 | }, 67 | { 68 | "@type": "Action", 69 | "target": { 70 | "@type": "EntryPoint", 71 | "actionApplication": { 72 | "@id": "https://reconciliation-api.github.io/specs/latest/", 73 | "@type": "SoftwareApplication" 74 | }, 75 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 76 | } 77 | } 78 | ] 79 | } 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /packages/catalog/catalog/datasets/brinkman.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://schema.org", 3 | "@id": "http://data.bibliotheken.nl/id/dataset/brinkman", 4 | "@type": "Dataset", 5 | "name": [ 6 | { 7 | "@language": "en", 8 | "@value": "Brinkman subjects" 9 | }, 10 | { 11 | "@language": "nl", 12 | "@value": "Brinkman trefwoordenthesaurus" 13 | } 14 | ], 15 | "alternateName": [ 16 | { 17 | "@language": "en", 18 | "@value": "Brinkman" 19 | }, 20 | { 21 | "@language": "nl", 22 | "@value": "Brinkman" 23 | } 24 | ], 25 | "genre": [ 26 | { 27 | "@id": "https://data.cultureelerfgoed.nl/termennetwerk/onderwerpen/Genres" 28 | } 29 | ], 30 | "creator": [ 31 | { 32 | "@id": "http://data.bibliotheken.nl/id/thes/p075301482" 33 | } 34 | ], 35 | "url": ["http://data.bibliotheken.nl/id/thes/"], 36 | "mainEntityOfPage": [ 37 | "https://data.bibliotheken.nl/KB/-/stories/Brinkman-trefwoordenthesaurus" 38 | ], 39 | "description": [ 40 | { 41 | "@language": "en", 42 | "@value": "Subjects that the National Library of the Netherlands assigns to publications" 43 | }, 44 | { 45 | "@language": "nl", 46 | "@value": "Onderwerpen die de Koninklijke Bibliotheek aan publicaties toekent" 47 | } 48 | ], 49 | "inLanguage": "nl", 50 | "distribution": [ 51 | { 52 | "@id": "http://data.bibliotheken.nl/thes/brinkman/sparql", 53 | "@type": "DataDownload", 54 | "contentUrl": "https://data.bibliotheken.nl/sparql", 55 | "encodingFormat": "application/sparql-query", 56 | "potentialAction": [ 57 | { 58 | "@type": "SearchAction", 59 | "query": "file://../queries/search/brinkman.rq" 60 | }, 61 | { 62 | "@type": "FindAction", 63 | "query": "file://../queries/lookup/brinkman-nta-stcn.rq" 64 | }, 65 | { 66 | "@type": "Action", 67 | "target": { 68 | "@type": "EntryPoint", 69 | "actionApplication": { 70 | "@id": "https://reconciliation-api.github.io/specs/latest/", 71 | "@type": "SoftwareApplication" 72 | }, 73 | "urlTemplate": "https://termennetwerk-api.netwerkdigitaalerfgoed.nl/reconcile/{dataset}" 74 | } 75 | } 76 | ] 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /packages/catalog/catalog/queries/search/wikidata-entities-streets.rq: -------------------------------------------------------------------------------- 1 | PREFIX bd: 2 | PREFIX mwapi: 3 | PREFIX rdfs: 4 | PREFIX schema: 5 | PREFIX skos: 6 | PREFIX vrank: 7 | PREFIX wd: 8 | PREFIX wdt: 9 | PREFIX wikibase: 10 | 11 | CONSTRUCT { 12 | ?item a skos:Concept ; 13 | skos:prefLabel ?prefLabel ; 14 | skos:altLabel ?altLabel ; 15 | skos:scopeNote ?description ; 16 | vrank:simpleRank ?score . 17 | } 18 | WHERE { 19 | { 20 | SELECT DISTINCT ?item ?score WHERE { 21 | SERVICE wikibase:mwapi { 22 | bd:serviceParam wikibase:endpoint "www.wikidata.org" ; 23 | wikibase:api "Search" ; 24 | wikibase:limit "once" ; # Disabling continuation improves performance. 25 | mwapi:language "nl" ; 26 | mwapi:srsearch ?query . 27 | ?item wikibase:apiOutputItem mwapi:title . 28 | ?ordinal wikibase:apiOrdinal true. 29 | } 30 | 31 | BIND(-?ordinal AS ?score) 32 | 33 | # Use UNION instead of VALUES because the latter doesn't properly restrict results. 34 | { 35 | # Streets. 36 | ?item wdt:P31 wd:Q79007 37 | } 38 | UNION { 39 | # Squares. 40 | ?item wdt:P31 wd:Q174782 41 | } 42 | UNION { 43 | # Main squares, e.g. http://www.wikidata.org/entity/Q1083850. 44 | ?item wdt:P31 wd:Q26987258 45 | } 46 | ?item wdt:P17 wd:Q55 . 47 | } 48 | ORDER BY ASC(?ordinal) 49 | #LIMIT# 50 | } 51 | OPTIONAL { 52 | ?item rdfs:label ?prefLabel . 53 | FILTER(LANG(?prefLabel) = "nl" || LANG(?prefLabel) = "en") 54 | } 55 | OPTIONAL { 56 | ?item skos:altLabel ?altLabel . 57 | FILTER(LANG(?altLabel) = "nl" || LANG(?altLabel) = "en") 58 | } 59 | OPTIONAL { 60 | ?item schema:description ?description . 61 | FILTER(LANG(?description) = "nl" || LANG(?description) = "en") 62 | } 63 | } 64 | --------------------------------------------------------------------------------