mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
Merge pull request #1192 from Freakz2z/fix/rdf4j-repository-id
fix(triplet_store): honor RDF4J repository id
This commit is contained in:
@@ -182,7 +182,7 @@ for row in result.bindings:
|
||||
store = TripletStore(
|
||||
backend="rdf4j",
|
||||
endpoint="http://localhost:8080/rdf4j-server",
|
||||
repository_id="semantica", # passed through **config
|
||||
repository_id="semantica", # selects the remote repository
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ This page is intentionally conservative: it distinguishes between an adapter exi
|
||||
| FalkorDB | LPG | Yes | Yes | Partial | Partial | Redis-based; provenance depends on node/edge properties, and multi-graph isolation depends on the selected graph name. |
|
||||
| Amazon Neptune | LPG | Yes | Yes | Partial | Partial | Use the property-graph endpoint; AWS auth, VPC, and endpoint configuration can affect local tests. Provenance depends on node/edge properties. |
|
||||
| Apache AGE | LPG | Yes | Yes | Partial | Partial | Runs through PostgreSQL/AGE; Cypher compatibility and property handling can differ from standalone LPG engines. |
|
||||
| RDF4J | RDF | Yes | Partial | Partial | Partial | Context separation relies on named graphs; triple-level provenance may require reification or graph-level metadata. `RDF4JStore(repository_id=...)` currently has no effect — the constructor always connects to the `"default"` repository regardless of the value passed; track a fix separately. |
|
||||
| RDF4J | RDF | Yes | Partial | Partial | Partial | Context separation relies on named graphs; triple-level provenance may require reification or graph-level metadata. |
|
||||
| Apache Jena | RDF | Yes | Partial | Partial | Partial | Named graphs are needed for context separation; backend configuration and transaction behavior matter. |
|
||||
| Blazegraph | RDF | Yes | Partial | Partial | Partial | Use quads/named graphs for context; IRI stability and graph naming matter for provenance. |
|
||||
| Anzo | RDF | Yes | Partial | Partial | Partial | Anzo deployments are environment-specific; validate `dataset_uri`/graphmart naming, named-graph support, and provenance mapping. |
|
||||
@@ -107,7 +107,7 @@ from semantica.triplet_store import RDF4JStore
|
||||
|
||||
store = RDF4JStore(
|
||||
endpoint='http://localhost:8080/rdf4j-server',
|
||||
repository_id='semantica' # currently has no effect; connects to "default" (see Known limitations)
|
||||
repository_id='semantica'
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ License: MIT
|
||||
|
||||
import re
|
||||
from typing import Any, Dict, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import quote, urlparse
|
||||
|
||||
import requests
|
||||
from rdflib import Graph, Literal
|
||||
@@ -67,7 +67,8 @@ class RDF4JStore:
|
||||
self.progress_tracker.enabled = True
|
||||
|
||||
self.endpoint = endpoint.rstrip("/")
|
||||
self.repository_id = config.get("repository_id", "default")
|
||||
self.repository_id = repository_id or config.get("repository_id", "default")
|
||||
self._encoded_repository_id = quote(self.repository_id, safe="")
|
||||
self.username = config.get("username")
|
||||
self.password = config.get("password")
|
||||
self.timeout = config.get("timeout", 30)
|
||||
@@ -79,7 +80,7 @@ class RDF4JStore:
|
||||
"""Connect to RDF4J server."""
|
||||
try:
|
||||
# Test connection
|
||||
test_url = f"{self.endpoint}/repositories/{self.repository_id}"
|
||||
test_url = f"{self.endpoint}/repositories/{self._encoded_repository_id}"
|
||||
response = requests.get(
|
||||
test_url,
|
||||
timeout=self.timeout,
|
||||
@@ -100,11 +101,11 @@ class RDF4JStore:
|
||||
|
||||
def _get_sparql_endpoint(self) -> str:
|
||||
"""Get SPARQL query endpoint."""
|
||||
return f"{self.endpoint}/repositories/{self.repository_id}"
|
||||
return f"{self.endpoint}/repositories/{self._encoded_repository_id}"
|
||||
|
||||
def _get_update_endpoint(self) -> str:
|
||||
"""Get SPARQL Update endpoint."""
|
||||
return f"{self.endpoint}/repositories/{self.repository_id}/statements"
|
||||
return f"{self.endpoint}/repositories/{self._encoded_repository_id}/statements"
|
||||
|
||||
def _is_construct_query(self, query: str) -> bool:
|
||||
"""
|
||||
@@ -163,7 +164,7 @@ class RDF4JStore:
|
||||
"""
|
||||
# RDF4J transaction support
|
||||
transaction_url = (
|
||||
f"{self.endpoint}/repositories/{self.repository_id}/transactions"
|
||||
f"{self.endpoint}/repositories/{self._encoded_repository_id}/transactions"
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -22,6 +22,71 @@ def _make_connected_store():
|
||||
CONSTRUCT_QUERY = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }"
|
||||
|
||||
|
||||
class TestRDF4JStoreInitialization(unittest.TestCase):
|
||||
|
||||
def test_explicit_repository_id_selects_repository(self):
|
||||
response = MagicMock(status_code=200)
|
||||
|
||||
with patch(
|
||||
"semantica.triplet_store.rdf4j_store.requests.get",
|
||||
return_value=response,
|
||||
) as mock_get:
|
||||
store = RDF4JStore(
|
||||
endpoint="http://localhost:8080/rdf4j-server/",
|
||||
repository_id="semantica",
|
||||
)
|
||||
|
||||
self.assertEqual(store.repository_id, "semantica")
|
||||
mock_get.assert_called_once_with(
|
||||
"http://localhost:8080/rdf4j-server/repositories/semantica",
|
||||
timeout=30,
|
||||
auth=None,
|
||||
)
|
||||
|
||||
def test_repository_id_is_encoded_as_a_single_url_path_segment(self):
|
||||
response = MagicMock(status_code=200)
|
||||
|
||||
with patch(
|
||||
"semantica.triplet_store.rdf4j_store.requests.get",
|
||||
return_value=response,
|
||||
) as mock_get:
|
||||
store = RDF4JStore(
|
||||
endpoint="http://localhost:8080/rdf4j-server",
|
||||
repository_id="team/repo ?#",
|
||||
)
|
||||
|
||||
self.assertEqual(store.repository_id, "team/repo ?#")
|
||||
mock_get.assert_called_once_with(
|
||||
"http://localhost:8080/rdf4j-server/repositories/team%2Frepo%20%3F%23",
|
||||
timeout=30,
|
||||
auth=None,
|
||||
)
|
||||
self.assertEqual(
|
||||
store._get_sparql_endpoint(),
|
||||
"http://localhost:8080/rdf4j-server/repositories/team%2Frepo%20%3F%23",
|
||||
)
|
||||
self.assertEqual(
|
||||
store._get_update_endpoint(),
|
||||
"http://localhost:8080/rdf4j-server/repositories/"
|
||||
"team%2Frepo%20%3F%23/statements",
|
||||
)
|
||||
|
||||
transaction_response = MagicMock()
|
||||
transaction_response.headers = {"Location": "/transactions/tx-1"}
|
||||
with patch(
|
||||
"semantica.triplet_store.rdf4j_store.requests.post",
|
||||
return_value=transaction_response,
|
||||
) as mock_post:
|
||||
self.assertEqual(store.begin_transaction(), "tx-1")
|
||||
|
||||
mock_post.assert_called_once_with(
|
||||
"http://localhost:8080/rdf4j-server/repositories/"
|
||||
"team%2Frepo%20%3F%23/transactions",
|
||||
timeout=30,
|
||||
auth=None,
|
||||
)
|
||||
|
||||
|
||||
class TestRDF4JStoreIsConstructQuery(unittest.TestCase):
|
||||
def test_detects_uppercase(self):
|
||||
self.assertTrue(_make_connected_store()._is_construct_query(
|
||||
|
||||
Reference in New Issue
Block a user