From 95e9e87353e7465180fbe9445dc0725bca231da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Mon, 6 Oct 2025 22:41:48 +0200 Subject: [PATCH 1/5] Add Elasticsearch licensing note for adopters --- site/content/about.md | 4 ++++ site/content/documentation/programming/repository.md | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/site/content/about.md b/site/content/about.md index 948c6c9c07d..0dd9fdae8e5 100644 --- a/site/content/about.md +++ b/site/content/about.md @@ -31,6 +31,10 @@ This is useful if you are already using Elasticsearch for other things in your p A good usecase is if you need reference data or an ontology for your application. The built-in read cache makes it a good choice for data that updates infrequently, though for most usecases the NativeStore will be considerably faster. +{{< alert title="Licensing note" color="warning" >}} +Elasticsearch itself is distributed under the Elastic License (with SSPL as an alternative). If you intend to use the optional Elasticsearch-backed features in RDF4J, please make sure to evaluate whether the licensing terms of Elasticsearch align with the needs of your project before adopting it. +{{< /alert >}} + On top of these core databases, RDF4J offers a number of functional extensions. These extensions add functionality such as improved full-text search, RDFS inferencing, rule-based reasoning and validation using SHACL/SPIN, and geospatial querying support. For more information see the [RDF4J documentation](/documentation). ### Third party database solutions diff --git a/site/content/documentation/programming/repository.md b/site/content/documentation/programming/repository.md index 691a839c74e..52cca3cd8c1 100644 --- a/site/content/documentation/programming/repository.md +++ b/site/content/documentation/programming/repository.md @@ -113,8 +113,8 @@ The ElasticsearchStore stores RDF data in Elasticsearch. Not to be confused with The ElasticsearchStore is experimental and future releases may be incompatible with the current version. Write-ahead-logging is not supported. This means that a write operation can appear to have partially succeeded if the ElasticsearchStore looses its connection to Elasticsearch during a commit. -Note that, while RDF4J is licensed under the EDL, several ElasticSearch dependencies are licensed under the Elastic License or the SSPL, -which may have implications for some projects. +Note that, while RDF4J is licensed under the EDL, Elasticsearch itself is distributed under the Elastic License (with SSPL as an alternative). +The Elasticsearch-backed functionality in RDF4J is optional, and adopters should carefully evaluate the Elasticsearch licensing terms to ensure they meet the needs of their projects before enabling it. Please consult the ElasticSearch website and [license FAQ](https://www.elastic.co/licensing/elastic-license/faq) for more information. Transaction isolation is not as strong as for the other stores. The highest supported level is READ_COMMITTED, and even this From 940b0a7728a69864fb46acc218c5915d6a2d7d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Tue, 7 Oct 2025 12:14:22 +0200 Subject: [PATCH 2/5] Remove Hugo alert shortcode regression test --- site/layouts/shortcodes/alert.html | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 site/layouts/shortcodes/alert.html diff --git a/site/layouts/shortcodes/alert.html b/site/layouts/shortcodes/alert.html new file mode 100644 index 00000000000..6b2eab23334 --- /dev/null +++ b/site/layouts/shortcodes/alert.html @@ -0,0 +1,19 @@ +{{- $type := lower (default "info" (.Get "type")) -}} +{{- $title := .Get "title" -}} +{{- $icons := dict "info" "fa-info-circle" "warning" "fa-exclamation-triangle" "danger" "fa-exclamation-circle" "success" "fa-check-circle" -}} +{{- $backgrounds := dict "info" "#e0f4ff" "warning" "#fff4e0" "danger" "#ffe3e3" "success" "#e7f5e7" -}} +{{- $borders := dict "info" "#228be6" "warning" "#f08c00" "danger" "#c92a2a" "success" "#2f9e44" -}} +{{- $icon := index $icons $type | default (index $icons "info") -}} +{{- $background := index $backgrounds $type | default (index $backgrounds "info") -}} +{{- $border := index $borders $type | default (index $borders "info") -}} + From 217d4fab73691239d1be3233dea83d515eda826c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Tue, 7 Oct 2025 12:28:58 +0200 Subject: [PATCH 3/5] Update agents.md (#5503) --- AGENTS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 43824dfe312..1a43fca9b46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -718,6 +718,12 @@ rdf4j: root project * Don’t commit or push unless explicitly asked. * Don’t add new dependencies without explicit approval. +### Version Control Conventions + +* Branch names must always start with the GitHub issue identifier in the form `GH-XXXX`, where `XXXX` is the numeric issue number. +* Every commit message must be prefixed with the corresponding `GH-XXXX` label. +* Exception: if no GitHub issue number is available for the task, clearly note this in your handoff and align with the requester on an appropriate branch/commit prefix before proceeding. + It is illegal to `-am` when running tests! It is illegal to `-q` when running tests! You must follow these rules and instructions exactly as stated. From 2f6ae3b3ff770c43b54566ef697a6974dcacc234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Tue, 7 Oct 2025 17:14:22 +0200 Subject: [PATCH 4/5] Propagate iteration cache sync threshold to LMDB store (#5502) --- .../java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java | 3 +++ .../rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java index f13bde517ab..2dc7fd6e517 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStore.java @@ -133,6 +133,9 @@ public LmdbStore(LmdbStoreConfig config) { IsolationLevels.SNAPSHOT, IsolationLevels.SERIALIZABLE); setDefaultIsolationLevel(IsolationLevels.SNAPSHOT_READ); config.getDefaultQueryEvaluationMode().ifPresent(this::setDefaultQueryEvaluationMode); + if (config.getIterationCacheSyncThreshold() > 0) { + setIterationCacheSyncThreshold(config.getIterationCacheSyncThreshold()); + } EvaluationStrategyFactory evalStrategyFactory = config.getEvaluationStrategyFactory(); if (evalStrategyFactory != null) { setEvaluationStrategyFactory(evalStrategyFactory); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java index 6bffee78436..62dffba9ffe 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfigTest.java @@ -22,6 +22,8 @@ import org.eclipse.rdf4j.model.impl.LinkedHashModel; import org.eclipse.rdf4j.model.util.ModelBuilder; import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.sail.lmdb.LmdbStore; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -65,6 +67,17 @@ void testThatLmdbStoreConfigParseAndExportValueCacheSize(final int valueCacheSiz // TODO: Add more tests for other properties + @Test + void setIterationCacheSyncThresholdShouldApplyToCreatedStore() { + final long threshold = 42; + final LmdbStoreConfig config = new LmdbStoreConfig(); + config.setIterationCacheSyncThreshold(threshold); + + final LmdbStore store = new LmdbStore(config); + + assertThat(store.getIterationCacheSyncThreshold()).isEqualTo(threshold); + } + /** * Generic method to test parsing and exporting of config properties. * From 0a5d5e3ea9d38fa8d13bb9d12a502fa98a7fb11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20M=2E=20Ottestad?= Date: Tue, 7 Oct 2025 20:32:58 +0200 Subject: [PATCH 5/5] Handle Instant literals in TemporalAccessorLiteral (#5506) --- .../rdf4j/model/base/AbstractLiteral.java | 18 +++++++++++++++--- .../eclipse/rdf4j/model/util/LiteralsTest.java | 13 +++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java index dd412527a9d..199d349ed7f 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractLiteral.java @@ -32,6 +32,9 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.time.DateTimeException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; @@ -704,15 +707,24 @@ private static int key(Predicate include, ChronoField... fields) { this.value = value; - datatype = DATATYPES.get(key(value)); + TemporalAccessor lexicalValue = value; - if (datatype == null) { + CoreDatatype.XSD detectedDatatype = DATATYPES.get(key(lexicalValue)); + + if (detectedDatatype == null && value.isSupported(ChronoField.INSTANT_SECONDS)) { + Instant instant = Instant.from(value); + lexicalValue = OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + detectedDatatype = DATATYPES.get(key(lexicalValue)); + } + + if (detectedDatatype == null) { throw new IllegalArgumentException(String.format( "value <%s> cannot be represented by an XML Schema date/time datatype", value )); } - this.label = FORMATTERS.get(datatype).format(value); + datatype = detectedDatatype; + this.label = FORMATTERS.get(datatype).format(lexicalValue); } @Override diff --git a/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java b/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java index 3fa09603f70..2e8930b9bd0 100644 --- a/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java +++ b/core/model/src/test/java/org/eclipse/rdf4j/model/util/LiteralsTest.java @@ -11,6 +11,7 @@ package org.eclipse.rdf4j.model.util; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -18,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import java.time.Instant; import java.util.Date; import java.util.GregorianCalendar; import java.util.IllformedLocaleException; @@ -107,6 +109,17 @@ public void testNormaliseBCP47Tag() { .isThrownBy(() -> Literals.normalizeLanguageTag("ru-ua-latn")); } + @Test + public void valuesLiteralSupportsInstant() { + Instant instant = Instant.parse("2022-08-01T21:14:38.470534100Z"); + + Literal literal = Values.literal(instant); + + assertThat(Instant.parse(literal.getLabel())).isEqualTo(instant); + assertThat(literal.getDatatype()).isEqualTo(XSD.DATETIME); + assertThat(literal.temporalAccessorValue()).isEqualTo(instant); + } + /** * Test method for * {@link org.eclipse.rdf4j.model.util.Literals#getLabel(org.eclipse.rdf4j.model.Literal, java.lang.String)} .