diff --git a/AGENTS.md b/AGENTS.md index 33fde3d827b..80144565041 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,8 +4,6 @@ Welcome, AI Agent! Your persistence, curiosity, and craftsmanship make a differe You need to read the entire AGENTS.md file and follow all instructions exactly. Keep this fresh in your context as you work. -> **Timebox:** Aim to complete each autonomous run in **15–30 minutes**. - --- ## Read‑Me‑Now: Proportional Test‑First Rule (Default) @@ -27,31 +25,36 @@ It is illegal to `-am` when running tests! It is illegal to `-q` when running tests! > **Clarification:** For **strictly behavior‑neutral refactors** that are already **fully exercised by existing tests**, or for **bugfixes with an existing failing test**, you may use **Routine B — Change without new tests**. In that case you must capture **pre‑change passing evidence** at the smallest scope that hits the code you’re about to edit, prove **Hit Proof**, then show **post‑change passing evidence** from the **same selection**. -> **No exceptions for any behavior‑changing change** — for those, you must follow **Routine A — Full TDD**. +> **No exceptions for any behavior‑changing change** — for those, you must follow **Routine A — Full TDD** or **Routine D — ExecPlans**. --- -## Three Routines: Choose Your Path +## Four Routines: Choose Your Path **Routine A — Full TDD (Default)** **Routine B — Change without new tests (Proportional, gated)** **Routine C — Spike/Investigate (No production changes)** +**Routine D — ExecPlans: Complex features or significant refactors** ### Decision quickstart -1. **Is new externally observable behavior required?** +1. **Is ExecPlans required (complex feature, significant refactor or requested by the user)?** + → **Yes:** **Routine D (ExecPlans)**. Use an ExecPlan (as described in .agent/PLANS.md) from design to implementation. + → **No:** continue. + +2**Is new externally observable behavior required?** → **Yes:** **Routine A (Full TDD)**. Add the smallest failing test first. → **No:** continue. -2. **Does a failing test already exist in this repo that pinpoints the issue?** +3**Does a failing test already exist in this repo that pinpoints the issue?** → **Yes:** **Routine B (Bugfix using existing failing test).** → **No:** continue. -3. **Is the edit strictly behavior‑neutral, local in scope, and clearly hit by existing tests?** +4**Is the edit strictly behavior‑neutral, local in scope, and clearly hit by existing tests?** → **Yes:** **Routine B (Refactor/micro‑perf/documentation/build).** → **No or unsure:** continue. -4. **Is this purely an investigation/design spike with no production code changes?** +5**Is this purely an investigation/design spike with no production code changes?** → **Yes:** **Routine C (Spike/Investigate).** → **No or unsure:** **Routine A.** @@ -59,27 +62,15 @@ It is illegal to `-q` when running tests! --- -## PIOSEE Decision Model (Adopted) +## ExecPlans -Use PIOSEE on every task to structure thinking and execution. It complements the routines below and ties directly into the Traceability trio (Description, Evidence, Plan). +When writing complex features or significant refactors, use an ExecPlan (as described in PLANS.md) from design to implementation. -- Problem: restate the task in one sentence, note constraints/timebox, and identify likely routine (A/B/C). -- Information: inspect modules and AGENTS.md, gather environment constraints, locate existing tests/reports, and search code to localize the work. -- Options: list 2–3 viable approaches (routine choice, test scope, fix location) and weigh them with the Proportionality Model. -- Select: choose one option and routine; update the Living Plan with exactly one `in_progress` step. -- Execute: follow the Working Loop and house rules; for Routine A add the smallest failing test first; capture an Evidence block after each grouped action. -- Evaluate: check against the Definition of Done; if gaps remain, adjust the plan or change routine; record final Evidence and a brief retrospective. - -PIOSEE → Traceability trio mapping -- P/I/O → Description -- S → Plan (one `in_progress`) -- E/E → Evidence and Verification - -For documentation‑only edits and other Routine B cases, still run PIOSEE briefly to confirm neutrality and reversibility. +--- ## Proportionality Model (Think before you test) -Score the change on these lenses. If any are **High**, prefer **Routine A**. +Score the change on these lenses. If any are **High**, prefer **Routine A or D**. - **Behavioral surface:** affects outputs, serialization, parsing, APIs, error text, timing/order? - **Blast radius:** number of modules/classes touched; public vs internal. @@ -101,7 +92,7 @@ Score the change on these lenses. If any are **High**, prefer **Routine A**. * Relevant module tests pass; failures triaged or crisply explained. * Only necessary files changed; headers correct for new files. * Clear final summary: what changed, why, where, how verified, next steps. - * **Evidence present:** failing test output (pre‑fix) and passing output (post‑fix) are shown for Routine A; for Routine B show **pre/post green** from the **same selection** plus **Hit Proof**. + * **Evidence present:** failing test output (pre‑fix) and passing output (post‑fix) are shown for Routine A; for Routine B show **pre/post green** from the **same selection** plus **Hit Proof**; for Routine D NO EVIDENCE. ### No Monkey‑Patching or Band‑Aid Fixes (Non‑Negotiable) @@ -336,6 +327,14 @@ It is illegal to `-q` when running tests! --- +## Routine D — ExecPlans + +> Use for **complex features or significant refactors**. + +When writing complex features or significant refactors, use an ExecPlan (as described in .agent/PLANS.md) from design to implementation. + +--- + ## Where to Draw the Line — A Short Debate > **Purist:** “All changes must start with a failing test.” @@ -349,7 +348,7 @@ It is illegal to `-q` when running tests! * Logging/message tweaks **not** asserted by tests. * Build/CI config that doesn’t alter runtime behavior. -**Out‑of‑scope (use Routine A)** +**Out‑of‑scope (use Routine A/D)** * Changing query results, serialization, or parsing behavior. * Altering error messages that tests assert. * Anything touching concurrency, timeouts, IO, or ordering. @@ -360,8 +359,7 @@ It is illegal to `-q` when running tests! ## Working Loop -* **PIOSEE first:** restate Problem, gather Information, list Options; then Select, Execute, Evaluate. -* **Plan:** small, verifiable steps; keep one `in_progress`. +* **Plan:** small, verifiable steps; keep one `in_progress`, or follow PLANS.md (ExecPlans) * **Change:** minimal, surgical edits; keep style/structure consistent. * **Format:** `mvn -o -Dmaven.repo.local=.m2_repo -q -T 2C formatter:format impsort:sort xml-format:xml-format` * **Compile (fast):** `mvn -o -Dmaven.repo.local=.m2_repo -pl -am -Pquick install | tail -500` @@ -525,11 +523,11 @@ Do **not** modify existing headers’ years. * **Files touched:** list file paths. * **Commands run:** key build/test commands. * **Verification:** which tests passed, where you checked reports. -* **PIOSEE trace (concise):** P/I/O summary, selected option/routine, key evaluate outcomes. * **Evidence:** *Routine A:* failing output (pre‑fix) and passing output (post‑fix). *Routine B:* pre‑ and post‑green snippets from the **same selection** + **Hit Proof**. *Routine C:* artifacts from investigation (logs/notes/measurements) and proposed next steps. + *Routine D:* NO EVIDENCE REQUIRED. * **Assumptions:** key assumptions and autonomous decisions. * **Limitations:** anything left or risky edge cases. * **Next steps:** optional follow‑ups. diff --git a/PLANS.md b/PLANS.md index 7d8044a9f71..6422d1771a5 100644 --- a/PLANS.md +++ b/PLANS.md @@ -1,77 +1,77 @@ # Codex Execution Plans (ExecPlans): - + This document describes the requirements for an execution plan ("ExecPlan"), a design document that a coding agent can follow to deliver a working feature or system change. Treat the reader as a complete beginner to this repository: they have only the current working tree and the single ExecPlan file you provide. There is no memory of prior plans and no external context. - + ## How to use ExecPlans and PLANS.md - + When authoring an executable specification (ExecPlan), follow PLANS.md _to the letter_. If it is not in your context, refresh your memory by reading the entire PLANS.md file. Be thorough in reading (and re-reading) source material to produce an accurate specification. When creating a spec, start from the skeleton and flesh it out as you do your research. - + When implementing an executable specification (ExecPlan), do not prompt the user for "next steps"; simply proceed to the next milestone. Keep all sections up to date, add or split entries in the list at every stopping point to affirmatively state the progress made and next steps. Resolve ambiguities autonomously, and commit frequently. - + When discussing an executable specification (ExecPlan), record decisions in a log in the spec for posterity; it should be unambiguously clear why any change to the specification was made. ExecPlans are living documents, and it should always be possible to restart from _only_ the ExecPlan and no other work. - + When researching a design with challenging requirements or significant unknowns, use milestones to implement proof of concepts, "toy implementations", etc., that allow validating whether the user's proposal is feasible. Read the source code of libraries by finding or acquiring them, research deeply, and include prototypes to guide a fuller implementation. - + ## Requirements - + NON-NEGOTIABLE REQUIREMENTS: - + * Every ExecPlan must be fully self-contained. Self-contained means that in its current form it contains all knowledge and instructions needed for a novice to succeed. * Every ExecPlan is a living document. Contributors are required to revise it as progress is made, as discoveries occur, and as design decisions are finalized. Each revision must remain fully self-contained. * Every ExecPlan must enable a complete novice to implement the feature end-to-end without prior knowledge of this repo. * Every ExecPlan must produce a demonstrably working behavior, not merely code changes to "meet a definition". * Every ExecPlan must define every term of art in plain language or do not use it. - + Purpose and intent come first. Begin by explaining, in a few sentences, why the work matters from a user's perspective: what someone can do after this change that they could not do before, and how to see it working. Then guide the reader through the exact steps to achieve that outcome, including what to edit, what to run, and what they should observe. - + The agent executing your plan can list files, read files, search, run the project, and run tests. It does not know any prior context and cannot infer what you meant from earlier milestones. Repeat any assumption you rely on. Do not point to external blogs or docs; if knowledge is required, embed it in the plan itself in your own words. If an ExecPlan builds upon a prior ExecPlan and that file is checked in, incorporate it by reference. If it is not, you must include all relevant context from that plan. - + ## Formatting - + Format and envelope are simple and strict. Each ExecPlan must be one single fenced code block labeled as `md` that begins and ends with triple backticks. Do not nest additional triple-backtick code fences inside; when you need to show commands, transcripts, diffs, or code, present them as indented blocks within that single fence. Use indentation for clarity rather than code fences inside an ExecPlan to avoid prematurely closing the ExecPlan's code fence. Use two newlines after every heading, use # and ## and so on, and correct syntax for ordered and unordered lists. - + When writing an ExecPlan to a Markdown (.md) file where the content of the file *is only* the single ExecPlan, you should omit the triple backticks. - + Write in plain prose. Prefer sentences over lists. Avoid checklists, tables, and long enumerations unless brevity would obscure meaning. Checklists are permitted only in the `Progress` section, where they are mandatory. Narrative sections must remain prose-first. - + ## Guidelines - + Self-containment and plain language are paramount. If you introduce a phrase that is not ordinary English ("daemon", "middleware", "RPC gateway", "filter graph"), define it immediately and remind the reader how it manifests in this repository (for example, by naming the files or commands where it appears). Do not say "as defined previously" or "according to the architecture doc." Include the needed explanation here, even if you repeat yourself. - + Avoid common failure modes. Do not rely on undefined jargon. Do not describe "the letter of a feature" so narrowly that the resulting code compiles but does nothing meaningful. Do not outsource key decisions to the reader. When ambiguity exists, resolve it in the plan itself and explain why you chose that path. Err on the side of over-explaining user-visible effects and under-specifying incidental implementation details. - + Anchor the plan with observable outcomes. State what the user can do after implementation, the commands to run, and the outputs they should see. Acceptance should be phrased as behavior a human can verify ("after starting the server, navigating to [http://localhost:8080/health](http://localhost:8080/health) returns HTTP 200 with body OK") rather than internal attributes ("added a HealthCheck struct"). If a change is internal, explain how its impact can still be demonstrated (for example, by running tests that fail before and pass after, and by showing a scenario that uses the new behavior). - + Specify repository context explicitly. Name files with full repository-relative paths, name functions and modules precisely, and describe where new files should be created. If touching multiple areas, include a short orientation paragraph that explains how those parts fit together so a novice can navigate confidently. When running commands, show the working directory and exact command line. When outcomes depend on environment, state the assumptions and provide alternatives when reasonable. - + Be idempotent and safe. Write the steps so they can be run multiple times without causing damage or drift. If a step can fail halfway, include how to retry or adapt. If a migration or destructive operation is necessary, spell out backups or safe fallbacks. Prefer additive, testable changes that can be validated as you go. - + Validation is not optional. Include instructions to run tests, to start the system if applicable, and to observe it doing something useful. Describe comprehensive testing for any new features or capabilities. Include expected outputs and error messages so a novice can tell success from failure. Where possible, show how to prove that the change is effective beyond compilation (for example, through a small end-to-end scenario, a CLI invocation, or an HTTP request/response transcript). State the exact test commands appropriate to the project’s toolchain and how to interpret their results. - + Capture evidence. When your steps produce terminal output, short diffs, or logs, include them inside the single fenced block as indented examples. Keep them concise and focused on what proves success. If you need to include a patch, prefer file-scoped diffs or small excerpts that a reader can recreate by following your instructions rather than pasting large blobs. - + ## Milestones - + Milestones are narrative, not bureaucracy. If you break the work into milestones, introduce each with a brief paragraph that describes the scope, what will exist at the end of the milestone that did not exist before, the commands to run, and the acceptance you expect to observe. Keep it readable as a story: goal, work, result, proof. Progress and milestones are distinct: milestones tell the story, progress tracks granular work. Both must exist. Never abbreviate a milestone merely for the sake of brevity, do not leave out details that could be crucial to a future implementation. - + Each milestone must be independently verifiable and incrementally implement the overall goal of the execution plan. - + ## Living plans and design decisions - + * ExecPlans are living documents. As you make key design decisions, update the plan to record both the decision and the thinking behind it. Record all decisions in the `Decision Log` section. * ExecPlans must contain and maintain a `Progress` section, a `Surprises & Discoveries` section, a `Decision Log`, and an `Outcomes & Retrospective` section. These are not optional. * When you discover optimizer behavior, performance tradeoffs, unexpected bugs, or inverse/unapply semantics that shaped your approach, capture those observations in the `Surprises & Discoveries` section with short evidence snippets (test output is ideal). * If you change course mid-implementation, document why in the `Decision Log` and reflect the implications in `Progress`. Plans are guides for the next contributor as much as checklists for you. * At completion of a major task or the full plan, write an `Outcomes & Retrospective` entry summarizing what was achieved, what remains, and lessons learned. - + # Prototyping milestones and parallel implementations - + It is acceptable—-and often encouraged—-to include explicit prototyping milestones when they de-risk a larger change. Examples: adding a low-level operator to a dependency to validate feasibility, or exploring two composition orders while measuring optimizer effects. Keep prototypes additive and testable. Clearly label the scope as “prototyping”; describe how to run and observe results; and state the criteria for promoting or discarding the prototype. - + Prefer additive code changes followed by subtractions that keep tests passing. Parallel implementations (e.g., keeping an adapter alongside an older path during migration) are fine when they reduce risk or enable tests to continue passing during a large migration. Describe how to validate both paths and how to retire one safely with tests. When working with multiple new libraries or feature areas, consider creating spikes that evaluate the feasibility of these features _independently_ of one another, proving that the external library performs as expected and implements the features we need in isolation. - + ## Skeleton of a Good ExecPlan - + ```md # @@ -146,7 +146,7 @@ In crates/foo/planner.rs, define: fn plan(&self, observed: &Observed) -> Vec; } ``` - + If you follow the guidance above, a single, stateless agent -- or a human novice -- can read your ExecPlan from top to bottom and produce a working, observable result. That is the bar: SELF-CONTAINED, SELF-SUFFICIENT, NOVICE-GUIDING, OUTCOME-FOCUSED. - + When you revise a plan, you must ensure your changes are comprehensively reflected across all sections, including the living document sections, and you must write a note at the bottom of the plan describing the change and the reason why. ExecPlans must describe not just the what but the why for almost everything. diff --git a/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/Iterations.java b/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/Iterations.java index 85184886f60..4d06940de82 100644 --- a/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/Iterations.java +++ b/core/common/iterator/src/main/java/org/eclipse/rdf4j/common/iteration/Iterations.java @@ -43,6 +43,17 @@ public static List asList(CloseableIteration iteration) { } } + public static long count(CloseableIteration iteration) { + try (iteration) { + long count = 0; + while (iteration.hasNext()) { + iteration.next(); + count++; + } + return count; + } + } + /** * Get a Set containing all elements obtained from the specified iteration. * diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java index b068fcff57f..ab252919e9c 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java @@ -124,20 +124,20 @@ private int split() { } } - @Override - public int hashCode() { - return iri.hashCode(); - } - @Override public boolean equals(Object o) { - if (o == this) { + if (this == o) { return true; } - if (!(o instanceof IRI)) { - return false; + if (o instanceof GenericIRI) { + // Fast-path for same concrete type: compare internal string directly + return this.iri.equals(((GenericIRI) o).iri); + } + if (o instanceof IRI) { + // Cross-type equality by lexical form + return stringValue().equals(((IRI) o).stringValue()); } - return iri.equals(o.toString()); + return false; } } diff --git a/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/GenericIRISymmetryContractTest.java b/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/GenericIRISymmetryContractTest.java new file mode 100644 index 00000000000..66834648136 --- /dev/null +++ b/core/model-api/src/test/java/org/eclipse/rdf4j/model/base/GenericIRISymmetryContractTest.java @@ -0,0 +1,64 @@ +/** + * ****************************************************************************** + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + * ****************************************************************************** + */ +package org.eclipse.rdf4j.model.base; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.Serializable; + +import org.eclipse.rdf4j.model.IRI; +import org.junit.jupiter.api.Test; + +/** + * Reproduces equals() symmetry issue for AbstractIRI.GenericIRI with third-party IRI implementations that rely on + * Object.equals. + */ +public class GenericIRISymmetryContractTest { + + private static final class ThirdPartyIri implements IRI, Serializable { + private static final long serialVersionUID = 1L; + private final String ns; + private final String ln; + + ThirdPartyIri(String ns, String ln) { + this.ns = ns; + this.ln = ln; + } + + @Override + public String getNamespace() { + return ns; + } + + @Override + public String getLocalName() { + return ln; + } + + @Override + public String stringValue() { + return ns + ln; + } + } + + @Test + public void equalsMustBeSymmetric() { + IRI generic = new AbstractIRI.GenericIRI("http://example.org/", "x"); + IRI third = new ThirdPartyIri("http://example.org/", "x"); + + boolean a = generic.equals(third); + boolean b = third.equals(generic); + + assertThat(a).as("GenericIRI.equals(third) must equal third.equals(GenericIRI)").isEqualTo(b); + } +} diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java index 08729a6c5a7..9092db42f9b 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java @@ -123,20 +123,20 @@ public String getLocalName() { return iriString.substring(localNameIdx); } - @Override - public int hashCode() { - return iriString.hashCode(); - } - @Override public boolean equals(Object o) { - if (o == this) { + if (this == o) { return true; } - if (!(o instanceof IRI)) { - return false; + if (o instanceof SimpleIRI) { + // Fast-path for same concrete type: compare internal string directly + return this.iriString.equals(((SimpleIRI) o).iriString); } - - return iriString.equals(o.toString()); + if (o instanceof IRI) { + // Cross-type equality by lexical form + return stringValue().equals(((IRI) o).stringValue()); + } + return false; } + } diff --git a/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleIRICrossTypeEqualityTest.java b/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleIRICrossTypeEqualityTest.java new file mode 100644 index 00000000000..5c043d8da39 --- /dev/null +++ b/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleIRICrossTypeEqualityTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.model.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.Serializable; + +import org.eclipse.rdf4j.model.IRI; +import org.junit.jupiter.api.Test; + +/** + * Verifies cross-type equality between {@link SimpleIRI} and a third-party {@link IRI} implementation that does not + * override {@link Object#equals(Object)} or {@link Object#hashCode()}. + */ +public class SimpleIRICrossTypeEqualityTest { + + @Test + public void simpleIriEqualsThirdPartyIriByStringValue() { + IRI simple = new SimpleIRI("http://example.org/x"); + IRI thirdParty = new ThirdPartyIri("http://example.org/", "x"); + + // Historical behavior: SimpleIRI compares by string value against any IRI implementation. + assertThat(simple).isEqualTo(thirdParty); + + // Note: The reverse direction is deliberately not asserted here. A third-party + // implementation may rely on Object.equals (identity) and thus not be symmetric. + } + + /** + * Minimal third-party IRI implementation: relies on Object.equals/hashCode (identity), only implements the methods + * required by the {@link IRI} contract for namespace/localname. + */ + private static final class ThirdPartyIri implements IRI, Serializable { + private static final long serialVersionUID = 1L; + + private final String namespace; + private final String localname; + + ThirdPartyIri(String namespace, String localname) { + this.namespace = namespace; + this.localname = localname; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getLocalName() { + return localname; + } + + @Override + public String stringValue() { + return namespace + localname; + } + } +} diff --git a/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleIRISymmetryContractTest.java b/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleIRISymmetryContractTest.java new file mode 100644 index 00000000000..aa6a5bd3d75 --- /dev/null +++ b/core/model/src/test/java/org/eclipse/rdf4j/model/impl/SimpleIRISymmetryContractTest.java @@ -0,0 +1,64 @@ +/** + * ****************************************************************************** + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + * ****************************************************************************** + */ +package org.eclipse.rdf4j.model.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.Serializable; + +import org.eclipse.rdf4j.model.IRI; +import org.junit.jupiter.api.Test; + +/** + * Reproduces equals() symmetry issue for SimpleIRI with third-party IRI implementations that rely on Object.equals. + */ +public class SimpleIRISymmetryContractTest { + + private static final class ThirdPartyIri implements IRI, Serializable { + private static final long serialVersionUID = 1L; + private final String ns; + private final String ln; + + ThirdPartyIri(String ns, String ln) { + this.ns = ns; + this.ln = ln; + } + + @Override + public String getNamespace() { + return ns; + } + + @Override + public String getLocalName() { + return ln; + } + + @Override + public String stringValue() { + return ns + ln; + } + } + + @Test + public void equalsMustBeSymmetric() { + IRI simple = new SimpleIRI("http://example.org/x"); + IRI third = new ThirdPartyIri("http://example.org/", "x"); + + boolean a = simple.equals(third); + boolean b = third.equals(simple); + + // Contract: equals must be symmetric + assertThat(a).as("SimpleIRI.equals(third) must equal third.equals(SimpleIRI)").isEqualTo(b); + } +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/InnerMergeJoinIterator.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/InnerMergeJoinIterator.java index 63448ba96aa..60404d0e31c 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/InnerMergeJoinIterator.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/InnerMergeJoinIterator.java @@ -185,7 +185,7 @@ private void lessThan() { leftPeekValue = null; currentLeftValueAndPeekEquals = -1; - if (oldLeftValue.equals(currentLeftValue)) { + if (oldLeftValue.getType() == currentLeftValue.getType() && oldLeftValue.equals(currentLeftValue)) { // we have duplicate keys on the leftIterator and need to reset the rightIterator (if it // is resettable) if (rightIterator.isResettable()) { @@ -219,7 +219,8 @@ private void doLeftPeek() { } if (currentLeftValueAndPeekEquals == -1) { - boolean equals = currentLeftValue.equals(leftPeekValue); + boolean equals = leftPeekValue != null && currentLeftValue.getType() == leftPeekValue.getType() + && currentLeftValue.equals(leftPeekValue); if (equals) { currentLeftValue = leftPeekValue; currentLeftValueAndPeekEquals = 0; diff --git a/core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/StatementPattern.java b/core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/StatementPattern.java index a2c7392059b..6f199e137ec 100644 --- a/core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/StatementPattern.java +++ b/core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/StatementPattern.java @@ -48,6 +48,77 @@ public enum Scope { NAMED_CONTEXTS } + /** + * Index selection hint for statement pattern evaluation. + */ + @Experimental + public enum Index { + S, + P, + O, + C, + SP, + SO, + SC, + PS, + PO, + PC, + OS, + OP, + OC, + CS, + CP, + CO, + SPO, + SPC, + SOP, + SOC, + SCP, + SCO, + PSO, + PSC, + POS, + POC, + PCS, + PCO, + OSP, + OSC, + OPS, + OPC, + OCS, + OCP, + CSP, + CSO, + CPS, + CPO, + COS, + COP, + SPOC, + SPCO, + SOPC, + SOCP, + SCPO, + SCOP, + PSOC, + PSCO, + POSC, + POCS, + PCSO, + PCOS, + OSPC, + OSCP, + OPSC, + OPCS, + OCSP, + OCPS, + CSPO, + CSOP, + CPSO, + CPOS, + COSP, + COPS + } + /*-----------* * Variables * *-----------*/ @@ -66,6 +137,8 @@ public enum Scope { private String indexName; + private Index index; + private Set assuredBindingNames; private List varList; @@ -427,6 +500,7 @@ public StatementPattern clone() { clone.assuredBindingNames = assuredBindingNames; clone.varList = null; clone.statementOrder = statementOrder; + clone.index = index; return clone; } @@ -540,8 +614,18 @@ public String getIndexName() { return indexName; } + @Experimental + public Index getIndex() { + return index; + } + @Experimental public void setIndexName(String indexName) { this.indexName = indexName; } + + @Experimental + public void setIndex(Index index) { + this.index = index; + } } diff --git a/core/repository/sail/src/main/java/org/eclipse/rdf4j/repository/sail/helpers/SailUpdateExecutor.java b/core/repository/sail/src/main/java/org/eclipse/rdf4j/repository/sail/helpers/SailUpdateExecutor.java index edd0f0a812c..24d7e7c1392 100644 --- a/core/repository/sail/src/main/java/org/eclipse/rdf4j/repository/sail/helpers/SailUpdateExecutor.java +++ b/core/repository/sail/src/main/java/org/eclipse/rdf4j/repository/sail/helpers/SailUpdateExecutor.java @@ -443,9 +443,16 @@ protected void executeModify(Modify modify, UpdateContext uc, int maxExecutionTi whereClause = new QueryRoot(whereClause); } + long count = 0; + try (CloseableIteration sourceBindings = evaluateWhereClause( whereClause, uc, maxExecutionTime)) { while (sourceBindings.hasNext()) { + if (logger.isDebugEnabled()) { + if (++count % 1_000_000 == 0) { + logger.debug("Update query processed {} bindings", count); + } + } BindingSet sourceBinding = sourceBindings.next(); deleteBoundTriples(sourceBinding, modify.getDeleteExpr(), uc); diff --git a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceBranch.java b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceBranch.java index 95ed831dc6e..6dbea84596a 100644 --- a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceBranch.java +++ b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/SailSourceBranch.java @@ -375,7 +375,29 @@ void preparedChangeset(Changeset changeset) { void merge(Changeset change) { try { - semaphore.lock(); + try { + boolean locked = semaphore.tryLock(10, TimeUnit.SECONDS); + if (!locked) { + + logger.warn( + "Waiting 10 seconds on semaphore for merging changesets without acquiring the semaphore {}. This may be a deadlock situation. Attempting to acquire semaphore interruptibly.", + semaphore); + if (Thread.interrupted()) { + throw new InterruptedException(); + } + semaphore.lockInterruptibly(); + } + } catch (InterruptedException e) { + logger.warn("Interrupted while trying to merge changes. Retrying once.", e); + try { + semaphore.lockInterruptibly(); + } catch (InterruptedException ex) { + logger.error("Interrupted while trying to merge changes. Giving up!", e); + Thread.currentThread().interrupt(); + throw new SailException(ex); + } + } + pending.remove(change); if (isChanged(change)) { Changeset merged; diff --git a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/UnionSailDataset.java b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/UnionSailDataset.java index b7a9ac90dca..28fbd6b5ce2 100644 --- a/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/UnionSailDataset.java +++ b/core/sail/base/src/main/java/org/eclipse/rdf4j/sail/base/UnionSailDataset.java @@ -180,6 +180,8 @@ public CloseableIteration getStatements(StatementOrder stat iteration1 = dataset1.getStatements(statementOrder, subj, pred, obj, contexts); iteration2 = dataset2.getStatements(statementOrder, subj, pred, obj, contexts); Comparator cmp = statementOrder.getComparator(dataset1.getComparator()); + assert dataset1.getComparator() != null && dataset2.getComparator() != null + : "At this point the code assumes both datasets have a comparator, since otherwise getStatements without order would have been used"; return DualUnionIteration.getWildcardInstance(cmp, iteration1, iteration2); } catch (Throwable t) { try { @@ -223,7 +225,9 @@ public Comparator getComparator() { Comparator comparator1 = dataset1.getComparator(); Comparator comparator2 = dataset2.getComparator(); - assert (comparator1 == null && comparator2 == null) || (comparator1 != null && comparator2 != null); + if (comparator1 == null || comparator2 == null) { + return null; + } return comparator1; } diff --git a/core/sail/base/src/test/java/org/eclipse/rdf4j/sail/base/UnionSailDatasetComparatorTest.java b/core/sail/base/src/test/java/org/eclipse/rdf4j/sail/base/UnionSailDatasetComparatorTest.java new file mode 100644 index 00000000000..5d679025c82 --- /dev/null +++ b/core/sail/base/src/test/java/org/eclipse/rdf4j/sail/base/UnionSailDatasetComparatorTest.java @@ -0,0 +1,107 @@ +/* + * ****************************************************************************** + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + * ****************************************************************************** + */ +package org.eclipse.rdf4j.sail.base; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.util.Comparator; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.EmptyIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Namespace; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Triple; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.evaluation.util.ValueComparator; +import org.eclipse.rdf4j.sail.SailException; +import org.junit.jupiter.api.Test; + +/** + * Reproduces a mismatch where one dataset reports a value comparator and the other doesn't. The union should not + * assert; it should degrade by reporting null and avoid ordered merging. + */ +public class UnionSailDatasetComparatorTest { + + private static class StubDataset implements SailDataset { + private final Comparator cmp; + + StubDataset(Comparator cmp) { + this.cmp = cmp; + } + + @Override + public void close() { + } + + @Override + public CloseableIteration getNamespaces() { + return new EmptyIteration<>(); + } + + @Override + public String getNamespace(String prefix) { + return null; + } + + @Override + public CloseableIteration getContextIDs() { + return new EmptyIteration<>(); + } + + @Override + public CloseableIteration getStatements(Resource s, IRI p, Value o, Resource... c) { + return new EmptyIteration<>(); + } + + @Override + public CloseableIteration getStatements(StatementOrder order, Resource s, IRI p, Value o, + Resource... c) { + return new EmptyIteration<>(); + } + + @Override + public CloseableIteration getTriples(Resource s, IRI p, Value o) { + return new EmptyIteration<>(); + } + + @Override + public Set getSupportedOrders(Resource subj, IRI pred, Value obj, Resource... contexts) { + return Set.of(); + } + + @Override + public Comparator getComparator() { + return cmp; + } + } + + @Test + public void mismatchedComparators_returnsNullAndNoAssert() throws QueryEvaluationException { + SailDataset withComparator = new StubDataset(new ValueComparator()); + SailDataset withoutComparator = new StubDataset(null); + + SailDataset union = UnionSailDataset.getInstance(withComparator, withoutComparator); + + // Expect graceful degradation + assertThat(union.getComparator()).isNull(); + + // And ordered getStatements should not attempt to use a comparator in this case + assertDoesNotThrow(() -> union.getStatements(StatementOrder.S, null, null, null)); + } +} diff --git a/core/sail/lmdb/pom.xml b/core/sail/lmdb/pom.xml index d818020171a..e73e88fdc49 100644 --- a/core/sail/lmdb/pom.xml +++ b/core/sail/lmdb/pom.xml @@ -175,6 +175,12 @@ ${project.version} test + + ${project.groupId} + rdf4j-sail-memory + ${project.version} + test + ${project.groupId} rdf4j-rio-nquads diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/IdAccessor.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/IdAccessor.java new file mode 100644 index 00000000000..80888ff5dca --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/IdAccessor.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Set; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; + +/** + * Read-only accessor for variable ID lookups against a record long[] produced by a RecordIterator. + */ +@InternalUseOnly +public interface IdAccessor { + long getId(long[] record, String varName); + + Set getVariableNames(); + + /** + * Returns the index inside the {@code long[]} record where the given variable's ID is stored. Implementations + * should return {@code -1} when the variable is not part of the record. + */ + int getRecordIndex(String varName); + + boolean applyRecord(long[] record, MutableBindingSet target, ValueStore valueStore) + throws QueryEvaluationException; +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/IdBindingInfo.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/IdBindingInfo.java new file mode 100644 index 00000000000..a73087beb1d --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/IdBindingInfo.java @@ -0,0 +1,454 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.evaluation.ArrayBindingSet; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinIterator; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinSettings; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Describes an ID-binding record shape (variables and their positions) and can materialize it to a BindingSet. + */ +@InternalUseOnly +public final class IdBindingInfo implements IdAccessor { + + private final Map indexByVar; // insertion order preserved + private final Map positionsMaskByVar; // aggregated from contributing patterns + private final Entry[] prepared; // optional fast path, aligned with insertion order + + IdBindingInfo(LinkedHashMap indexByVar, Map positionsMaskByVar) { + this(indexByVar, positionsMaskByVar, null); + } + + IdBindingInfo(LinkedHashMap indexByVar, Map positionsMaskByVar, + QueryEvaluationContext ctx) { + this.indexByVar = Collections.unmodifiableMap(new LinkedHashMap<>(indexByVar)); + this.positionsMaskByVar = Collections.unmodifiableMap(positionsMaskByVar); + if (ctx != null) { + this.prepared = buildPrepared(ctx); + } else { + this.prepared = null; + } + } + + private Entry[] buildPrepared(QueryEvaluationContext ctx) { + Entry[] arr = new Entry[indexByVar.size()]; + int i = 0; + for (Map.Entry e : indexByVar.entrySet()) { + String name = e.getKey(); + int idx = e.getValue(); + int mask = positionsMaskByVar.getOrDefault(name, 0); + Function getter = ctx.getValue(name); + BiConsumer setter = ctx.setBinding(name); + arr[i++] = new Entry(name, idx, mask, getter, setter); + } + return arr; + } + + @Override + public Set getVariableNames() { + return indexByVar.keySet(); + } + + public int size() { + return indexByVar.size(); + } + + public int getIndex(String name) { + Integer idx = indexByVar.get(name); + return idx == null ? -1 : idx; + } + + public int getPositionsMask(String name) { + Integer m = positionsMaskByVar.get(name); + return m == null ? 0 : m; + } + + @Override + public int getRecordIndex(String varName) { + return getIndex(varName); + } + + @Override + public long getId(long[] record, String varName) { + int i = getIndex(varName); + if (i < 0 || i >= record.length) { + return LmdbValue.UNKNOWN_ID; + } + return record[i]; + } + + public long[] createInitialBinding(BindingSet bindings, ValueStore valueStore) throws QueryEvaluationException { + long[] binding = new long[size()]; + Arrays.fill(binding, LmdbValue.UNKNOWN_ID); + if (bindings == null || bindings.isEmpty()) { + return binding; + } + + if (prepared != null) { + for (Entry entry : prepared) { + Value value = entry.getter.apply(bindings); + if (value == null) { + continue; + } + long id = resolveId(valueStore, value); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + binding[entry.recordIndex] = id; + } + return binding; + } + + if (bindings instanceof ArrayBindingSet) { + ArrayBindingSet abs = (ArrayBindingSet) bindings; + for (Map.Entry entry : indexByVar.entrySet()) { + Function getter = abs.getDirectGetValue(entry.getKey()); + if (getter == null) { + continue; + } + Value value = getter.apply(abs); + if (value == null) { + continue; + } + long id = resolveId(valueStore, value); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + binding[entry.getValue()] = id; + } + return binding; + } + + for (Map.Entry entry : indexByVar.entrySet()) { + Value value = bindings.getValue(entry.getKey()); + if (value == null) { + continue; + } + long id = resolveId(valueStore, value); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + binding[entry.getValue()] = id; + } + return binding; + } + + private static long resolveId(ValueStore valueStore, Value value) throws QueryEvaluationException { + if (value == null) { + return LmdbValue.UNKNOWN_ID; + } + if (value instanceof LmdbValue) { + LmdbValue lmdbValue = (LmdbValue) value; + if (lmdbValue.getValueStoreRevision().getValueStore() == valueStore) { + long id = lmdbValue.getInternalID(); + if (id != LmdbValue.UNKNOWN_ID) { + return id; + } + } + } + try { + return valueStore.getId(value); + } catch (IOException e) { + throw new QueryEvaluationException(e); + } + } + + @Override + public boolean applyRecord(long[] record, MutableBindingSet target, ValueStore valueStore) + throws QueryEvaluationException { + // Fast path: pre-resolved getters/setters from the evaluation context + if (prepared != null) { + for (Entry entry : prepared) { + long id = record[entry.recordIndex]; + if (id == LmdbValue.UNKNOWN_ID) { + continue; + } + if (((entry.positionsMask >> TripleStore.CONTEXT_IDX) & 1) == 1 && id == 0L) { + continue; // default graph treated as null (unbound) + } + + Value existing = entry.getter.apply(target); + if (existing != null) { + if (existing instanceof LmdbValue + && ((LmdbValue) existing).getValueStoreRevision().getValueStore() == valueStore) { + long existingId = ((LmdbValue) existing).getInternalID(); + if (existingId != LmdbValue.UNKNOWN_ID && existingId != id) { + return false; + } + continue; + } + // Fallback: resolve and compare + Value candidate; + try { + candidate = LmdbIdJoinSettings.resolveValue(valueStore, id); + } catch (IOException ex) { + throw new QueryEvaluationException(ex); + } + if (!existing.equals(candidate)) { + return false; + } + } else { + Value candidate; + try { + candidate = LmdbIdJoinSettings.resolveValue(valueStore, id); + } catch (IOException ex) { + throw new QueryEvaluationException(ex); + } + entry.setter.accept(candidate, target); + } + } + return true; + } + if (target instanceof ArrayBindingSet) { + ArrayBindingSet abs = (ArrayBindingSet) target; + for (Map.Entry e : indexByVar.entrySet()) { + String name = e.getKey(); + int idx = e.getValue(); + long id = record[idx]; + if (id == LmdbValue.UNKNOWN_ID) { + continue; + } + int mask = getPositionsMask(name); + if (((mask >> TripleStore.CONTEXT_IDX) & 1) == 1 && id == 0L) { + continue; // default graph treated as null (unbound) + } + + // Fast existing lookup without map churn + Value existing = abs.getDirectGetValue(name).apply(abs); + if (existing != null) { + // If existing is an LmdbValue from same store, compare by ID first + if (existing instanceof LmdbValue + && ((LmdbValue) existing).getValueStoreRevision().getValueStore() == valueStore) { + long existingId = ((LmdbValue) existing).getInternalID(); + if (existingId != LmdbValue.UNKNOWN_ID && existingId != id) { + return false; + } + continue; + } + // Fallback: resolve candidate and compare + Value candidate; + try { + candidate = LmdbIdJoinSettings.resolveValue(valueStore, id); + } catch (IOException ex) { + throw new QueryEvaluationException(ex); + } + if (!existing.equals(candidate)) { + return false; + } + } else { + // No existing value: resolve once and set via direct setter + Value candidate; + try { + candidate = LmdbIdJoinSettings.resolveValue(valueStore, id); + } catch (IOException ex) { + throw new QueryEvaluationException(ex); + } + abs.getDirectSetBinding(name).accept(candidate, abs); + } + } + return true; + } + + // Generic path + for (Map.Entry e : indexByVar.entrySet()) { + String name = e.getKey(); + int idx = e.getValue(); + long id = record[idx]; + if (id == LmdbValue.UNKNOWN_ID) { + continue; + } + int mask = getPositionsMask(name); + if (((mask >> TripleStore.CONTEXT_IDX) & 1) == 1 && id == 0L) { + continue; // default graph treated as null (unbound) + } + Value existing = target.getValue(name); + if (existing != null) { + // If existing is an LmdbValue from same store, compare by ID first + if (existing instanceof LmdbValue + && ((LmdbValue) existing).getValueStoreRevision().getValueStore() == valueStore) { + long existingId = ((LmdbValue) existing).getInternalID(); + if (existingId != LmdbValue.UNKNOWN_ID && existingId != id) { + return false; + } + continue; + } + } + Value candidate; + try { + candidate = LmdbIdJoinSettings.resolveValue(valueStore, id); + } catch (IOException ex) { + throw new QueryEvaluationException(ex); + } + if (existing != null && !existing.equals(candidate)) { + return false; + } + if (existing == null) { + target.setBinding(name, candidate); + } + } + return true; + } + + public static IdBindingInfo combine(IdBindingInfo left, LmdbIdJoinIterator.PatternInfo right) { + LinkedHashMap map = new LinkedHashMap<>(); + Map masks = new java.util.HashMap<>(); + // left variables first + if (left != null) { + for (String v : left.getVariableNames()) { + map.put(v, map.size()); + masks.put(v, left.getPositionsMask(v)); + } + } + // add right-only variables and merge masks for shared + for (String v : right.getVariableNames()) { + Integer mask = right.getPositionsMask(v); + if (!map.containsKey(v)) { + map.put(v, map.size()); + masks.put(v, mask); + } else { + masks.put(v, masks.getOrDefault(v, 0) | mask); + } + } + return new IdBindingInfo(map, masks); + } + + public static IdBindingInfo fromFirstPattern(LmdbIdJoinIterator.PatternInfo first) { + LinkedHashMap map = new LinkedHashMap<>(); + Map masks = new java.util.HashMap<>(); + // Use SPOC order for stability + String s = firstVarName(first, TripleStore.SUBJ_IDX); + if (s != null) { + map.put(s, map.size()); + masks.put(s, first.getPositionsMask(s)); + } + String p = firstVarName(first, TripleStore.PRED_IDX); + if (p != null && !map.containsKey(p)) { + map.put(p, map.size()); + masks.put(p, first.getPositionsMask(p)); + } + String o = firstVarName(first, TripleStore.OBJ_IDX); + if (o != null && !map.containsKey(o)) { + map.put(o, map.size()); + masks.put(o, first.getPositionsMask(o)); + } + String c = firstVarName(first, TripleStore.CONTEXT_IDX); + if (c != null && !map.containsKey(c)) { + map.put(c, map.size()); + masks.put(c, first.getPositionsMask(c)); + } + // include any remaining (e.g., repeated vars not in SPOC scan) + for (String v : first.getVariableNames()) { + if (!map.containsKey(v)) { + map.put(v, map.size()); + masks.put(v, first.getPositionsMask(v)); + } + } + return new IdBindingInfo(map, masks); + } + + public static IdBindingInfo combine(IdBindingInfo left, LmdbIdJoinIterator.PatternInfo right, + QueryEvaluationContext ctx) { + LinkedHashMap map = new LinkedHashMap<>(); + Map masks = new java.util.HashMap<>(); + if (left != null) { + for (String v : left.getVariableNames()) { + map.put(v, map.size()); + masks.put(v, left.getPositionsMask(v)); + } + } + for (String v : right.getVariableNames()) { + Integer mask = right.getPositionsMask(v); + if (!map.containsKey(v)) { + map.put(v, map.size()); + masks.put(v, mask); + } else { + masks.put(v, masks.getOrDefault(v, 0) | mask); + } + } + return new IdBindingInfo(map, masks, ctx); + } + + public static IdBindingInfo fromFirstPattern(LmdbIdJoinIterator.PatternInfo first, QueryEvaluationContext ctx) { + LinkedHashMap map = new LinkedHashMap<>(); + Map masks = new java.util.HashMap<>(); + String s = firstVarName(first, TripleStore.SUBJ_IDX); + if (s != null) { + map.put(s, map.size()); + masks.put(s, first.getPositionsMask(s)); + } + String p = firstVarName(first, TripleStore.PRED_IDX); + if (p != null && !map.containsKey(p)) { + map.put(p, map.size()); + masks.put(p, first.getPositionsMask(p)); + } + String o = firstVarName(first, TripleStore.OBJ_IDX); + if (o != null && !map.containsKey(o)) { + map.put(o, map.size()); + masks.put(o, first.getPositionsMask(o)); + } + String c = firstVarName(first, TripleStore.CONTEXT_IDX); + if (c != null && !map.containsKey(c)) { + map.put(c, map.size()); + masks.put(c, first.getPositionsMask(c)); + } + for (String v : first.getVariableNames()) { + if (!map.containsKey(v)) { + map.put(v, map.size()); + masks.put(v, first.getPositionsMask(v)); + } + } + return new IdBindingInfo(map, masks, ctx); + } + + private static final class Entry { + final String name; + final int recordIndex; + final int positionsMask; + final Function getter; + final BiConsumer setter; + + Entry(String name, int recordIndex, int positionsMask, Function getter, + BiConsumer setter) { + this.name = name; + this.recordIndex = recordIndex; + this.positionsMask = positionsMask; + this.getter = getter; + this.setter = setter; + } + } + + private static String firstVarName(LmdbIdJoinIterator.PatternInfo info, int position) { + for (String v : info.getVariableNames()) { + int mask = info.getPositionsMask(v); + if (((mask >> position) & 1) == 1) { + return v; + } + } + return null; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDatasetContext.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDatasetContext.java new file mode 100644 index 00000000000..345029cb37c --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDatasetContext.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Optional; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; + +/** + * Exposes LMDB-specific resources from a + * {@link org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext}. + */ +@InternalUseOnly +public interface LmdbDatasetContext { + + Optional getLmdbDataset(); + + Optional getValueStore(); +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDelegatingQueryEvaluationContext.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDelegatingQueryEvaluationContext.java new file mode 100644 index 00000000000..51f744a4052 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDelegatingQueryEvaluationContext.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Comparator; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.Binding; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.Dataset; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; + +class LmdbDelegatingQueryEvaluationContext implements QueryEvaluationContext, LmdbDatasetContext { + + private final QueryEvaluationContext delegate; + private final LmdbEvaluationDataset dataset; + private final ValueStore valueStore; + + LmdbDelegatingQueryEvaluationContext(QueryEvaluationContext delegate, LmdbEvaluationDataset dataset, + ValueStore valueStore) { + this.delegate = delegate; + this.dataset = dataset; + this.valueStore = valueStore; + } + + @Override + public Comparator getComparator() { + return delegate.getComparator(); + } + + @Override + public org.eclipse.rdf4j.model.Literal getNow() { + return delegate.getNow(); + } + + @Override + public Dataset getDataset() { + return delegate.getDataset(); + } + + @Override + public MutableBindingSet createBindingSet() { + return delegate.createBindingSet(); + } + + @Override + public Predicate hasBinding(String variableName) { + return delegate.hasBinding(variableName); + } + + @Override + public Function getBinding(String variableName) { + return delegate.getBinding(variableName); + } + + @Override + public Function getValue(String variableName) { + return delegate.getValue(variableName); + } + + @Override + public BiConsumer setBinding(String variableName) { + return delegate.setBinding(variableName); + } + + @Override + public BiConsumer addBinding(String variableName) { + return delegate.addBinding(variableName); + } + + @Override + public MutableBindingSet createBindingSet(BindingSet bindings) { + return delegate.createBindingSet(bindings); + } + + @Override + public Optional getLmdbDataset() { + return Optional.ofNullable(dataset); + } + + @Override + public Optional getValueStore() { + return Optional.ofNullable(valueStore); + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDelegatingSailDataset.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDelegatingSailDataset.java new file mode 100644 index 00000000000..8e3e944faaa --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDelegatingSailDataset.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Comparator; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Namespace; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.base.SailDataset; + +/** + * LMDB-aware delegating dataset that forwards ID-level calls when the delegate supports them, otherwise conservatively + * falls back to value-based conversion using a ValueStore. + */ +final class LmdbDelegatingSailDataset implements SailDataset, LmdbEvaluationDataset { + + private final SailDataset delegate; + private final ValueStore valueStore; + + LmdbDelegatingSailDataset(SailDataset delegate, ValueStore valueStore) { + this.delegate = delegate; + this.valueStore = valueStore; + } + + @Override + public void close() throws SailException { + delegate.close(); + } + + @Override + public CloseableIteration getNamespaces() throws SailException { + return delegate.getNamespaces(); + } + + @Override + public String getNamespace(String prefix) throws SailException { + return delegate.getNamespace(prefix); + } + + @Override + public CloseableIteration getContextIDs() throws SailException { + return delegate.getContextIDs(); + } + + @Override + public CloseableIteration getStatements(Resource subj, IRI pred, Value obj, + Resource... contexts) throws SailException { + return delegate.getStatements(subj, pred, obj, contexts); + } + + @Override + public CloseableIteration getStatements(StatementOrder statementOrder, Resource subj, IRI pred, + Value obj, Resource... contexts) throws SailException { + return delegate.getStatements(statementOrder, subj, pred, obj, contexts); + } + + @Override + public Set getSupportedOrders(Resource subj, IRI pred, Value obj, Resource... contexts) { + return delegate.getSupportedOrders(subj, pred, obj, contexts); + } + + @Override + public Comparator getComparator() { + return delegate.getComparator(); + } + + @Override + public RecordIterator getRecordIterator(org.eclipse.rdf4j.query.algebra.StatementPattern pattern, + org.eclipse.rdf4j.query.BindingSet bindings) throws QueryEvaluationException { + if (delegate instanceof LmdbEvaluationDataset) { + return ((LmdbEvaluationDataset) delegate).getRecordIterator(pattern, bindings); + } + // Fallback via overlay helper using ValueStore (conservative) + LmdbOverlayEvaluationDataset helper = new LmdbOverlayEvaluationDataset( + new LmdbSailDatasetTripleSource(valueStore, delegate), valueStore); + return helper.getRecordIterator(pattern, bindings); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] reuse) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, reuse, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] reuse, long[] quadReuse) throws QueryEvaluationException { + if (delegate instanceof LmdbEvaluationDataset) { + return ((LmdbEvaluationDataset) delegate).getRecordIterator(binding, subjIndex, predIndex, objIndex, + ctxIndex, patternIds, reuse, quadReuse); + } + // Fallback via TripleSource with Value conversion + return new LmdbSailDatasetTripleSource(valueStore, delegate) + .getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, reuse, + quadReuse, null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, reuse, + null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + if (delegate instanceof LmdbEvaluationDataset) { + return ((LmdbEvaluationDataset) delegate).getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, + ctxIndex, patternIds, order, reuse, quadReuse); + } + return LmdbEvaluationDataset.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, reuse, quadReuse); + } + + @Override + public ValueStore getValueStore() { + return valueStore; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIterator.java new file mode 100644 index 00000000000..dc18b88239e --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIterator.java @@ -0,0 +1,508 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.eclipse.rdf4j.sail.lmdb.LmdbUtil.E; +import static org.lwjgl.util.lmdb.LMDB.MDB_GET_MULTIPLE; +import static org.lwjgl.util.lmdb.LMDB.MDB_NEXT; +import static org.lwjgl.util.lmdb.LMDB.MDB_NEXT_MULTIPLE; +import static org.lwjgl.util.lmdb.LMDB.MDB_NOTFOUND; +import static org.lwjgl.util.lmdb.LMDB.MDB_SET_KEY; +import static org.lwjgl.util.lmdb.LMDB.MDB_SET_RANGE; +import static org.lwjgl.util.lmdb.LMDB.MDB_SUCCESS; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_close; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_get; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_open; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_renew; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.eclipse.rdf4j.common.concurrent.locks.StampedLongAdderLockManager; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.lmdb.TripleStore.DupIndex; +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.lmdb.MDBVal; + +/** + * A dupsort/dupfixed-optimized record iterator using MDB_GET_MULTIPLE/NEXT_MULTIPLE to reduce JNI calls. + */ +class LmdbDupRecordIterator implements RecordIterator { + + private boolean explicit; + private boolean exhausted; + private boolean wasEmpty; + + @FunctionalInterface + interface FallbackSupplier { + RecordIterator get(long[] quadReuse, ByteBuffer minKeyBuf, ByteBuffer maxKeyBuf, + LmdbRecordIterator iteratorReuse) + throws IOException; + } + + /** Toggle copying of duplicate blocks for extra safety (defaults to copying). */ + private static final boolean COPY_DUP_BLOCKS = Boolean.parseBoolean( + System.getProperty("rdf4j.lmdb.copyDupBlocks", "true")); + + /** Size in bytes of one (v3,v4) tuple. */ + private static final int DUP_PAIR_BYTES = Long.BYTES * 2; + + private final Pool pool = Pool.get(); + private DupIndex index; + private int dupDbi; + private long cursor; + + private Txn txnRef; + private long txn; // refreshed on txn version changes + private long txnRefVersion; + private StampedLongAdderLockManager txnLockManager; + private final Thread ownerThread = Thread.currentThread(); + + private MDBVal keyData; + private MDBVal valueData; + + /** Reused output buffer required by the RecordIterator API. */ + private long[] quad; + + /** Scalars defining the prefix to scan (subject, predicate). */ + private long prefixSubj; + private long prefixPred; + + private ByteBuffer prefixKeyBuf; + + /** Current duplicate block view and read indices. */ + private ByteBuffer dupBuf; + private int dupPos; + private int dupLimit; + + private int lastResult; + private boolean closed = true; + + LmdbDupRecordIterator(DupIndex index, long subj, long pred, + boolean explicit, Txn txnRef) throws IOException { + initialize(index, subj, pred, explicit, txnRef, null); + } + + LmdbDupRecordIterator(DupIndex index, long subj, long pred, + boolean explicit, Txn txnRef, long[] quadReuse) throws IOException { + initialize(index, subj, pred, explicit, txnRef, quadReuse); + } + + void initialize(DupIndex index, long subj, long pred, boolean explicit, Txn txnRef, long[] quadReuse) + throws IOException { + + this.exhausted = false; + + try { + + // TODO: Find out why this is slower than if we called close() +// close(); + if (closed) { + this.index = index; + this.explicit = explicit; + this.dupDbi = index.getDupDB(explicit); + this.txnRef = txnRef; + assert this.txnRef != null; + this.txnLockManager = txnRef.lockManager(); + this.prefixSubj = subj; + this.prefixPred = pred; + assert this.keyData == null; + assert this.valueData == null; + assert this.prefixKeyBuf == null; + this.keyData = pool.getVal(); + this.valueData = pool.getVal(); + this.prefixKeyBuf = pool.getKeyBuffer(); + + prefixKeyBuf.clear(); + Varint.writeUnsigned(prefixKeyBuf, prefixSubj); + Varint.writeUnsigned(prefixKeyBuf, prefixPred); + prefixKeyBuf.flip(); + + if (quadReuse != null && quadReuse.length >= 4) { + this.quad = quadReuse; + } else if (this.quad == null || this.quad.length < 4) { + this.quad = new long[4]; + } + + this.quad[0] = subj; + this.quad[1] = pred; + this.quad[2] = -1L; + this.quad[3] = -1L; + + } else { + // TODO + assert this.index == index; + assert this.explicit == explicit; + assert this.txnRef == txnRef; + assert this.txnRef != null; + assert this.txnLockManager != null; + assert this.keyData != null; + assert this.valueData != null; + assert this.prefixKeyBuf != null; + + if (this.prefixSubj == subj && this.prefixPred == pred) { + assert this.quad[0] == this.prefixSubj; + assert this.quad[1] == this.prefixPred; + + if (wasEmpty) { + exhausted = true; + return; + } + + // We can do a lot more reuse here! + this.quad[2] = -1L; + this.quad[3] = -1L; + } else { + this.prefixSubj = subj; + this.prefixPred = pred; + this.quad[0] = subj; + this.quad[1] = pred; + this.quad[2] = -1L; + this.quad[3] = -1L; + + prefixKeyBuf.clear(); + Varint.writeUnsigned(prefixKeyBuf, prefixSubj); + Varint.writeUnsigned(prefixKeyBuf, prefixPred); + prefixKeyBuf.flip(); + } + + } + + this.dupBuf = null; + this.dupPos = 0; + this.dupLimit = 0; + this.lastResult = MDB_SUCCESS; + this.wasEmpty = false; + + if (closed) { + long readStamp; + try { + readStamp = txnLockManager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + this.txnRefVersion = txnRef.version(); + this.txn = txnRef.get(); + + cursor = openCursor(txn, dupDbi, txnRef.isReadOnly()); + + boolean positioned = positionOnPrefix(); + if (positioned) { + positioned = primeDuplicateBlock(); + } + if (!positioned) { + this.exhausted = true; + this.wasEmpty = true; + // closeInternal(false); + // TODO: We must be empty???? + } + } finally { + txnLockManager.unlockRead(readStamp); + } + } else { + long readStamp; + try { + readStamp = txnLockManager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + + if (txnRefVersion != txnRef.version()) { + this.txn = txnRef.get(); + E(mdb_cursor_renew(txn, cursor)); + txnRefVersion = txnRef.version(); + } else { + E(mdb_cursor_renew(txn, cursor)); + } + + boolean positioned = positionOnPrefix(); + if (positioned) { + positioned = primeDuplicateBlock(); + } + if (!positioned) { + this.exhausted = true; + this.wasEmpty = true; + // closeInternal(false); + // TODO: We must be empty???? + } + } finally { + txnLockManager.unlockRead(readStamp); + } + } + + } finally { + assert this.txnRef != null; + this.closed = false; + + } + + } + + @Override + public long[] next() { + if (exhausted) + return null; + long readStamp; + try { + readStamp = txnLockManager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + if (closed) { + return null; + } + + // Txn renewal if the TxnManager rotated its underlying LMDB txn + if (txnRefVersion != txnRef.version()) { + this.txn = txnRef.get(); + E(mdb_cursor_renew(txn, cursor)); + txnRefVersion = txnRef.version(); + if (!positionOnPrefix() || !primeDuplicateBlock()) { +// closeInternal(false); + exhausted = true; + return null; + } + } + + while (true) { + // Fast-path: emit from current duplicate block if at least one pair remains + if (dupBuf != null && dupLimit - dupPos >= DUP_PAIR_BYTES) { + long v3 = dupBuf.getLong(dupPos); + long v4 = dupBuf.getLong(dupPos + Long.BYTES); + dupPos += DUP_PAIR_BYTES; + + // s,p are constant; update only the tail + quad[2] = v3; + quad[3] = v4; + return quad; + } + + // Ask LMDB for the next duplicate block under the same key + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT_MULTIPLE); + if (lastResult == MDB_SUCCESS) { + resetDuplicateBuffer(valueData.mv_data()); + continue; + } + if (lastResult != MDB_NOTFOUND) { + E(lastResult); + } + + // No more duplicate blocks for this key; advance to next key in range + do { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + if (lastResult != MDB_SUCCESS) { +// closeInternal(false); + exhausted = true; + return null; + } + // Ensure we're still within the requested (subj,pred) prefix + if (!currentKeyHasPrefix() && !adjustCursorToPrefix()) { +// closeInternal(false); + exhausted = true; + return null; + } + } while (!primeDuplicateBlock()); // skip any keys without a duplicate block (defensive) + } + } catch (IOException e) { + throw new SailException(e); + } finally { + txnLockManager.unlockRead(readStamp); + } + } + + /* ---------- Positioning & Prefix handling ---------- */ + + private boolean positionOnPrefix() throws IOException { + keyData.mv_data(prefixKeyBuf); + + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_KEY); + if (lastResult == MDB_NOTFOUND) { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + } + if (lastResult != MDB_SUCCESS) { + return false; + } + if (currentKeyHasPrefix()) { + return true; + } + return adjustCursorToPrefix(); + } + + private boolean adjustCursorToPrefix() throws IOException { + int cmp = comparePrefix(); + while (cmp < 0) { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + if (lastResult != MDB_SUCCESS) { + return false; + } + cmp = comparePrefix(); + } + return cmp == 0; + } + + /** + * Compare current cursor key with (prefixSubj, prefixPred) without allocating a duplicate buffer. + */ + private int comparePrefix() { + ByteBuffer key = keyData.mv_data(); + final int pos = key.position(); + try { + long a0 = Varint.readUnsigned(key); + int c0 = Long.compare(a0, prefixSubj); + if (c0 != 0) { + return c0; + } + long a1 = Varint.readUnsigned(key); + return Long.compare(a1, prefixPred); + } finally { + key.position(pos); // restore buffer position + } + } + + private boolean currentKeyHasPrefix() { + return comparePrefix() == 0; + } + + /* ---------- Duplicate block priming ---------- */ + + /** + * Prime the duplicate buffer for the current key using MDB_GET_MULTIPLE. + */ + private boolean primeDuplicateBlock() throws IOException { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_GET_MULTIPLE); + if (lastResult == MDB_SUCCESS) { + resetDuplicateBuffer(valueData.mv_data()); + return dupBuf != null && dupLimit - dupPos >= DUP_PAIR_BYTES; + } + if (lastResult == MDB_NOTFOUND) { + resetDuplicateBuffer(null); + return false; + } + E(lastResult); + return false; // unreachable + } + + /** + * Prepare a readable view over the LMDB-provided value buffer. Default: zero-copy {@code slice()}. If + * COPY_DUP_BLOCKS is true, heap-copy for extra safety. + */ + private void resetDuplicateBuffer(ByteBuffer buffer) { + if (buffer == null) { + dupBuf = null; + dupPos = dupLimit = 0; + return; + } + + if (!COPY_DUP_BLOCKS) { + // Zero-copy: view over [position, limit) of the native buffer + ByteBuffer view = buffer.slice(); + view.order(ByteOrder.BIG_ENDIAN); + dupBuf = view; + dupPos = view.position(); // 0 + dupLimit = view.limit(); + } else { + // Conservative path: copy to Java heap to decouple lifetime from cursor operations + ByteBuffer src = buffer.duplicate(); + src.position(buffer.position()); + src.limit(buffer.limit()); + ByteBuffer copy = ByteBuffer.allocate(src.remaining()); + copy.put(src); + copy.flip(); + copy.order(ByteOrder.BIG_ENDIAN); + dupBuf = copy; + dupPos = dupBuf.position(); + dupLimit = dupBuf.limit(); + } + } + + /* ---------- Lifecycle ---------- */ + + private void closeInternal(boolean maybeCalledAsync) { + if (!closed) { + long writeStamp = 0L; + boolean writeLocked = false; + if (maybeCalledAsync && ownerThread != Thread.currentThread()) { + try { + writeStamp = txnLockManager.writeLock(); + writeLocked = true; + } catch (InterruptedException e) { + throw new SailException(e); + } + } + try { + if (!closed) { + if (cursor != 0L && txnRef != null) { + if (txnRef.isReadOnly()) { + pool.freeCursor(dupDbi, index, cursor); + } else { + mdb_cursor_close(cursor); + } + } + cursor = 0L; + if (keyData != null) { + pool.free(keyData); + keyData = null; + } + if (valueData != null) { + pool.free(valueData); + valueData = null; + } + if (prefixKeyBuf != null) { + pool.free(prefixKeyBuf); + prefixKeyBuf = null; + } + } + } finally { + closed = true; + if (writeLocked) { + txnLockManager.unlockWrite(writeStamp); + } + txnRef = null; + dupBuf = null; + dupPos = dupLimit = 0; + } + } + } + + @Override + public void close() { + closeInternal(true); + txnLockManager = null; + txnRef = null; + } + + private long openCursor(long txn, int dbi, boolean tryReuse) throws IOException { + if (tryReuse) { + long pooled = pool.getCursor(dbi, index); + if (pooled != 0L) { + try { + E(mdb_cursor_renew(txn, pooled)); + return pooled; + } catch (IOException renewEx) { + mdb_cursor_close(pooled); + } + } + } + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + return pp.get(0); + } + } + +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationDataset.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationDataset.java new file mode 100644 index 00000000000..bba475824d6 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationDataset.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.nio.ByteBuffer; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.common.transaction.IsolationLevel; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.StatementPattern; + +/** + * Provides LMDB-specific access needed during query evaluation. + */ +public interface LmdbEvaluationDataset { + + @InternalUseOnly + final class KeyRangeBuffers { + private final ByteBuffer minKey; + private final ByteBuffer maxKey; + + public KeyRangeBuffers(ByteBuffer minKey, ByteBuffer maxKey) { + this.minKey = minKey; + this.maxKey = maxKey; + } + + public static KeyRangeBuffers acquire() { + Pool pool = Pool.get(); + return new KeyRangeBuffers(pool.getKeyBuffer(), pool.getKeyBuffer()); + } + + public ByteBuffer minKey() { + return minKey; + } + + public ByteBuffer maxKey() { + return maxKey; + } + } + + /** + * Create a {@link RecordIterator} for the supplied {@link StatementPattern}, taking into account any existing + * bindings. + * + * @param pattern the statement pattern to evaluate + * @param bindings the bindings that should be respected when creating the iterator + * @return a {@link RecordIterator} that yields matching statement records + * @throws QueryEvaluationException if the iterator could not be created + */ + RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings) throws QueryEvaluationException; + + @InternalUseOnly + default RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, KeyRangeBuffers keyBuffers) + throws QueryEvaluationException { + return getRecordIterator(pattern, bindings); + } + + @InternalUseOnly + default RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, KeyRangeBuffers keyBuffers, + RecordIterator iteratorReuse) throws QueryEvaluationException { + return getRecordIterator(pattern, bindings, keyBuffers); + } + + /** + * Create a {@link RecordIterator} for the supplied pattern, expressed as internal IDs, using the current binding + * snapshot. + * + *

+ * The {@code binding} array represents the accumulated variable IDs for the join so far and must be treated as + * read-only. The {@code patternIds} array contains four entries (subject, predicate, object, context) where a value + * of {@link org.eclipse.rdf4j.sail.lmdb.model.LmdbValue#UNKNOWN_ID} indicates that the corresponding position is + * unbound in the pattern. The index arguments point to the slots in {@code binding} where the resolved IDs should + * be written (or {@code -1} if that pattern position does not correspond to a variable). Implementations may reuse + * internal buffers by copying {@code binding} into a scratch array before mutating; callers can supply such an + * array via {@link #getRecordIterator(long[], int, int, int, int, long[], long[])} to avoid per-iterator + * allocation. + *

+ * + * @param binding the current binding snapshot; implementations must copy before mutating + * @param subjIndex index in {@code binding} for the subject variable, or {@code -1} if none + * @param predIndex index in {@code binding} for the predicate variable, or {@code -1} if none + * @param objIndex index in {@code binding} for the object variable, or {@code -1} if none + * @param ctxIndex index in {@code binding} for the context variable, or {@code -1} if none + * @param patternIds pattern constants for subject/predicate/object/context + * @return a {@link RecordIterator} that yields binding snapshots with the pattern applied + * @throws QueryEvaluationException if the iterator could not be created + */ + @InternalUseOnly + RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds) throws QueryEvaluationException; + + @InternalUseOnly + default RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + assert keyBuffers == null : "We are not passing keyBuffers into the next method"; + + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds); + } + + /** + * Variant of {@link #getRecordIterator(long[], int, int, int, int, long[])} that allows callers to supply reusable + * scratch buffers. Implementations should treat {@code binding} as read-only and (when {@code bindingReuse} is + * non-null and large enough) seed the scratch buffer with the binding state before producing rows from this + * iterator. The {@code quadReuse} buffer (length 4) can be forwarded to iterators that materialize raw quadruples + * from the underlying store. + * + * @param bindingReuse optional binding scratch buffer returned from {@link RecordIterator#next()} + * @param quadReuse optional quad scratch buffer (length 4) + */ + @InternalUseOnly + default RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] bindingReuse) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, bindingReuse, + null); + } + + @InternalUseOnly + default RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] bindingReuse, long[] quadReuse) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, bindingReuse, + quadReuse); + } + + @InternalUseOnly + default RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers, long[] bindingReuse, long[] quadReuse) + throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds); + } + + @InternalUseOnly + default RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers, long[] bindingReuse, long[] quadReuse, + RecordIterator iteratorReuse) + throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + bindingReuse, quadReuse); + } + + /** + * Create an ordered {@link RecordIterator} for the supplied pattern expressed via internal IDs and binding indexes. + * Implementations may fall back to the unordered iterator when the requested order is unsupported. + */ + @InternalUseOnly + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + null, null); + } + + /** + * Variant of {@link #getOrderedRecordIterator(long[], int, int, int, int, long[], StatementOrder)} that accepts a + * reusable scratch buffer. + */ + @InternalUseOnly + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + reuse, null); + } + + @InternalUseOnly + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] bindingReuse, long[] quadReuse) + throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + bindingReuse, quadReuse); + } + + @InternalUseOnly + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, KeyRangeBuffers keyBuffers, long[] bindingReuse, + long[] quadReuse) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + bindingReuse, quadReuse); + } + return null; + } + + @InternalUseOnly + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, KeyRangeBuffers keyBuffers, long[] bindingReuse, + long[] quadReuse, RecordIterator iteratorReuse) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, + keyBuffers, + bindingReuse, quadReuse); + } + + /** + * Create an ordered {@link RecordIterator} for the supplied pattern. Implementations may fall back to the unordered + * iterator when the requested order is unsupported. + */ + default RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, StatementOrder order) + throws QueryEvaluationException { + return getOrderedRecordIterator(pattern, bindings, order, null); + } + + default RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, StatementOrder order, + KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(pattern, bindings, keyBuffers); + } + return null; + } + + default boolean supportsOrder(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, StatementOrder order) { + return order == null; + } + + /** + * Determine the most suitable LMDB index for the supplied binding pattern. + * + * @return the field sequence of the selected index (e.g. {@code \"spoc\"}), or {@code null} when no advice is + * available + */ + default String selectBestIndex(long subj, long pred, long obj, long context) { + return null; + } + + /** + * @return the {@link ValueStore} backing this dataset. + */ + ValueStore getValueStore(); + + /** + * @return the isolation level associated with this dataset. + */ + default IsolationLevel getIsolationLevel() { + return null; + } + + /** + * Refresh the underlying snapshot, if applicable, to ensure subsequent reads observe the latest committed data. + * Implementations that do not maintain a snapshot may ignore this call. + */ + default void refreshSnapshot() throws QueryEvaluationException { + // no-op by default + } + + /** + * Indicates whether the current evaluation should consider the active transaction as containing uncommitted changes + * that require reading through an overlay rather than directly from the LMDB indexes. + * + *

+ * Implementations that expose a transaction overlay should override this to return {@code true}. The default is + * {@code false} for plain snapshot datasets. + *

+ * + * @return {@code true} if the evaluation is layered over uncommitted transaction changes; {@code false} otherwise. + */ + default boolean hasTransactionChanges() { + return false; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategy.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategy.java new file mode 100644 index 00000000000..ac22a1a3cb5 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategy.java @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; +import org.eclipse.rdf4j.common.transaction.IsolationLevel; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.common.transaction.QueryEvaluationMode; +import org.eclipse.rdf4j.query.Dataset; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.QueryRoot; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.ArrayBindingBasedQueryEvaluationContext; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.StrictEvaluationStrategy; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdBGPQueryEvaluationStep; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinQueryEvaluationStep; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdMergeJoinQueryEvaluationStep; + +/** + * Evaluation strategy that can use LMDB-specific join iterators. + */ +@InternalUseOnly +public class LmdbEvaluationStrategy extends StrictEvaluationStrategy { + + private static final ThreadLocal CURRENT_DATASET = new ThreadLocal<>(); + private static final ThreadLocal CONNECTION_CHANGES = new ThreadLocal<>(); + + LmdbEvaluationStrategy(TripleSource tripleSource, Dataset dataset, FederatedServiceResolver serviceResolver, + long iterationCacheSyncThreshold, EvaluationStatistics evaluationStatistics, boolean trackResultSize) { + super(tripleSource, dataset, serviceResolver, iterationCacheSyncThreshold, evaluationStatistics, + trackResultSize); + setQueryEvaluationMode(QueryEvaluationMode.STRICT); + } + + @Override + public QueryEvaluationStep precompile(TupleExpr expr) { + LmdbEvaluationDataset datasetRef = CURRENT_DATASET.get(); + ValueStore valueStore = datasetRef != null ? datasetRef.getValueStore() : null; + LmdbEvaluationDataset effectiveDataset = datasetRef; + if (connectionHasChanges() && valueStore != null) { + TripleSource idCapableTs = (tripleSource instanceof LmdbIdTripleSource) + ? tripleSource + : new LmdbIdTripleSourceAdapter(tripleSource, valueStore); + effectiveDataset = new LmdbOverlayEvaluationDataset(idCapableTs, valueStore); + } + LmdbQueryEvaluationContext baseContext = new LmdbQueryEvaluationContext(dataset, tripleSource.getValueFactory(), + tripleSource.getComparator(), effectiveDataset, valueStore); + QueryEvaluationContext context = baseContext; + if (expr instanceof QueryRoot) { + String[] allVariables = ArrayBindingBasedQueryEvaluationContext + .findAllVariablesUsedInQuery((QueryRoot) expr); + QueryEvaluationContext arrayContext = new ArrayBindingBasedQueryEvaluationContext(baseContext, allVariables, + tripleSource.getComparator()); + context = new LmdbDelegatingQueryEvaluationContext(arrayContext, effectiveDataset, valueStore); + } + return precompile(expr, context); + } + + @Override + protected QueryEvaluationStep prepare(Join node, QueryEvaluationContext context) { + QueryEvaluationStep defaultStep = super.prepare(node, context); + if (Boolean.getBoolean("rdf4j.lmdb.disableIdJoins")) { + return defaultStep; + } + if (context instanceof LmdbDatasetContext && ((LmdbDatasetContext) context).getLmdbDataset().isPresent()) { + LmdbEvaluationDataset ds = ((LmdbDatasetContext) context).getLmdbDataset().get(); + Optional valueStoreOpt = ((LmdbDatasetContext) context).getValueStore(); + // If the active transaction has uncommitted changes, avoid ID-only join shortcuts. + if (ds.hasTransactionChanges() || connectionHasChanges()) { + return defaultStep; + } + + if (node.isMergeJoin() && node.getOrder() != null && node.getLeftArg() instanceof StatementPattern + && node.getRightArg() instanceof StatementPattern) { + return new LmdbIdMergeJoinQueryEvaluationStep(node, context, defaultStep); + } + // Try to flatten a full BGP of statement patterns + List patterns = new ArrayList<>(); + if (LmdbIdBGPQueryEvaluationStep.flattenBGP(node, patterns) + && !patterns.isEmpty()) { + LmdbIdBGPQueryEvaluationStep step = new LmdbIdBGPQueryEvaluationStep(node, patterns, context, + defaultStep); + if (step.shouldUseFallbackImmediately()) { + if (valueStoreOpt.isPresent()) { + TripleSource idCapableTs = (tripleSource instanceof LmdbIdTripleSource) + ? tripleSource + : new LmdbIdTripleSourceAdapter(tripleSource, valueStoreOpt.get()); + LmdbOverlayEvaluationDataset overlay = new LmdbOverlayEvaluationDataset(idCapableTs, + valueStoreOpt.get()); + QueryEvaluationContext overlayContext = new LmdbDelegatingQueryEvaluationContext(context, + overlay, + valueStoreOpt.get()); + return new LmdbIdBGPQueryEvaluationStep(node, patterns, + overlayContext, defaultStep); + } + return defaultStep; + } + boolean hasPreBound = hasPreBoundVariables(patterns); + if ((requiresSnapshotOverlay(ds) || hasPreBound) && valueStoreOpt.isPresent()) { + TripleSource idCapableTs = (tripleSource instanceof LmdbIdTripleSource) + ? tripleSource + : new LmdbIdTripleSourceAdapter(tripleSource, valueStoreOpt.get()); + LmdbOverlayEvaluationDataset overlay = new LmdbOverlayEvaluationDataset(idCapableTs, + valueStoreOpt.get()); + QueryEvaluationContext overlayContext = new LmdbDelegatingQueryEvaluationContext(context, overlay, + valueStoreOpt.get()); + LmdbIdBGPQueryEvaluationStep overlayStep = new LmdbIdBGPQueryEvaluationStep(node, patterns, + overlayContext, defaultStep); + overlayStep.applyAlgorithmTag(); + return overlayStep; + } + step.applyAlgorithmTag(); + return step; + } + // Fallback to two-pattern ID join + if (node.getLeftArg() instanceof StatementPattern && node.getRightArg() instanceof StatementPattern) { + LmdbIdJoinQueryEvaluationStep step = new LmdbIdJoinQueryEvaluationStep(this, node, context, + defaultStep); + if (step.shouldUseFallbackImmediately()) { + if (valueStoreOpt.isPresent()) { + TripleSource idCapableTs = (tripleSource instanceof LmdbIdTripleSource) + ? tripleSource + : new LmdbIdTripleSourceAdapter(tripleSource, valueStoreOpt.get()); + LmdbOverlayEvaluationDataset overlay = new LmdbOverlayEvaluationDataset(idCapableTs, + valueStoreOpt.get()); + QueryEvaluationContext overlayContext = new LmdbDelegatingQueryEvaluationContext(context, + overlay, + valueStoreOpt.get()); + return new LmdbIdJoinQueryEvaluationStep(this, node, + overlayContext, defaultStep); + } + return defaultStep; + } + if (requiresSnapshotOverlay(ds) && valueStoreOpt.isPresent()) { + TripleSource idCapableTs = (tripleSource instanceof LmdbIdTripleSource) + ? tripleSource + : new LmdbIdTripleSourceAdapter(tripleSource, valueStoreOpt.get()); + LmdbOverlayEvaluationDataset overlay = new LmdbOverlayEvaluationDataset(idCapableTs, + valueStoreOpt.get()); + QueryEvaluationContext overlayContext = new LmdbDelegatingQueryEvaluationContext(context, overlay, + valueStoreOpt.get()); + LmdbIdJoinQueryEvaluationStep overlayStep = new LmdbIdJoinQueryEvaluationStep(this, node, + overlayContext, defaultStep); + overlayStep.applyAlgorithmTag(node); + return overlayStep; + } + step.applyAlgorithmTag(node); + return step; + } + } + return defaultStep; + } + + private boolean hasPreBoundVariables(List patterns) { + for (StatementPattern pattern : patterns) { + if (isPreBound(pattern.getSubjectVar()) || isPreBound(pattern.getPredicateVar()) + || isPreBound(pattern.getObjectVar()) || isPreBound(pattern.getContextVar())) { + return true; + } + } + return false; + } + + private boolean isPreBound(org.eclipse.rdf4j.query.algebra.Var var) { + return var != null && var.hasValue() && !var.isConstant(); + } + + private boolean requiresSnapshotOverlay(LmdbEvaluationDataset dataset) { + IsolationLevel isolation = dataset.getIsolationLevel(); + return isolation == IsolationLevels.SNAPSHOT || isolation == IsolationLevels.SERIALIZABLE + || isolation == IsolationLevels.SNAPSHOT_READ; + } + + static void setCurrentDataset(LmdbEvaluationDataset dataset) { + CURRENT_DATASET.set(dataset); + } + + static void clearCurrentDataset() { + CURRENT_DATASET.remove(); + } + + public static Optional getCurrentDataset() { + return Optional.ofNullable(CURRENT_DATASET.get()); + } + + static void pushConnectionChangesFlag(boolean hasUncommittedChanges) { + if (!hasUncommittedChanges && CONNECTION_CHANGES.get() == null) { + return; + } + + ConnectionChangeState state = CONNECTION_CHANGES.get(); + if (state == null) { + state = new ConnectionChangeState(); + CONNECTION_CHANGES.set(state); + } + state.depth++; + state.hasChanges |= hasUncommittedChanges; + } + + static void popConnectionChangesFlag() { + ConnectionChangeState state = CONNECTION_CHANGES.get(); + if (state == null) { + return; + } + state.depth--; + if (state.depth <= 0) { + CONNECTION_CHANGES.remove(); + } + } + + private static boolean connectionHasChanges() { + ConnectionChangeState state = CONNECTION_CHANGES.get(); + return state != null && state.hasChanges; + } + + public static boolean hasActiveConnectionChanges() { + return connectionHasChanges(); + } + + private static final class ConnectionChangeState { + private int depth; + private boolean hasChanges; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategyFactory.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategyFactory.java new file mode 100644 index 00000000000..e589e4d9fd5 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategyFactory.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.function.Supplier; + +import org.eclipse.rdf4j.collection.factory.api.CollectionFactory; +import org.eclipse.rdf4j.collection.factory.impl.DefaultCollectionFactory; +import org.eclipse.rdf4j.query.Dataset; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.StrictEvaluationStrategyFactory; + +/** + * Evaluation strategy factory that installs LMDB-specific join behaviour. + */ +class LmdbEvaluationStrategyFactory extends StrictEvaluationStrategyFactory { + + private FederatedServiceResolver serviceResolver; + private Supplier collectionFactorySupplier = DefaultCollectionFactory::new; + + LmdbEvaluationStrategyFactory(FederatedServiceResolver resolver) { + this.serviceResolver = resolver; + } + + @Override + public void setFederatedServiceResolver(FederatedServiceResolver resolver) { + this.serviceResolver = resolver; + } + + @Override + public FederatedServiceResolver getFederatedServiceResolver() { + return serviceResolver; + } + + @Override + public void setCollectionFactory(Supplier collectionFactory) { + this.collectionFactorySupplier = collectionFactory; + } + + @Override + public EvaluationStrategy createEvaluationStrategy(Dataset dataset, TripleSource tripleSource, + EvaluationStatistics evaluationStatistics) { + LmdbEvaluationStrategy strategy = new LmdbEvaluationStrategy(tripleSource, dataset, serviceResolver, + getQuerySolutionCacheThreshold(), evaluationStatistics, isTrackResultSize()); + getOptimizerPipeline().ifPresent(strategy::setOptimizerPipeline); + strategy.setCollectionFactory(collectionFactorySupplier); + return strategy; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdTripleSource.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdTripleSource.java new file mode 100644 index 00000000000..78662bb317f --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdTripleSource.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.query.QueryEvaluationException; + +/** + * LMDB extension of TripleSource that supports ID-level statement access without materializing RDF4J Value objects. + */ +@InternalUseOnly +public interface LmdbIdTripleSource { + + /** + * Create an iterator over ID-level bindings for the given pattern and binding snapshot. + * + * @param binding current binding snapshot (read-only for implementations) + * @param subjIndex index in binding for subject var, or -1 + * @param predIndex index in binding for predicate var, or -1 + * @param objIndex index in binding for object var, or -1 + * @param ctxIndex index in binding for context var, or -1 + * @param patternIds constants for S/P/O/C positions (UNKNOWN_ID for wildcard) + */ + RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, LmdbEvaluationDataset.KeyRangeBuffers keyBuffers, long[] bindingReuse, long[] quadReuse, + RecordIterator reuse) throws QueryEvaluationException; + + /** + * Create an ordered iterator over ID-level bindings; may fall back to the unordered iterator if unsupported. + */ + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, null, null, + null); + } + return null; + } + + /** + * Variant that accepts reusable scratch buffers. + */ + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, reuse, null, + null); + } + return null; + } + + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] bindingReuse, long[] quadReuse) + throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, bindingReuse, + quadReuse, null); + } + return null; + } + + default RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, LmdbEvaluationDataset.KeyRangeBuffers keyBuffers, + long[] bindingReuse, long[] quadReuse) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + bindingReuse, quadReuse, null); + } + return null; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdTripleSourceAdapter.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdTripleSourceAdapter.java new file mode 100644 index 00000000000..e261c3f83b7 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdTripleSourceAdapter.java @@ -0,0 +1,260 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Comparator; +import java.util.Set; + +import org.eclipse.rdf4j.common.annotation.Experimental; +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.EmptyIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.sail.TripleSourceIterationWrapper; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinIterator; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Adapter that adds ID-level access to an arbitrary TripleSource by resolving IDs via the provided ValueStore. This + * does not avoid materialization when the underlying TripleSource cannot serve IDs directly; it centralizes the + * fallback for overlay scenarios. + */ +final class LmdbIdTripleSourceAdapter implements TripleSource, LmdbIdTripleSource { + + private final TripleSource delegate; + private final ValueStore valueStore; + + LmdbIdTripleSourceAdapter(TripleSource delegate, ValueStore valueStore) { + this.delegate = delegate; + this.valueStore = valueStore; + } + + // TripleSource delegation + @Override + public CloseableIteration getStatements(Resource subj, IRI pred, Value obj, + Resource... contexts) throws QueryEvaluationException { + CloseableIteration statements = delegate.getStatements(subj, pred, obj, contexts); + if (statements instanceof EmptyIteration) { + return statements; + } + return new TripleSourceIterationWrapper<>(statements); + } + + @Override + @Experimental + public CloseableIteration getStatements(org.eclipse.rdf4j.common.order.StatementOrder order, + Resource subj, IRI pred, Value obj, Resource... contexts) throws QueryEvaluationException { + return delegate.getStatements(order, subj, pred, obj, contexts); + } + + @Override + @Experimental + public Set getSupportedOrders(Resource subj, IRI pred, Value obj, + Resource... contexts) { + return delegate.getSupportedOrders(subj, pred, obj, contexts); + } + + @Override + @Experimental + public Comparator getComparator() { + return delegate.getComparator(); + } + + @Override + public ValueFactory getValueFactory() { + return delegate.getValueFactory(); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, LmdbEvaluationDataset.KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse, + RecordIterator iteratorReuse) + throws QueryEvaluationException { + // Prefer direct ID-level access if the delegate already supports it + if (delegate instanceof LmdbIdTripleSource) { + return ((LmdbIdTripleSource) delegate).getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, keyBuffers, reuse, quadReuse, iteratorReuse); + } + + // If no active connection changes, delegate to the current LMDB dataset to avoid materialization + if (!LmdbEvaluationStrategy.hasActiveConnectionChanges()) { + var dsOpt = LmdbEvaluationStrategy.getCurrentDataset(); + if (dsOpt.isPresent()) { + return dsOpt.get() + .getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + reuse, quadReuse, iteratorReuse); + } + } + + // Fallback: materializing path via delegate TripleSource (only when unavoidable) + Value subjValue = valueForQuery(patternIds[TripleStore.SUBJ_IDX], binding, subjIndex, true, false); + Resource subjRes = subjValue == null ? null : (Resource) subjValue; + Value predValue = valueForQuery(patternIds[TripleStore.PRED_IDX], binding, predIndex, false, true); + IRI predIri = predValue == null ? null : (IRI) predValue; + Value objValue = valueForQuery(patternIds[TripleStore.OBJ_IDX], binding, objIndex, false, false); + long ctxQueryId = selectQueryId(patternIds[TripleStore.CONTEXT_IDX], binding, ctxIndex); + boolean requireDefaultContext = ctxQueryId == 0; + Resource[] contexts; + if (ctxQueryId > 0) { + try { + Value ctxValue = valueStore.getLazyValue(ctxQueryId); + if (!(ctxValue instanceof Resource) || ((Resource) ctxValue).isTriple()) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + contexts = new Resource[] { (Resource) ctxValue }; + } catch (Exception e) { + throw new QueryEvaluationException(e); + } + } else { + contexts = new Resource[0]; + } + + CloseableIteration stmts = contexts.length == 0 + ? delegate.getStatements(subjRes, predIri, objValue) + : delegate.getStatements(subjRes, predIri, objValue, contexts); + + final boolean defaultOnly = requireDefaultContext; + return new RecordIterator() { + @Override + public long[] next() throws QueryEvaluationException { + while (true) { + try { + if (!stmts.hasNext()) { + stmts.close(); + return null; + } + Statement st = stmts.next(); + if (defaultOnly && st.getContext() != null) { + continue; + } + long subjId = resolveId(st.getSubject()); + long predId = resolveId(st.getPredicate()); + long objId = resolveId(st.getObject()); + long ctxId = st.getContext() == null ? 0L : resolveId(st.getContext()); + long[] merged = mergeBinding(binding, subjId, predId, objId, ctxId, subjIndex, predIndex, + objIndex, ctxIndex); + if (merged != null) { + return merged; + } + } catch (QueryEvaluationException e) { + throw e; + } catch (Exception e) { + try { + stmts.close(); + } catch (Exception ignore) { + } + throw new QueryEvaluationException(e); + } + } + } + + @Override + public void close() { + try { + stmts.close(); + } catch (Exception ignore) { + } + } + }; + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, LmdbEvaluationDataset.KeyRangeBuffers keyBuffers, + long[] bindingReuse, long[] quadReuse) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + bindingReuse, quadReuse, null); + } + if (delegate instanceof LmdbIdTripleSource) { + return ((LmdbIdTripleSource) delegate).getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, + ctxIndex, patternIds, order, keyBuffers, bindingReuse, quadReuse); + } + return null; + } + + private long selectQueryId(long patternId, long[] binding, int index) { + if (patternId != LmdbValue.UNKNOWN_ID) { + return patternId; + } + if (index >= 0 && index < binding.length) { + return binding[index]; + } + return LmdbValue.UNKNOWN_ID; + } + + private Value valueForQuery(long patternId, long[] binding, int index, boolean requireResource, boolean requireIri) + throws QueryEvaluationException { + long id = selectQueryId(patternId, binding, index); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + try { + Value value = valueStore.getLazyValue(id); + if (requireResource && !(value instanceof Resource)) { + throw new QueryEvaluationException("Expected resource-bound value"); + } + if (requireIri && !(value instanceof IRI)) { + throw new QueryEvaluationException("Expected IRI-bound value"); + } + if (value instanceof Resource && value.isTriple()) { + throw new QueryEvaluationException("Triple-valued resources are not supported in LMDB joins"); + } + return value; + } catch (Exception e) { + throw e instanceof QueryEvaluationException ? (QueryEvaluationException) e + : new QueryEvaluationException(e); + } + } + + private long[] mergeBinding(long[] binding, long subjId, long predId, long objId, long ctxId, int subjIndex, + int predIndex, int objIndex, int ctxIndex) { + long[] out = java.util.Arrays.copyOf(binding, binding.length); + if (!applyValue(out, subjIndex, subjId)) + return null; + if (!applyValue(out, predIndex, predId)) + return null; + if (!applyValue(out, objIndex, objId)) + return null; + if (!applyValue(out, ctxIndex, ctxId)) + return null; + return out; + } + + private boolean applyValue(long[] target, int index, long value) { + if (index < 0) + return true; + long existing = target[index]; + if (existing != LmdbValue.UNKNOWN_ID && existing != value) + return false; + target[index] = value; + return true; + } + + private long resolveId(Value value) throws Exception { + if (value == null) { + return LmdbValue.UNKNOWN_ID; + } + if (value instanceof LmdbValue) { + LmdbValue lmdb = (LmdbValue) value; + if (valueStore.getRevision().equals(lmdb.getValueStoreRevision())) { + return lmdb.getInternalID(); + } + } + return valueStore.getId(value); + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbOverlayEvaluationDataset.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbOverlayEvaluationDataset.java new file mode 100644 index 00000000000..e655feaf78d --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbOverlayEvaluationDataset.java @@ -0,0 +1,447 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Arrays; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinIterator; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Delegates reads via the Sail {@link TripleSource} to honor transaction overlays and isolation while exposing + * LMDB-style {@link RecordIterator}s for ID-based joins. + */ +final class LmdbOverlayEvaluationDataset implements LmdbEvaluationDataset { + + private static final Logger log = LoggerFactory.getLogger(LmdbOverlayEvaluationDataset.class); + private final TripleSource tripleSource; + private final ValueStore valueStore; + + LmdbOverlayEvaluationDataset(TripleSource tripleSource, ValueStore valueStore) { + this.tripleSource = tripleSource; + this.valueStore = valueStore; + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings) + throws QueryEvaluationException { + return getRecordIteratorInternal(pattern, bindings, null); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, KeyRangeBuffers keyBuffers) + throws QueryEvaluationException { + return getRecordIteratorInternal(pattern, bindings, keyBuffers); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, KeyRangeBuffers keyBuffers, + RecordIterator iteratorReuse) throws QueryEvaluationException { + if (iteratorReuse != null) { + iteratorReuse.close(); + } + return getRecordIteratorInternal(pattern, bindings, keyBuffers); + } + + private RecordIterator getRecordIteratorInternal(StatementPattern pattern, BindingSet bindings, + KeyRangeBuffers keyBuffers) + throws QueryEvaluationException { + + Value subj = resolveValue(pattern.getSubjectVar(), bindings); + if (subj != null && !(subj instanceof Resource)) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + Value pred = resolveValue(pattern.getPredicateVar(), bindings); + if (pred != null && !(pred instanceof IRI)) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + Value obj = resolveValue(pattern.getObjectVar(), bindings); + Value ctxVal = resolveValue(pattern.getContextVar(), bindings); + + Resource subjRes = (Resource) subj; // may be null + IRI predIri = (IRI) pred; // may be null + + if (ctxVal != null && !(ctxVal instanceof Resource)) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + if (ctxVal != null && ctxVal.isTriple()) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + + Resource[] contexts; + if (ctxVal == null) { + contexts = new Resource[0]; + } else { + contexts = new Resource[] { (Resource) ctxVal }; + } + + final CloseableIteration stmts = contexts.length == 0 + ? tripleSource.getStatements(subjRes, predIri, obj) + : tripleSource.getStatements(subjRes, predIri, obj, contexts); + + return new RecordIterator() { + @Override + public long[] next() throws QueryEvaluationException { + try { + if (!stmts.hasNext()) { + stmts.close(); + return null; + } + Statement st = stmts.next(); + if (st == null) { + stmts.close(); + return null; + } + long s = resolveId(st.getSubject()); + long p = resolveId(st.getPredicate()); + long o = resolveId(st.getObject()); + long c = st.getContext() == null ? 0L : resolveId(st.getContext()); + return new long[] { s, p, o, c }; + } catch (Exception e) { + try { + stmts.close(); + } catch (Exception ignore) { + } + if (e instanceof QueryEvaluationException) { + throw (QueryEvaluationException) e; + } + throw new QueryEvaluationException(e); + } + } + + @Override + public void close() { + try { + stmts.close(); + } catch (Exception ignore) { + } + } + }; + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds) throws QueryEvaluationException { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, null, + null, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + null, null, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] reuse) throws QueryEvaluationException { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, reuse, + null, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] reuse, long[] quadReuse) throws QueryEvaluationException { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, reuse, + quadReuse, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + reuse, quadReuse, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse, RecordIterator iteratorReuse) + throws QueryEvaluationException { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + reuse, quadReuse, iteratorReuse); + } + + private RecordIterator getRecordIteratorInternal(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse, + RecordIterator iteratorReuse) + throws QueryEvaluationException { + // Prefer an ID-level path if the TripleSource supports it and we can trust overlay correctness. + if (tripleSource instanceof LmdbIdTripleSource) { + // The overlay dataset is represented by this LmdbOverlayEvaluationDataset; the tripleSource reflects the + // current branch dataset state (including transaction overlays). Therefore, using ID-level access here is + // correct when available. + RecordIterator viaIds = ((LmdbIdTripleSource) tripleSource) + .getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, reuse, + quadReuse, iteratorReuse); + if (viaIds != null) { + return viaIds; + } + } + + // Fallback: Value-based overlay path with per-statement ID resolution (minimal unavoidable materialization). + try { + Value subjValue = valueForQuery(patternIds[TripleStore.SUBJ_IDX], binding, subjIndex, true, false); + Value predValue = valueForQuery(patternIds[TripleStore.PRED_IDX], binding, predIndex, false, true); + Value objValue = valueForQuery(patternIds[TripleStore.OBJ_IDX], binding, objIndex, false, false); + + Resource subjRes = subjValue == null ? null : (Resource) subjValue; + IRI predIri = predValue == null ? null : (IRI) predValue; + + long ctxQueryId = selectQueryId(patternIds[TripleStore.CONTEXT_IDX], binding, ctxIndex); + boolean requireDefaultContext = ctxQueryId == 0; + Resource[] contexts; + if (ctxQueryId > 0) { + Value ctxValue = valueStore.getLazyValue(ctxQueryId); + if (!(ctxValue instanceof Resource)) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + Resource ctxRes = (Resource) ctxValue; + if (ctxRes.isTriple()) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + contexts = new Resource[] { ctxRes }; + } else { + contexts = new Resource[0]; + } + + CloseableIteration stmts = contexts.length == 0 + ? tripleSource.getStatements(subjRes, predIri, objValue) + : tripleSource.getStatements(subjRes, predIri, objValue, contexts); + + final boolean defaultOnly = requireDefaultContext; + + return new RecordIterator() { + @Override + public long[] next() throws QueryEvaluationException { + while (true) { + try { + if (!stmts.hasNext()) { + stmts.close(); + return null; + } + Statement st = stmts.next(); + if (defaultOnly && st.getContext() != null) { + continue; + } + long subjId = resolveId(st.getSubject()); + long predId = resolveId(st.getPredicate()); + long objId = resolveId(st.getObject()); + long ctxId = st.getContext() == null ? 0L : resolveId(st.getContext()); + + long[] merged = mergeBinding(binding, subjId, predId, objId, ctxId, subjIndex, predIndex, + objIndex, ctxIndex); + if (merged != null) { + return merged; + } + } catch (QueryEvaluationException e) { + throw e; + } catch (Exception e) { + try { + stmts.close(); + } catch (Exception ignore) { + } + throw new QueryEvaluationException(e); + } + } + } + + @Override + public void close() { + try { + stmts.close(); + } catch (Exception ignore) { + } + } + }; + } catch (QueryEvaluationException e) { + throw e; + } catch (Exception e) { + throw new QueryEvaluationException(e); + } + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + return LmdbEvaluationDataset.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, null, null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) throws QueryEvaluationException { + return LmdbEvaluationDataset.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, reuse, null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + return LmdbEvaluationDataset.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, reuse, quadReuse); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, KeyRangeBuffers keyBuffers, long[] bindingReuse, + long[] quadReuse) throws QueryEvaluationException { + if (order == null) { + return getRecordIteratorInternal(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + bindingReuse, quadReuse, null); + } + return LmdbEvaluationDataset.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, keyBuffers, bindingReuse, quadReuse); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, KeyRangeBuffers keyBuffers, long[] bindingReuse, + long[] quadReuse, RecordIterator iteratorReuse) throws QueryEvaluationException { + if (iteratorReuse != null) { + iteratorReuse.close(); + log.warn("getOrderedRecordIterator does not support reusing RecordIterator"); + } + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, + keyBuffers, bindingReuse, quadReuse); + } + + @Override + public RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, StatementOrder order, + KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + if (order == null) { + return getRecordIteratorInternal(pattern, bindings, keyBuffers); + } + return LmdbEvaluationDataset.super.getOrderedRecordIterator(pattern, bindings, order, keyBuffers); + } + + @Override + public ValueStore getValueStore() { + return valueStore; + } + + @Override + public String selectBestIndex(long subj, long pred, long obj, long context) { + var currentDataset = LmdbEvaluationStrategy.getCurrentDataset(); + if (currentDataset.isPresent()) { + LmdbEvaluationDataset delegate = currentDataset.get(); + if (delegate != this) { + return delegate.selectBestIndex(subj, pred, obj, context); + } + } + return null; + } + + private long selectQueryId(long patternId, long[] binding, int index) { + if (patternId != LmdbValue.UNKNOWN_ID) { + return patternId; + } + if (index >= 0 && index < binding.length) { + return binding[index]; + } + return LmdbValue.UNKNOWN_ID; + } + + private Value valueForQuery(long patternId, long[] binding, int index, boolean requireResource, boolean requireIri) + throws QueryEvaluationException { + long id = selectQueryId(patternId, binding, index); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + try { + Value value = valueStore.getLazyValue(id); + if (requireResource && !(value instanceof Resource)) { + throw new QueryEvaluationException("Expected resource-bound value"); + } + if (requireIri && !(value instanceof IRI)) { + throw new QueryEvaluationException("Expected IRI-bound value"); + } + if (value instanceof Resource && value.isTriple()) { + throw new QueryEvaluationException("Triple-valued resources are not supported in LMDB joins"); + } + return value; + } catch (Exception e) { + throw e instanceof QueryEvaluationException ? (QueryEvaluationException) e + : new QueryEvaluationException(e); + } + } + + private long[] mergeBinding(long[] binding, long subjId, long predId, long objId, long ctxId, int subjIndex, + int predIndex, int objIndex, int ctxIndex) { + long[] out = Arrays.copyOf(binding, binding.length); + if (!applyValue(out, subjIndex, subjId)) { + return null; + } + if (!applyValue(out, predIndex, predId)) { + return null; + } + if (!applyValue(out, objIndex, objId)) { + return null; + } + if (!applyValue(out, ctxIndex, ctxId)) { + return null; + } + return out; + } + + private boolean applyValue(long[] target, int index, long value) { + if (index < 0) { + return true; + } + long existing = target[index]; + if (existing != LmdbValue.UNKNOWN_ID && existing != value) { + return false; + } + target[index] = value; + return true; + } + + private Value resolveValue(Var var, BindingSet bindings) { + if (var == null) { + return null; + } + if (var.hasValue()) { + return var.getValue(); + } + if (bindings != null) { + return bindings.getValue(var.getName()); + } + return null; + } + + private long resolveId(Value value) throws Exception { + if (value == null) { + return LmdbValue.UNKNOWN_ID; + } + if (value instanceof LmdbValue) { + LmdbValue lmdb = (LmdbValue) value; + if (valueStore.getRevision().equals(lmdb.getValueStoreRevision())) { + return lmdb.getInternalID(); + } + } + return valueStore.getId(value, true); + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbQueryEvaluationContext.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbQueryEvaluationContext.java new file mode 100644 index 00000000000..2c253f33cf9 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbQueryEvaluationContext.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Comparator; +import java.util.Optional; + +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.Dataset; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; + +/** + * Extension of {@link QueryEvaluationContext} that exposes LMDB specific resources to evaluation steps. + */ +public class LmdbQueryEvaluationContext extends QueryEvaluationContext.Minimal implements LmdbDatasetContext { + + private final LmdbEvaluationDataset dataset; + private final ValueStore valueStore; + + public LmdbQueryEvaluationContext(Dataset dataset, ValueFactory valueFactory, Comparator comparator, + LmdbEvaluationDataset lmdbDataset, ValueStore valueStore) { + super(dataset, valueFactory, comparator); + this.dataset = lmdbDataset; + this.valueStore = valueStore; + } + + @Override + public Optional getLmdbDataset() { + return Optional.ofNullable(dataset); + } + + @Override + public Optional getValueStore() { + return Optional.ofNullable(valueStore); + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java index 68c5352a73b..48c9b5d67cd 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java @@ -27,6 +27,7 @@ import org.eclipse.rdf4j.common.concurrent.locks.StampedLongAdderLockManager; import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.lmdb.TripleStore.KeyBuilder; import org.eclipse.rdf4j.sail.lmdb.TripleStore.TripleIndex; import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; @@ -41,83 +42,239 @@ */ class LmdbRecordIterator implements RecordIterator { private static final Logger log = LoggerFactory.getLogger(LmdbRecordIterator.class); - private final Pool pool; + private final Pool pool = Pool.get(); - private final TripleIndex index; + private TripleIndex index; - private final long subj; - private final long pred; - private final long obj; - private final long context; + private long subj; + private long pred; + private long obj; + private long context; - private final long cursor; + private long cursor; - private final MDBVal maxKey; + private MDBVal maxKey; - private final boolean matchValues; + private boolean matchValues; private GroupMatcher groupMatcher; + private GroupMatcher matcherForEvaluation; - private final Txn txnRef; + /** + * True when late-bound variables exist beyond the contiguous prefix of the chosen index order, requiring + * value-level filtering. When false, range bounds already guarantee that every visited key matches and the + * GroupMatcher is redundant. + */ + private boolean needMatcher; + + private Txn txnRef; private long txnRefVersion; - private final long txn; + private long txn; - private final int dbi; + private int dbi; private volatile boolean closed = false; - private final MDBVal keyData; + private MDBVal keyData; - private final MDBVal valueData; + private MDBVal valueData; private ByteBuffer minKeyBuf; private ByteBuffer maxKeyBuf; + private boolean externalMinKeyBuf; + private boolean externalMaxKeyBuf; private int lastResult; - private final long[] quad; - private final long[] originalQuad; + private long[] quad; private boolean fetchNext = false; - private final StampedLongAdderLockManager txnLockManager; + private StampedLongAdderLockManager txnLockManager; private final Thread ownerThread = Thread.currentThread(); + private boolean initialized = false; + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, long context, boolean explicit, Txn txnRef) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, null, null, null); + } + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, null, null); + } + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse, ByteBuffer minKeyBufParam, + ByteBuffer maxKeyBufParam) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, minKeyBufParam, + maxKeyBufParam); + } + + LmdbRecordIterator(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef) throws IOException { + this(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, null, null, null); + } + + LmdbRecordIterator(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef, long[] quadReuse, + ByteBuffer minKeyBufParam, ByteBuffer maxKeyBufParam) throws IOException { + initializeInternal(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, + minKeyBufParam, maxKeyBufParam); + initialized = true; + } + + void initialize(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse, ByteBuffer minKeyBufParam, + ByteBuffer maxKeyBufParam) throws IOException { + if (initialized && !closed) { + // prepareForReuse(); + } + initializeInternal(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, + minKeyBufParam, maxKeyBufParam); + initialized = true; + } + + private void initializeInternal(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef, long[] quadReuse, + ByteBuffer minKeyBufParam, ByteBuffer maxKeyBufParam) throws IOException { + +// if (!initialized) { +// System.out.println(); +// } else { +// System.out.println(); +// } + + this.index = index; + long prevSubj = minKeyBuf != null ? this.subj : TripleStore.NO_PREVIOUS_ID; + long prevPred = minKeyBuf != null ? this.pred : TripleStore.NO_PREVIOUS_ID; + long prevObj = minKeyBuf != null ? this.obj : TripleStore.NO_PREVIOUS_ID; + long prevContext = minKeyBuf != null ? this.context : TripleStore.NO_PREVIOUS_ID; + this.subj = subj; this.pred = pred; this.obj = obj; this.context = context; - this.originalQuad = new long[] { subj, pred, obj, context }; - this.quad = new long[] { subj, pred, obj, context }; - this.pool = Pool.get(); - this.keyData = pool.getVal(); - this.valueData = pool.getVal(); - this.index = index; + + boolean prevExternalMinKeyBuf = this.externalMinKeyBuf; + boolean prevExternalMaxKeyBuf = this.externalMaxKeyBuf; + + if (quadReuse != null && quadReuse.length >= 4) { + this.quad = quadReuse; + } else if (this.quad == null || this.quad.length < 4) { + this.quad = new long[] { subj, pred, obj, context }; + } + this.quad[0] = subj; + this.quad[1] = pred; + this.quad[2] = obj; + this.quad[3] = context; + + if (this.keyData == null) { + this.keyData = pool.getVal(); + } + if (this.valueData == null) { + this.valueData = pool.getVal(); + } + if (rangeSearch) { - minKeyBuf = pool.getKeyBuffer(); - index.getMinKey(minKeyBuf, subj, pred, obj, context); + this.externalMinKeyBuf = minKeyBufParam != null; + if (externalMinKeyBuf) { + this.minKeyBuf = minKeyBufParam; + } else { + if (this.minKeyBuf == null || prevExternalMinKeyBuf) { + this.minKeyBuf = pool.getKeyBuffer(); + } else { + this.minKeyBuf.clear(); + } + } + minKeyBuf.clear(); + if (keyBuilder != null) { + keyBuilder.writeMin(minKeyBuf); + } else { + index.getMinKey(minKeyBuf, subj, pred, obj, context, prevSubj, prevPred, prevObj, prevContext); + } minKeyBuf.flip(); - this.maxKey = pool.getVal(); - this.maxKeyBuf = pool.getKeyBuffer(); - index.getMaxKey(maxKeyBuf, subj, pred, obj, context); + if (this.maxKey == null) { + this.maxKey = pool.getVal(); + } + this.externalMaxKeyBuf = maxKeyBufParam != null; + if (externalMaxKeyBuf) { + this.maxKeyBuf = maxKeyBufParam; + } else { + if (this.maxKeyBuf == null || prevExternalMaxKeyBuf) { + this.maxKeyBuf = pool.getKeyBuffer(); + } else { + this.maxKeyBuf.clear(); + } + } + maxKeyBuf.clear(); + if (keyBuilder != null) { + keyBuilder.writeMax(maxKeyBuf); + } else { + index.getMaxKey(maxKeyBuf, subj, pred, obj, context, prevSubj, prevPred, prevObj, prevContext); + } maxKeyBuf.flip(); this.maxKey.mv_data(maxKeyBuf); } else { - minKeyBuf = null; - this.maxKey = null; + if (this.maxKey != null) { + pool.free(maxKey); + this.maxKey = null; + } + if (this.maxKeyBuf != null && !prevExternalMaxKeyBuf) { + pool.free(maxKeyBuf); + this.maxKeyBuf = null; + } + this.externalMaxKeyBuf = maxKeyBufParam != null; + this.maxKeyBuf = externalMaxKeyBuf ? maxKeyBufParam : null; + + if (subj > 0 || pred > 0 || obj > 0 || context >= 0) { + this.externalMinKeyBuf = minKeyBufParam != null; + if (externalMinKeyBuf) { + this.minKeyBuf = minKeyBufParam; + } else { + if (this.minKeyBuf == null || prevExternalMinKeyBuf) { + this.minKeyBuf = pool.getKeyBuffer(); + } else { + this.minKeyBuf.clear(); + } + } + minKeyBuf.clear(); + index.getMinKey(minKeyBuf, subj, pred, obj, context, prevSubj, prevPred, prevObj, prevContext); + minKeyBuf.flip(); + } else { + if (this.minKeyBuf != null && !prevExternalMinKeyBuf) { + pool.free(minKeyBuf); + this.minKeyBuf = null; + } + this.externalMinKeyBuf = minKeyBufParam != null; + if (externalMinKeyBuf) { + this.minKeyBuf = minKeyBufParam; + } else { + this.minKeyBuf = null; + } + } } this.matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0; - - this.dbi = index.getDB(explicit); - this.txnRef = txnRef; - this.txnLockManager = txnRef.lockManager(); + int prefixLen = index.getPatternScore(subj, pred, obj, context); + int boundCount = (subj > 0 ? 1 : 0) + (pred > 0 ? 1 : 0) + (obj > 0 ? 1 : 0) + (context >= 0 ? 1 : 0); + this.needMatcher = boundCount > prefixLen; + this.groupMatcher = null; + this.matcherForEvaluation = null; + this.fetchNext = false; + this.lastResult = MDB_SUCCESS; + this.closed = false; + + if (!initialized) { + this.dbi = index.getDB(explicit); + this.txnRef = txnRef; + this.txnLockManager = txnRef.lockManager(); + } long readStamp; try { @@ -129,10 +286,44 @@ class LmdbRecordIterator implements RecordIterator { this.txnRefVersion = txnRef.version(); this.txn = txnRef.get(); - try (MemoryStack stack = MemoryStack.stackPush()) { - PointerBuffer pp = stack.mallocPointer(1); - E(mdb_cursor_open(txn, dbi, pp)); - cursor = pp.get(0); + // Try to reuse a pooled cursor only for read-only transactions; otherwise open a new one + if (txnRef.isReadOnly()) { + if (cursor == 0L) { + cursor = pool.getCursor(dbi, index); + } + + if (cursor != 0L) { + long c = cursor; + try { + E(mdb_cursor_renew(txn, c)); + } catch (IOException renewEx) { + // Renewal failed (e.g., incompatible txn). Close pooled cursor and open a fresh one. + mdb_cursor_close(c); + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + c = pp.get(0); + } + } + cursor = c; + } else { + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + cursor = pp.get(0); + } + } + } else { + if (cursor != 0L) { + pool.freeCursor(dbi, index, cursor); + cursor = 0L; + } + + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + cursor = pp.get(0); + } } } finally { txnLockManager.unlockRead(readStamp); @@ -141,18 +332,20 @@ class LmdbRecordIterator implements RecordIterator { @Override public long[] next() { + if (closed) { + return null; + } + StampedLongAdderLockManager manager = txnLockManager; + if (manager == null) { + throw new SailException("Iterator not initialized"); + } long readStamp; try { - readStamp = txnLockManager.readLock(); + readStamp = manager.readLock(); } catch (InterruptedException e) { throw new SailException(e); } try { - if (closed) { - log.debug("Calling next() on an LmdbRecordIterator that is already closed, returning null"); - return null; - } - if (txnRefVersion != txnRef.version()) { // TODO: None of the tests in the LMDB Store cover this case! // cursor must be renewed @@ -199,66 +392,125 @@ public long[] next() { if (maxKey != null && mdb_cmp(txn, dbi, keyData, maxKey) > 0) { lastResult = MDB_NOTFOUND; } else if (matches()) { - // value doesn't match search key/mask, fetch next value lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); } else { // Matching value found - index.keyToQuad(keyData.mv_data(), originalQuad, quad); + index.keyToQuad(keyData.mv_data(), subj, pred, obj, context, quad); // fetch next value fetchNext = true; return quad; } } - closeInternal(false); +// closeInternal(false); return null; } finally { - txnLockManager.unlockRead(readStamp); + manager.unlockRead(readStamp); } } private boolean matches() { + // When there are no late-bound variables beyond the contiguous prefix, range bounds fully determine matches. + if (!needMatcher) { + return false; + } - if (groupMatcher != null) { - return !this.groupMatcher.matches(keyData.mv_data()); + if (matcherForEvaluation != null) { + return !matcherForEvaluation.matches(keyData.mv_data()); } else if (matchValues) { - this.groupMatcher = index.createMatcher(subj, pred, obj, context); - return !this.groupMatcher.matches(keyData.mv_data()); + matcherForEvaluation = index.createMatcher(subj, pred, obj, context); + groupMatcher = matcherForEvaluation; + return !matcherForEvaluation.matches(keyData.mv_data()); } else { return false; } } - private void closeInternal(boolean maybeCalledAsync) { - if (!closed) { - long writeStamp = 0L; - boolean writeLocked = false; - if (maybeCalledAsync && ownerThread != Thread.currentThread()) { - try { - writeStamp = txnLockManager.writeLock(); - writeLocked = true; - } catch (InterruptedException e) { - throw new SailException(e); - } + private void prepareForReuse() { + if (cursor != 0L && txnRef != null) { + if (txnRef.isReadOnly()) { + pool.freeCursor(dbi, index, cursor); + } else { + mdb_cursor_close(cursor); } + } + cursor = 0L; + groupMatcher = null; + matcherForEvaluation = null; + fetchNext = false; + lastResult = MDB_SUCCESS; + matchValues = false; + needMatcher = false; + txnRef = null; + txn = 0L; + txnRefVersion = 0L; + txnLockManager = null; + closed = true; + } + + private void closeInternal(boolean maybeCalledAsync) { + StampedLongAdderLockManager manager = this.txnLockManager; + if (closed) { + return; + } + long writeStamp = 0L; + boolean writeLocked = false; + if (maybeCalledAsync && ownerThread != Thread.currentThread() && manager != null) { try { - if (!closed) { + writeStamp = manager.writeLock(); + writeLocked = true; + } catch (InterruptedException e) { + throw new SailException(e); + } + } + try { + if (cursor != 0L && txnRef != null) { + if (txnRef.isReadOnly()) { + pool.freeCursor(dbi, index, cursor); + } else { mdb_cursor_close(cursor); - pool.free(keyData); - pool.free(valueData); - if (minKeyBuf != null) { - pool.free(minKeyBuf); - } - if (maxKey != null) { - pool.free(maxKeyBuf); - pool.free(maxKey); - } - } - } finally { - closed = true; - if (writeLocked) { - txnLockManager.unlockWrite(writeStamp); } + cursor = 0L; + } + if (keyData != null) { + pool.free(keyData); + keyData = null; + } + if (valueData != null) { + pool.free(valueData); + valueData = null; + } + if (minKeyBuf != null && !externalMinKeyBuf) { + pool.free(minKeyBuf); + } + if (maxKeyBuf != null && !externalMaxKeyBuf) { + pool.free(maxKeyBuf); + } + if (maxKey != null) { + pool.free(maxKey); + maxKey = null; + } + minKeyBuf = null; + maxKeyBuf = null; + externalMinKeyBuf = false; + externalMaxKeyBuf = false; + if (maybeCalledAsync) { + groupMatcher = null; + } + matcherForEvaluation = null; + fetchNext = false; + lastResult = 0; + matchValues = false; + needMatcher = false; + txnRef = null; + txn = 0L; + dbi = 0; + index = null; + } finally { + closed = true; + if (writeLocked) { + manager.unlockWrite(writeStamp); } + txnLockManager = null; } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailDatasetTripleSource.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailDatasetTripleSource.java new file mode 100644 index 00000000000..a14f80b7c3c --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailDatasetTripleSource.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Arrays; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.EmptyIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.base.SailDataset; +import org.eclipse.rdf4j.sail.base.SailDatasetTripleSource; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinIterator; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * LMDB-aware {@link TripleSource} that exposes ID-level access when supported by the backing dataset. + */ +public class LmdbSailDatasetTripleSource extends SailDatasetTripleSource implements LmdbIdTripleSource { + + private final SailDataset dataset; + + public LmdbSailDatasetTripleSource(ValueFactory vf, SailDataset dataset) { + super(vf, dataset); + this.dataset = dataset; + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, LmdbEvaluationDataset.KeyRangeBuffers keyBuffers, long[] bindingReuse, long[] quadReuse, + RecordIterator iteratorReuse) throws QueryEvaluationException { + + // Fast path: backing dataset supports ID-level access + if (dataset instanceof LmdbEvaluationDataset) { + return ((LmdbEvaluationDataset) dataset).getRecordIterator(binding, subjIndex, predIndex, objIndex, + ctxIndex, patternIds, keyBuffers, bindingReuse, quadReuse, iteratorReuse); + } + + // Fallback path: value-level iteration converted to IDs using ValueStore + ValueStore valueStore = LmdbEvaluationStrategy.getCurrentDataset() + .map(LmdbEvaluationDataset::getValueStore) + .orElse(null); + if (valueStore == null) { + // No way to resolve IDs safely; return empty to avoid incorrect results + return LmdbIdJoinIterator.emptyRecordIterator(); + } + + // Resolve fixed values for the pattern using the binding and pattern IDs + Value subjValue = valueForQuery(valueStore, patternIds[TripleStore.SUBJ_IDX], binding, subjIndex, true, false); + Resource subjRes = subjValue == null ? null : (Resource) subjValue; + + Value predValue = valueForQuery(valueStore, patternIds[TripleStore.PRED_IDX], binding, predIndex, false, true); + IRI predIri = predValue == null ? null : (IRI) predValue; + + Value objValue = valueForQuery(valueStore, patternIds[TripleStore.OBJ_IDX], binding, objIndex, false, false); + + long ctxQueryId = selectQueryId(patternIds[TripleStore.CONTEXT_IDX], binding, ctxIndex); + boolean requireDefaultContext = ctxQueryId == 0; + Resource[] contexts; + if (ctxQueryId > 0) { + try { + Value ctxValue = valueStore.getLazyValue(ctxQueryId); + if (!(ctxValue instanceof Resource) || ((Resource) ctxValue).isTriple()) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + contexts = new Resource[] { (Resource) ctxValue }; + } catch (Exception e) { + throw new QueryEvaluationException(e); + } + } else { + contexts = new Resource[0]; + } + + try { + final CloseableIteration stmts = contexts.length == 0 + ? dataset.getStatements(subjRes, predIri, objValue) + : dataset.getStatements(subjRes, predIri, objValue, contexts); + + if (stmts instanceof EmptyIteration) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + + return new RecordIterator() { + @Override + public long[] next() throws QueryEvaluationException { + while (true) { + try { + if (!stmts.hasNext()) { + stmts.close(); + return null; + } + Statement st = stmts.next(); + if (requireDefaultContext && st.getContext() != null) { + continue; + } + + long subjId = resolveId(valueStore, st.getSubject()); + long predId = resolveId(valueStore, st.getPredicate()); + long objId = resolveId(valueStore, st.getObject()); + long ctxId = st.getContext() == null ? 0L : resolveId(valueStore, st.getContext()); + + long[] merged = mergeBinding(binding, subjId, predId, objId, ctxId, subjIndex, predIndex, + objIndex, ctxIndex); + if (merged != null) { + return merged; + } + } catch (QueryEvaluationException e) { + throw e; + } catch (Exception e) { + try { + stmts.close(); + } catch (Exception ignore) { + } + throw new QueryEvaluationException(e); + } + } + } + + @Override + public void close() { + try { + stmts.close(); + } catch (Exception ignore) { + } + } + }; + } catch (SailException e) { + throw new QueryEvaluationException(e); + } + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, reuse, + null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + if (dataset instanceof LmdbEvaluationDataset) { + return ((LmdbEvaluationDataset) dataset).getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, + ctxIndex, patternIds, order, reuse, quadReuse); + } + return LmdbIdTripleSource.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, reuse, quadReuse); + } + + private long selectQueryId(long patternId, long[] binding, int index) { + if (patternId != LmdbValue.UNKNOWN_ID) { + return patternId; + } + if (index >= 0 && index < binding.length) { + return binding[index]; + } + return LmdbValue.UNKNOWN_ID; + } + + private Value valueForQuery(ValueStore valueStore, long patternId, long[] binding, int index, + boolean requireResource, + boolean requireIri) throws QueryEvaluationException { + long id = selectQueryId(patternId, binding, index); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + try { + Value value = valueStore.getLazyValue(id); + if (requireResource && !(value instanceof Resource)) { + throw new QueryEvaluationException("Expected resource-bound value"); + } + if (requireIri && !(value instanceof IRI)) { + throw new QueryEvaluationException("Expected IRI-bound value"); + } + if (value instanceof Resource && value.isTriple()) { + throw new QueryEvaluationException("Triple-valued resources are not supported in LMDB joins"); + } + return value; + } catch (Exception e) { + throw e instanceof QueryEvaluationException ? (QueryEvaluationException) e + : new QueryEvaluationException(e); + } + } + + private long[] mergeBinding(long[] binding, long subjId, long predId, long objId, long ctxId, int subjIndex, + int predIndex, int objIndex, int ctxIndex) { + long[] out = Arrays.copyOf(binding, binding.length); + if (!applyValue(out, subjIndex, subjId)) { + return null; + } + if (!applyValue(out, predIndex, predId)) { + return null; + } + if (!applyValue(out, objIndex, objId)) { + return null; + } + if (!applyValue(out, ctxIndex, ctxId)) { + return null; + } + return out; + } + + private boolean applyValue(long[] target, int index, long value) { + if (index < 0) { + return true; + } + long existing = target[index]; + if (existing != LmdbValue.UNKNOWN_ID && existing != value) { + return false; + } + target[index] = value; + return true; + } + + private long resolveId(ValueStore valueStore, Value value) throws Exception { + if (value == null) { + return LmdbValue.UNKNOWN_ID; + } + if (value instanceof LmdbValue) { + LmdbValue lmdb = (LmdbValue) value; + if (valueStore.getRevision().equals(lmdb.getValueStoreRevision())) { + return lmdb.getInternalID(); + } + } + return valueStore.getId(value); + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java index 6d87d4bc33e..7d30eea5ad0 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java @@ -15,8 +15,13 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; +import java.util.EnumSet; +import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -33,13 +38,19 @@ import org.eclipse.rdf4j.common.iteration.UnionIteration; import org.eclipse.rdf4j.common.order.StatementOrder; import org.eclipse.rdf4j.common.transaction.IsolationLevel; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Namespace; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics; +import org.eclipse.rdf4j.query.algebra.evaluation.util.ValueComparator; import org.eclipse.rdf4j.sail.InterruptedSailException; import org.eclipse.rdf4j.sail.SailException; import org.eclipse.rdf4j.sail.base.BackingSailSource; @@ -58,12 +69,41 @@ */ class LmdbSailStore implements SailStore { + private static final RecordIterator EMPTY_RECORD_ITERATOR = new RecordIterator() { + @Override + public long[] next() { + return null; + } + + @Override + public void close() { + // no-op + } + }; + + private static final boolean SCRATCH_REUSE_ENABLED = !"false" + .equalsIgnoreCase(System.getProperty("rdf4j.lmdb.experimentalScratchReuse", "true")); + final Logger logger = LoggerFactory.getLogger(LmdbSailStore.class); private final TripleStore tripleStore; private final ValueStore valueStore; + // Precomputed lookup: for each bound-mask (bits for S=1,P=2,O=4,C=8), the union of + // supported StatementOrder across all configured indexes that are compatible with that mask. + @SuppressWarnings("unchecked") + private final EnumSet[] supportedOrdersLookup = (EnumSet[]) new EnumSet[16]; + + @SuppressWarnings("unchecked") + private final List[] compatibleIndexesByMask = (List[]) new List[16]; + + // firstFreeOrderByIndexAndMask[indexPos][mask] -> first free StatementOrder in that index for that mask, or null + private StatementOrder[][] firstFreeOrderByIndexAndMask; + + // Map index instance -> its stable position used in the lookup arrays + private Map indexPositionMap; + private final ExecutorService tripleStoreExecutor = Executors.newCachedThreadPool(); private final CircularBuffer opQueue = new CircularBuffer<>(1024); private volatile Throwable tripleStoreException; @@ -83,7 +123,7 @@ class LmdbSailStore implements SailStore { * * @param Type of elements within this buffer */ - final class CircularBuffer { + static final class CircularBuffer { private final T[] elements; private volatile int head = 0; @@ -206,6 +246,125 @@ public LmdbSailStore(File dataDir, LmdbStoreConfig config) throws IOException, S } } + private static int bitFor(char f) { + switch (f) { + case 's': + return 1; + case 'p': + return 1 << 1; + case 'o': + return 1 << 2; + case 'c': + return 1 << 3; + default: + return 0; + } + } + + private static StatementOrder orderFor(char f) { + switch (f) { + case 's': + return StatementOrder.S; + case 'p': + return StatementOrder.P; + case 'o': + return StatementOrder.O; + case 'c': + return StatementOrder.C; + default: + throw new IllegalArgumentException("Unknown field: " + f); + } + } + + private static boolean isIndexCompatible(char[] seq, int mask) { + boolean seenUnbound = false; + for (char f : seq) { + boolean bound = (mask & bitFor(f)) != 0; + if (!bound) { + seenUnbound = true; + } else if (seenUnbound) { + return false; + } + } + return true; + } + + private EnumSet[] getSupportedOrdersLookup() { + EnumSet[] local = supportedOrdersLookup; + if (local[0] == null) { + synchronized (this) { + local = supportedOrdersLookup; + if (local[0] == null) { + buildSupportedOrdersLookup(local); + } + } + } + return local; + } + + private void buildSupportedOrdersLookup(EnumSet[] table) { + for (int i = 0; i < table.length; i++) { + table[i] = EnumSet.noneOf(StatementOrder.class); + compatibleIndexesByMask[i] = new ArrayList<>(); + } + List indexes = tripleStore.getAllIndexes(); + char[][] seqs = new char[indexes.size()][]; + for (int i = 0; i < indexes.size(); i++) { + seqs[i] = indexes.get(i).getFieldSeq(); + } + // Build index position map + Map posMap = new IdentityHashMap<>(); + for (int i = 0; i < indexes.size(); i++) { + posMap.put(indexes.get(i), i); + } + StatementOrder[][] firstFree = new StatementOrder[indexes.size()][16]; + for (int i = 0; i < indexes.size(); i++) { + char[] seq = seqs[i]; + for (int mask = 0; mask < 16; mask++) { + StatementOrder first = null; + for (char f : seq) { + if ((mask & bitFor(f)) == 0) { + first = orderFor(f); + break; + } + } + firstFree[i][mask] = first; + } + } + for (int mask = 0; mask < 16; mask++) { + EnumSet set = table[mask]; + boolean anyCompatible = false; + for (int i = 0; i < indexes.size(); i++) { + char[] seq = seqs[i]; + if (!isIndexCompatible(seq, mask)) { + continue; + } + anyCompatible = true; + compatibleIndexesByMask[mask].add(indexes.get(i)); + // add bound dimensions (trivial order) + if ((mask & 1) != 0) + set.add(StatementOrder.S); + if ((mask & (1 << 1)) != 0) + set.add(StatementOrder.P); + if ((mask & (1 << 2)) != 0) + set.add(StatementOrder.O); + if ((mask & (1 << 3)) != 0) + set.add(StatementOrder.C); + // add first free variable for this index & mask if present + StatementOrder first = firstFree[i][mask]; + if (first != null) { + set.add(first); + } + } + if (!anyCompatible) { + set.clear(); + } + } + // publish + this.firstFreeOrderByIndexAndMask = firstFree; + this.indexPositionMap = posMap; + } + @Override public ValueFactory getValueFactory() { return valueStore; @@ -234,6 +393,9 @@ void rollback() throws SailException { throw e instanceof SailException ? (SailException) e : new SailException(e); } finally { tripleStoreException = null; + // Reset transaction-started flag after rollback so subsequent reads don't + // assume pending uncommitted changes and disable LMDB ID join optimizations. + storeTxnStarted.set(false); sinkStoreAccessLock.unlock(); } } @@ -382,10 +544,35 @@ CloseableIteration createStatementIterator( } } - List contextIDList = new ArrayList<>(contexts.length); + List contextIDList; if (contexts.length == 0) { - contextIDList.add(LmdbValue.UNKNOWN_ID); + RecordIterator records = tripleStore.getTriples(txn, subjID, predID, objID, LmdbValue.UNKNOWN_ID, explicit); + boolean sBound = subj != null; + boolean pBound = pred != null; + boolean oBound = obj != null; + Resource cachedS = null; + IRI cachedP = null; + Value cachedO = null; + if (sBound && subj instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) subj).getValueStoreRevision())) { + cachedS = subj; + } + if (pBound && pred instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) pred).getValueStoreRevision())) { + cachedP = pred; + } + if (oBound && obj instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) obj).getValueStoreRevision())) { + cachedO = obj; + } + LmdbStatementIterator.StatementCreator creator = new LmdbStatementIterator.StatementCreator(valueStore, + cachedS, cachedP, cachedO, null, sBound, pBound, oBound, false); + return new LmdbStatementIterator(records, creator); } else { + contextIDList = new ArrayList<>(contexts.length); for (Resource context : contexts) { if (context == null) { contextIDList.add(0L); @@ -403,7 +590,40 @@ CloseableIteration createStatementIterator( for (long contextID : contextIDList) { RecordIterator records = tripleStore.getTriples(txn, subjID, predID, objID, contextID, explicit); - perContextIterList.add(new LmdbStatementIterator(records, valueStore)); + boolean sBound = subj != null; + boolean pBound = pred != null; + boolean oBound = obj != null; + Resource cachedS = null; + IRI cachedP = null; + Value cachedO = null; + Resource cachedC = null; + if (sBound && subj instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) subj).getValueStoreRevision())) { + cachedS = subj; + } + if (pBound && pred instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) pred).getValueStoreRevision())) { + cachedP = pred; + } + if (oBound && obj instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) obj).getValueStoreRevision())) { + cachedO = obj; + } + // If exactly one context was provided and is revision-compatible LmdbValue, pass it + if (contexts.length == 1) { + Resource ctx = contexts[0]; + if (ctx != null && !ctx.isTriple() && ctx instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) ctx).getValueStoreRevision())) { + cachedC = ctx; + } + } + LmdbStatementIterator.StatementCreator creator = new LmdbStatementIterator.StatementCreator(valueStore, + cachedS, cachedP, cachedO, cachedC, sBound, pBound, oBound, true); + perContextIterList.add(new LmdbStatementIterator(records, creator)); } if (perContextIterList.size() == 1) { @@ -433,7 +653,7 @@ public SailSink sink(IsolationLevel level) throws SailException { @Override public LmdbSailDataset dataset(IsolationLevel level) throws SailException { - return new LmdbSailDataset(explicit); + return new LmdbSailDataset(explicit, level); } } @@ -894,15 +1114,17 @@ public boolean supportsDeprecateByQuery() { } } - private final class LmdbSailDataset implements SailDataset { - + private final class LmdbSailDataset implements SailDataset, LmdbEvaluationDataset { private final boolean explicit; + private final IsolationLevel isolationLevel; private final Txn txn; - public LmdbSailDataset(boolean explicit) throws SailException { + public LmdbSailDataset(boolean explicit, IsolationLevel isolationLevel) throws SailException { this.explicit = explicit; + this.isolationLevel = isolationLevel; try { this.txn = tripleStore.getTxnManager().createReadTxn(); + LmdbEvaluationStrategy.setCurrentDataset(this); } catch (IOException e) { throw new SailException(e); } @@ -910,8 +1132,12 @@ public LmdbSailDataset(boolean explicit) throws SailException { @Override public void close() { - // close the associated txn - txn.close(); + try { + // close the associated txn + txn.close(); + } finally { + LmdbEvaluationStrategy.clearCurrentDataset(); + } } @Override @@ -953,17 +1179,965 @@ public CloseableIteration getStatements(Resource subj, IRI @Override public CloseableIteration getStatements(StatementOrder statementOrder, Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException { - throw new UnsupportedOperationException("Not implemented yet"); + try { + // Fast reject: inferred-only dataset but store has no inferred + if (!explicit && !mayHaveInferred) { + return CloseableIteration.EMPTY_STATEMENT_ITERATION; + } + + // Resolve value ids + long subjID = LmdbValue.UNKNOWN_ID; + if (subj != null) { + subjID = valueStore.getId(subj); + if (subjID == LmdbValue.UNKNOWN_ID) { + return CloseableIteration.EMPTY_STATEMENT_ITERATION; + } + } + + long predID = LmdbValue.UNKNOWN_ID; + if (pred != null) { + predID = valueStore.getId(pred); + if (predID == LmdbValue.UNKNOWN_ID) { + return CloseableIteration.EMPTY_STATEMENT_ITERATION; + } + } + + long objID = LmdbValue.UNKNOWN_ID; + if (obj != null) { + objID = valueStore.getId(obj); + if (objID == LmdbValue.UNKNOWN_ID) { + return CloseableIteration.EMPTY_STATEMENT_ITERATION; + } + } + + // Context handling: if more than one context is requested, we cannot efficiently guarantee a global + // order + // without a k-way merge. In that case, fall back to default behavior (unordered union). + if (contexts != null && contexts.length > 1) { + throw new IllegalArgumentException( + "LMDB SailStore does not support ordered scans over multiple contexts"); + } + + long contextID; + if (contexts == null || contexts.length == 0) { + contextID = LmdbValue.UNKNOWN_ID; // wildcard over all contexts + } else { + Resource ctx = contexts[0]; + if (ctx == null) { + contextID = 0L; // default graph + } else if (!ctx.isTriple()) { + contextID = valueStore.getId(ctx); + if (contextID == LmdbValue.UNKNOWN_ID) { + return CloseableIteration.EMPTY_STATEMENT_ITERATION; + } + } else { + // RDF* triple as context not supported by LMDB index order; fall back to default behavior + return createStatementIterator(txn, subj, pred, obj, explicit, contexts); + } + } + + // Pick an index that can provide the requested order given current bindings + TripleStore.TripleIndex chosen = chooseIndexForOrder(statementOrder, subjID, predID, objID, contextID); + if (chosen == null) { + // No compatible index for ordered scan; fall back to default iterator + return createStatementIterator(txn, subj, pred, obj, explicit, contexts); + } + + boolean rangeSearch = chosen.getPatternScore(subjID, predID, objID, contextID) > 0; + TripleStore.KeyBuilder keyBuilder = rangeSearch + ? chosen.keyBuilder(subjID, predID, objID, contextID) + : null; + RecordIterator records = keyBuilder != null + ? new LmdbRecordIterator(chosen, keyBuilder, rangeSearch, subjID, predID, objID, contextID, + explicit, txn) + : new LmdbRecordIterator(chosen, rangeSearch, subjID, predID, objID, contextID, explicit, txn); + + boolean sBound = subj != null; + boolean pBound = pred != null; + boolean oBound = obj != null; + boolean cBound; + // exactly one context allowed at this point + cBound = contexts != null && contexts.length != 0; + + Resource cachedS = null; + IRI cachedP = null; + Value cachedO = null; + Resource cachedC = null; + + if (sBound && subj instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) subj).getValueStoreRevision())) { + cachedS = subj; + } + if (pBound && pred instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) pred).getValueStoreRevision())) { + cachedP = pred; + } + if (oBound && obj instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) obj).getValueStoreRevision())) { + cachedO = obj; + } + + if (cBound) { + Resource ctx = contexts[0]; + if (ctx != null && !ctx.isTriple() + && ctx instanceof LmdbValue + && valueStore.getRevision() + .equals(((LmdbValue) ctx) + .getValueStoreRevision())) { + cachedC = ctx; + } + } + + LmdbStatementIterator.StatementCreator creator = new LmdbStatementIterator.StatementCreator(valueStore, + cachedS, cachedP, cachedO, cachedC, sBound, pBound, oBound, cBound); + return new LmdbStatementIterator(records, creator); + } catch (IOException e) { + throw new SailException("Unable to get ordered statements", e); + } } @Override public Set getSupportedOrders(Resource subj, IRI pred, Value obj, Resource... contexts) { - return Set.of(); + // If multiple contexts are specified, LMDB currently returns a union without a global ordering guarantee + if (contexts != null && contexts.length > 1) { + return Set.of(); + } + + boolean sBound = subj != null; + boolean pBound = pred != null; + boolean oBound = obj != null; + boolean cBound = false; + if (contexts != null && contexts.length == 1) { + Resource ctx = contexts[0]; + if (ctx == null) { + cBound = true; + } else if (!ctx.isTriple()) { + cBound = true; + } else { + // triple context not supported for ordered scans + return Set.of(); + } + } + + int mask = (sBound ? 1 : 0) | (pBound ? (1 << 1) : 0) | (oBound ? (1 << 2) : 0) | (cBound ? (1 << 3) : 0); + EnumSet res = getSupportedOrdersLookup()[mask]; + return res.isEmpty() ? Set.of() : EnumSet.copyOf(res); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings) + throws QueryEvaluationException { + return getRecordIterator(pattern, bindings, null); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, + KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + return getRecordIterator(pattern, bindings, keyBuffers, null); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, + KeyRangeBuffers keyBuffers, + RecordIterator iteratorReuse) throws QueryEvaluationException { + try { + PatternArrays arrays = describePattern(pattern); + if (!arrays.valid) { + return emptyRecordIterator(); + } + long subjID = resolveIdWithBindings(arrays.ids[TripleStore.SUBJ_IDX], + arrays.varNames[TripleStore.SUBJ_IDX], + bindings, true, false); + if (subjID == INVALID_ID) { + return emptyRecordIterator(); + } + + long predID = resolveIdWithBindings(arrays.ids[TripleStore.PRED_IDX], + arrays.varNames[TripleStore.PRED_IDX], + bindings, false, true); + if (predID == INVALID_ID) { + return emptyRecordIterator(); + } + + long objID = resolveIdWithBindings(arrays.ids[TripleStore.OBJ_IDX], + arrays.varNames[TripleStore.OBJ_IDX], + bindings, false, false); + if (objID == INVALID_ID) { + return emptyRecordIterator(); + } + + long contextID = resolveContextWithBindings(arrays.ids[TripleStore.CONTEXT_IDX], + arrays.varNames[TripleStore.CONTEXT_IDX], bindings); + if (contextID == INVALID_ID) { + return emptyRecordIterator(); + } + + ByteBuffer minKeyBuf = keyBuffers != null ? keyBuffers.minKey() : null; + ByteBuffer maxKeyBuf = keyBuffers != null ? keyBuffers.maxKey() : null; + RecordIterator reuse = (iteratorReuse instanceof LmdbRecordIterator + || iteratorReuse instanceof LmdbDupRecordIterator) ? iteratorReuse : null; + return tripleStore.getTriples(txn, subjID, predID, objID, contextID, explicit, minKeyBuf, maxKeyBuf, + null, reuse); + } catch (IOException e) { + throw new QueryEvaluationException("Unable to create LMDB record iterator", e); + } + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, + long[] patternIds) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, null, null, + null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, + long[] patternIds, long[] reuse) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, reuse, null, + null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, + long[] patternIds, long[] reuse, long[] quadReuse) throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, reuse, + quadReuse, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, reuse, + quadReuse, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, + long[] patternIds, KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse, + RecordIterator iteratorReuse) + throws QueryEvaluationException { + try { + long subjQuery = selectQueryId(patternIds[TripleStore.SUBJ_IDX], binding, subjIndex); + long predQuery = selectQueryId(patternIds[TripleStore.PRED_IDX], binding, predIndex); + long objQuery = selectQueryId(patternIds[TripleStore.OBJ_IDX], binding, objIndex); + long ctxQuery = selectQueryId(patternIds[TripleStore.CONTEXT_IDX], binding, ctxIndex); + + ByteBuffer minKeyBuf = keyBuffers != null ? keyBuffers.minKey() : null; + ByteBuffer maxKeyBuf = keyBuffers != null ? keyBuffers.maxKey() : null; + + BindingProjectingIterator projectingReuse = iteratorReuse instanceof BindingProjectingIterator + ? (BindingProjectingIterator) iteratorReuse + : null; + RecordIterator baseReuse = projectingReuse != null ? projectingReuse.getReusableBase() + : (iteratorReuse instanceof LmdbRecordIterator || iteratorReuse instanceof LmdbDupRecordIterator + ? iteratorReuse + : null); + + RecordIterator raw = tripleStore.getTriples(txn, subjQuery, predQuery, objQuery, ctxQuery, explicit, + minKeyBuf, maxKeyBuf, quadReuse, baseReuse); + + if (!SCRATCH_REUSE_ENABLED) { + RecordIterator baseIterator = raw; + return new RecordIterator() { + @Override + public long[] next() throws QueryEvaluationException { + try { + long[] quad; + while ((quad = baseIterator.next()) != null) { + long[] merged = mergeBinding(binding, quad[TripleStore.SUBJ_IDX], + quad[TripleStore.PRED_IDX], quad[TripleStore.OBJ_IDX], + quad[TripleStore.CONTEXT_IDX], subjIndex, predIndex, objIndex, ctxIndex); + if (merged != null) { + return merged; + } + } + return null; + } catch (QueryEvaluationException e) { + throw e; + } catch (Exception e) { + throw new QueryEvaluationException(e); + } + } + + @Override + public void close() { + baseIterator.close(); + } + }; + } + + BindingProjectingIterator result = projectingReuse != null ? projectingReuse + : new BindingProjectingIterator(); + RecordIterator reusableBase = (raw instanceof LmdbRecordIterator + || raw instanceof LmdbDupRecordIterator) + ? raw + : null; + result.configure(raw, reusableBase, binding, subjIndex, predIndex, objIndex, ctxIndex, reuse); + return result; + } catch (IOException e) { + throw new QueryEvaluationException("Unable to create LMDB record iterator", e); + } + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + null, null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + reuse, null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + reuse, quadReuse); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, KeyRangeBuffers keyBuffers, long[] bindingReuse, + long[] quadReuse) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, keyBuffers, + bindingReuse, quadReuse); + } + try { + long subjQuery = selectQueryId(patternIds[TripleStore.SUBJ_IDX], binding, subjIndex); + long predQuery = selectQueryId(patternIds[TripleStore.PRED_IDX], binding, predIndex); + long objQuery = selectQueryId(patternIds[TripleStore.OBJ_IDX], binding, objIndex); + long ctxQuery = selectQueryId(patternIds[TripleStore.CONTEXT_IDX], binding, ctxIndex); + + RecordIterator orderedIter = orderedRecordIterator(order, subjQuery, predQuery, objQuery, ctxQuery, + quadReuse); + if (orderedIter != null) { + return orderedIter; + } + + if (order == StatementOrder.S) { + int sortIndex = indexForOrder(order, subjIndex, predIndex, objIndex, ctxIndex); + if (sortIndex >= 0) { + RecordIterator fallback = getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, keyBuffers, bindingReuse, quadReuse); + return sortedRecordIterator(fallback, sortIndex); + } + } + return null; + } catch (IOException e) { + throw new QueryEvaluationException("Unable to create ordered LMDB record iterator", e); + } + } + + @Override + public boolean supportsOrder(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, StatementOrder order) { + if (order == null) { + return true; + } + long subjQuery = selectQueryId(patternIds[TripleStore.SUBJ_IDX], binding, subjIndex); + long predQuery = selectQueryId(patternIds[TripleStore.PRED_IDX], binding, predIndex); + long objQuery = selectQueryId(patternIds[TripleStore.OBJ_IDX], binding, objIndex); + long ctxQuery = selectQueryId(patternIds[TripleStore.CONTEXT_IDX], binding, ctxIndex); + try { + try (RecordIterator recordIterator = orderedRecordIterator(order, subjQuery, predQuery, objQuery, + ctxQuery)) { + if (recordIterator != null) { + return true; + } + } + return order == StatementOrder.S && indexForOrder(order, subjIndex, predIndex, objIndex, ctxIndex) >= 0; + } catch (IOException e) { + return false; + } + } + + @Override + public RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, + StatementOrder order) throws QueryEvaluationException { + return getOrderedRecordIterator(pattern, bindings, order, null); + } + + @Override + public RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, + StatementOrder order, KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + if (order == null) { + return getRecordIterator(pattern, bindings, keyBuffers); + } + try { + Value subj = resolveValue(pattern.getSubjectVar(), bindings); + if (subj != null && !(subj instanceof Resource)) { + return emptyRecordIterator(); + } + Value pred = resolveValue(pattern.getPredicateVar(), bindings); + if (pred != null && !(pred instanceof IRI)) { + return emptyRecordIterator(); + } + Value obj = resolveValue(pattern.getObjectVar(), bindings); + + long subjID = resolveId(subj); + if (subj != null && subjID == LmdbValue.UNKNOWN_ID) { + return emptyRecordIterator(); + } + + long predID = resolveId(pred); + if (pred != null && predID == LmdbValue.UNKNOWN_ID) { + return emptyRecordIterator(); + } + + long objID = resolveId(obj); + if (obj != null && objID == LmdbValue.UNKNOWN_ID) { + return emptyRecordIterator(); + } + + Value contextValue = resolveValue(pattern.getContextVar(), bindings); + long contextID; + if (contextValue == null) { + contextID = LmdbValue.UNKNOWN_ID; + } else if (contextValue instanceof Resource) { + Resource ctx = (Resource) contextValue; + if (ctx.isTriple()) { + return emptyRecordIterator(); + } + contextID = resolveId(ctx); + if (contextID == LmdbValue.UNKNOWN_ID) { + return emptyRecordIterator(); + } + } else { + return emptyRecordIterator(); + } + + RecordIterator orderedIter = orderedRecordIterator(order, subjID, predID, objID, contextID); + if (orderedIter != null) { + return orderedIter; + } + return getRecordIterator(pattern, bindings, keyBuffers); + } catch (IOException e) { + throw new QueryEvaluationException("Unable to create ordered LMDB record iterator", e); + } + } + + @Override + public ValueStore getValueStore() { + return valueStore; + } + + @Override + public String selectBestIndex(long subj, long pred, long obj, long context) { + TripleStore.TripleIndex index = tripleStore.getBestIndex(subj, pred, obj, context); + return index == null ? null : new String(index.getFieldSeq()); + } + + @Override + public IsolationLevel getIsolationLevel() { + return isolationLevel; + } + + @Override + public void refreshSnapshot() throws QueryEvaluationException { + if (isolationLevel == IsolationLevels.SNAPSHOT || isolationLevel == IsolationLevels.SERIALIZABLE) { + try { + txn.reset(); + } catch (IOException e) { + throw new QueryEvaluationException("Unable to refresh LMDB read transaction", e); + } + } + } + + @Override + public boolean hasTransactionChanges() { + // storeTxnStarted is flipped to true when a writer begins and only cleared + // after commit/rollback, so a true value indicates pending uncommitted changes. + return storeTxnStarted.get(); + } + + private RecordIterator emptyRecordIterator() { + return EMPTY_RECORD_ITERATOR; + } + + private Value resolveValue(Var var, BindingSet bindings) { + if (var == null) { + return null; + } + if (var.hasValue()) { + return var.getValue(); + } + if (bindings != null) { + Value bound = bindings.getValue(var.getName()); + return bound; + } + return null; + } + + private static final long INVALID_ID = Long.MIN_VALUE; + + private PatternArrays describePattern(StatementPattern pattern) throws IOException { + long[] ids = new long[4]; + String[] varNames = new String[4]; + boolean valid = true; + + valid &= populateSubject(pattern.getSubjectVar(), ids, varNames); + valid &= populatePredicate(pattern.getPredicateVar(), ids, varNames); + valid &= populateObject(pattern.getObjectVar(), ids, varNames); + valid &= populateContext(pattern.getContextVar(), ids, varNames); + + return new PatternArrays(ids, varNames, valid); + } + + private boolean populateSubject(Var var, long[] ids, String[] varNames) + throws IOException { + if (var == null) { + ids[TripleStore.SUBJ_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.SUBJ_IDX] = null; + return true; + } + if (var.hasValue()) { + Value value = var.getValue(); + if (!(value instanceof Resource)) { + return false; + } + long id = resolveId(value); + if (id == LmdbValue.UNKNOWN_ID) { + return false; + } + ids[TripleStore.SUBJ_IDX] = id; + varNames[TripleStore.SUBJ_IDX] = null; + return true; + } + ids[TripleStore.SUBJ_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.SUBJ_IDX] = var.getName(); + return true; + } + + private boolean populatePredicate(Var var, long[] ids, String[] varNames) + throws IOException { + if (var == null) { + ids[TripleStore.PRED_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.PRED_IDX] = null; + return true; + } + if (var.hasValue()) { + Value value = var.getValue(); + if (!(value instanceof IRI)) { + return false; + } + long id = resolveId(value); + if (id == LmdbValue.UNKNOWN_ID) { + return false; + } + ids[TripleStore.PRED_IDX] = id; + varNames[TripleStore.PRED_IDX] = null; + return true; + } + ids[TripleStore.PRED_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.PRED_IDX] = var.getName(); + return true; + } + + private boolean populateObject(Var var, long[] ids, String[] varNames) + throws IOException { + if (var == null) { + ids[TripleStore.OBJ_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.OBJ_IDX] = null; + return true; + } + if (var.hasValue()) { + Value value = var.getValue(); + long id = resolveId(value); + if (id == LmdbValue.UNKNOWN_ID) { + return false; + } + ids[TripleStore.OBJ_IDX] = id; + varNames[TripleStore.OBJ_IDX] = null; + return true; + } + ids[TripleStore.OBJ_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.OBJ_IDX] = var.getName(); + return true; + } + + private boolean populateContext(Var var, long[] ids, String[] varNames) + throws IOException { + if (var == null) { + ids[TripleStore.CONTEXT_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.CONTEXT_IDX] = null; + return true; + } + if (var.hasValue()) { + Value value = var.getValue(); + if (!(value instanceof Resource)) { + return false; + } + Resource ctx = (Resource) value; + if (ctx.isTriple()) { + return false; + } + long id = resolveId(ctx); + if (id == LmdbValue.UNKNOWN_ID) { + return false; + } + ids[TripleStore.CONTEXT_IDX] = id; + varNames[TripleStore.CONTEXT_IDX] = null; + return true; + } + ids[TripleStore.CONTEXT_IDX] = LmdbValue.UNKNOWN_ID; + varNames[TripleStore.CONTEXT_IDX] = var.getName(); + return true; + } + + private long selectQueryId(long patternId, long[] binding, int index) { + if (patternId != LmdbValue.UNKNOWN_ID) { + return patternId; + } + if (index >= 0 && index < binding.length) { + long bound = binding[index]; + return bound; + } + return LmdbValue.UNKNOWN_ID; + } + + private long[] mergeBinding(long[] binding, long subjId, long predId, long objId, long ctxId, int subjIndex, + int predIndex, int objIndex, int ctxIndex) { + long[] out = Arrays.copyOf(binding, binding.length); + if (!applyValue(out, subjIndex, subjId)) { + return null; + } + if (!applyValue(out, predIndex, predId)) { + return null; + } + if (!applyValue(out, objIndex, objId)) { + return null; + } + if (!applyValue(out, ctxIndex, ctxId)) { + return null; + } + return out; + } + + private boolean applyValue(long[] target, int index, long value) { + if (index < 0) { + return true; + } + long existing = target[index]; + if (existing != LmdbValue.UNKNOWN_ID && existing != value) { + return false; + } + target[index] = value; + return true; + } + + private long resolveIdWithBindings(long patternId, String varName, BindingSet bindings, boolean requireResource, + boolean requireIri) throws IOException { + if (patternId == INVALID_ID) { + return INVALID_ID; + } + if (varName == null) { + return patternId; + } + if (bindings == null) { + return LmdbValue.UNKNOWN_ID; + } + Value bound = bindings.getValue(varName); + if (bound == null) { + return LmdbValue.UNKNOWN_ID; + } + if (requireResource && !(bound.isResource())) { + return INVALID_ID; + } + if (requireIri && !(bound.isIRI())) { + return INVALID_ID; + } + if (bound.isTriple()) { + return INVALID_ID; + } + long id = resolveId(bound); + if (id == LmdbValue.UNKNOWN_ID) { + return INVALID_ID; + } + return id; + } + + private long resolveContextWithBindings(long patternId, String varName, BindingSet bindings) + throws IOException { + if (patternId == INVALID_ID) { + return INVALID_ID; + } + if (varName == null) { + return patternId; + } + if (bindings == null) { + return LmdbValue.UNKNOWN_ID; + } + Value bound = bindings.getValue(varName); + if (bound == null) { + return LmdbValue.UNKNOWN_ID; + } + if (!(bound instanceof Resource)) { + return INVALID_ID; + } + Resource ctx = (Resource) bound; + if (ctx.isTriple()) { + return INVALID_ID; + } + long id = resolveId(ctx); + if (id == LmdbValue.UNKNOWN_ID) { + return INVALID_ID; + } + return id; + } + + private final class PatternArrays { + private final long[] ids; + private final String[] varNames; + private final boolean valid; + + private PatternArrays(long[] ids, String[] varNames, boolean valid) { + this.ids = ids; + this.varNames = varNames; + this.valid = valid; + } + } + + private long resolveId(Value value) throws IOException { + if (value == null) { + return LmdbValue.UNKNOWN_ID; + } + if (value instanceof LmdbValue) { + LmdbValue lmdbValue = (LmdbValue) value; + if (valueStore.getRevision().equals(lmdbValue.getValueStoreRevision())) { + return lmdbValue.getInternalID(); + } + } + long id = valueStore.getId(value); + return id; + } + + private RecordIterator orderedRecordIterator(StatementOrder order, long subjID, long predID, long objID, + long contextID) throws IOException { + return orderedRecordIterator(order, subjID, predID, objID, contextID, null); + } + + private RecordIterator orderedRecordIterator(StatementOrder order, long subjID, long predID, long objID, + long contextID, long[] quadReuse) throws IOException { + if (order == null) { + return null; + } + TripleStore.TripleIndex chosen = chooseIndexForOrder(order, subjID, predID, objID, contextID); + if (chosen == null) { + return null; + } + boolean rangeSearch = chosen.getPatternScore(subjID, predID, objID, contextID) > 0; + return new LmdbRecordIterator(chosen, rangeSearch, subjID, predID, objID, contextID, explicit, txn, + quadReuse); + } + + private int indexForOrder(StatementOrder order, int subjIndex, int predIndex, int objIndex, int ctxIndex) { + switch (order) { + case S: + return subjIndex; + case P: + return predIndex; + case O: + return objIndex; + case C: + return ctxIndex; + default: + return -1; + } + } + + private RecordIterator sortedRecordIterator(RecordIterator base, int sortIndex) + throws QueryEvaluationException { + List rows = new ArrayList<>(); + try (base) { + long[] next; + while ((next = base.next()) != null) { + // Ensure buffered rows are immutable snapshots regardless of iterator reuse + rows.add(next.clone()); + } + } + rows.sort(Comparator.comparingLong(a -> a[sortIndex])); + + Iterator iterator = rows.iterator(); + return new RecordIterator() { + @Override + public long[] next() { + if (!iterator.hasNext()) { + return null; + } + return iterator.next(); + } + + @Override + public void close() { + // nothing to close + } + }; + } + + private TripleStore.TripleIndex chooseIndexForOrder(StatementOrder order, long s, long p, long o, long c) + throws IOException { + // ensure metadata initialized + getSupportedOrdersLookup(); + boolean sBound = s >= 0; + boolean pBound = p >= 0; + boolean oBound = o >= 0; + boolean cBound = c >= 0; // 0 is a concrete (null) context; unknown is -1 + + int mask = (sBound ? 1 : 0) | (pBound ? (1 << 1) : 0) | (oBound ? (1 << 2) : 0) | (cBound ? (1 << 3) : 0); + List compat = compatibleIndexesByMask[mask]; + if (compat == null || compat.isEmpty()) { + return null; + } + // If requested order var is bound, any compatible index will do + if ((order == StatementOrder.S && sBound) || (order == StatementOrder.P && pBound) + || (order == StatementOrder.O && oBound) || (order == StatementOrder.C && cBound)) { + return compat.get(0); + } + // Else pick one whose first free variable matches + for (TripleStore.TripleIndex index : compat) { + Integer pos = indexPositionMap.get(index); + if (pos != null) { + StatementOrder first = firstFreeOrderByIndexAndMask[pos][mask]; + if (first == order) { + return index; + } + } + } + return null; } @Override public Comparator getComparator() { + return (v1, v2) -> { + try { + long id1 = valueStore.getId(v1); + long id2 = valueStore.getId(v2); + if (id1 != LmdbValue.UNKNOWN_ID && id2 != LmdbValue.UNKNOWN_ID) { + return Long.compare(id1, id2); + } + } catch (IOException ignore) { + // fall through to lexical comparator + } + // Fallback to standard SPARQL value comparator when IDs are not available + return new ValueComparator().compare(v1, v2); + }; + } + } +} + +final class BindingProjectingIterator implements RecordIterator { + private RecordIterator base; + private RecordIterator reusableBase; + private long[] binding; + private int subjIndex; + private int predIndex; + private int objIndex; + private int ctxIndex; + private long[] scratch; + + void configure(RecordIterator base, RecordIterator reusableBase, long[] binding, int subjIndex, + int predIndex, int objIndex, + int ctxIndex, long[] bindingReuse) { + this.base = base; + this.reusableBase = reusableBase; + this.binding = binding; + this.subjIndex = subjIndex; + this.predIndex = predIndex; + this.objIndex = objIndex; + this.ctxIndex = ctxIndex; + int bindingLength = binding.length; + if (bindingReuse != null && bindingReuse.length >= bindingLength) { + System.arraycopy(binding, 0, bindingReuse, 0, bindingLength); + this.scratch = bindingReuse; + } else if (this.scratch == null || this.scratch.length != bindingLength) { + this.scratch = Arrays.copyOf(binding, bindingLength); + } else { + System.arraycopy(binding, 0, this.scratch, 0, bindingLength); + } + } + + RecordIterator getReusableBase() { + return reusableBase; + } + + @Override + public long[] next() throws QueryEvaluationException { + if (base == null) { return null; } + try { + long[] quad; + while ((quad = base.next()) != null) { + System.arraycopy(binding, 0, scratch, 0, binding.length); + boolean conflict = false; + if (subjIndex >= 0) { + long baseVal = binding[subjIndex]; + long v = quad[TripleStore.SUBJ_IDX]; + if (baseVal != LmdbValue.UNKNOWN_ID && baseVal != v) { + conflict = true; + } else { + scratch[subjIndex] = (baseVal != LmdbValue.UNKNOWN_ID) ? baseVal : v; + } + } + if (!conflict && predIndex >= 0) { + long baseVal = binding[predIndex]; + long v = quad[TripleStore.PRED_IDX]; + if (baseVal != LmdbValue.UNKNOWN_ID && baseVal != v) { + conflict = true; + } else { + scratch[predIndex] = (baseVal != LmdbValue.UNKNOWN_ID) ? baseVal : v; + } + } + if (!conflict && objIndex >= 0) { + long baseVal = binding[objIndex]; + long v = quad[TripleStore.OBJ_IDX]; + if (baseVal != LmdbValue.UNKNOWN_ID && baseVal != v) { + conflict = true; + } else { + scratch[objIndex] = (baseVal != LmdbValue.UNKNOWN_ID) ? baseVal : v; + } + } + if (!conflict && ctxIndex >= 0) { + long baseVal = binding[ctxIndex]; + long v = quad[TripleStore.CONTEXT_IDX]; + if (baseVal != LmdbValue.UNKNOWN_ID && baseVal != v) { + conflict = true; + } else { + scratch[ctxIndex] = (baseVal != LmdbValue.UNKNOWN_ID) ? baseVal : v; + } + } + if (!conflict) { + return scratch; + } + } + return null; + } catch (QueryEvaluationException e) { + throw e; + } catch (Exception e) { + throw new QueryEvaluationException(e); + } + } + + @Override + public void close() { + if (base != null) { + base.close(); + } } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java index e4b6429afa8..43b2f472cfd 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java @@ -13,7 +13,7 @@ import java.io.IOException; import java.util.NoSuchElementException; -import org.eclipse.rdf4j.common.iteration.AbstractCloseableIteration; +import org.eclipse.rdf4j.common.iteration.CloseableIteration; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; @@ -24,7 +24,7 @@ * A statement iterator that wraps a RecordIterator containing statement records and translates these records to * {@link Statement} objects. */ -class LmdbStatementIterator extends AbstractCloseableIteration { +class LmdbStatementIterator implements CloseableIteration { /*-----------* * Variables * @@ -33,18 +33,35 @@ class LmdbStatementIterator extends AbstractCloseableIteration { private final RecordIterator recordIt; private final ValueStore valueStore; + private final StatementCreator statementCreator; private Statement nextElement; + /** + * Flag indicating whether this iteration has been closed. + */ + private boolean closed = false; /*--------------* * Constructors * *--------------*/ /** - * Creates a new LmdbStatementIterator. + * Creates a new LmdbStatementIterator with the default statement creation logic. */ public LmdbStatementIterator(RecordIterator recordIt, ValueStore valueStore) { this.recordIt = recordIt; this.valueStore = valueStore; + this.statementCreator = new StatementCreator(valueStore, false, false, false, false); + } + + /** + * Creates a new LmdbStatementIterator using a custom statement creator. The provided {@link StatementCreator} is + * responsible for creating the Values and Statement for each record and can cache bound values so the iterator + * always returns LmdbValue-backed instances. + */ + public LmdbStatementIterator(RecordIterator recordIt, StatementCreator statementCreator) { + this.recordIt = recordIt; + this.valueStore = null; + this.statementCreator = statementCreator; } /*---------* @@ -53,33 +70,23 @@ public LmdbStatementIterator(RecordIterator recordIt, ValueStore valueStore) { public Statement getNextElement() throws SailException { try { + + if (Thread.currentThread().isInterrupted()) { + close(); + throw new SailException("The iteration has been interrupted"); + } + long[] quad = recordIt.next(); if (quad == null) { return null; } - long subjID = quad[TripleStore.SUBJ_IDX]; - Resource subj = (Resource) valueStore.getLazyValue(subjID); - - long predID = quad[TripleStore.PRED_IDX]; - IRI pred = (IRI) valueStore.getLazyValue(predID); - - long objID = quad[TripleStore.OBJ_IDX]; - Value obj = valueStore.getLazyValue(objID); - - Resource context = null; - long contextID = quad[TripleStore.CONTEXT_IDX]; - if (contextID != 0) { - context = (Resource) valueStore.getLazyValue(contextID); - } - - return valueStore.createStatement(subj, pred, obj, context); + return statementCreator.create(quad); } catch (IOException e) { throw causeIOException(e); } } - @Override protected void handleClose() throws SailException { recordIt.close(); } @@ -135,4 +142,111 @@ private Statement lookAhead() { public void remove() { throw new UnsupportedOperationException(); } + + /** + * Checks whether this CloseableIteration has been closed. + * + * @return true if the CloseableIteration has been closed, false otherwise. + */ + public final boolean isClosed() { + return closed; + } + + /** + * Calls {@link #handleClose()} upon first call and makes sure the resource closures are only executed once. + */ + @Override + public final void close() { + if (!closed) { + closed = true; + handleClose(); + } + } + + /** + * Statement creator that can cache bound values (S, P, O, and/or C) the first time they are seen via + * {@link ValueStore#getLazyValue(long)} and reuse them for subsequent records. This ensures bound values are always + * returned as LmdbValue-backed instances. + */ + static final class StatementCreator { + private final ValueStore valueStore; + private final boolean sBound; + private final boolean pBound; + private final boolean oBound; + private final boolean cBound; + + private Resource cachedS; + private IRI cachedP; + private Value cachedO; + private Resource cachedC; + + StatementCreator(ValueStore valueStore, boolean sBound, boolean pBound, boolean oBound, boolean cBound) { + this.valueStore = valueStore; + this.sBound = sBound; + this.pBound = pBound; + this.oBound = oBound; + this.cBound = cBound; + } + + StatementCreator(ValueStore valueStore, Resource cachedS, IRI cachedP, Value cachedO, Resource cachedC, + boolean sBound, boolean pBound, boolean oBound, boolean cBound) { + this.valueStore = valueStore; + this.cachedS = cachedS; + this.cachedP = cachedP; + this.cachedO = cachedO; + this.cachedC = cachedC; + this.sBound = sBound; + this.pBound = pBound; + this.oBound = oBound; + this.cBound = cBound; + } + + Statement create(long[] quad) throws IOException { + final ValueStore vs = this.valueStore; + + // subject + Resource s = this.cachedS; + if (sBound) { + if (s == null) { + s = this.cachedS = (Resource) vs.getLazyValue(quad[TripleStore.SUBJ_IDX]); + } + } else { + s = (Resource) vs.getLazyValue(quad[TripleStore.SUBJ_IDX]); + } + + // predicate + IRI p = this.cachedP; + if (pBound) { + if (p == null) { + p = this.cachedP = (IRI) vs.getLazyValue(quad[TripleStore.PRED_IDX]); + } + } else { + p = (IRI) vs.getLazyValue(quad[TripleStore.PRED_IDX]); + } + + // object + Value o = this.cachedO; + if (oBound) { + if (o == null) { + o = this.cachedO = vs.getLazyValue(quad[TripleStore.OBJ_IDX]); + } + } else { + o = vs.getLazyValue(quad[TripleStore.OBJ_IDX]); + } + + // context + final long contextID = quad[TripleStore.CONTEXT_IDX]; + Resource c = this.cachedC; + if (cBound) { + if (c == null) { + c = this.cachedC = (contextID != 0) ? (Resource) vs.getLazyValue(contextID) : null; + } + // if cBound and cachedC already set, ignore contextID (preserves original semantics) + } else { + c = (contextID != 0) ? (Resource) vs.getLazyValue(contextID) : null; + } + + return vs.createStatement(s, p, o, c); + } + } } 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 2dc7fd6e517..feabd5e7419 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 @@ -34,7 +34,6 @@ import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategyFactory; import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver; import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolverClient; -import org.eclipse.rdf4j.query.algebra.evaluation.impl.StrictEvaluationStrategyFactory; import org.eclipse.rdf4j.repository.sparql.federation.SPARQLServiceResolver; import org.eclipse.rdf4j.sail.InterruptedSailException; import org.eclipse.rdf4j.sail.NotifyingSailConnection; @@ -169,7 +168,7 @@ public void setDataDir(File dataDir) { */ public synchronized EvaluationStrategyFactory getEvaluationStrategyFactory() { if (evalStratFactory == null) { - evalStratFactory = new StrictEvaluationStrategyFactory(getFederatedServiceResolver()); + evalStratFactory = new LmdbEvaluationStrategyFactory(getFederatedServiceResolver()); } evalStratFactory.setQuerySolutionCacheThreshold(getIterationCacheSyncThreshold()); evalStratFactory.setTrackResultSize(isTrackResultSize()); diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnection.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnection.java index 9c0577e655a..b8e3339fc24 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnection.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnection.java @@ -48,6 +48,7 @@ public class LmdbStoreConnection extends SailSourceConnection { * The transaction lock held by this connection during transactions. */ private volatile Lock txnLock; + private volatile boolean hasPendingChanges; /*--------------* * Constructors * @@ -90,6 +91,7 @@ protected void commitInternal() throws SailException { try { super.commitInternal(); } finally { + hasPendingChanges = false; if (txnLock != null && txnLock.isActive()) { txnLock.release(); } @@ -106,6 +108,7 @@ protected void rollbackInternal() throws SailException { try { super.rollbackInternal(); } finally { + hasPendingChanges = false; if (txnLock != null && txnLock.isActive()) { txnLock.release(); } @@ -118,6 +121,7 @@ protected void rollbackInternal() throws SailException { protected void addStatementInternal(Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException { // assume the triple is not yet present in the triple store sailChangedEvent.setStatementsAdded(true); + hasPendingChanges = true; } @Override @@ -125,6 +129,7 @@ public boolean addInferredStatement(Resource subj, IRI pred, Value obj, Resource boolean ret = super.addInferredStatement(subj, pred, obj, contexts); // assume the triple is not yet present in the triple store sailChangedEvent.setStatementsAdded(true); + hasPendingChanges = true; return ret; } @@ -132,35 +137,80 @@ public boolean addInferredStatement(Resource subj, IRI pred, Value obj, Resource protected CloseableIteration evaluateInternal(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings, boolean includeInferred) throws SailException { - // ensure that all elements of the binding set are initialized (lazy values are resolved) - return new IterationWrapper( - super.evaluateInternal(tupleExpr, dataset, bindings, includeInferred)) { - @Override - public BindingSet next() throws QueryEvaluationException { - BindingSet bs = super.next(); - bs.forEach(b -> initValue(b.getValue())); - return bs; + boolean flagPushed = false; + boolean success = false; + if (hasPendingChanges) { + LmdbEvaluationStrategy.pushConnectionChangesFlag(true); + flagPushed = true; + } else { + LmdbEvaluationStrategy.pushConnectionChangesFlag(false); + flagPushed = true; + } + try { + CloseableIteration base = super.evaluateInternal(tupleExpr, dataset, bindings, + includeInferred); + success = true; + // Do not force materialization of lazy values; simply ensure we pop the thread-local flag. + return new IterationWrapper(base) { + @Override + protected void handleClose() throws QueryEvaluationException { + try { + super.handleClose(); + } finally { + LmdbEvaluationStrategy.popConnectionChangesFlag(); + } + } + }; + } catch (RuntimeException e) { + throw e; + } finally { + if (!success && flagPushed) { + LmdbEvaluationStrategy.popConnectionChangesFlag(); } - }; + } } @Override protected CloseableIteration getStatementsInternal(Resource subj, IRI pred, Value obj, boolean includeInferred, Resource... contexts) throws SailException { - return new IterationWrapper( - super.getStatementsInternal(subj, pred, obj, includeInferred, contexts)) { - @Override - public Statement next() throws SailException { - // ensure that all elements of the statement are initialized (lazy values are resolved) - Statement stmt = super.next(); - initValue(stmt.getSubject()); - initValue(stmt.getPredicate()); - initValue(stmt.getObject()); - initValue(stmt.getContext()); - return stmt; + boolean flagPushed = false; + if (hasPendingChanges) { + LmdbEvaluationStrategy.pushConnectionChangesFlag(true); + flagPushed = true; + } else { + LmdbEvaluationStrategy.pushConnectionChangesFlag(false); + flagPushed = true; + } + try { + return new IterationWrapper( + super.getStatementsInternal(subj, pred, obj, includeInferred, contexts)) { + @Override + public Statement next() throws SailException { + // ensure that all elements of the statement are initialized (lazy values are resolved) + Statement stmt = super.next(); + initValue(stmt.getSubject()); + initValue(stmt.getPredicate()); + initValue(stmt.getObject()); + initValue(stmt.getContext()); + return stmt; + } + + @Override + protected void handleClose() throws SailException { + try { + super.handleClose(); + } finally { + LmdbEvaluationStrategy.popConnectionChangesFlag(); + } + } + }; + } catch (RuntimeException e) { + if (flagPushed) { + LmdbEvaluationStrategy.popConnectionChangesFlag(); } - }; + throw e; + } } /** @@ -178,6 +228,7 @@ protected void initValue(Value value) { protected void removeStatementsInternal(Resource subj, IRI pred, Value obj, Resource... contexts) throws SailException { sailChangedEvent.setStatementsRemoved(true); + hasPendingChanges = true; } @Override @@ -185,6 +236,7 @@ public boolean removeInferredStatement(Resource subj, IRI pred, Value obj, Resou throws SailException { boolean ret = super.removeInferredStatement(subj, pred, obj, contexts); sailChangedEvent.setStatementsRemoved(true); + hasPendingChanges = true; return ret; } @@ -192,12 +244,14 @@ public boolean removeInferredStatement(Resource subj, IRI pred, Value obj, Resou protected void clearInternal(Resource... contexts) throws SailException { super.clearInternal(contexts); sailChangedEvent.setStatementsRemoved(true); + hasPendingChanges = true; } @Override public void clearInferred(Resource... contexts) throws SailException { super.clearInferred(contexts); sailChangedEvent.setStatementsRemoved(true); + hasPendingChanges = true; } @Override diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbUnionSailDataset.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbUnionSailDataset.java new file mode 100644 index 00000000000..23bbc05ee21 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbUnionSailDataset.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.util.Comparator; +import java.util.EnumSet; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.DualUnionIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Namespace; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Triple; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.base.SailDataset; + +/** + * LMDB-aware union dataset that exposes ID-level access when both sides provide it, with a conservative fallback. + */ +final class LmdbUnionSailDataset implements SailDataset, LmdbEvaluationDataset { + + private final SailDataset dataset1; + private final SailDataset dataset2; + private final ValueStore valueStore; + + LmdbUnionSailDataset(SailDataset dataset1, SailDataset dataset2, ValueStore valueStore) { + this.dataset1 = dataset1; + this.dataset2 = dataset2; + this.valueStore = valueStore; + } + + @Override + public void close() throws SailException { + try { + dataset1.close(); + } finally { + dataset2.close(); + } + } + + @Override + public CloseableIteration getNamespaces() throws SailException { + return DualUnionIteration.getWildcardInstance(dataset1.getNamespaces(), dataset2.getNamespaces()); + } + + @Override + public String getNamespace(String prefix) throws SailException { + String ns = dataset1.getNamespace(prefix); + return ns != null ? ns : dataset2.getNamespace(prefix); + } + + @Override + public CloseableIteration getContextIDs() throws SailException { + return DualUnionIteration.getWildcardInstance(dataset1.getContextIDs(), dataset2.getContextIDs()); + } + + @Override + public CloseableIteration getStatements(Resource subj, IRI pred, Value obj, + Resource... contexts) throws SailException { + return DualUnionIteration.getWildcardInstance(dataset1.getStatements(subj, pred, obj, contexts), + dataset2.getStatements(subj, pred, obj, contexts)); + } + + @Override + public CloseableIteration getTriples(Resource subj, IRI pred, Value obj) throws SailException { + return DualUnionIteration.getWildcardInstance(dataset1.getTriples(subj, pred, obj), + dataset2.getTriples(subj, pred, obj)); + } + + @Override + public CloseableIteration getStatements(StatementOrder statementOrder, Resource subj, IRI pred, + Value obj, Resource... contexts) throws SailException { + CloseableIteration it1 = dataset1.getStatements(statementOrder, subj, pred, obj, contexts); + CloseableIteration it2 = dataset2.getStatements(statementOrder, subj, pred, obj, contexts); + Comparator cmp = dataset1.getComparator(); + assert cmp != null && dataset2.getComparator() != null; + return DualUnionIteration.getWildcardInstance(statementOrder.getComparator(cmp), it1, it2); + } + + @Override + public Set getSupportedOrders(Resource subj, IRI pred, Value obj, Resource... contexts) { + Set s1 = dataset1.getSupportedOrders(subj, pred, obj, contexts); + if (s1.isEmpty()) + return Set.of(); + Set s2 = dataset2.getSupportedOrders(subj, pred, obj, contexts); + if (s2.isEmpty()) + return Set.of(); + if (s1.equals(s2)) + return s1; + EnumSet common = EnumSet.copyOf(s1); + common.retainAll(s2); + return common; + } + + @Override + public Comparator getComparator() { + Comparator c1 = dataset1.getComparator(); + Comparator c2 = dataset2.getComparator(); + if (c1 == null || c2 == null) + return null; + return c1; + } + + @Override + public RecordIterator getRecordIterator(org.eclipse.rdf4j.query.algebra.StatementPattern pattern, + org.eclipse.rdf4j.query.BindingSet bindings) throws org.eclipse.rdf4j.query.QueryEvaluationException { + boolean d1 = dataset1 instanceof LmdbEvaluationDataset; + boolean d2 = dataset2 instanceof LmdbEvaluationDataset; + if (d1 && d2) { + RecordIterator r1 = ((LmdbEvaluationDataset) dataset1).getRecordIterator(pattern, bindings); + RecordIterator r2 = ((LmdbEvaluationDataset) dataset2).getRecordIterator(pattern, bindings); + return chain(r1, r2); + } + LmdbDelegatingSailDataset a = new LmdbDelegatingSailDataset(dataset1, valueStore); + LmdbDelegatingSailDataset b = new LmdbDelegatingSailDataset(dataset2, valueStore); + return chain(a.getRecordIterator(pattern, bindings), b.getRecordIterator(pattern, bindings)); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds) throws org.eclipse.rdf4j.query.QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, null, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] reuse) throws org.eclipse.rdf4j.query.QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, reuse, null); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, int ctxIndex, + long[] patternIds, long[] reuse, long[] quadReuse) throws org.eclipse.rdf4j.query.QueryEvaluationException { + boolean d1 = dataset1 instanceof LmdbEvaluationDataset; + boolean d2 = dataset2 instanceof LmdbEvaluationDataset; + if (d1 && d2) { + RecordIterator r1 = ((LmdbEvaluationDataset) dataset1).getRecordIterator(binding, subjIndex, predIndex, + objIndex, ctxIndex, patternIds, reuse, quadReuse); + RecordIterator r2 = ((LmdbEvaluationDataset) dataset2).getRecordIterator(binding, subjIndex, predIndex, + objIndex, ctxIndex, patternIds, reuse, quadReuse); + return chain(r1, r2); + } + LmdbDelegatingSailDataset a = new LmdbDelegatingSailDataset(dataset1, valueStore); + LmdbDelegatingSailDataset b = new LmdbDelegatingSailDataset(dataset2, valueStore); + return chain(a.getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, reuse, + quadReuse), + b.getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, reuse, quadReuse)); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) + throws org.eclipse.rdf4j.query.QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, null, + null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse) + throws org.eclipse.rdf4j.query.QueryEvaluationException { + return getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, order, reuse, + null); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, long[] reuse, long[] quadReuse) + throws org.eclipse.rdf4j.query.QueryEvaluationException { + boolean d1 = dataset1 instanceof LmdbEvaluationDataset; + boolean d2 = dataset2 instanceof LmdbEvaluationDataset; + if (d1 && d2) { + RecordIterator r1 = ((LmdbEvaluationDataset) dataset1).getOrderedRecordIterator(binding, subjIndex, + predIndex, objIndex, ctxIndex, patternIds, order, reuse, quadReuse); + RecordIterator r2 = ((LmdbEvaluationDataset) dataset2).getOrderedRecordIterator(binding, subjIndex, + predIndex, objIndex, ctxIndex, patternIds, order, reuse, quadReuse); + return chain(r1, r2); + } + return LmdbEvaluationDataset.super.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, + patternIds, order, reuse, quadReuse); + } + + @Override + public ValueStore getValueStore() { + return valueStore; + } + + private RecordIterator chain(RecordIterator left, RecordIterator right) { + return new RecordIterator() { + boolean leftDone = false; + + @Override + public long[] next() throws org.eclipse.rdf4j.query.QueryEvaluationException { + if (!leftDone) { + long[] n = left.next(); + if (n != null) + return n; + leftDone = true; + } + return right.next(); + } + + @Override + public void close() { + try { + left.close(); + } finally { + right.close(); + } + } + }; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Pool.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Pool.java index ebb99525824..27c56ee5413 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Pool.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Pool.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.rdf4j.sail.lmdb; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_close; + import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; @@ -25,9 +27,14 @@ class Pool { private final MDBVal[] valPool = new MDBVal[1024]; private final ByteBuffer[] keyPool = new ByteBuffer[1024]; private final Statistics[] statisticsPool = new Statistics[512]; + // LMDB cursor pooling (per-thread). We store cursor pointers along with their DBI. + private final long[] cursorPool = new long[1024]; + private final int[] cursorDbiPool = new int[1024]; + private final Object[] cursorOwnerPool = new Object[1024]; private int valPoolIndex = -1; private int keyPoolIndex = -1; private int statisticsPoolIndex = -1; + private int cursorPoolIndex = -1; final MDBVal getVal() { if (valPoolIndex >= 0) { @@ -74,6 +81,39 @@ final void free(Statistics statistics) { } } + /** + * Try to obtain a pooled LMDB cursor for the given {@code dbi}. Returns {@code 0} if none is available. + */ + final long getCursor(int dbi, Object owner) { + for (int i = cursorPoolIndex; i >= 0; i--) { + if (cursorDbiPool[i] == dbi && cursorOwnerPool[i] == owner) { + long cursor = cursorPool[i]; + // compact the stack by moving the top entry to this slot + if (i != cursorPoolIndex) { + cursorPool[i] = cursorPool[cursorPoolIndex]; + cursorDbiPool[i] = cursorDbiPool[cursorPoolIndex]; + cursorOwnerPool[i] = cursorOwnerPool[cursorPoolIndex]; + } + cursorPoolIndex--; + return cursor; + } + } + return 0L; + } + + /** + * Return an LMDB cursor to the pool for potential reuse. If the pool is full, the cursor is closed. + */ + final void freeCursor(int dbi, Object owner, long cursor) { + if (cursorPoolIndex < cursorPool.length - 1) { + cursorPool[++cursorPoolIndex] = cursor; + cursorDbiPool[cursorPoolIndex] = dbi; + cursorOwnerPool[cursorPoolIndex] = owner; + } else { + mdb_cursor_close(cursor); + } + } + final void close() { while (valPoolIndex >= 0) { valPool[valPoolIndex--].close(); @@ -81,6 +121,9 @@ final void close() { while (keyPoolIndex >= 0) { MemoryUtil.memFree(keyPool[keyPoolIndex--]); } + while (cursorPoolIndex >= 0) { + mdb_cursor_close(cursorPool[cursorPoolIndex--]); + } } /** diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/RecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/RecordIterator.java index c0a89c0628c..1f29b9d68d5 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/RecordIterator.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/RecordIterator.java @@ -12,10 +12,13 @@ import java.io.Closeable; +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; + /** * An iterator that iterates over records, for example those in a key-value database. */ -interface RecordIterator extends Closeable { +@InternalUseOnly +public interface RecordIterator extends Closeable { /** * Returns the next record. diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Statistics.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Statistics.java index b0c96d8b8cf..0c9b6b88940 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Statistics.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Statistics.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.rdf4j.sail.lmdb; +import java.util.Arrays; + /** * Helper class for computing cardinalities within triple store. */ @@ -33,4 +35,19 @@ class Statistics { final double[] avgRowsPerValue = new double[4]; final long[] avgRowsPerValueCounts = new long[4]; final long[] counts = new long[4]; + + void reset() { + for (long[] startValue : startValues) { + Arrays.fill(startValue, 0); + } + for (long[] lastValue : lastValues) { + Arrays.fill(lastValue, 0); + } + Arrays.fill(values, 0); + Arrays.fill(minValues, 0); + Arrays.fill(maxValues, 0); + Arrays.fill(avgRowsPerValue, 1.0); + Arrays.fill(avgRowsPerValueCounts, 0); + Arrays.fill(counts, 0); + } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java index 059bb51e666..df3c87f861f 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TripleStore.java @@ -78,6 +78,7 @@ import java.util.concurrent.atomic.LongAdder; import java.util.function.Consumer; +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; import org.eclipse.rdf4j.common.concurrent.locks.StampedLongAdderLockManager; import org.eclipse.rdf4j.sail.SailException; import org.eclipse.rdf4j.sail.lmdb.TxnManager.Mode; @@ -85,7 +86,9 @@ import org.eclipse.rdf4j.sail.lmdb.TxnRecordCache.Record; import org.eclipse.rdf4j.sail.lmdb.TxnRecordCache.RecordCacheIterator; import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; +import org.eclipse.rdf4j.sail.lmdb.util.IndexKeyReaders; import org.eclipse.rdf4j.sail.lmdb.util.IndexKeyWriters; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; @@ -102,7 +105,8 @@ * an actual RDF value. */ @SuppressWarnings("deprecation") -class TripleStore implements Closeable { +@InternalUseOnly +public class TripleStore implements Closeable { static ConcurrentHashMap stats = new ConcurrentHashMap<>(); static long hit = 0; @@ -114,12 +118,13 @@ class TripleStore implements Closeable { *-----------*/ // triples are represented by 4 varints for subject, predicate, object and context - static final int SUBJ_IDX = 0; - static final int PRED_IDX = 1; - static final int OBJ_IDX = 2; - static final int CONTEXT_IDX = 3; + public static final int SUBJ_IDX = 0; + public static final int PRED_IDX = 1; + public static final int OBJ_IDX = 2; + public static final int CONTEXT_IDX = 3; static final int MAX_KEY_LENGTH = 4 * 9; + static final long NO_PREVIOUS_ID = Long.MIN_VALUE; /** * The default triple indexes. @@ -137,6 +142,10 @@ class TripleStore implements Closeable { * The key used to store the triple indexes specification that specifies which triple indexes exist. */ private static final String INDEXES_KEY = "triple-indexes"; + /** + * The key used to store whether dupsort indices are enabled. + */ + private static final String DUPSORT_INDICES_KEY = "dupsort-indices"; /** * The version number for the current triple store. *
    @@ -160,6 +169,8 @@ class TripleStore implements Closeable { * The list of triple indexes that are used to store and retrieve triples. */ private final List indexes = new ArrayList<>(); + private volatile TripleIndex[] bestIndexLookup = new TripleIndex[IndexPattern.lookupSize()]; + private final SubjectPredicateIndex subjectPredicateIndex; private final ValueStore valueStore; private long env; @@ -167,6 +178,8 @@ class TripleStore implements Closeable { private int pageSize; private final boolean forceSync; private final boolean autoGrow; + private final boolean dupsortIndices; + private final boolean dupsortRead; private long mapSize; private long writeTxn; private final TxnManager txnManager; @@ -209,8 +222,10 @@ public int compareRegion(ByteBuffer array1, int startIdx1, ByteBuffer array2, in env = pp.get(0); } - // 1 for contexts, 12 for triple indexes (2 per index) - E(mdb_env_set_maxdbs(env, 13)); + // Max DBs: 1 for contexts, 12 for triple indexes (2 per index), + // plus up to 12 dupsort companion DBs when enabled and an additional subject-predicate index. + // Use a safe upper bound of 27. + E(mdb_env_set_maxdbs(env, 27)); E(mdb_env_set_maxreaders(env, 256)); // Open environment @@ -232,9 +247,12 @@ public int compareRegion(ByteBuffer array1, int startIdx1, ByteBuffer array2, in txnManager = new TxnManager(env, Mode.RESET); File propFile = new File(this.dir, PROPERTIES_FILE); + boolean isNewStore = !propFile.exists(); + this.dupsortRead = config.isDupsortRead(); String indexSpecStr = config.getTripleIndexes(); - if (!propFile.exists()) { + if (isNewStore) { // newly created lmdb store + this.dupsortIndices = config.isDupsortIndices(); properties = new Properties(); Set indexSpecs = parseIndexSpecList(indexSpecStr); @@ -251,13 +269,14 @@ public int compareRegion(ByteBuffer array1, int startIdx1, ByteBuffer array2, in properties = loadProperties(propFile); checkVersion(); + this.dupsortIndices = determineExistingDupsortSetting(config.isDupsortIndices()); + // Initialize existing indexes Set indexSpecs = getIndexSpecs(); initIndexes(indexSpecs, config.getTripleDBSize()); // Compare the existing indexes with the requested indexes Set reqIndexSpecs = parseIndexSpecList(indexSpecStr); - if (reqIndexSpecs.isEmpty()) { // No indexes specified, use the existing ones indexSpecStr = properties.getProperty(INDEXES_KEY); @@ -267,11 +286,24 @@ public int compareRegion(ByteBuffer array1, int startIdx1, ByteBuffer array2, in } } - if (!String.valueOf(SCHEME_VERSION).equals(properties.getProperty(VERSION_KEY)) - || !indexSpecStr.equals(properties.getProperty(INDEXES_KEY))) { - // Store up-to-date properties + subjectPredicateIndex = dupsortIndices ? new SubjectPredicateIndex() : null; + + boolean propertiesDirty = false; + if (!String.valueOf(SCHEME_VERSION).equals(properties.getProperty(VERSION_KEY))) { properties.setProperty(VERSION_KEY, String.valueOf(SCHEME_VERSION)); + propertiesDirty = true; + } + if (!indexSpecStr.equals(properties.getProperty(INDEXES_KEY))) { properties.setProperty(INDEXES_KEY, indexSpecStr); + propertiesDirty = true; + } + String dupsortProperty = properties.getProperty(DUPSORT_INDICES_KEY); + String dupsortValue = Boolean.toString(dupsortIndices); + if (!dupsortValue.equals(dupsortProperty)) { + properties.setProperty(DUPSORT_INDICES_KEY, dupsortValue); + propertiesDirty = true; + } + if (propertiesDirty) { storeProperties(propFile); } } @@ -293,6 +325,45 @@ private void checkVersion() throws SailException { } } + private boolean determineExistingDupsortSetting(boolean requestedDupsort) throws IOException { + String storedValue = properties.getProperty(DUPSORT_INDICES_KEY); + if (storedValue != null) { + return Boolean.parseBoolean(storedValue); + } + if (hasSubjectPredicateDupIndex()) { + return true; + } + if (requestedDupsort) { + logger.debug("Dupsort indices requested but not present on disk for store at {}", dir); + } + return false; + } + + private boolean hasSubjectPredicateDupIndex() throws IOException { + return readTransaction(env, (stack, txn) -> { + IntBuffer ip = stack.mallocInt(1); + + int rc = mdb_dbi_open(txn, "sp-dup", 0, ip); + if (rc == MDB_NOTFOUND) { + return false; + } + E(rc); + int explicitDbi = ip.get(0); + try { + rc = mdb_dbi_open(txn, "sp-dup-inf", 0, ip); + if (rc == MDB_NOTFOUND) { + return false; + } + E(rc); + int inferredDbi = ip.get(0); + mdb_dbi_close(env, inferredDbi); + return true; + } finally { + mdb_dbi_close(env, explicitDbi); + } + }); + } + private Set getIndexSpecs() throws SailException { String indexesStr = properties.getProperty(INDEXES_KEY); @@ -344,7 +415,7 @@ private Set parseIndexSpecList(String indexSpecStr) throws SailException private void initIndexes(Set indexSpecs, long tripleDbSize) throws IOException { for (String fieldSeq : indexSpecs) { logger.trace("Initializing index '{}'...", fieldSeq); - indexes.add(new TripleIndex(fieldSeq)); + indexes.add(new TripleIndex(fieldSeq, dupsortIndices)); } // initialize page size and set map size for env @@ -371,6 +442,8 @@ private void initIndexes(Set indexSpecs, long tripleDbSize) throws IOExc } return null; }); + + rebuildBestIndexLookup(); } private void reindex(Set currentIndexSpecs, Set newIndexSpecs) throws IOException, SailException { @@ -395,7 +468,7 @@ private void reindex(Set currentIndexSpecs, Set newIndexSpecs) t for (String fieldSeq : addedIndexSpecs) { logger.debug("Initializing new index '{}'...", fieldSeq); - TripleIndex addedIndex = new TripleIndex(fieldSeq); + TripleIndex addedIndex = new TripleIndex(fieldSeq, dupsortIndices); RecordIterator[] sourceIter = { null }; try { sourceIter[0] = new LmdbRecordIterator(sourceIndex, false, -1, -1, -1, -1, @@ -455,6 +528,8 @@ private void reindex(Set currentIndexSpecs, Set newIndexSpecs) t for (String fieldSeq : newIndexSpecs) { indexes.add(currentIndexes.remove(fieldSeq)); } + + rebuildBestIndexLookup(); } @Override @@ -472,6 +547,15 @@ public void close() throws IOException { } } + if (subjectPredicateIndex != null) { + try { + subjectPredicateIndex.close(); + } catch (Throwable e) { + logger.warn("Failed to close subject-predicate dup index", e); + caughtExceptions.add(e); + } + } + mdb_env_close(env); env = 0; @@ -502,7 +586,14 @@ public RecordIterator getAllTriplesSortedByContext(Txn txn) throws IOException { for (TripleIndex index : indexes) { if (index.getFieldSeq()[0] == 'c') { // found a context-first index - return getTriplesUsingIndex(txn, -1, -1, -1, -1, true, index, false); + LmdbDupRecordIterator.FallbackSupplier fallback = (quad, minBuf, maxBuf, reuse) -> { + if (reuse != null) { + reuse.initialize(index, null, false, -1, -1, -1, -1, true, txn, quad, minBuf, maxBuf); + return reuse; + } + return new LmdbRecordIterator(index, null, false, -1, -1, -1, -1, true, txn, quad, minBuf, maxBuf); + }; + return getTriplesUsingIndex(txn, -1, -1, -1, -1, true, index, false, fallback, null, null, null, null); } } return null; @@ -510,10 +601,63 @@ public RecordIterator getAllTriplesSortedByContext(Txn txn) throws IOException { public RecordIterator getTriples(Txn txn, long subj, long pred, long obj, long context, boolean explicit) throws IOException { + return getTriples(txn, subj, pred, obj, context, explicit, null, null, null); + } + + public RecordIterator getTriples(Txn txn, long subj, long pred, long obj, long context, boolean explicit, + long[] quadReuse) throws IOException { + return getTriples(txn, subj, pred, obj, context, explicit, null, null, quadReuse, null); + } + + public RecordIterator getTriples(Txn txn, long subj, long pred, long obj, long context, boolean explicit, + ByteBuffer minKeyBuf, ByteBuffer maxKeyBuf, long[] quadReuse) throws IOException { + return getTriples(txn, subj, pred, obj, context, explicit, minKeyBuf, maxKeyBuf, quadReuse, null); + } + + public RecordIterator getTriples(Txn txn, long subj, long pred, long obj, long context, boolean explicit, + ByteBuffer minKeyBuf, ByteBuffer maxKeyBuf, long[] quadReuse, RecordIterator iteratorReuse) + throws IOException { TripleIndex index = getBestIndex(subj, pred, obj, context); // System.out.println("get triples: " + Arrays.asList(subj, pred, obj,context)); boolean doRangeSearch = index.getPatternScore(subj, pred, obj, context) > 0; - return getTriplesUsingIndex(txn, subj, pred, obj, context, explicit, index, doRangeSearch); + + LmdbRecordIterator recordReuse = iteratorReuse instanceof LmdbRecordIterator + ? (LmdbRecordIterator) iteratorReuse + : null; + LmdbDupRecordIterator dupReuse = iteratorReuse instanceof LmdbDupRecordIterator + ? (LmdbDupRecordIterator) iteratorReuse + : null; + if (dupsortRead && subjectPredicateIndex != null && subj >= 0 && pred >= 0 && obj == -1 && context == -1) { + assert context == -1 && obj == -1 : "subject-predicate index can only be used for (s,p,?,?) patterns"; + +// LmdbDupRecordIterator.FallbackSupplier fallbackSupplier = (quad, minBuf, maxBuf, reuse) -> { +// if (reuse != null) { +// reuse.initialize(index, null, doRangeSearch, subj, pred, obj, context, explicit, txn, quad, minBuf, +// maxBuf); +// return reuse; +// } +// return new LmdbRecordIterator(index, null, doRangeSearch, subj, pred, obj, context, explicit, txn, quad, +// minBuf, maxBuf); +// }; + + // Use SP dup iterator, but union with the standard iterator to guard against any edge cases + // in SP storage/retrieval; de-duplicate at the record level. + if (dupReuse != null) { + dupReuse.initialize(subjectPredicateIndex, subj, pred, explicit, txn, quadReuse); + return dupReuse; + } + return new LmdbDupRecordIterator(subjectPredicateIndex, subj, pred, explicit, txn, quadReuse); + } + + if (recordReuse != null) { + recordReuse.initialize(index, null, doRangeSearch, subj, pred, obj, context, explicit, txn, quadReuse, + minKeyBuf, + maxKeyBuf); + return recordReuse; + } + return new LmdbRecordIterator(index, null, doRangeSearch, subj, pred, obj, context, explicit, txn, quadReuse, + minKeyBuf, maxKeyBuf); + } boolean hasTriples(boolean explicit) throws IOException { @@ -526,8 +670,40 @@ boolean hasTriples(boolean explicit) throws IOException { } private RecordIterator getTriplesUsingIndex(Txn txn, long subj, long pred, long obj, long context, - boolean explicit, TripleIndex index, boolean rangeSearch) throws IOException { - return new LmdbRecordIterator(index, rangeSearch, subj, pred, obj, context, explicit, txn); + boolean explicit, TripleIndex index, boolean rangeSearch, + LmdbDupRecordIterator.FallbackSupplier fallbackSupplier, ByteBuffer minKeyBuf, ByteBuffer maxKeyBuf, + long[] quadReuse, LmdbRecordIterator iteratorReuse) throws IOException { + return fallbackSupplier.get(quadReuse, minKeyBuf, maxKeyBuf, iteratorReuse); + } + + private int leadingBoundCount(char[] fieldSeq, long subj, long pred, long obj, long context) { + int count = 0; + + for (int i = 0; i < fieldSeq.length; i++) { + boolean bound; + switch (fieldSeq[i]) { + case 's': + bound = subj >= 0; + break; + case 'p': + bound = pred >= 0; + break; + case 'o': + bound = obj >= 0; + break; + case 'c': + bound = context >= 0; + break; + default: + bound = false; + } + if (bound) { + count++; + } else { + break; + } + } + return count; } /** @@ -633,12 +809,13 @@ protected void filterUsedIds(Collection ids) throws IOException { GroupMatcher matcher = index.createMatcher(subj, pred, obj, context); maxKeyBuf.clear(); - index.getMaxKey(maxKeyBuf, subj, pred, obj, context); + index.getMaxKey(maxKeyBuf, subj, pred, obj, context, -1, -1, -1, -1); maxKeyBuf.flip(); maxKey.mv_data(maxKeyBuf); keyBuf.clear(); - index.getMinKey(keyBuf, subj, pred, obj, context); + index.getMinKey(keyBuf, subj, pred, obj, context, NO_PREVIOUS_ID, NO_PREVIOUS_ID, + NO_PREVIOUS_ID, NO_PREVIOUS_ID); keyBuf.flip(); // set cursor to min key @@ -675,13 +852,38 @@ protected void filterUsedIds(Collection ids) throws IOException { protected double cardinality(long subj, long pred, long obj, long context) throws IOException { TripleIndex index = getBestIndex(subj, pred, obj, context); + CardinalityEstimator estimator = new CardinalityEstimator(this, index, subj, pred, obj, context); int relevantParts = index.getPatternScore(subj, pred, obj, context); if (relevantParts == 0) { - // it's worthless to use the index, just retrieve all entries in the db - return txnManager.doWith((stack, txn) -> { + return estimator.countAllEntries(); + } + return estimator.estimateWithSampling(); + } + + private static final class CardinalityEstimator { + + private final TripleStore store; + private final TripleIndex index; + private final long subj; + private final long pred; + private final long obj; + private final long context; + + CardinalityEstimator(TripleStore store, TripleIndex index, long subj, long pred, long obj, long context) { + this.store = store; + this.index = index; + this.subj = subj; + this.pred = pred; + this.obj = obj; + this.context = context; + } + + double countAllEntries() throws IOException { + return store.txnManager.doWith((stack, txn) -> { double cardinality = 0; - for (boolean explicit : new boolean[] { true, false }) { + for (int i = 0; i < 2; i++) { + boolean explicit = i == 0; int dbi = index.getDB(explicit); MDBStat stat = MDBStat.mallocStack(stack); mdb_stat(txn, dbi, stat); @@ -691,155 +893,231 @@ protected double cardinality(long subj, long pred, long obj, long context) throw }); } - return txnManager.doWith((stack, txn) -> { + double estimateWithSampling() throws IOException { Pool pool = Pool.get(); - final Statistics s = pool.getStatistics(); + Statistics statistics = pool.getStatistics(); try { - MDBVal maxKey = MDBVal.malloc(stack); - ByteBuffer maxKeyBuf = stack.malloc(TripleStore.MAX_KEY_LENGTH); - index.getMaxKey(maxKeyBuf, subj, pred, obj, context); - maxKeyBuf.flip(); - maxKey.mv_data(maxKeyBuf); + return store.txnManager.doWith((stack, txn) -> estimateWithTransaction(pool, statistics, stack, txn)); + } finally { + pool.free(statistics); + } + } - PointerBuffer pp = stack.mallocPointer(1); + private double estimateWithTransaction(Pool pool, Statistics statistics, MemoryStack stack, long txn) + throws IOException { + MDBVal maxKey = MDBVal.malloc(stack); + ByteBuffer maxKeyBuf = stack.malloc(TripleStore.MAX_KEY_LENGTH); + index.getMaxKey(maxKeyBuf, subj, pred, obj, context, -1, -1, -1, -1); + maxKeyBuf.flip(); + maxKey.mv_data(maxKeyBuf); - MDBVal keyData = MDBVal.mallocStack(stack); - ByteBuffer keyBuf = stack.malloc(TripleStore.MAX_KEY_LENGTH); - MDBVal valueData = MDBVal.mallocStack(stack); + MDBVal keyData = MDBVal.mallocStack(stack); + ByteBuffer keyBuf = stack.malloc(TripleStore.MAX_KEY_LENGTH); + MDBVal valueData = MDBVal.mallocStack(stack); - double cardinality = 0; - for (boolean explicit : new boolean[] { true, false }) { - Arrays.fill(s.avgRowsPerValue, 1.0); - Arrays.fill(s.avgRowsPerValueCounts, 0); + double cardinality = 0; + for (int i = 0; i < 2; i++) { + boolean explicit = i == 0; + statistics.reset(); - keyBuf.clear(); - index.getMinKey(keyBuf, subj, pred, obj, context); - keyBuf.flip(); + keyBuf.clear(); + index.getMinKey(keyBuf, subj, pred, obj, context, NO_PREVIOUS_ID, NO_PREVIOUS_ID, NO_PREVIOUS_ID, + NO_PREVIOUS_ID); + keyBuf.flip(); - int dbi = index.getDB(explicit); + int dbi = index.getDB(explicit); - int pos = 0; - long cursor = 0; + try (CursorHandle cursor = CursorHandle.open(pool, this, stack, txn, dbi)) { + if (!positionRangeStart(txn, dbi, cursor.cursor(), keyData, keyBuf, valueData, maxKey, + statistics)) { + break; + } + if (!positionRangeEnd(txn, dbi, cursor.cursor(), keyData, valueData, maxKeyBuf, statistics)) { + break; + } - try { - E(mdb_cursor_open(txn, dbi, pp)); - cursor = pp.get(0); + BucketSummary summary = sampleBuckets(txn, dbi, cursor.cursor(), keyData, valueData, keyBuf, maxKey, + statistics); + cardinality += summary.totalSamples; + cardinality += interpolateBuckets(summary.bucketCount, statistics); + } + } - // set cursor to min key - keyData.mv_data(keyBuf); - int rc = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); - if (rc != MDB_SUCCESS || mdb_cmp(txn, dbi, keyData, maxKey) >= 0) { - break; - } else { - Varint.readListUnsigned(keyData.mv_data(), s.minValues); - } + return cardinality; + } - // set cursor to max key - keyData.mv_data(maxKeyBuf); - rc = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); - if (rc != MDB_SUCCESS) { - // directly go to last value - rc = mdb_cursor_get(cursor, keyData, valueData, MDB_LAST); - } else { - // go to previous value of selected key - rc = mdb_cursor_get(cursor, keyData, valueData, MDB_PREV); - } - if (rc == MDB_SUCCESS) { - Varint.readListUnsigned(keyData.mv_data(), s.maxValues); - // this is required to correctly estimate the range size at a later point - s.startValues[s.MAX_BUCKETS] = s.maxValues; - } else { - break; - } + private boolean positionRangeStart(long txn, int dbi, long cursor, MDBVal keyData, ByteBuffer keyBuf, + MDBVal valueData, MDBVal maxKey, Statistics statistics) { + keyData.mv_data(keyBuf); + int rc = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + if (rc != MDB_SUCCESS || mdb_cmp(txn, dbi, keyData, maxKey) >= 0) { + return false; + } + Varint.readQuadUnsigned(keyData.mv_data(), statistics.minValues); + return true; + } - long allSamplesCount = 0; - int bucket = 0; - boolean endOfRange = false; - for (; bucket < s.MAX_BUCKETS && !endOfRange; bucket++) { - if (bucket != 0) { - bucketStart((double) bucket / s.MAX_BUCKETS, s.minValues, s.maxValues, s.values); - keyBuf.clear(); - Varint.writeListUnsigned(keyBuf, s.values); - keyBuf.flip(); - } - // this is the min key for the first iteration - keyData.mv_data(keyBuf); - - int currentSamplesCount = 0; - rc = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); - while (rc == MDB_SUCCESS && currentSamplesCount < s.MAX_SAMPLES_PER_BUCKET) { - if (mdb_cmp(txn, dbi, keyData, maxKey) >= 0) { - endOfRange = true; - break; - } else { - allSamplesCount++; - currentSamplesCount++; - - System.arraycopy(s.values, 0, s.lastValues[bucket], 0, s.values.length); - Varint.readListUnsigned(keyData.mv_data(), s.values); - - if (currentSamplesCount == 1) { - Arrays.fill(s.counts, 1); - System.arraycopy(s.values, 0, s.startValues[bucket], 0, s.values.length); - } else { - for (int i = 0; i < s.values.length; i++) { - if (s.values[i] == s.lastValues[bucket][i]) { - s.counts[i]++; - } else { - long diff = s.values[i] - s.lastValues[bucket][i]; - s.avgRowsPerValueCounts[i]++; - s.avgRowsPerValue[i] = (s.avgRowsPerValue[i] - * (s.avgRowsPerValueCounts[i] - 1) + - (double) s.counts[i] / diff) / s.avgRowsPerValueCounts[i]; - s.counts[i] = 0; - } - } - } - rc = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); - if (rc != MDB_SUCCESS) { - // no more elements are available - endOfRange = true; - } - } - } - } + private boolean positionRangeEnd(long txn, int dbi, long cursor, MDBVal keyData, MDBVal valueData, + ByteBuffer maxKeyBuf, Statistics statistics) { + keyData.mv_data(maxKeyBuf); + int rc = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + if (rc != MDB_SUCCESS) { + rc = mdb_cursor_get(cursor, keyData, valueData, MDB_LAST); + } else { + rc = mdb_cursor_get(cursor, keyData, valueData, MDB_PREV); + } + if (rc != MDB_SUCCESS) { + return false; + } + Varint.readQuadUnsigned(keyData.mv_data(), statistics.maxValues); + statistics.startValues[Statistics.MAX_BUCKETS] = statistics.maxValues; + return true; + } - // at least the seen samples must be counted - cardinality += allSamplesCount; - - // the actual number of buckets (bucket - 1 "real" buckets and one for the last element within - // the range) - int buckets = bucket; - for (bucket = 1; bucket < buckets; bucket++) { - // find first element that has been changed - pos = 0; - while (pos < s.lastValues[bucket].length - && s.startValues[bucket][pos] == s.lastValues[bucket - 1][pos]) { - pos++; - } - if (pos < s.lastValues[bucket].length) { - // this may be < 0 if two groups are overlapping - long diffBetweenGroups = Math - .max(s.startValues[bucket][pos] - s.lastValues[bucket - 1][pos], 0); - // estimate number of elements between last element of previous bucket and first element - // of current bucket - cardinality += s.avgRowsPerValue[pos] * diffBetweenGroups; + private BucketSummary sampleBuckets(long txn, int dbi, long cursor, MDBVal keyData, MDBVal valueData, + ByteBuffer keyBuf, MDBVal maxKey, Statistics statistics) { + long allSamplesCount = 0; + int bucket = 0; + boolean endOfRange = false; + for (; bucket < Statistics.MAX_BUCKETS && !endOfRange; bucket++) { + if (bucket != 0) { + store.bucketStart((double) bucket / Statistics.MAX_BUCKETS, statistics.minValues, + statistics.maxValues, statistics.values); + keyBuf.clear(); + Varint.writeQuadUnsigned(keyBuf, statistics.values); + keyBuf.flip(); + } + + keyData.mv_data(keyBuf); + + int currentSamplesCount = 0; + int rc = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + while (rc == MDB_SUCCESS && currentSamplesCount < Statistics.MAX_SAMPLES_PER_BUCKET) { + if (mdb_cmp(txn, dbi, keyData, maxKey) >= 0) { + endOfRange = true; + break; + } + allSamplesCount++; + currentSamplesCount++; + + System.arraycopy(statistics.values, 0, statistics.lastValues[bucket], 0, + statistics.values.length); + Varint.readQuadUnsigned(keyData.mv_data(), statistics.values); + + if (currentSamplesCount == 1) { + Arrays.fill(statistics.counts, 1); + System.arraycopy(statistics.values, 0, statistics.startValues[bucket], 0, + statistics.values.length); + } else { + for (int i = 0; i < statistics.values.length; i++) { + if (statistics.values[i] == statistics.lastValues[bucket][i]) { + statistics.counts[i]++; + } else { + long diff = statistics.values[i] - statistics.lastValues[bucket][i]; + statistics.avgRowsPerValueCounts[i]++; + statistics.avgRowsPerValue[i] = (statistics.avgRowsPerValue[i] + * (statistics.avgRowsPerValueCounts[i] - 1) + + (double) statistics.counts[i] / diff) / statistics.avgRowsPerValueCounts[i]; + statistics.counts[i] = 0; } } - } finally { - if (cursor != 0) { - mdb_cursor_close(cursor); - } + } + rc = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + if (rc != MDB_SUCCESS) { + endOfRange = true; } } - return cardinality; - } finally { - pool.free(s); } - }); + return new BucketSummary(allSamplesCount, bucket); + } + + private double interpolateBuckets(int bucketCount, Statistics statistics) { + double cardinality = 0; + for (int bucket = 1; bucket < bucketCount; bucket++) { + int pos = 0; + while (pos < statistics.lastValues[bucket].length + && statistics.startValues[bucket][pos] == statistics.lastValues[bucket - 1][pos]) { + pos++; + } + if (pos < statistics.lastValues[bucket].length) { + long diffBetweenGroups = Math.max( + statistics.startValues[bucket][pos] - statistics.lastValues[bucket - 1][pos], 0); + cardinality += statistics.avgRowsPerValue[pos] * diffBetweenGroups; + } + } + return cardinality; + } + + private static final class BucketSummary { + private final long totalSamples; + private final int bucketCount; + + BucketSummary(long totalSamples, int bucketCount) { + this.totalSamples = totalSamples; + this.bucketCount = bucketCount; + } + } + + private static final class CursorHandle implements AutoCloseable { + private final Pool pool; + private final Object owner; + private final int dbi; + private final boolean pooled; + private long cursor; + + private CursorHandle(Pool pool, Object owner, int dbi, long cursor, boolean pooled) { + this.pool = pool; + this.owner = owner; + this.dbi = dbi; + this.cursor = cursor; + this.pooled = pooled; + } + + static CursorHandle open(Pool pool, Object owner, MemoryStack stack, long txn, int dbi) throws IOException { + long pooledCursor = pool.getCursor(dbi, owner); + if (pooledCursor != 0) { + return new CursorHandle(pool, owner, dbi, pooledCursor, true); + } + PointerBuffer pointer = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pointer)); + return new CursorHandle(pool, owner, dbi, pointer.get(0), false); + } + + long cursor() { + return cursor; + } + + @Override + public void close() { + if (cursor == 0) { + return; + } + if (pooled) { + pool.freeCursor(dbi, owner, cursor); + } else { + mdb_cursor_close(cursor); + } + cursor = 0; + } + } } protected TripleIndex getBestIndex(long subj, long pred, long obj, long context) { + TripleIndex[] lookup = bestIndexLookup; + int mask = IndexPattern.toMask(subj, pred, obj, context); + TripleIndex bestIndex = lookup[mask]; + if (bestIndex != null) { + return bestIndex; + } + + bestIndex = selectBestIndex(subj, pred, obj, context); + if (bestIndex != null) { + lookup[mask] = bestIndex; + } + return bestIndex; + } + + private TripleIndex selectBestIndex(long subj, long pred, long obj, long context) { int bestScore = -1; TripleIndex bestIndex = null; @@ -854,6 +1132,93 @@ protected TripleIndex getBestIndex(long subj, long pred, long obj, long context) return bestIndex; } + private void rebuildBestIndexLookup() { + TripleIndex[] newLookup = new TripleIndex[IndexPattern.lookupSize()]; + if (!indexes.isEmpty()) { + for (IndexPattern pattern : IndexPattern.values()) { + TripleIndex bestIndex = selectBestIndex(pattern.subjValue, pattern.predValue, pattern.objValue, + pattern.contextValue); + newLookup[pattern.mask] = bestIndex; + } + } + bestIndexLookup = newLookup; + } + + // Package-private: allow LMDB dataset to inspect available indexes for order support + List getAllIndexes() { + return indexes; + } + + private enum IndexPattern { + NONE(-1, -1, -1, -1), + S(0, -1, -1, -1), + P(-1, 0, -1, -1), + SP(0, 0, -1, -1), + O(-1, -1, 0, -1), + SO(0, -1, 0, -1), + PO(-1, 0, 0, -1), + SPO(0, 0, 0, -1), + C(-1, -1, -1, 0), + SC(0, -1, -1, 0), + PC(-1, 0, -1, 0), + SPC(0, 0, -1, 0), + OC(-1, -1, 0, 0), + SOC(0, -1, 0, 0), + POC(-1, 0, 0, 0), + SPOC(0, 0, 0, 0); + + private final long subjValue; + private final long predValue; + private final long objValue; + private final long contextValue; + private final int mask; + + IndexPattern(long subjValue, long predValue, long objValue, long contextValue) { + this.subjValue = subjValue; + this.predValue = predValue; + this.objValue = objValue; + this.contextValue = contextValue; + int mask = 0; + if (subjValue >= 0) { + mask |= 1; + } + if (predValue >= 0) { + mask |= 1 << 1; + } + if (objValue >= 0) { + mask |= 1 << 2; + } + if (contextValue >= 0) { + mask |= 1 << 3; + } + this.mask = mask; + } + + private static final IndexPattern[] LOOKUP = buildLookup(); + + private static IndexPattern[] buildLookup() { + IndexPattern[] lookup = new IndexPattern[1 << 4]; + for (IndexPattern pattern : values()) { + lookup[pattern.mask] = pattern; + } + return lookup; + } + + static int toMask(long s, long p, long o, long c) { + // For a signed long, (x >>> 63) == 0 when x >= 0, == 1 when x < 0. + // XOR with 1 flips that so we get 1 when x >= 0, 0 when x < 0. + long b0 = (s >>> 63) ^ 1L; + long b1 = (p >>> 63) ^ 1L; + long b2 = (o >>> 63) ^ 1L; + long b3 = (c >>> 63) ^ 1L; + return (int) (b0 | (b1 << 1) | (b2 << 2) | (b3 << 3)); + } + + static int lookupSize() { + return LOOKUP.length; + } + } + private boolean requiresResize() { if (autoGrow) { return LmdbUtil.requiresResize(mapSize, pageSize, writeTxn, 0); @@ -900,6 +1265,52 @@ public boolean storeTriple(long subj, long pred, long obj, long context, boolean boolean foundImplicit = false; if (explicit && stAdded) { foundImplicit = mdb_del(writeTxn, mainIndex.getDB(false), keyVal, dataVal) == MDB_SUCCESS; + if (foundImplicit && mainIndex.isDupsortEnabled()) { + // Also remove from inferred dup DB when promoting to explicit + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + mainIndex.toDupKeyPrefix(dupKeyBuf, subj, pred, obj, context); + dupKeyBuf.flip(); + dupValBuf.clear(); + mainIndex.toDupValue(dupValBuf, subj, pred, obj, context); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_del(writeTxn, mainIndex.getDupDB(false), dupKeyVal, dupDataVal)); + } finally { + stack.setPointer(frame); + } + } + } + + if (stAdded && mainIndex.isDupsortEnabled()) { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + mainIndex.toDupKeyPrefix(dupKeyBuf, subj, pred, obj, context); + dupKeyBuf.flip(); + dupValBuf.clear(); + mainIndex.toDupValue(dupValBuf, subj, pred, obj, context); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_put(writeTxn, mainIndex.getDupDB(explicit), dupKeyVal, dupDataVal, 0)); + } finally { + stack.setPointer(frame); + } + } + + if (stAdded && subjectPredicateIndex != null) { + subjectPredicateIndex.put(writeTxn, subj, pred, obj, context, explicit, stack); } if (stAdded) { @@ -917,12 +1328,36 @@ public boolean storeTriple(long subj, long pred, long obj, long context, boolean E(mdb_del(writeTxn, mainIndex.getDB(false), keyVal, dataVal)); } E(mdb_put(writeTxn, index.getDB(explicit), keyVal, dataVal, 0)); + + if (index.isDupsortEnabled()) { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + index.toDupKeyPrefix(dupKeyBuf, subj, pred, obj, context); + dupKeyBuf.flip(); + dupValBuf.clear(); + index.toDupValue(dupValBuf, subj, pred, obj, context); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_put(writeTxn, index.getDupDB(explicit), dupKeyVal, dupDataVal, 0)); + } finally { + stack.setPointer(frame); + } + } } if (stAdded) { incrementContext(stack, context); } } + if (foundImplicit && subjectPredicateIndex != null) { + subjectPredicateIndex.delete(writeTxn, subj, pred, obj, context, false, stack); + } } return stAdded; @@ -1029,6 +1464,35 @@ public void removeTriples(RecordIterator it, boolean explicit, Consumer keyValue.mv_data(keyBuf); E(mdb_del(writeTxn, index.getDB(explicit), keyValue, null)); + + if (index.isDupsortEnabled()) { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + index.toDupKeyPrefix(dupKeyBuf, quad[SUBJ_IDX], quad[PRED_IDX], quad[OBJ_IDX], + quad[CONTEXT_IDX]); + dupKeyBuf.flip(); + dupValBuf.clear(); + index.toDupValue(dupValBuf, quad[SUBJ_IDX], quad[PRED_IDX], quad[OBJ_IDX], + quad[CONTEXT_IDX]); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_del(writeTxn, index.getDupDB(explicit), dupKeyVal, dupDataVal)); + } finally { + stack.setPointer(frame); + } + } + + } + + if (subjectPredicateIndex != null) { + subjectPredicateIndex.delete(writeTxn, quad[SUBJ_IDX], quad[PRED_IDX], quad[OBJ_IDX], + quad[CONTEXT_IDX], explicit, stack); } decrementContext(stack, quad[CONTEXT_IDX]); @@ -1070,8 +1534,68 @@ protected void updateFromCache() throws IOException { if (r.add) { E(mdb_put(writeTxn, index.getDB(explicit), keyVal, dataVal, 0)); + if (index.isDupsortEnabled()) { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + index.toDupKeyPrefix(dupKeyBuf, r.quad[0], r.quad[1], r.quad[2], r.quad[3]); + dupKeyBuf.flip(); + dupValBuf.clear(); + index.toDupValue(dupValBuf, r.quad[0], r.quad[1], r.quad[2], r.quad[3]); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_put(writeTxn, index.getDupDB(explicit), dupKeyVal, dupDataVal, 0)); + } finally { + stack.setPointer(frame); + } + } } else { E(mdb_del(writeTxn, index.getDB(explicit), keyVal, null)); + if (index.isDupsortEnabled()) { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + index.toDupKeyPrefix(dupKeyBuf, r.quad[0], r.quad[1], r.quad[2], r.quad[3]); + dupKeyBuf.flip(); + dupValBuf.clear(); + index.toDupValue(dupValBuf, r.quad[0], r.quad[1], r.quad[2], r.quad[3]); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_del(writeTxn, index.getDupDB(explicit), dupKeyVal, dupDataVal)); + } finally { + stack.setPointer(frame); + } + } + } + } + + if (subjectPredicateIndex != null) { + if (requiresResize()) { + // resize map if required before touching the dup index + E(mdb_txn_commit(writeTxn)); + mapSize = LmdbUtil.autoGrowMapSize(mapSize, pageSize, 0); + E(mdb_env_set_mapsize(env, mapSize)); + logger.debug("resized map to {}", mapSize); + E(mdb_txn_begin(env, NULL, 0, pp)); + writeTxn = pp.get(0); + } + + if (r.add) { + subjectPredicateIndex.put(writeTxn, r.quad[0], r.quad[1], r.quad[2], r.quad[3], explicit, + stack); + } else { + subjectPredicateIndex.delete(writeTxn, r.quad[0], r.quad[1], r.quad[2], r.quad[3], explicit, + stack); } } } @@ -1177,22 +1701,63 @@ private void storeProperties(File propFile) throws IOException { } } - class TripleIndex { + interface DupIndex { + char[] getFieldSeq(); + + int getDupDB(boolean explicit); + + void toDupKeyPrefix(ByteBuffer bb, long subj, long pred, long obj, long context); + + int getPatternScore(long subj, long pred, long obj, long context); + } + + interface KeyBuilder { + void writeMin(ByteBuffer buffer); + + void writeMax(ByteBuffer buffer); + } + + @FunctionalInterface + private interface PatternScoreFunction { + int score(long subj, long pred, long obj, long context); + } + + class TripleIndex implements DupIndex { private final char[] fieldSeq; private final IndexKeyWriters.KeyWriter keyWriter; + private final IndexKeyReaders.KeyToQuadReader keyReader; private final IndexKeyWriters.MatcherFactory matcherFactory; private final int dbiExplicit, dbiInferred; + private final boolean dupsortEnabled; + private final int dbiDupExplicit; + private final int dbiDupInferred; private final int[] indexMap; + private final PatternScoreFunction patternScoreFunction; - public TripleIndex(String fieldSeq) throws IOException { + public TripleIndex(String fieldSeq, boolean dupsortEnabled) throws IOException { this.fieldSeq = fieldSeq.toCharArray(); this.keyWriter = IndexKeyWriters.forFieldSeq(fieldSeq); this.matcherFactory = IndexKeyWriters.matcherFactory(fieldSeq); + this.keyReader = IndexKeyReaders.forFieldSeq(fieldSeq); this.indexMap = getIndexes(this.fieldSeq); + this.patternScoreFunction = PatternScoreFunctions.forFieldSeq(fieldSeq); // open database and use native sort order without comparator dbiExplicit = openDatabase(env, fieldSeq, MDB_CREATE, null); dbiInferred = openDatabase(env, fieldSeq + "-inf", MDB_CREATE, null); + this.dupsortEnabled = dupsortEnabled; + if (dupsortEnabled) { + int flags = MDB_CREATE | org.lwjgl.util.lmdb.LMDB.MDB_DUPSORT | org.lwjgl.util.lmdb.LMDB.MDB_DUPFIXED; + dbiDupExplicit = openDatabase(env, fieldSeq + "-dup", flags, null); + dbiDupInferred = openDatabase(env, fieldSeq + "-dup-inf", flags, null); + } else { + dbiDupExplicit = 0; + dbiDupInferred = 0; + } + } + + public TripleIndex(String fieldSeq) throws IOException { + this(fieldSeq, false); } public char[] getFieldSeq() { @@ -1203,6 +1768,91 @@ public int getDB(boolean explicit) { return explicit ? dbiExplicit : dbiInferred; } + public boolean isDupsortEnabled() { + return dupsortEnabled; + } + + public int getDupDB(boolean explicit) { + return explicit ? dbiDupExplicit : dbiDupInferred; + } + + @Override + public void toDupKeyPrefix(ByteBuffer bb, long subj, long pred, long obj, long context) { + long s = subj, p = pred, o = obj, c = context; + { + char f = fieldSeq[0]; + switch (f) { + case 's': + Varint.writeUnsigned(bb, s); + break; + case 'p': + Varint.writeUnsigned(bb, p); + break; + case 'o': + Varint.writeUnsigned(bb, o); + break; + case 'c': + Varint.writeUnsigned(bb, c); + break; + } + } + { + char f = fieldSeq[1]; + switch (f) { + case 's': + Varint.writeUnsigned(bb, s); + break; + case 'p': + Varint.writeUnsigned(bb, p); + break; + case 'o': + Varint.writeUnsigned(bb, o); + break; + case 'c': + Varint.writeUnsigned(bb, c); + break; + } + } + } + + void toDupValue(ByteBuffer bb, long subj, long pred, long obj, long context) { + // write last two fields as two longs + char f3 = fieldSeq[2]; + char f4 = fieldSeq[3]; + long v3; + switch (f3) { + case 's': + v3 = subj; + break; + case 'p': + v3 = pred; + break; + case 'o': + v3 = obj; + break; + default: + v3 = context; + break; + } + long v4; + switch (f4) { + case 's': + v4 = subj; + break; + case 'p': + v4 = pred; + break; + case 'o': + v4 = obj; + break; + default: + v4 = context; + break; + } + bb.putLong(v3); + bb.putLong(v4); + } + protected int[] getIndexes(char[] fieldSeq) { int[] indexes = new int[fieldSeq.length]; for (int i = 0; i < fieldSeq.length; i++) { @@ -1236,61 +1886,53 @@ protected int[] getIndexes(char[] fieldSeq) { * that the index will perform a sequential scan. */ public int getPatternScore(long subj, long pred, long obj, long context) { - int score = 0; + return patternScoreFunction.score(subj, pred, obj, context); + } - for (char field : fieldSeq) { - switch (field) { - case 's': - if (subj >= 0) { - score++; - } else { - return score; - } - break; - case 'p': - if (pred >= 0) { - score++; - } else { - return score; - } - break; - case 'o': - if (obj >= 0) { - score++; - } else { - return score; - } - break; - case 'c': - if (context >= 0) { - score++; - } else { - return score; - } - break; - default: - throw new RuntimeException("invalid character '" + field + "' in field sequence: " - + new String(fieldSeq)); + KeyBuilder keyBuilder(long subj, long pred, long obj, long context) { + return new KeyBuilder() { + + @Override + public void writeMin(ByteBuffer buffer) { + getMinKey(buffer, subj, pred, obj, context, NO_PREVIOUS_ID, NO_PREVIOUS_ID, NO_PREVIOUS_ID, + NO_PREVIOUS_ID); } - } - return score; + @Override + public void writeMax(ByteBuffer buffer) { + getMaxKey(buffer, subj, pred, obj, context, -1, -1, -1, -1); + } + }; } - void getMinKey(ByteBuffer bb, long subj, long pred, long obj, long context) { + void getMinKey(ByteBuffer bb, long subj, long pred, long obj, long context, long prevSubj, long prevPred, + long prevObj, long prevContext) { subj = subj <= 0 ? 0 : subj; pred = pred <= 0 ? 0 : pred; obj = obj <= 0 ? 0 : obj; context = context <= 0 ? 0 : context; - toKey(bb, subj, pred, obj, context); + long prevSubjNorm = prevSubj == NO_PREVIOUS_ID ? NO_PREVIOUS_ID : (prevSubj <= 0 ? 0 : prevSubj); + long prevPredNorm = prevPred == NO_PREVIOUS_ID ? NO_PREVIOUS_ID : (prevPred <= 0 ? 0 : prevPred); + long prevObjNorm = prevObj == NO_PREVIOUS_ID ? NO_PREVIOUS_ID : (prevObj <= 0 ? 0 : prevObj); + long prevContextNorm = prevContext == NO_PREVIOUS_ID ? NO_PREVIOUS_ID + : (prevContext <= 0 ? 0 : prevContext); + toKey(bb, subj, pred, obj, context, prevSubjNorm, prevPredNorm, prevObjNorm, prevContextNorm); } - void getMaxKey(ByteBuffer bb, long subj, long pred, long obj, long context) { + void getMaxKey(ByteBuffer bb, long subj, long pred, long obj, long context, long prevSubj, long prevPred, + long prevObj, long prevContext) { subj = subj <= 0 ? Long.MAX_VALUE : subj; pred = pred <= 0 ? Long.MAX_VALUE : pred; obj = obj <= 0 ? Long.MAX_VALUE : obj; context = context < 0 ? Long.MAX_VALUE : context; - toKey(bb, subj, pred, obj, context); + long prevSubjNorm = prevSubj == NO_PREVIOUS_ID ? NO_PREVIOUS_ID + : (prevSubj <= 0 ? Long.MAX_VALUE : prevSubj); + long prevPredNorm = prevPred == NO_PREVIOUS_ID ? NO_PREVIOUS_ID + : (prevPred <= 0 ? Long.MAX_VALUE : prevPred); + long prevObjNorm = prevObj == NO_PREVIOUS_ID ? NO_PREVIOUS_ID : (prevObj <= 0 ? Long.MAX_VALUE : prevObj); + long prevContextNorm = prevContext == NO_PREVIOUS_ID ? NO_PREVIOUS_ID + : (prevContext <= 0 ? Long.MAX_VALUE : prevContext); + toKey(bb, subj, pred, obj, context, prevSubjNorm, prevPredNorm, prevObjNorm, prevContextNorm); } GroupMatcher createMatcher(long subj, long pred, long obj, long context) { @@ -1374,6 +2016,11 @@ public void print() { } void toKey(ByteBuffer bb, long subj, long pred, long obj, long context) { + toKey(bb, subj, pred, obj, context, NO_PREVIOUS_ID, NO_PREVIOUS_ID, NO_PREVIOUS_ID, NO_PREVIOUS_ID); + } + + void toKey(ByteBuffer bb, long subj, long pred, long obj, long context, long prevSubj, long prevPred, + long prevObj, long prevContext) { boolean shouldCache = threeOfFourAreZeroOrMax(subj, pred, obj, context); if (shouldCache) { @@ -1390,35 +2037,17 @@ void toKey(ByteBuffer bb, long subj, long pred, long obj, long context) { } // Pass through to the keyWriter with caching hint - keyWriter.write(bb, subj, pred, obj, context, shouldCache); + boolean hasPrev = prevSubj != NO_PREVIOUS_ID; + keyWriter.write(bb, subj, pred, obj, context, shouldCache, hasPrev, prevSubj, prevPred, prevObj, + prevContext); } void keyToQuad(ByteBuffer key, long[] quad) { readQuadUnsigned(key, indexMap, quad); } - void keyToQuad(ByteBuffer key, long[] originalQuad, long[] quad) { - // directly use index map to read values in to correct positions - if (originalQuad[indexMap[0]] != -1) { - Varint.skipUnsigned(key); - } else { - quad[indexMap[0]] = Varint.readUnsigned(key); - } - if (originalQuad[indexMap[1]] != -1) { - Varint.skipUnsigned(key); - } else { - quad[indexMap[1]] = Varint.readUnsigned(key); - } - if (originalQuad[indexMap[2]] != -1) { - Varint.skipUnsigned(key); - } else { - quad[indexMap[2]] = Varint.readUnsigned(key); - } - if (originalQuad[indexMap[3]] != -1) { - Varint.skipUnsigned(key); - } else { - quad[indexMap[3]] = Varint.readUnsigned(key); - } + void keyToQuad(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + keyReader.read(key, subj, pred, obj, context, quad); } @Override @@ -1442,6 +2071,573 @@ void destroy(long txn) { } } + private static final class PatternScoreFunctions { + + private PatternScoreFunctions() { + } + + private static PatternScoreFunction forFieldSeq(String fieldSeq) { + switch (fieldSeq) { + case "spoc": + return PatternScoreFunctions::score_spoc; + case "spco": + return PatternScoreFunctions::score_spco; + case "sopc": + return PatternScoreFunctions::score_sopc; + case "socp": + return PatternScoreFunctions::score_socp; + case "scpo": + return PatternScoreFunctions::score_scpo; + case "scop": + return PatternScoreFunctions::score_scop; + case "psoc": + return PatternScoreFunctions::score_psoc; + case "psco": + return PatternScoreFunctions::score_psco; + case "posc": + return PatternScoreFunctions::score_posc; + case "pocs": + return PatternScoreFunctions::score_pocs; + case "pcso": + return PatternScoreFunctions::score_pcso; + case "pcos": + return PatternScoreFunctions::score_pcos; + case "ospc": + return PatternScoreFunctions::score_ospc; + case "oscp": + return PatternScoreFunctions::score_oscp; + case "opsc": + return PatternScoreFunctions::score_opsc; + case "opcs": + return PatternScoreFunctions::score_opcs; + case "ocsp": + return PatternScoreFunctions::score_ocsp; + case "ocps": + return PatternScoreFunctions::score_ocps; + case "cspo": + return PatternScoreFunctions::score_cspo; + case "csop": + return PatternScoreFunctions::score_csop; + case "cpso": + return PatternScoreFunctions::score_cpso; + case "cpos": + return PatternScoreFunctions::score_cpos; + case "cosp": + return PatternScoreFunctions::score_cosp; + case "cops": + return PatternScoreFunctions::score_cops; + default: + throw new IllegalArgumentException("Unsupported field sequence: " + fieldSeq); + } + } + + private static int score_spoc(long subj, long pred, long obj, long context) { + if (subj < 0) { + return 0; + } + if (pred < 0) { + return 1; + } + if (obj < 0) { + return 2; + } + if (context < 0) { + return 3; + } + return 4; + } + + private static int score_spco(long subj, long pred, long obj, long context) { + if (subj < 0) { + return 0; + } + if (pred < 0) { + return 1; + } + if (context < 0) { + return 2; + } + if (obj < 0) { + return 3; + } + return 4; + } + + private static int score_sopc(long subj, long pred, long obj, long context) { + if (subj < 0) { + return 0; + } + if (obj < 0) { + return 1; + } + if (pred < 0) { + return 2; + } + if (context < 0) { + return 3; + } + return 4; + } + + private static int score_socp(long subj, long pred, long obj, long context) { + if (subj < 0) { + return 0; + } + if (obj < 0) { + return 1; + } + if (context < 0) { + return 2; + } + if (pred < 0) { + return 3; + } + return 4; + } + + private static int score_scpo(long subj, long pred, long obj, long context) { + if (subj < 0) { + return 0; + } + if (context < 0) { + return 1; + } + if (pred < 0) { + return 2; + } + if (obj < 0) { + return 3; + } + return 4; + } + + private static int score_scop(long subj, long pred, long obj, long context) { + if (subj < 0) { + return 0; + } + if (context < 0) { + return 1; + } + if (obj < 0) { + return 2; + } + if (pred < 0) { + return 3; + } + return 4; + } + + private static int score_psoc(long subj, long pred, long obj, long context) { + if (pred < 0) { + return 0; + } + if (subj < 0) { + return 1; + } + if (obj < 0) { + return 2; + } + if (context < 0) { + return 3; + } + return 4; + } + + private static int score_psco(long subj, long pred, long obj, long context) { + if (pred < 0) { + return 0; + } + if (subj < 0) { + return 1; + } + if (context < 0) { + return 2; + } + if (obj < 0) { + return 3; + } + return 4; + } + + private static int score_posc(long subj, long pred, long obj, long context) { + if (pred < 0) { + return 0; + } + if (obj < 0) { + return 1; + } + if (subj < 0) { + return 2; + } + if (context < 0) { + return 3; + } + return 4; + } + + private static int score_pocs(long subj, long pred, long obj, long context) { + if (pred < 0) { + return 0; + } + if (obj < 0) { + return 1; + } + if (context < 0) { + return 2; + } + if (subj < 0) { + return 3; + } + return 4; + } + + private static int score_pcso(long subj, long pred, long obj, long context) { + if (pred < 0) { + return 0; + } + if (context < 0) { + return 1; + } + if (subj < 0) { + return 2; + } + if (obj < 0) { + return 3; + } + return 4; + } + + private static int score_pcos(long subj, long pred, long obj, long context) { + if (pred < 0) { + return 0; + } + if (context < 0) { + return 1; + } + if (obj < 0) { + return 2; + } + if (subj < 0) { + return 3; + } + return 4; + } + + private static int score_ospc(long subj, long pred, long obj, long context) { + if (obj < 0) { + return 0; + } + if (subj < 0) { + return 1; + } + if (pred < 0) { + return 2; + } + if (context < 0) { + return 3; + } + return 4; + } + + private static int score_oscp(long subj, long pred, long obj, long context) { + if (obj < 0) { + return 0; + } + if (subj < 0) { + return 1; + } + if (context < 0) { + return 2; + } + if (pred < 0) { + return 3; + } + return 4; + } + + private static int score_opsc(long subj, long pred, long obj, long context) { + if (obj < 0) { + return 0; + } + if (pred < 0) { + return 1; + } + if (subj < 0) { + return 2; + } + if (context < 0) { + return 3; + } + return 4; + } + + private static int score_opcs(long subj, long pred, long obj, long context) { + if (obj < 0) { + return 0; + } + if (pred < 0) { + return 1; + } + if (context < 0) { + return 2; + } + if (subj < 0) { + return 3; + } + return 4; + } + + private static int score_ocsp(long subj, long pred, long obj, long context) { + if (obj < 0) { + return 0; + } + if (context < 0) { + return 1; + } + if (subj < 0) { + return 2; + } + if (pred < 0) { + return 3; + } + return 4; + } + + private static int score_ocps(long subj, long pred, long obj, long context) { + if (obj < 0) { + return 0; + } + if (context < 0) { + return 1; + } + if (pred < 0) { + return 2; + } + if (subj < 0) { + return 3; + } + return 4; + } + + private static int score_cspo(long subj, long pred, long obj, long context) { + if (context < 0) { + return 0; + } + if (subj < 0) { + return 1; + } + if (pred < 0) { + return 2; + } + if (obj < 0) { + return 3; + } + return 4; + } + + private static int score_csop(long subj, long pred, long obj, long context) { + if (context < 0) { + return 0; + } + if (subj < 0) { + return 1; + } + if (obj < 0) { + return 2; + } + if (pred < 0) { + return 3; + } + return 4; + } + + private static int score_cpso(long subj, long pred, long obj, long context) { + if (context < 0) { + return 0; + } + if (pred < 0) { + return 1; + } + if (subj < 0) { + return 2; + } + if (obj < 0) { + return 3; + } + return 4; + } + + private static int score_cpos(long subj, long pred, long obj, long context) { + if (context < 0) { + return 0; + } + if (pred < 0) { + return 1; + } + if (obj < 0) { + return 2; + } + if (subj < 0) { + return 3; + } + return 4; + } + + private static int score_cosp(long subj, long pred, long obj, long context) { + if (context < 0) { + return 0; + } + if (obj < 0) { + return 1; + } + if (subj < 0) { + return 2; + } + if (pred < 0) { + return 3; + } + return 4; + } + + private static int score_cops(long subj, long pred, long obj, long context) { + if (context < 0) { + return 0; + } + if (obj < 0) { + return 1; + } + if (pred < 0) { + return 2; + } + if (subj < 0) { + return 3; + } + return 4; + } + } + + class SubjectPredicateIndex implements DupIndex { + + private final char[] fieldSeq = new char[] { 's', 'p', 'o', 'c' }; + private final int dbiDupExplicit; + private final int dbiDupInferred; + + SubjectPredicateIndex() throws IOException { + int flags = MDB_CREATE | org.lwjgl.util.lmdb.LMDB.MDB_DUPSORT + | org.lwjgl.util.lmdb.LMDB.MDB_DUPFIXED; + dbiDupExplicit = openDatabase(env, "sp-dup", flags, null); + dbiDupInferred = openDatabase(env, "sp-dup-inf", flags, null); + } + + @Override + public char[] getFieldSeq() { + return fieldSeq; + } + + @Override + public int getDupDB(boolean explicit) { + return explicit ? dbiDupExplicit : dbiDupInferred; + } + + @Override + public void toDupKeyPrefix(ByteBuffer bb, long subj, long pred, long obj, long context) { + Varint.writeUnsigned(bb, subj); + Varint.writeUnsigned(bb, pred); + } + + void toDupValue(ByteBuffer bb, long subj, long pred, long obj, long context) { + bb.putLong(obj); + bb.putLong(context); + } + + @Override + public int getPatternScore(long subj, long pred, long obj, long context) { + int score = 0; + if (subj >= 0) { + score++; + } else { + return score; + } + if (pred >= 0) { + score++; + } else { + return score; + } + if (obj >= 0) { + score++; + } else { + return score; + } + if (context >= 0) { + score++; + } + return score; + } + + void put(long txn, long subj, long pred, long obj, long context, boolean explicit, MemoryStack stack) + throws IOException { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + toDupKeyPrefix(dupKeyBuf, subj, pred, obj, context); + dupKeyBuf.flip(); + dupValBuf.clear(); + // store as two 8-byte big-endian longs to preserve LMDB's lexicographic ordering + writeLongBigEndian(dupValBuf, obj); + writeLongBigEndian(dupValBuf, context); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_put(txn, getDupDB(explicit), dupKeyVal, dupDataVal, 0)); + } finally { + stack.setPointer(frame); + } + } + + void delete(long txn, long subj, long pred, long obj, long context, boolean explicit, MemoryStack stack) + throws IOException { + int frame = stack.getPointer(); + try { + ByteBuffer dupKeyBuf = stack.malloc(MAX_KEY_LENGTH); + ByteBuffer dupValBuf = stack.malloc(Long.BYTES * 2); + dupKeyBuf.clear(); + toDupKeyPrefix(dupKeyBuf, subj, pred, obj, context); + dupKeyBuf.flip(); + dupValBuf.clear(); + writeLongBigEndian(dupValBuf, obj); + writeLongBigEndian(dupValBuf, context); + dupValBuf.flip(); + MDBVal dupKeyVal = MDBVal.malloc(stack); + MDBVal dupDataVal = MDBVal.malloc(stack); + dupKeyVal.mv_data(dupKeyBuf); + dupDataVal.mv_data(dupValBuf); + E(mdb_del(txn, getDupDB(explicit), dupKeyVal, dupDataVal)); + } finally { + stack.setPointer(frame); + } + } + + void close() { + mdb_dbi_close(env, dbiDupExplicit); + mdb_dbi_close(env, dbiDupInferred); + } + + private void writeLongBigEndian(ByteBuffer buffer, long value) { + buffer.put((byte) ((value >> (7 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (6 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (5 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (4 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (3 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (2 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (1 * 8)) & 0xFF)); + buffer.put((byte) ((value >> (0)) & 0xFF)); + } + } + static boolean threeOfFourAreZeroOrMax(long subj, long pred, long obj, long context) { // Precompute the 8 equalities once (cheapest operations here) boolean zS = subj == 0L, zP = pred == 0L, zO = obj == 0L, zC = context == 0L; diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnManager.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnManager.java index cf3d486b6fa..30dd3dcbd32 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnManager.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnManager.java @@ -64,7 +64,7 @@ private long startReadTxn() throws IOException { * @return the txn reference object */ Txn createTxn(long txn) { - return new Txn(txn) { + return new Txn(txn, false) { @Override public void close() { // do nothing @@ -79,7 +79,7 @@ public void close() { * @throws IOException if the transaction cannot be started for some reason */ Txn createReadTxn() throws IOException { - Txn txnRef = new Txn(createReadTxnInternal()); + Txn txnRef = new Txn(createReadTxnInternal(), true); synchronized (active) { active.put(txnRef, Boolean.TRUE); } @@ -164,9 +164,11 @@ class Txn implements Closeable, AutoCloseable { private long txn; private long version; + private final boolean readOnly; - Txn(long txn) { + Txn(long txn, boolean readOnly) { this.txn = txn; + this.readOnly = readOnly; } long get() { @@ -229,5 +231,9 @@ void setActive(boolean active) throws IOException { long version() { return version; } + + boolean isReadOnly() { + return readOnly; + } } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnRecordCache.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnRecordCache.java index 9974f6a2df2..6b1f59069f0 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnRecordCache.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/TxnRecordCache.java @@ -130,7 +130,7 @@ protected boolean update(long[] quad, boolean explicit, boolean add) throws IOEx // use calloc to get an empty data value MDBVal dataVal = MDBVal.calloc(stack); ByteBuffer keyBuf = stack.malloc(TripleStore.MAX_KEY_LENGTH); - Varint.writeListUnsigned(keyBuf, quad); + Varint.writeQuadUnsigned(keyBuf, quad); keyBuf.flip(); keyVal.mv_data(keyBuf); @@ -197,7 +197,7 @@ protected RecordCacheIterator(int dbi) throws IOException { public Record next() { if (mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT) == MDB_SUCCESS) { - Varint.readListUnsigned(keyData.mv_data(), quad); + Varint.readQuadUnsigned(keyData.mv_data(), quad); byte op = valueData.mv_data().get(0); Record r = new Record(); r.quad = quad; diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java index 2d8ac34f7e5..54a5152487c 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java @@ -67,6 +67,7 @@ import java.util.concurrent.locks.StampedLock; import java.util.zip.CRC32; +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; import org.eclipse.rdf4j.common.concurrent.locks.diagnostics.ConcurrentCleaner; import org.eclipse.rdf4j.common.io.ByteArrayUtil; import org.eclipse.rdf4j.model.BNode; @@ -96,7 +97,8 @@ /** * LMDB-based indexed storage and retrieval of RDF values. ValueStore maps RDF values to integer IDs and vice-versa. */ -class ValueStore extends AbstractValueFactory { +@InternalUseOnly +public class ValueStore extends AbstractValueFactory { private final static Logger logger = LoggerFactory.getLogger(ValueStore.class); diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java index af6632804d6..b926fbf9fd1 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java @@ -246,6 +246,18 @@ public static int calcListLengthUnsigned(long a, long b, long c, long d) { return calcLengthUnsigned(a) + calcLengthUnsigned(b) + calcLengthUnsigned(c) + calcLengthUnsigned(d); } + /** + * Advances the {@link ByteBuffer#position()} by the number of bytes required to encode the supplied value using the + * unsigned variable-length format, without writing any data. This assumes the buffer already contains the encoded + * representation at the current position. + * + * @param bb buffer whose position should be advanced + * @param value value whose encoded length should be skipped + */ + public static void advanceUnsigned(ByteBuffer bb, long value) { + bb.position(bb.position() + calcLengthUnsigned(value)); + } + /** * The number of bytes required to represent the given number minus one. The descriptor can be encoded in 3 bits. * @@ -446,26 +458,24 @@ public static long readListElementUnsigned(ByteBuffer bb, int index) { * @param values array with values to write */ public static void writeListUnsigned(final ByteBuffer bb, final long[] values) { - // TODO: Optimise for quads and also call writeUnsigned for (int i = 0; i < values.length; i++) { - final long value = values[i]; - if (value <= 240) { - bb.put((byte) value); - } else if (value <= 2287) { - bb.put((byte) ((value - 240) / 256 + 241)); - bb.put((byte) ((value - 240) % 256)); - } else if (value <= 67823) { - bb.put((byte) 249); - bb.put((byte) ((value - 2288) / 256)); - bb.put((byte) ((value - 2288) % 256)); - } else { - int bytes = descriptor(value) + 1; - bb.put((byte) (250 + (bytes - 3))); - writeSignificantBits(bb, value, bytes); - } + writeUnsigned(bb, values[i]); } } + /** + * Encodes multiple values using variable-length encoding into the given buffer. + * + * @param bb buffer for writing bytes + * @param values array with values to write + */ + public static void writeQuadUnsigned(final ByteBuffer bb, final long[] values) { + writeUnsigned(bb, values[0]); + writeUnsigned(bb, values[1]); + writeUnsigned(bb, values[2]); + writeUnsigned(bb, values[3]); + } + /** * Decodes multiple values using variable-length encoding from the given buffer. * diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java index 4c072e91317..882cb9fe51e 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreConfig.java @@ -73,6 +73,9 @@ public class LmdbStoreConfig extends BaseSailConfig { private boolean autoGrow = true; + private boolean dupsortIndices = true; + private boolean dupsortRead = true; + private long valueEvictionInterval = Duration.ofSeconds(60).toMillis(); /*--------------* @@ -190,6 +193,24 @@ public LmdbStoreConfig setValueEvictionInterval(long valueEvictionInterval) { return this; } + public boolean isDupsortIndices() { + return dupsortIndices; + } + + public LmdbStoreConfig setDupsortIndices(boolean dupsortIndices) { + this.dupsortIndices = dupsortIndices; + return this; + } + + public boolean isDupsortRead() { + return dupsortRead; + } + + public LmdbStoreConfig setDupsortRead(boolean dupsortRead) { + this.dupsortRead = dupsortRead; + return this; + } + @Override public Resource export(Model m) { Resource implNode = super.export(m); @@ -226,6 +247,13 @@ public Resource export(Model m) { if (valueEvictionInterval != Duration.ofSeconds(60).toMillis()) { m.add(implNode, LmdbStoreSchema.VALUE_EVICTION_INTERVAL, vf.createLiteral(valueEvictionInterval)); } + // Persist only when deviating from defaults (defaults: true) + if (!dupsortIndices) { + m.add(implNode, LmdbStoreSchema.DUPSORT_INDICES, vf.createLiteral(false)); + } + if (!dupsortRead) { + m.add(implNode, LmdbStoreSchema.DUPSORT_READ, vf.createLiteral(false)); + } return implNode; } @@ -330,6 +358,25 @@ public void parse(Model m, Resource implNode) throws SailConfigException { + " property, found " + lit); } }); + + Models.objectLiteral(m.getStatements(implNode, LmdbStoreSchema.DUPSORT_INDICES, null)).ifPresent(lit -> { + try { + setDupsortIndices(lit.booleanValue()); + } catch (IllegalArgumentException e) { + throw new SailConfigException( + "Boolean value required for " + LmdbStoreSchema.DUPSORT_INDICES + " property, found " + + lit); + } + }); + Models.objectLiteral(m.getStatements(implNode, LmdbStoreSchema.DUPSORT_READ, null)).ifPresent(lit -> { + try { + setDupsortRead(lit.booleanValue()); + } catch (IllegalArgumentException e) { + throw new SailConfigException( + "Boolean value required for " + LmdbStoreSchema.DUPSORT_READ + " property, found " + + lit); + } + }); } catch (ModelException e) { throw new SailConfigException(e.getMessage(), e); } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java index 8a9c5acca8d..6ad43f6518f 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/config/LmdbStoreSchema.java @@ -76,6 +76,16 @@ public class LmdbStoreSchema { */ public final static IRI VALUE_EVICTION_INTERVAL; + /** + * http://rdf4j.org/config/sail/lmdb#dupsortIndices + */ + public final static IRI DUPSORT_INDICES; + + /** + * http://rdf4j.org/config/sail/lmdb#dupsortRead + */ + public final static IRI DUPSORT_READ; + static { ValueFactory factory = SimpleValueFactory.getInstance(); TRIPLE_INDEXES = factory.createIRI(NAMESPACE, "tripleIndexes"); @@ -88,5 +98,7 @@ public class LmdbStoreSchema { NAMESPACE_ID_CACHE_SIZE = factory.createIRI(NAMESPACE, "namespaceIDCacheSize"); AUTO_GROW = factory.createIRI(NAMESPACE, "autoGrow"); VALUE_EVICTION_INTERVAL = factory.createIRI(NAMESPACE, "valueEvictionInterval"); + DUPSORT_INDICES = factory.createIRI(NAMESPACE, "dupsortIndices"); + DUPSORT_READ = factory.createIRI(NAMESPACE, "dupsortRead"); } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/IdJoinRecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/IdJoinRecordIterator.java new file mode 100644 index 00000000000..4da0fd4ea7f --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/IdJoinRecordIterator.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; + +/** + * Record iterator that interleaves left and right record iterators in the same way that + * {@link org.eclipse.rdf4j.query.algebra.evaluation.iterator.JoinIterator} does for binding sets, but operating purely + * on ID-based record arrays. + */ +public final class IdJoinRecordIterator implements RecordIterator { + + @FunctionalInterface + public interface RightFactory { + RecordIterator apply(long[] leftRecord) throws QueryEvaluationException; + } + + private final RecordIterator left; + private final RightFactory rightFactory; + private RecordIterator currentRight; + + public IdJoinRecordIterator(RecordIterator left, RightFactory rightFactory) { + this.left = left; + this.rightFactory = rightFactory; + } + + @Override + public long[] next() { + try { + while (true) { + if (currentRight != null) { + long[] rightRec = currentRight.next(); + if (rightRec != null) { + return rightRec; + } + currentRight.close(); + currentRight = null; + } + + long[] leftRec = left.next(); + if (leftRec == null) { + return null; + } + + currentRight = rightFactory.apply(leftRec); + if (currentRight == null) { + currentRight = LmdbIdJoinIterator.emptyRecordIterator(); + } + } + } catch (QueryEvaluationException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() { + left.close(); + if (currentRight != null) { + currentRight.close(); + currentRight = null; + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdBGPQueryEvaluationStep.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdBGPQueryEvaluationStep.java new file mode 100644 index 00000000000..830a1532fdc --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdBGPQueryEvaluationStep.java @@ -0,0 +1,891 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.EmptyIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.LmdbDatasetContext; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationDataset; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationDataset.KeyRangeBuffers; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationStrategy; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.TripleStore; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Builds a left-deep chain of ID-only join iterators for an entire BGP and materializes bindings only once. + */ +public final class LmdbIdBGPQueryEvaluationStep implements QueryEvaluationStep { + + private static final String ID_JOIN_ALGORITHM = LmdbIdJoinIterator.class.getSimpleName(); + + private final List plans; + private final List rawPatterns; + private final IdBindingInfo finalInfo; + private final QueryEvaluationContext context; + private final LmdbDatasetContext datasetContext; + private final Join root; + private final QueryEvaluationStep fallbackStep; + private final boolean hasInvalidPattern; + private final boolean createdDynamicIds; + private final boolean allowCreateConstantIds; + private final Map constantBindings; + private final List mergeSpecs; + private final Set mergeJoinNodes; + private final Map patternBuffers = new HashMap<>(); + + public LmdbIdBGPQueryEvaluationStep(Join root, List patterns, QueryEvaluationContext context, + QueryEvaluationStep fallbackStep) { + if (!(context instanceof LmdbDatasetContext)) { + throw new IllegalArgumentException("LMDB ID BGP join requires LMDB query evaluation context"); + } + this.root = root; + this.context = context; + this.datasetContext = (LmdbDatasetContext) context; + this.fallbackStep = fallbackStep; + + ValueStore valueStore = this.datasetContext.getValueStore() + .orElseThrow(() -> new IllegalStateException("LMDB ID BGP join requires ValueStore access")); + + Optional datasetOpt = this.datasetContext.getLmdbDataset(); + boolean overlayDataset = datasetOpt + .map(ds -> LmdbEvaluationStrategy.getCurrentDataset().map(current -> current != ds).orElse(true)) + .orElse(false); + boolean allowCreate = datasetOpt + .map(ds -> ds.hasTransactionChanges() || overlayDataset) + .orElseGet(LmdbEvaluationStrategy::hasActiveConnectionChanges); + this.allowCreateConstantIds = allowCreate; + + List mergeSpecs = new ArrayList<>(); + List flattened = new ArrayList<>(patterns.size()); + boolean flattenedOk = flattenBGP(root, flattened, mergeSpecs) && !flattened.isEmpty(); + List effectivePatterns = flattenedOk ? flattened : patterns; + + List rawPatterns = new ArrayList<>(effectivePatterns.size()); + boolean invalid = false; + boolean created = false; + Map constants = new HashMap<>(); + for (StatementPattern pattern : effectivePatterns) { + RawPattern raw = RawPattern.create(pattern, valueStore, allowCreate); + rawPatterns.add(raw); + invalid |= raw.invalid; + created |= raw.createdIds; + constants.putAll(raw.getConstantIds()); + } + this.hasInvalidPattern = invalid; + this.createdDynamicIds = created; + this.constantBindings = Collections.unmodifiableMap(constants); + + if (rawPatterns.isEmpty()) { + throw new IllegalArgumentException("Basic graph pattern must contain at least one statement pattern"); + } + + IdBindingInfo info = null; + for (RawPattern raw : rawPatterns) { + if (info == null) { + info = IdBindingInfo.fromFirstPattern(raw.patternInfo, context); + } else { + info = IdBindingInfo.combine(info, raw.patternInfo, context); + } + } + this.finalInfo = info; + + List planList = new ArrayList<>(rawPatterns.size()); + for (int i = 0; i < rawPatterns.size(); i++) { + planList.add(rawPatterns.get(i).toPlan(finalInfo)); + } + this.plans = planList; + this.rawPatterns = rawPatterns; + this.mergeSpecs = mergeSpecs; + this.mergeJoinNodes = new HashSet<>(); + for (MergeSpec spec : mergeSpecs) { + if (spec.join != null) { + mergeJoinNodes.add(spec.join); + } + } + } + + public boolean shouldUseFallbackImmediately() { + return hasInvalidPattern && fallbackStep != null && !allowCreateConstantIds; + } + + public void applyAlgorithmTag() { + markJoinTreeWithIdAlgorithm(root, mergeJoinNodes); + } + + @Override + public CloseableIteration evaluate(BindingSet bindings) { + if (fallbackStep != null && LmdbEvaluationStrategy.hasActiveConnectionChanges()) { + return fallbackStep.evaluate(bindings); + } + try { + LmdbEvaluationDataset dataset = resolveDataset(); + if (!dataset.hasTransactionChanges()) { + dataset.refreshSnapshot(); + } + if (fallbackStep != null && dataset.hasTransactionChanges()) { + return fallbackStep.evaluate(bindings); + } + if (hasInvalidPattern) { + return fallbackStep != null ? fallbackStep.evaluate(bindings) : new EmptyIteration<>(); + } + if (!dataset.hasTransactionChanges() && createdDynamicIds && fallbackStep != null + && !allowCreateConstantIds) { + return fallbackStep.evaluate(bindings); + } + + ValueStore valueStore = dataset.getValueStore(); + long[] initialBinding = finalInfo.createInitialBinding(bindings, valueStore); + if (initialBinding == null) { + return new EmptyIteration<>(); + } + + applyIndexHints(dataset, initialBinding); + List stages = buildStages(); + if (stages.isEmpty()) { + return new EmptyIteration<>(); + } + + RecordIterator iter = stages.get(0).createInitialIterator(dataset, initialBinding, valueStore); + for (int i = 1; i < stages.size(); i++) { + iter = stages.get(i).joinWith(iter, dataset, valueStore); + } + + return new LmdbIdFinalBindingSetIteration(iter, finalInfo, context, bindings, valueStore, constantBindings); + } catch (QueryEvaluationException e) { + throw e; + } + } + + private LmdbEvaluationDataset resolveDataset() { + Optional fromContext = datasetContext.getLmdbDataset(); + if (fromContext.isPresent()) { + return fromContext.get(); + } + return LmdbEvaluationStrategy.getCurrentDataset() + .orElseThrow(() -> new IllegalStateException("No active LMDB dataset available for join evaluation")); + } + + public static boolean flattenBGP(TupleExpr expr, List out) { + return flattenBGP(expr, out, null); + } + + public static boolean flattenBGP(TupleExpr expr, List out, List merges) { + if (expr instanceof StatementPattern) { + out.add((StatementPattern) expr); + return true; + } + if (expr instanceof Join) { + Join j = (Join) expr; + int leftStart = out.size(); + boolean left = flattenBGP(j.getLeftArg(), out, merges); + int leftEnd = out.size(); + boolean right = flattenBGP(j.getRightArg(), out, merges); + int rightEnd = out.size(); + if (!left || !right) { + return false; + } + if (j.isMergeJoin() && merges != null) { + if (rightEnd - leftStart != 2 || leftEnd - leftStart != 1) { + return false; + } + Var orderVar = j.getOrder(); + if (orderVar == null) { + return false; + } + merges.add(new MergeSpec(leftStart, leftEnd, rightEnd, orderVar.getName(), j)); + } + return true; + } + return false; + } + + private static void markJoinTreeWithIdAlgorithm(TupleExpr expr, Set mergeJoins) { + if (expr instanceof Join) { + Join join = (Join) expr; + if (mergeJoins != null && mergeJoins.contains(join)) { + join.setAlgorithm(LmdbIdMergeJoinIterator.class.getSimpleName()); + } else { + join.setAlgorithm(ID_JOIN_ALGORITHM); + } + markJoinTreeWithIdAlgorithm(join.getLeftArg(), mergeJoins); + markJoinTreeWithIdAlgorithm(join.getRightArg(), mergeJoins); + } + } + + private List buildStages() { + List stages = new ArrayList<>(); + boolean[] consumed = new boolean[plans.size()]; + for (MergeSpec spec : mergeSpecs) { + int leftIndex = spec.leftIndex; + int rightIndex = spec.rightIndex(); + if (leftIndex < 0 || rightIndex >= plans.size()) { + continue; + } + RawPattern leftRaw = rawPatterns.get(leftIndex); + RawPattern rightRaw = rawPatterns.get(rightIndex); + StatementOrder leftOrder = determineOrder(leftRaw, spec.mergeVariable); + StatementOrder rightOrder = determineOrder(rightRaw, spec.mergeVariable); + PatternPlan leftPlan = leftRaw.toPlan(finalInfo, leftOrder); + PatternPlan rightPlan = rightRaw.toPlan(finalInfo, rightOrder); + KeyRangeBuffers leftBuffers = keyBuffersFor(leftPlan.pattern); + KeyRangeBuffers rightBuffers = keyBuffersFor(rightPlan.pattern); + stages.add(new MergeStage(leftPlan, rightPlan, leftBuffers, rightBuffers, leftRaw.patternInfo, + rightRaw.patternInfo, spec.mergeVariable)); + consumed[leftIndex] = true; + consumed[rightIndex] = true; + } + for (int i = 0; i < plans.size(); i++) { + if (!consumed[i]) { + PatternPlan plan = plans.get(i); + stages.add(new PatternStage(plan, keyBuffersFor(plan.pattern))); + } + } + return stages; + } + + private StatementOrder determineOrder(RawPattern pattern, String mergeVariable) { + if (pattern.pattern.getStatementOrder() != null) { + return pattern.pattern.getStatementOrder(); + } + if (mergeVariable == null) { + return null; + } + int mask = pattern.patternInfo.getPositionsMask(mergeVariable); + if ((mask & (1 << TripleStore.SUBJ_IDX)) != 0) { + return StatementOrder.S; + } + if ((mask & (1 << TripleStore.PRED_IDX)) != 0) { + return StatementOrder.P; + } + if ((mask & (1 << TripleStore.OBJ_IDX)) != 0) { + return StatementOrder.O; + } + if ((mask & (1 << TripleStore.CONTEXT_IDX)) != 0) { + return StatementOrder.C; + } + return null; + } + + private void applyIndexHints(LmdbEvaluationDataset dataset) { + applyIndexHints(dataset, null); + } + + private void applyIndexHints(LmdbEvaluationDataset dataset, long[] binding) { + for (int i = 0; i < rawPatterns.size(); i++) { + RawPattern raw = rawPatterns.get(i); + StatementPattern pattern = raw.pattern; + pattern.setIndex(null); + pattern.setIndexName(null); + if (dataset == null) { + continue; + } + long subj = resolveComponent(raw.patternIds[TripleStore.SUBJ_IDX], plans.get(i).subjIndex, binding); + long pred = resolveComponent(raw.patternIds[TripleStore.PRED_IDX], plans.get(i).predIndex, binding); + long obj = resolveComponent(raw.patternIds[TripleStore.OBJ_IDX], plans.get(i).objIndex, binding); + long ctx = resolveComponent(raw.patternIds[TripleStore.CONTEXT_IDX], plans.get(i).ctxIndex, binding); + String fieldSeq = dataset.selectBestIndex(subj, pred, obj, ctx); + if (fieldSeq == null) { + continue; + } + pattern.setIndexName(fieldSeq); + String enumKey = fieldSeq.toUpperCase(Locale.ROOT); + try { + pattern.setIndex(StatementPattern.Index.valueOf(enumKey)); + } catch (IllegalArgumentException ignore) { + pattern.setIndex(null); + } + } + } + + private KeyRangeBuffers keyBuffersFor(StatementPattern pattern) { + return patternBuffers.computeIfAbsent(pattern, p -> KeyRangeBuffers.acquire()); + } + + private static long resolveComponent(long constantId, int bindingIndex, long[] binding) { + if (constantId != LmdbValue.UNKNOWN_ID) { + return constantId; + } + if (binding != null && bindingIndex >= 0 && bindingIndex < binding.length) { + long fromBinding = binding[bindingIndex]; + if (fromBinding != LmdbValue.UNKNOWN_ID) { + return fromBinding; + } + } + return LmdbValue.UNKNOWN_ID; + } + + private interface Stage { + RecordIterator createInitialIterator(LmdbEvaluationDataset dataset, long[] initialBinding, + ValueStore valueStore) throws QueryEvaluationException; + + RecordIterator joinWith(RecordIterator left, LmdbEvaluationDataset dataset, ValueStore valueStore) + throws QueryEvaluationException; + } + + private static final class PatternStage implements Stage { + private final PatternPlan plan; + private final KeyRangeBuffers keyBuffers; + private long[] bindingScratch; + private long[] quadScratch; + private RecordIterator reusableIterator; + + private PatternStage(PatternPlan plan, KeyRangeBuffers keyBuffers) { + this.plan = plan; + this.keyBuffers = keyBuffers; + } + + @Override + public RecordIterator createInitialIterator(LmdbEvaluationDataset dataset, long[] initialBinding, + ValueStore valueStore) throws QueryEvaluationException { + if (bindingScratch == null || bindingScratch.length < initialBinding.length) { + bindingScratch = new long[initialBinding.length]; + } + if (quadScratch == null) { + quadScratch = new long[4]; + } + RecordIterator iter = dataset.getRecordIterator(initialBinding, plan.subjIndex, plan.predIndex, + plan.objIndex, plan.ctxIndex, plan.patternIds, keyBuffers, bindingScratch, quadScratch, + reusableIterator); + if (iter != null && iter != LmdbIdJoinIterator.emptyRecordIterator()) { + reusableIterator = iter; + } else { + reusableIterator = null; + } + return iter == null ? LmdbIdJoinIterator.emptyRecordIterator() : iter; + } + + @Override + public RecordIterator joinWith(RecordIterator left, LmdbEvaluationDataset dataset, ValueStore valueStore) + throws QueryEvaluationException { + return new BindingJoinRecordIterator(left, dataset, plan, keyBuffers); + } + } + + private final class MergeStage implements Stage { + private final PatternPlan leftPlan; + private final PatternPlan rightPlan; + private final KeyRangeBuffers leftKeyBuffers; + private final KeyRangeBuffers rightKeyBuffers; + private final LmdbIdJoinIterator.PatternInfo leftInfo; + private final LmdbIdJoinIterator.PatternInfo rightInfo; + private final String mergeVariable; + private long[] sequentialLeftScratch; + private long[] orderedLeftScratch; + private long[] orderedRightScratch; + private long[] sequentialLeftQuadScratch; + private long[] orderedLeftQuadScratch; + private long[] orderedRightQuadScratch; + private RecordIterator sequentialLeftReusable; + private RecordIterator orderedLeftReusable; + private RecordIterator orderedRightReusable; + + private MergeStage(PatternPlan leftPlan, PatternPlan rightPlan, KeyRangeBuffers leftKeyBuffers, + KeyRangeBuffers rightKeyBuffers, LmdbIdJoinIterator.PatternInfo leftInfo, + LmdbIdJoinIterator.PatternInfo rightInfo, String mergeVariable) { + this.leftPlan = leftPlan; + this.rightPlan = rightPlan; + this.leftKeyBuffers = leftKeyBuffers; + this.rightKeyBuffers = rightKeyBuffers; + this.leftInfo = leftInfo; + this.rightInfo = rightInfo; + this.mergeVariable = mergeVariable; + } + + @Override + public RecordIterator createInitialIterator(LmdbEvaluationDataset dataset, long[] initialBinding, + ValueStore valueStore) throws QueryEvaluationException { + RecordIterator merge = createMergeIterator(dataset, initialBinding, valueStore); + return merge == null ? LmdbIdJoinIterator.emptyRecordIterator() : merge; + } + + @Override + public RecordIterator joinWith(RecordIterator left, LmdbEvaluationDataset dataset, ValueStore valueStore) + throws QueryEvaluationException { + return new RecordIterator() { + private final RecordIterator leftIter = left; + private RecordIterator currentMerge; + private long[] mergeScratch; + + @Override + public long[] next() throws QueryEvaluationException { + while (true) { + if (currentMerge != null) { + long[] merged = currentMerge.next(); + if (merged != null) { + return merged; + } + currentMerge.close(); + currentMerge = null; + } + long[] leftBinding = leftIter.next(); + if (leftBinding == null) { + return null; + } + if (mergeScratch == null || mergeScratch.length < leftBinding.length) { + mergeScratch = new long[leftBinding.length]; + } + System.arraycopy(leftBinding, 0, mergeScratch, 0, leftBinding.length); + currentMerge = createMergeIterator(dataset, mergeScratch, valueStore); + } + } + + @Override + public void close() { + if (currentMerge != null) { + currentMerge.close(); + } + leftIter.close(); + } + }; + } + + private RecordIterator createMergeIterator(LmdbEvaluationDataset dataset, long[] binding, ValueStore valueStore) + throws QueryEvaluationException { + if (leftPlan.order == null || rightPlan.order == null) { + return createSequentialIterator(dataset, binding, valueStore); + } + if (orderedLeftScratch == null || orderedLeftScratch.length < binding.length) { + orderedLeftScratch = new long[binding.length]; + } + if (orderedLeftQuadScratch == null) { + orderedLeftQuadScratch = new long[4]; + } + RecordIterator leftIterator = dataset.getOrderedRecordIterator(binding, leftPlan.subjIndex, + leftPlan.predIndex, leftPlan.objIndex, leftPlan.ctxIndex, leftPlan.patternIds, leftPlan.order, + leftKeyBuffers, orderedLeftScratch, orderedLeftQuadScratch, orderedLeftReusable); + if (leftIterator == null) { + orderedLeftReusable = null; + return createSequentialIterator(dataset, binding, valueStore); + } + if (leftIterator != LmdbIdJoinIterator.emptyRecordIterator()) { + orderedLeftReusable = leftIterator; + } else { + orderedLeftReusable = null; + } + + if (orderedRightScratch == null || orderedRightScratch.length < binding.length) { + orderedRightScratch = new long[binding.length]; + } + if (orderedRightQuadScratch == null) { + orderedRightQuadScratch = new long[4]; + } + RecordIterator rightIterator = dataset.getOrderedRecordIterator(binding, rightPlan.subjIndex, + rightPlan.predIndex, rightPlan.objIndex, rightPlan.ctxIndex, rightPlan.patternIds, rightPlan.order, + rightKeyBuffers, orderedRightScratch, orderedRightQuadScratch, orderedRightReusable); + if (rightIterator == null) { + leftIterator.close(); + orderedRightReusable = null; + return createSequentialIterator(dataset, binding, valueStore); + } + if (rightIterator != LmdbIdJoinIterator.emptyRecordIterator()) { + orderedRightReusable = rightIterator; + } else { + orderedRightReusable = null; + } + + return new LmdbIdMergeJoinIterator(leftIterator, rightIterator, leftInfo, rightInfo, mergeVariable, + finalInfo); + } + + private RecordIterator createSequentialIterator(LmdbEvaluationDataset dataset, long[] binding, + ValueStore valueStore) throws QueryEvaluationException { + if (sequentialLeftScratch == null || sequentialLeftScratch.length < binding.length) { + sequentialLeftScratch = new long[binding.length]; + } + if (sequentialLeftQuadScratch == null) { + sequentialLeftQuadScratch = new long[4]; + } + RecordIterator leftIterator = dataset.getRecordIterator(binding, leftPlan.subjIndex, leftPlan.predIndex, + leftPlan.objIndex, leftPlan.ctxIndex, leftPlan.patternIds, leftKeyBuffers, sequentialLeftScratch, + sequentialLeftQuadScratch, sequentialLeftReusable); + if (leftIterator != null && leftIterator != LmdbIdJoinIterator.emptyRecordIterator()) { + sequentialLeftReusable = leftIterator; + } else { + sequentialLeftReusable = null; + } + if (leftIterator == null) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + return new BindingJoinRecordIterator(leftIterator, dataset, rightPlan, rightKeyBuffers); + } + } + + private static final class BindingJoinRecordIterator implements RecordIterator { + private final RecordIterator left; + private final LmdbEvaluationDataset dataset; + private final PatternPlan plan; + private final KeyRangeBuffers keyBuffers; + private RecordIterator currentRight; + private long[] rightScratch; + private long[] quadScratch; + private RecordIterator reusableRight; + + private BindingJoinRecordIterator(RecordIterator left, LmdbEvaluationDataset dataset, PatternPlan plan, + KeyRangeBuffers keyBuffers) { + this.left = left; + this.dataset = dataset; + this.plan = plan; + this.keyBuffers = keyBuffers; + } + + @Override + public long[] next() throws QueryEvaluationException { + while (true) { + if (currentRight != null) { + long[] next = currentRight.next(); + if (next != null) { + return next; + } + if (reusableRight == null && currentRight != LmdbIdJoinIterator.EMPTY_RECORD_ITERATOR) { + reusableRight = currentRight; + } else if (reusableRight != currentRight + && currentRight != LmdbIdJoinIterator.EMPTY_RECORD_ITERATOR) { + reusableRight.close(); + reusableRight = currentRight; + } + currentRight = null; + } + + long[] leftBinding = left.next(); + if (leftBinding == null) { + return null; + } + if (rightScratch == null || rightScratch.length < leftBinding.length) { + rightScratch = new long[leftBinding.length]; + } + if (quadScratch == null) { + quadScratch = new long[4]; + } + currentRight = dataset.getRecordIterator(leftBinding, plan.subjIndex, plan.predIndex, plan.objIndex, + plan.ctxIndex, plan.patternIds, keyBuffers, rightScratch, quadScratch, reusableRight); + if (currentRight != null && currentRight != LmdbIdJoinIterator.EMPTY_RECORD_ITERATOR) { + reusableRight = currentRight; + } else { + reusableRight = null; + } + } + } + + @Override + public void close() { + // capture references and null out fields early to avoid double-closing if re-entered + RecordIterator toCloseCurrent = currentRight; + RecordIterator toCloseLeft = left; + RecordIterator toCloseReusable = reusableRight; + + currentRight = null; + reusableRight = null; + + RuntimeException first = null; + + // Close currentRight + try { + if (toCloseCurrent != null) { + toCloseCurrent.close(); + } + } catch (RuntimeException e) { + first = e; + } finally { + // Always attempt to close left + try { + if (toCloseLeft != null) { + toCloseLeft.close(); + } + } catch (RuntimeException e) { + if (first == null) { + first = e; + } + } finally { + // Always attempt to close reusableRight + try { + if (toCloseReusable != null) { + toCloseReusable.close(); + } + } catch (RuntimeException e) { + if (first == null) { + first = e; + } + } + } + } + + // Rethrow the first failure after attempting all closes + if (first != null) { + throw first; + } + } + } + + private static final class PatternPlan { + private final long[] patternIds; + private final int subjIndex; + private final int predIndex; + private final int objIndex; + private final int ctxIndex; + private final StatementOrder order; + private final StatementPattern pattern; + + private PatternPlan(long[] patternIds, int subjIndex, int predIndex, int objIndex, int ctxIndex, + StatementOrder order, StatementPattern pattern) { + this.patternIds = patternIds; + this.subjIndex = subjIndex; + this.predIndex = predIndex; + this.objIndex = objIndex; + this.ctxIndex = ctxIndex; + this.order = order; + this.pattern = pattern; + } + } + + private static final class RawPattern { + private final StatementPattern pattern; + private final long[] patternIds; + private final String subjVar; + private final String predVar; + private final String objVar; + private final String ctxVar; + private final LmdbIdJoinIterator.PatternInfo patternInfo; + private final boolean invalid; + private final boolean createdIds; + private final Map constantIds; + + private RawPattern(StatementPattern pattern, long[] patternIds, String subjVar, String predVar, String objVar, + String ctxVar, + LmdbIdJoinIterator.PatternInfo patternInfo, boolean invalid, boolean createdIds, + Map constantIds) { + this.pattern = pattern; + this.patternIds = patternIds; + this.subjVar = subjVar; + this.predVar = predVar; + this.objVar = objVar; + this.ctxVar = ctxVar; + this.patternInfo = patternInfo; + this.invalid = invalid; + this.createdIds = createdIds; + this.constantIds = constantIds; + } + + Map getConstantIds() { + return constantIds; + } + + static RawPattern create(StatementPattern pattern, ValueStore valueStore, boolean allowCreate) { + long[] ids = new long[4]; + Arrays.fill(ids, LmdbValue.UNKNOWN_ID); + + boolean invalid = false; + boolean createdAny = false; + Map constantIds = new HashMap<>(); + + Var subj = pattern.getSubjectVar(); + String subjVar = null; + if (subj != null) { + if (subj.hasValue()) { + ConstantIdResult result = constantId(valueStore, subj.getValue(), true, false, allowCreate); + if (result.isInvalid()) { + invalid = true; + } else { + ids[TripleStore.SUBJ_IDX] = result.id; + createdAny |= result.created; + constantIds.put(subj.getName(), result.id); + } + } else { + subjVar = subj.getName(); + } + } + + Var pred = pattern.getPredicateVar(); + String predVar = null; + if (pred != null) { + if (pred.hasValue()) { + ConstantIdResult result = constantId(valueStore, pred.getValue(), false, true, allowCreate); + if (result.isInvalid()) { + invalid = true; + } else { + ids[TripleStore.PRED_IDX] = result.id; + createdAny |= result.created; + constantIds.put(pred.getName(), result.id); + } + } else { + predVar = pred.getName(); + } + } + + Var obj = pattern.getObjectVar(); + String objVar = null; + if (obj != null) { + if (obj.hasValue()) { + ConstantIdResult result = constantId(valueStore, obj.getValue(), false, false, allowCreate); + if (result.isInvalid()) { + invalid = true; + } else { + ids[TripleStore.OBJ_IDX] = result.id; + createdAny |= result.created; + constantIds.put(obj.getName(), result.id); + } + } else { + objVar = obj.getName(); + } + } + + Var ctx = pattern.getContextVar(); + String ctxVar = null; + if (ctx != null) { + if (ctx.hasValue()) { + ConstantIdResult result = constantId(valueStore, ctx.getValue(), true, false, allowCreate); + if (result.isInvalid()) { + invalid = true; + } else { + ids[TripleStore.CONTEXT_IDX] = result.id; + createdAny |= result.created; + constantIds.put(ctx.getName(), result.id); + } + } else { + ctxVar = ctx.getName(); + } + } + + LmdbIdJoinIterator.PatternInfo info = LmdbIdJoinIterator.PatternInfo.create(pattern); + return new RawPattern(pattern, ids, subjVar, predVar, objVar, ctxVar, info, invalid, createdAny, + constantIds); + } + + PatternPlan toPlan(IdBindingInfo finalInfo) { + return toPlan(finalInfo, null); + } + + PatternPlan toPlan(IdBindingInfo finalInfo, StatementOrder order) { + return new PatternPlan(patternIds.clone(), indexFor(subjVar, finalInfo), + indexFor(predVar, finalInfo), indexFor(objVar, finalInfo), indexFor(ctxVar, finalInfo), order, + pattern); + } + + private static int indexFor(String varName, IdBindingInfo info) { + return varName == null ? -1 : info.getIndex(varName); + } + + private static ConstantIdResult constantId(ValueStore valueStore, Value value, boolean requireResource, + boolean requireIri, boolean allowCreate) { + if (requireResource && !(value instanceof Resource)) { + return ConstantIdResult.invalid(); + } + if (requireIri && !(value instanceof IRI)) { + return ConstantIdResult.invalid(); + } + if (value instanceof Resource && ((Resource) value).isTriple()) { + return ConstantIdResult.invalid(); + } + try { + if (value instanceof LmdbValue) { + LmdbValue lmdbValue = (LmdbValue) value; + if (lmdbValue.getValueStoreRevision().getValueStore() == valueStore) { + long id = lmdbValue.getInternalID(); + if (id != LmdbValue.UNKNOWN_ID) { + return ConstantIdResult.existing(id); + } + } + } + long id = valueStore.getId(value); + if (id == LmdbValue.UNKNOWN_ID && !allowCreate) { + id = valueStore.getId(value); + } + if (id == LmdbValue.UNKNOWN_ID) { + if (!allowCreate) { + return ConstantIdResult.invalid(); + } + id = valueStore.getId(value, true); + if (id == LmdbValue.UNKNOWN_ID) { + return ConstantIdResult.invalid(); + } + return ConstantIdResult.created(id); + } + return ConstantIdResult.existing(id); + } catch (IOException e) { + return ConstantIdResult.invalid(); + } + } + + private static final class ConstantIdResult { + private final long id; + private final boolean created; + private final boolean valid; + + private ConstantIdResult(long id, boolean created, boolean valid) { + this.id = id; + this.created = created; + this.valid = valid; + } + + static ConstantIdResult invalid() { + return new ConstantIdResult(Long.MIN_VALUE, false, false); + } + + static ConstantIdResult existing(long id) { + return new ConstantIdResult(id, false, true); + } + + static ConstantIdResult created(long id) { + return new ConstantIdResult(id, true, true); + } + + boolean isInvalid() { + return !valid; + } + } + } + + private static final class MergeSpec { + private final int leftIndex; + private final int leftEnd; + private final int rightEnd; + private final String mergeVariable; + private final Join join; + + private MergeSpec(int leftIndex, int leftEnd, int rightEnd, String mergeVariable, Join join) { + this.leftIndex = leftIndex; + this.leftEnd = leftEnd; + this.rightEnd = rightEnd; + this.mergeVariable = mergeVariable; + this.join = join; + } + + int rightIndex() { + return rightEnd - 1; + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdFinalBindingSetIteration.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdFinalBindingSetIteration.java new file mode 100644 index 00000000000..aa9eefee674 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdFinalBindingSetIteration.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import org.eclipse.rdf4j.common.iteration.LookAheadIteration; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; + +/** + * Final adapter that materializes ID-binding records to BindingSets once, at the end of a join chain. + */ +final class LmdbIdFinalBindingSetIteration extends LookAheadIteration { + + private final RecordIterator input; + private final IdBindingInfo info; + private final QueryEvaluationContext context; + private final BindingSet initial; + private final ValueStore valueStore; + private final Map constantBindings; + private List constantEntries; + + LmdbIdFinalBindingSetIteration(RecordIterator input, IdBindingInfo info, QueryEvaluationContext context, + BindingSet initial, ValueStore valueStore, Map constantBindings) { + this.input = input; + this.info = info; + this.context = context; + this.initial = initial; + this.valueStore = valueStore; + this.constantBindings = constantBindings; + this.constantEntries = Collections.emptyList(); + if (!constantBindings.isEmpty()) { + List entries = new ArrayList<>(constantBindings.size()); + for (Map.Entry e : constantBindings.entrySet()) { + String name = e.getKey(); + long id = e.getValue(); + Function getter = context.getValue(name); + BiConsumer setter = context.setBinding(name); + entries.add(new ConstEntry(getter, setter, id)); + } + this.constantEntries = Collections.unmodifiableList(entries); + } + } + + @Override + protected BindingSet getNextElement() throws QueryEvaluationException { + // No global map; each constant entry resolves lazily once. + long[] rec; + while ((rec = input.next()) != null) { + MutableBindingSet bs = context.createBindingSet(initial); + if (!constantEntries.isEmpty()) { + for (ConstEntry ce : constantEntries) { + // Only set constants not already present + if (ce.getter.apply(bs) != null) { + continue; + } + if (!ce.valueResolved) { + try { + ce.value = LmdbIdJoinSettings.resolveValue(valueStore, ce.id); + } catch (IOException e) { + throw new QueryEvaluationException(e); + } + ce.valueResolved = true; + } + if (ce.value != null) { + ce.setter.accept(ce.value, bs); + } + } + } + if (info.applyRecord(rec, bs, valueStore)) { + return bs; + } + } + return null; + } + + @Override + protected void handleClose() throws QueryEvaluationException { + input.close(); + } + + private static final class ConstEntry { + final Function getter; + final BiConsumer setter; + final long id; + boolean valueResolved; + Value value; + + ConstEntry(Function getter, + BiConsumer setter, + long id) { + this.getter = getter; + this.setter = setter; + this.id = id; + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinIterator.java new file mode 100644 index 00000000000..ac0483f2003 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinIterator.java @@ -0,0 +1,329 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.LookAheadIteration; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.sail.lmdb.IdAccessor; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.TripleStore; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Join iterator that operates on LMDB internal IDs (long arrays) instead of binding sets. + */ +public class LmdbIdJoinIterator extends LookAheadIteration { + + @FunctionalInterface + interface RecordIteratorFactory { + RecordIterator apply(long[] leftRecord, RecordIterator reuse) throws QueryEvaluationException; + } + + public static final RecordIterator EMPTY_RECORD_ITERATOR = new RecordIterator() { + @Override + public long[] next() { + return null; + } + + @Override + public void close() { + // no-op + } + }; + + public static RecordIterator emptyRecordIterator() { + return EMPTY_RECORD_ITERATOR; + } + + public static final class PatternInfo implements IdAccessor { + private final Map indexByVar; + private final Set variableNames; + private final boolean hasContextVar; + + private PatternInfo(Map indexByVar, boolean hasContextVar) { + this.indexByVar = indexByVar; + this.variableNames = Collections.unmodifiableSet(indexByVar.keySet()); + this.hasContextVar = hasContextVar; + } + + static PatternInfo create(StatementPattern pattern) { + Map map = new HashMap<>(); + registerVar(map, pattern.getSubjectVar(), TripleStore.SUBJ_IDX); + registerVar(map, pattern.getPredicateVar(), TripleStore.PRED_IDX); + registerVar(map, pattern.getObjectVar(), TripleStore.OBJ_IDX); + boolean hasContext = registerVar(map, pattern.getContextVar(), TripleStore.CONTEXT_IDX); + return new PatternInfo(map, hasContext); + } + + private static boolean registerVar(Map map, Var var, int index) { + if (var == null || var.hasValue()) { + return false; + } + map.compute(var.getName(), (k, v) -> { + if (v == null) { + return new int[] { index }; + } + int[] expanded = Arrays.copyOf(v, v.length + 1); + expanded[v.length] = index; + return expanded; + }); + return index == TripleStore.CONTEXT_IDX; + } + + @Override + public Set getVariableNames() { + return variableNames; + } + + @Override + public int getRecordIndex(String varName) { + int[] indices = indexByVar.get(varName); + if (indices == null || indices.length == 0) { + return -1; + } + return indices[0]; + } + + boolean hasContextVar() { + return hasContextVar; + } + + public int getPositionsMask(String varName) { + int[] indices = indexByVar.get(varName); + if (indices == null) { + return 0; + } + int mask = 0; + for (int idx : indices) { + mask |= (1 << idx); + } + return mask; + } + + @Override + public long getId(long[] record, String varName) { + int[] indices = indexByVar.get(varName); + if (indices == null || indices.length == 0) { + return LmdbValue.UNKNOWN_ID; + } + return record[indices[0]]; + } + + @Override + public boolean applyRecord(long[] record, MutableBindingSet target, ValueStore valueStore) + throws QueryEvaluationException { + for (Map.Entry entry : indexByVar.entrySet()) { + String name = entry.getKey(); + int[] indices = entry.getValue(); + Value existing = target.getValue(name); + + // Fast path: if an existing binding is an LmdbValue from the same store, + // compare IDs directly and avoid resolving candidate Values. + if (existing instanceof LmdbValue) { + LmdbValue lmdbExisting = (LmdbValue) existing; + if (lmdbExisting.getValueStoreRevision().getValueStore() == valueStore) { + long existingId = lmdbExisting.getInternalID(); + if (existingId != LmdbValue.UNKNOWN_ID) { + for (int index : indices) { + long id = record[index]; + // Context id of 0 is effectively null; conflicts with an existing non-null + if (index == TripleStore.CONTEXT_IDX && id == 0L) { + return false; + } + if (id != LmdbValue.UNKNOWN_ID && id != existingId) { + return false; + } + } + continue; // this variable satisfied + } + } + } + + // General path: collect a consistent candidate id across positions (if any), + // minimize value resolutions to at most one per variable. + long chosenId = LmdbValue.UNKNOWN_ID; + boolean haveId = false; + for (int index : indices) { + long id = record[index]; + // Treat default context (0) as null candidate; it conflicts with an existing non-null value. + if (index == TripleStore.CONTEXT_IDX && id == 0L) { + if (existing != null) { + return false; + } + continue; + } + if (id == LmdbValue.UNKNOWN_ID) { + continue; + } + if (existing != null) { + // Fallback: existing is non-LMDB or unknown id; resolve candidate once and compare + Value candidate = resolveValue(id, index, valueStore); + if (candidate == null || !existing.equals(candidate)) { + return false; + } + } else { + if (!haveId) { + chosenId = id; + haveId = true; + } else if (chosenId != id) { + // Variable appears in multiple positions with conflicting ids + return false; + } + } + } + + if (existing == null && haveId) { + Value candidate = resolveValue(chosenId, TripleStore.SUBJ_IDX /* ignored */, valueStore); + if (candidate != null) { + target.setBinding(name, candidate); + } + } + } + return true; + } + + private Value resolveValue(long id, int position, ValueStore valueStore) throws QueryEvaluationException { + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + if (position == TripleStore.CONTEXT_IDX && id == 0L) { + return null; + } + try { + return LmdbIdJoinSettings.resolveValue(valueStore, id); + } catch (IOException e) { + throw new QueryEvaluationException(e); + } + } + } + + private final RecordIterator leftIterator; + private final RecordIteratorFactory rightFactory; + private final IdAccessor leftInfo; + private final IdAccessor rightInfo; + private final Set sharedVariables; + private final QueryEvaluationContext context; + private final BindingSet initialBindings; + private final ValueStore valueStore; + + private RecordIterator reusableRightIterator; + private RecordIterator currentRightIterator; + private long[] currentLeftRecord; + private BindingSet currentLeftBinding; + + LmdbIdJoinIterator(RecordIterator leftIterator, RecordIteratorFactory rightFactory, IdAccessor leftInfo, + IdAccessor rightInfo, Set sharedVariables, QueryEvaluationContext context, + BindingSet initialBindings, ValueStore valueStore) { + this.leftIterator = leftIterator; + this.rightFactory = rightFactory; + this.leftInfo = leftInfo; + this.rightInfo = rightInfo; + this.sharedVariables = sharedVariables; + this.context = context; + this.initialBindings = initialBindings; + this.valueStore = valueStore; + } + + @Override + protected BindingSet getNextElement() throws QueryEvaluationException { + while (true) { + if (currentRightIterator != null) { + long[] rightRecord; + while ((rightRecord = nextRecord(currentRightIterator)) != null) { + if (!matchesJoin(currentLeftRecord, rightRecord)) { + continue; + } + MutableBindingSet result = context.createBindingSet(initialBindings); + if (!leftInfo.applyRecord(currentLeftRecord, result, valueStore)) { + continue; + } + if (!rightInfo.applyRecord(rightRecord, result, valueStore)) { + continue; + } + return result; + } + RecordIterator completed = currentRightIterator; + currentRightIterator = null; + if (completed != EMPTY_RECORD_ITERATOR) { + reusableRightIterator = completed; + } else { + reusableRightIterator = null; + } + completed.close(); + } + + long[] leftRecord = nextRecord(leftIterator); + if (leftRecord == null) { + reusableRightIterator = null; + return null; + } + + currentLeftRecord = leftRecord; + currentLeftBinding = null; + + RecordIterator reuseCandidate = reusableRightIterator; + if (reuseCandidate == EMPTY_RECORD_ITERATOR) { + reuseCandidate = null; + } + + currentRightIterator = rightFactory.apply(leftRecord, reuseCandidate); + if (currentRightIterator == null) { + currentRightIterator = emptyRecordIterator(); + reusableRightIterator = null; + } else if (currentRightIterator == EMPTY_RECORD_ITERATOR) { + reusableRightIterator = null; + } else { + reusableRightIterator = currentRightIterator; + } + } + } + + private boolean matchesJoin(long[] leftRecord, long[] rightRecord) { + for (String name : sharedVariables) { + long leftId = leftInfo.getId(leftRecord, name); + long rightId = rightInfo.getId(rightRecord, name); + if (leftId != LmdbValue.UNKNOWN_ID && rightId != LmdbValue.UNKNOWN_ID && leftId != rightId) { + return false; + } + } + return true; + } + + private long[] nextRecord(RecordIterator iterator) throws QueryEvaluationException { + // Avoid per-record array copy: LmdbRecordIterator returns a stable array instance + // that is only mutated on subsequent next() calls. We consume the array fully + // before advancing the iterator again, so returning it directly is safe here. + return iterator.next(); + } + + @Override + protected void handleClose() throws QueryEvaluationException { + leftIterator.close(); + if (currentRightIterator != null) { + currentRightIterator.close(); + currentRightIterator = null; + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinQueryEvaluationStep.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinQueryEvaluationStep.java new file mode 100644 index 00000000000..4090314dac4 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinQueryEvaluationStep.java @@ -0,0 +1,369 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import static org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.LmdbDatasetContext; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationDataset; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationDataset.KeyRangeBuffers; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationStrategy; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Query evaluation step that wires up the LMDB ID join iterator. + */ +public class LmdbIdJoinQueryEvaluationStep implements QueryEvaluationStep { + + private final StatementPattern leftPattern; + private final StatementPattern rightPattern; + private final QueryEvaluationContext context; + private final LmdbIdJoinIterator.PatternInfo leftInfo; + private final LmdbIdJoinIterator.PatternInfo rightInfo; + private final Set sharedVariables; + private final LmdbDatasetContext datasetContext; + private final QueryEvaluationStep fallbackStep; + private final boolean fallbackImmediately; + private final Map patternBuffers = new HashMap<>(); + + public LmdbIdJoinQueryEvaluationStep(EvaluationStrategy strategy, Join join, QueryEvaluationContext context, + QueryEvaluationStep fallbackStep) { + if (!(join.getLeftArg() instanceof StatementPattern) || !(join.getRightArg() instanceof StatementPattern)) { + throw new IllegalArgumentException("LMDB ID join requires StatementPattern operands"); + } + if (!(context instanceof LmdbDatasetContext)) { + throw new IllegalArgumentException("LMDB ID join requires LMDB query evaluation context"); + } + + this.datasetContext = (LmdbDatasetContext) context; + this.leftPattern = (StatementPattern) join.getLeftArg(); + this.rightPattern = (StatementPattern) join.getRightArg(); + this.context = context; + + this.leftInfo = LmdbIdJoinIterator.PatternInfo.create(leftPattern); + this.rightInfo = LmdbIdJoinIterator.PatternInfo.create(rightPattern); + this.sharedVariables = computeSharedVariables(leftInfo, rightInfo); + this.fallbackStep = fallbackStep; + + boolean allowCreate = this.datasetContext.getLmdbDataset() + .map(LmdbEvaluationDataset::hasTransactionChanges) + .orElse(LmdbEvaluationStrategy.hasActiveConnectionChanges()); + ValueStore valueStore = this.datasetContext.getValueStore().orElse(null); + this.fallbackImmediately = valueStore == null + || (!allowCreate && (!constantsResolvable(leftPattern, valueStore, allowCreate) + || !constantsResolvable(rightPattern, valueStore, allowCreate))); + + } + + private Set computeSharedVariables(LmdbIdJoinIterator.PatternInfo left, + LmdbIdJoinIterator.PatternInfo right) { + Set shared = new HashSet<>(left.getVariableNames()); + shared.retainAll(right.getVariableNames()); + return Collections.unmodifiableSet(shared); + } + + private KeyRangeBuffers keyBuffersFor(StatementPattern pattern) { + return patternBuffers.computeIfAbsent(pattern, p -> KeyRangeBuffers.acquire()); + } + + public boolean shouldUseFallbackImmediately() { + return fallbackImmediately; + } + + public void applyAlgorithmTag(Join join) { + join.setAlgorithm(LmdbIdJoinIterator.class.getSimpleName()); + } + + private static boolean constantsResolvable(StatementPattern pattern, ValueStore valueStore, boolean allowCreate) { + try { + Var subj = pattern.getSubjectVar(); + if (subj != null && subj.hasValue()) { + if (!resolveConstantId(valueStore, subj.getValue(), true, false, allowCreate)) { + return false; + } + } + Var pred = pattern.getPredicateVar(); + if (pred != null && pred.hasValue()) { + if (!resolveConstantId(valueStore, pred.getValue(), false, true, allowCreate)) { + return false; + } + } + Var obj = pattern.getObjectVar(); + if (obj != null && obj.hasValue()) { + if (!resolveConstantId(valueStore, obj.getValue(), false, false, allowCreate)) { + return false; + } + } + Var ctx = pattern.getContextVar(); + if (ctx != null && ctx.hasValue()) { + if (!resolveConstantId(valueStore, ctx.getValue(), true, false, allowCreate)) { + return false; + } + } + return true; + } catch (IOException e) { + return false; + } + } + + private static boolean resolveConstantId(ValueStore valueStore, Value value, boolean requireResource, + boolean requireIri, boolean allowCreate) throws IOException { + if (requireResource && !(value instanceof Resource)) { + return false; + } + if (requireIri && !(value instanceof IRI)) { + return false; + } + if (value instanceof Resource + && ((Resource) value).isTriple()) { + return false; + } + if (value instanceof LmdbValue) { + LmdbValue lmdbValue = (LmdbValue) value; + if (lmdbValue.getValueStoreRevision().getValueStore() == valueStore) { + long id = lmdbValue.getInternalID(); + if (id != UNKNOWN_ID) { + return true; + } + } + } + long id = valueStore.getId(value); + if (id == UNKNOWN_ID && !allowCreate) { + id = valueStore.getId(value); + } + if (id == UNKNOWN_ID && allowCreate) { + id = valueStore.getId(value, true); + } + return id != UNKNOWN_ID; + } + + @Override + public CloseableIteration evaluate(BindingSet bindings) { + if (fallbackStep != null && LmdbEvaluationStrategy.hasActiveConnectionChanges()) { + return fallbackStep.evaluate(bindings); + } + if (fallbackImmediately && fallbackStep != null) { + return fallbackStep.evaluate(bindings); + } + try { + LmdbEvaluationDataset dataset = resolveDataset(); + if (!dataset.hasTransactionChanges()) { + dataset.refreshSnapshot(); + } + if (fallbackStep != null && dataset.hasTransactionChanges()) { + return fallbackStep.evaluate(bindings); + } + + final RecordIterator[] leftReuseHolder = new RecordIterator[1]; + + if (!"false".equalsIgnoreCase(System.getProperty("rdf4j.lmdb.experimentalTwoPatternArrayJoin", "true"))) { + ValueStore valueStore = dataset.getValueStore(); + IdBindingInfo bindingInfo = IdBindingInfo.combine( + IdBindingInfo.fromFirstPattern(leftInfo, context), rightInfo, context); + + int subjIdx = indexFor(rightPattern.getSubjectVar(), bindingInfo); + int predIdx = indexFor(rightPattern.getPredicateVar(), bindingInfo); + int objIdx = indexFor(rightPattern.getObjectVar(), bindingInfo); + int ctxIdx = indexFor(rightPattern.getContextVar(), bindingInfo); + long[] patternIds = resolvePatternIds(rightPattern, valueStore); + + long[] initialBinding = createInitialBinding(bindingInfo, bindings, valueStore); + if (initialBinding == null) { + return new org.eclipse.rdf4j.common.iteration.EmptyIteration<>(); + } + + RecordIterator leftIterator = dataset.getRecordIterator(leftPattern, bindings, + keyBuffersFor(leftPattern), leftReuseHolder[0]); + if (leftIterator != null && leftIterator != LmdbIdJoinIterator.emptyRecordIterator()) { + leftReuseHolder[0] = leftIterator; + } else { + leftReuseHolder[0] = null; + } + long[] bindingSnapshot = new long[initialBinding.length]; + long[] rightScratch = new long[initialBinding.length]; + long[] rightQuadScratch = new long[4]; + LmdbIdJoinIterator.RecordIteratorFactory rightFactory = (leftRecord, reuse) -> { + System.arraycopy(initialBinding, 0, bindingSnapshot, 0, initialBinding.length); + for (String name : leftInfo.getVariableNames()) { + int pos = bindingInfo.getIndex(name); + if (pos >= 0) { + long id = leftInfo.getId(leftRecord, name); + if (id != org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID) { + bindingSnapshot[pos] = id; + } + } + } + RecordIterator iter = dataset.getRecordIterator(bindingSnapshot, subjIdx, predIdx, objIdx, ctxIdx, + patternIds, keyBuffersFor(rightPattern), rightScratch, rightQuadScratch, reuse); + return iter; + }; + + return new LmdbIdJoinIterator(leftIterator, rightFactory, leftInfo, bindingInfo, sharedVariables, + context, bindings, valueStore); + } + + // Default: materialize right side via BindingSet and use LmdbIdJoinIterator + ValueStore valueStore = dataset.getValueStore(); + RecordIterator leftIterator = dataset.getRecordIterator(leftPattern, bindings, + keyBuffersFor(leftPattern), leftReuseHolder[0]); + if (leftIterator != null && leftIterator != LmdbIdJoinIterator.emptyRecordIterator()) { + leftReuseHolder[0] = leftIterator; + } else { + leftReuseHolder[0] = null; + } + + LmdbIdJoinIterator.RecordIteratorFactory rightFactory = (leftRecord, reuse) -> { + MutableBindingSet bs = context.createBindingSet(); + bindings.forEach(binding -> bs.addBinding(binding.getName(), binding.getValue())); + if (!leftInfo.applyRecord(leftRecord, bs, valueStore)) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + RecordIterator rightIter = dataset.getRecordIterator(rightPattern, bs, keyBuffersFor(rightPattern), + reuse); + return rightIter; + }; + + return new LmdbIdJoinIterator(leftIterator, rightFactory, leftInfo, rightInfo, sharedVariables, context, + bindings, valueStore); + } catch (QueryEvaluationException e) { + throw e; + } + } + + private LmdbEvaluationDataset resolveDataset() { + // Honor the delegated SailDataset provided via the evaluation context first. + // This preserves transaction-local visibility (e.g., SNAPSHOT/SERIALIZABLE overlays). + Optional fromContext = datasetContext.getLmdbDataset(); + if (fromContext.isPresent()) { + return fromContext.get(); + } + // Fall back to the thread-local only if the context did not carry a dataset reference. + return LmdbEvaluationStrategy.getCurrentDataset() + .orElseThrow(() -> new IllegalStateException("No active LMDB dataset available for join evaluation")); + } + + private static int indexFor(Var var, IdBindingInfo info) { + if (var == null || var.hasValue()) { + return -1; + } + return info.getIndex(var.getName()); + } + + private static long[] resolvePatternIds(StatementPattern pattern, ValueStore valueStore) + throws QueryEvaluationException { + long[] ids = new long[4]; + ids[org.eclipse.rdf4j.sail.lmdb.TripleStore.SUBJ_IDX] = resolveIdIfConstant(pattern.getSubjectVar(), valueStore, + true, false); + ids[org.eclipse.rdf4j.sail.lmdb.TripleStore.PRED_IDX] = resolveIdIfConstant(pattern.getPredicateVar(), + valueStore, + false, true); + ids[org.eclipse.rdf4j.sail.lmdb.TripleStore.OBJ_IDX] = resolveIdIfConstant(pattern.getObjectVar(), valueStore, + false, false); + ids[org.eclipse.rdf4j.sail.lmdb.TripleStore.CONTEXT_IDX] = resolveIdIfConstant(pattern.getContextVar(), + valueStore, true, false); + return ids; + } + + private static long resolveIdIfConstant(Var var, ValueStore valueStore, boolean requireResource, boolean requireIri) + throws QueryEvaluationException { + if (var == null || !var.hasValue()) { + return org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID; + } + Value v = var.getValue(); + if (requireResource && !(v instanceof org.eclipse.rdf4j.model.Resource)) { + return Long.MIN_VALUE; + } + if (requireIri && !(v instanceof org.eclipse.rdf4j.model.IRI)) { + return Long.MIN_VALUE; + } + if (v instanceof org.eclipse.rdf4j.model.Resource && ((org.eclipse.rdf4j.model.Resource) v).isTriple()) { + return Long.MIN_VALUE; + } + try { + long id = valueStore.getId(v); + if (id == org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID) { + id = valueStore.getId(v, true); + } + return id == org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID ? Long.MIN_VALUE : id; + } catch (IOException e) { + throw new QueryEvaluationException(e); + } + } + + private static long[] createInitialBinding(IdBindingInfo info, BindingSet bindings, ValueStore valueStore) + throws QueryEvaluationException { + long[] binding = new long[info.size()]; + Arrays.fill(binding, org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID); + if (bindings == null || bindings.isEmpty()) { + return binding; + } + for (String name : info.getVariableNames()) { + Value value = bindings.getValue(name); + if (value == null) { + continue; + } + long id = resolveId(valueStore, value); + if (id == org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID) { + return null; + } + int index = info.getIndex(name); + if (index >= 0) { + binding[index] = id; + } + } + return binding; + } + + private static long resolveId(ValueStore valueStore, Value value) throws QueryEvaluationException { + if (value == null) { + return org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID; + } + if (value instanceof org.eclipse.rdf4j.sail.lmdb.model.LmdbValue) { + org.eclipse.rdf4j.sail.lmdb.model.LmdbValue lmdbValue = (org.eclipse.rdf4j.sail.lmdb.model.LmdbValue) value; + if (lmdbValue.getValueStoreRevision().getValueStore() == valueStore) { + long id = lmdbValue.getInternalID(); + if (id != org.eclipse.rdf4j.sail.lmdb.model.LmdbValue.UNKNOWN_ID) { + return id; + } + } + } + try { + return valueStore.getId(value); + } catch (IOException e) { + throw new QueryEvaluationException(e); + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinSettings.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinSettings.java new file mode 100644 index 00000000000..e5d828cc59d --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinSettings.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.io.IOException; + +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Centralizes runtime switches for LMDB ID-based joins. + */ +public final class LmdbIdJoinSettings { + + private static final String LAZY_MATERIALIZATION_PROPERTY = "rdf4j.lmdb.idJoin.lazyMaterialization"; + + private LmdbIdJoinSettings() { + // no instances + } + + public static boolean lazyMaterializationEnabled() { + String value = System.getProperty(LAZY_MATERIALIZATION_PROPERTY); + if (value == null) { + return true; + } + return !("false".equalsIgnoreCase(value) || "0".equals(value) || "off".equalsIgnoreCase(value)); + } + + public static LmdbValue resolveLmdbValue(ValueStore valueStore, long id) throws IOException { + LmdbValue value = valueStore.getLazyValue(id); + if (value != null && !lazyMaterializationEnabled()) { + value.init(); + } + return value; + } + + public static Value resolveValue(ValueStore valueStore, long id) throws IOException { + return resolveLmdbValue(valueStore, id); + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinIterator.java new file mode 100644 index 00000000000..0dc24ca3adb --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinIterator.java @@ -0,0 +1,339 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.TripleStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Merge join iterator that operates entirely on LMDB ID records. Materialization to + * {@link org.eclipse.rdf4j.query.BindingSet} happens in a separate {@link LmdbIdFinalBindingSetIteration}. + */ +public class LmdbIdMergeJoinIterator implements RecordIterator { + + private final PeekMarkRecordIterator leftIterator; + private final PeekMarkRecordIterator rightIterator; + private final String mergeVariable; + private final IdBindingInfo bindingInfo; + private final Set sharedVariables; + private final int bindingSize; + private static final int COLUMN_COUNT = TripleStore.CONTEXT_IDX + 1; + private final long[] combinedScratch; + + private long[] currentLeftRecord; + private long currentLeftKey; + private boolean hasCurrentLeftKey; + + private long[] leftPeekRecord; + private long leftPeekKey; + private boolean hasLeftPeekKey; + private int currentLeftValueAndPeekEquals = -1; + private boolean closed; + private final int[] leftColumnBindingIndex; + private final int[] rightColumnBindingIndex; + private final int leftMergeColumn; + private final int rightMergeColumn; + private static final boolean DEBUG = Boolean.getBoolean("rdf4j.lmdb.mergeJoinDebug"); + private final AtomicInteger debugCounter = DEBUG ? new AtomicInteger() : null; + + public LmdbIdMergeJoinIterator(RecordIterator leftIterator, RecordIterator rightIterator, + LmdbIdJoinIterator.PatternInfo leftInfo, LmdbIdJoinIterator.PatternInfo rightInfo, String mergeVariable, + IdBindingInfo bindingInfo) { + if (mergeVariable == null || mergeVariable.isEmpty()) { + throw new IllegalArgumentException("Merge variable must be provided for LMDB merge join"); + } + if (bindingInfo == null) { + throw new IllegalArgumentException("Binding info must be provided for LMDB merge join"); + } + if (leftInfo.getRecordIndex(mergeVariable) < 0 || rightInfo.getRecordIndex(mergeVariable) < 0) { + throw new IllegalArgumentException("Merge variable " + mergeVariable + + " must be present in both join operands for LMDB merge join"); + } + + this.leftMergeColumn = leftInfo.getRecordIndex(mergeVariable); + this.rightMergeColumn = rightInfo.getRecordIndex(mergeVariable); + this.leftIterator = new PeekMarkRecordIterator(leftIterator); + this.rightIterator = new PeekMarkRecordIterator(rightIterator); + this.mergeVariable = mergeVariable; + this.bindingInfo = bindingInfo; + + int idx = bindingInfo.getIndex(mergeVariable); + if (idx < 0) { + throw new IllegalArgumentException( + "Merge variable " + mergeVariable + " is not tracked in the binding info for LMDB merge join"); + } + this.bindingSize = bindingInfo.size(); + + Set shared = new HashSet<>(leftInfo.getVariableNames()); + shared.retainAll(rightInfo.getVariableNames()); + this.sharedVariables = Collections.unmodifiableSet(shared); + this.leftColumnBindingIndex = computeColumnBindingMap(leftInfo, bindingInfo); + this.rightColumnBindingIndex = computeColumnBindingMap(rightInfo, bindingInfo); + this.combinedScratch = new long[bindingSize]; + if (DEBUG) { + System.out.println("DEBUG bindingSize=" + bindingSize + " mergeVar=" + mergeVariable + " shared=" + + shared + " bindingVars=" + bindingInfo.getVariableNames()); + } + } + + @Override + public long[] next() { + if (closed) { + return null; + } + try { + long[] nextRecord = computeNextElement(); + if (nextRecord == null) { + close(); + } + return nextRecord; + } catch (RuntimeException e) { + close(); + throw e; + } + } + + private long[] computeNextElement() throws QueryEvaluationException { + if (closed) { + return null; + } + if (!ensureCurrentLeft()) { + return null; + } + + while (true) { + if (!rightIterator.hasNext()) { + if (rightIterator.isResettable() && leftIterator.hasNext()) { + rightIterator.reset(); + if (!advanceLeft()) { + return null; + } + continue; + } + return null; + } + + long[] rightPeek = rightIterator.peek(); + if (rightPeek == null) { + return null; + } + + int compare = compare(currentLeftKey, key(rightPeek, rightMergeColumn)); + if (compare == 0) { + long[] result = equal(); + if (result != null) { + return result; + } + } else if (compare < 0) { + if (leftIterator.hasNext()) { + if (!lessThan()) { + return null; + } + } else { + return null; + } + } else { + rightIterator.next(); + } + } + } + + private boolean ensureCurrentLeft() { + if (currentLeftRecord != null) { + return true; + } + return advanceLeft(); + } + + private boolean advanceLeft() { + leftPeekRecord = null; + hasLeftPeekKey = false; + currentLeftValueAndPeekEquals = -1; + + while (leftIterator.hasNext()) { + long[] candidate = leftIterator.next(); + if (candidate == null) { + continue; + } + if (DEBUG && debugCounter.getAndIncrement() < 5) { + System.out.println("DEBUG advanceLeft len=" + candidate.length + " record=" + + Arrays.toString(candidate)); + } + currentLeftRecord = candidate; + currentLeftKey = key(candidate, leftMergeColumn); + hasCurrentLeftKey = true; + return true; + } + + currentLeftRecord = null; + hasCurrentLeftKey = false; + return false; + } + + private boolean lessThan() { + long previous = hasCurrentLeftKey ? currentLeftKey : Long.MIN_VALUE; + if (!advanceLeft()) { + return false; + } + if (hasCurrentLeftKey && previous == currentLeftKey) { + if (rightIterator.isResettable()) { + rightIterator.reset(); + } + } else { + rightIterator.unmark(); + } + return true; + } + + private long[] equal() { + while (rightIterator.hasNext()) { + if (rightIterator.isResettable()) { + long[] result = joinWithCurrentLeft(rightIterator.next()); + if (result != null) { + return result; + } + } else { + doLeftPeek(); + if (currentLeftValueAndPeekEquals == 0 && !rightIterator.isMarked()) { + rightIterator.mark(); + } + long[] result = joinWithCurrentLeft(rightIterator.next()); + if (result != null) { + return result; + } + } + } + return null; + } + + private void doLeftPeek() { + if (leftPeekRecord == null) { + leftPeekRecord = leftIterator.peek(); + if (leftPeekRecord != null) { + leftPeekKey = key(leftPeekRecord, leftMergeColumn); + hasLeftPeekKey = true; + } else { + hasLeftPeekKey = false; + } + currentLeftValueAndPeekEquals = -1; + } + + if (currentLeftValueAndPeekEquals == -1) { + boolean equals = hasLeftPeekKey && hasCurrentLeftKey && currentLeftKey == leftPeekKey; + currentLeftValueAndPeekEquals = equals ? 0 : 1; + } + } + + private long[] joinWithCurrentLeft(long[] rightRecord) { + if (rightRecord == null) { + return null; + } + + long[] combined = combinedScratch; + Arrays.fill(combined, LmdbValue.UNKNOWN_ID); + + if (!mergeRecordInto(combined, currentLeftRecord, leftColumnBindingIndex)) { + return null; + } + if (!mergeRecordInto(combined, rightRecord, rightColumnBindingIndex)) { + return null; + } + if (DEBUG && debugCounter.get() < 50) { + System.out.println("DEBUG combined=" + Arrays.toString(combined)); + } + return combined; + } + + private static int[] computeColumnBindingMap(LmdbIdJoinIterator.PatternInfo patternInfo, + IdBindingInfo bindingInfo) { + int[] map = new int[COLUMN_COUNT]; + Arrays.fill(map, -1); + for (String var : patternInfo.getVariableNames()) { + int bindingIdx = bindingInfo.getIndex(var); + if (bindingIdx < 0) { + continue; + } + int mask = patternInfo.getPositionsMask(var); + for (int pos = 0; pos < COLUMN_COUNT; pos++) { + if (((mask >> pos) & 1) != 0) { + map[pos] = bindingIdx; + } + } + } + return map; + } + + private boolean mergeRecordInto(long[] target, long[] source, int[] colMap) { + if (source == null) { + return true; + } + for (int col = 0; col < colMap.length; col++) { + int bindingIdx = colMap[col]; + if (bindingIdx < 0 || bindingIdx >= target.length) { + continue; + } + long value = col < source.length ? source[col] : LmdbValue.UNKNOWN_ID; + if (col == TripleStore.CONTEXT_IDX && value == 0L) { + value = LmdbValue.UNKNOWN_ID; + } + long existing = target[bindingIdx]; + if (existing == LmdbValue.UNKNOWN_ID) { + if (value != LmdbValue.UNKNOWN_ID) { + target[bindingIdx] = value; + } + } else if (value != LmdbValue.UNKNOWN_ID && existing != value) { + return false; + } + } + return true; + } + + private int compare(long left, long right) { + return Long.compare(left, right); + } + + private long key(long[] record, int columnIndex) { + long id = record[columnIndex]; + if (id == LmdbValue.UNKNOWN_ID) { + throw new QueryEvaluationException( + "Merge variable " + mergeVariable + " is unbound in the current record; cannot perform merge join"); + } + return id; + } + + @Override + public void close() { + if (closed) { + return; + } + closed = true; + currentLeftRecord = null; + hasCurrentLeftKey = false; + leftPeekRecord = null; + hasLeftPeekKey = false; + currentLeftValueAndPeekEquals = -1; + try { + leftIterator.close(); + } finally { + rightIterator.close(); + } + } + +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinQueryEvaluationStep.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinQueryEvaluationStep.java new file mode 100644 index 00000000000..cc7b8e31ef7 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinQueryEvaluationStep.java @@ -0,0 +1,417 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.EmptyIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.LmdbDatasetContext; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationDataset; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationDataset.KeyRangeBuffers; +import org.eclipse.rdf4j.sail.lmdb.LmdbEvaluationStrategy; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.TripleStore; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +/** + * Query evaluation step that wires up the LMDB merge join iterator. + */ +public class LmdbIdMergeJoinQueryEvaluationStep implements QueryEvaluationStep { + + private final PatternPlan leftPlan; + private final PatternPlan rightPlan; + private final IdBindingInfo bindingInfo; + private final QueryEvaluationContext context; + private final LmdbDatasetContext datasetContext; + private final Join join; + private final LmdbIdJoinIterator.PatternInfo leftInfo; + private final LmdbIdJoinIterator.PatternInfo rightInfo; + private final String mergeVariable; + private final QueryEvaluationStep fallbackStep; + private final boolean hasInvalidPattern; + private final String fallbackAlgorithmName; + private final Map patternBuffers = new HashMap<>(); + + public LmdbIdMergeJoinQueryEvaluationStep(Join join, QueryEvaluationContext context, + QueryEvaluationStep fallbackStep) { + if (!(join.getLeftArg() instanceof StatementPattern) || !(join.getRightArg() instanceof StatementPattern)) { + throw new IllegalArgumentException("LMDB merge join requires StatementPattern operands"); + } + if (!(context instanceof LmdbDatasetContext)) { + throw new IllegalArgumentException("LMDB merge join requires LMDB query evaluation context"); + } + if (!join.isMergeJoin()) { + throw new IllegalArgumentException("Merge join flag must be set on the Join node"); + } + Var orderVar = join.getOrder(); + if (orderVar == null) { + throw new IllegalArgumentException("Merge join requires join order variable to be set"); + } + + this.context = context; + this.datasetContext = (LmdbDatasetContext) context; + this.join = join; + + StatementPattern leftPattern = (StatementPattern) join.getLeftArg(); + StatementPattern rightPattern = (StatementPattern) join.getRightArg(); + + this.mergeVariable = orderVar.getName(); + this.leftInfo = LmdbIdJoinIterator.PatternInfo.create(leftPattern); + this.rightInfo = LmdbIdJoinIterator.PatternInfo.create(rightPattern); + this.fallbackStep = fallbackStep; + this.fallbackAlgorithmName = fallbackStep != null ? join.getAlgorithmName() : null; + + if (leftPattern.getStatementOrder() == null) { + leftPattern.setOrder(orderVar); + } + if (rightPattern.getStatementOrder() == null) { + rightPattern.setOrder(orderVar); + } + + ValueStore valueStore = this.datasetContext.getValueStore() + .orElseThrow(() -> new IllegalStateException("LMDB merge join requires ValueStore access")); + + RawPattern leftRaw = RawPattern.create(leftPattern, valueStore); + RawPattern rightRaw = RawPattern.create(rightPattern, valueStore); + this.hasInvalidPattern = leftRaw.invalid || rightRaw.invalid; + + IdBindingInfo info = IdBindingInfo.fromFirstPattern(leftInfo, context); + info = IdBindingInfo.combine(info, rightInfo, context); + this.bindingInfo = info; + + StatementOrder leftOrder = determineOrder(leftPattern, leftInfo); + StatementOrder rightOrder = determineOrder(rightPattern, rightInfo); + + this.leftPlan = leftRaw.toPlan(info, leftOrder); + this.rightPlan = rightRaw.toPlan(info, rightOrder); + } + + @Override + public CloseableIteration evaluate(BindingSet bindings) { + if (fallbackStep != null && LmdbEvaluationStrategy.hasActiveConnectionChanges()) { + return fallbackStep.evaluate(bindings); + } + try { + LmdbEvaluationDataset dataset = resolveDataset(); + if (!dataset.hasTransactionChanges()) { + dataset.refreshSnapshot(); + } + if (fallbackStep != null && dataset.hasTransactionChanges()) { + return fallbackStep.evaluate(bindings); + } + if (hasInvalidPattern) { + return new EmptyIteration<>(); + } + if (leftPlan.order == null || rightPlan.order == null) { + return evaluateFallback(bindings); + } + + ValueStore valueStore = dataset.getValueStore(); + long[] initialBinding = createInitialBinding(bindingInfo, bindings, valueStore); + if (initialBinding == null) { + return new EmptyIteration<>(); + } + + KeyRangeBuffers leftBuffers = keyBuffersFor(leftPlan.pattern); + KeyRangeBuffers rightBuffers = keyBuffersFor(rightPlan.pattern); + + RecordIterator leftIterator = dataset.getOrderedRecordIterator(initialBinding, leftPlan.subjIndex, + leftPlan.predIndex, leftPlan.objIndex, leftPlan.ctxIndex, leftPlan.patternIds, leftPlan.order, + leftBuffers, null, null); + if (leftIterator == null) { + return evaluateFallback(bindings); + } + + RecordIterator rightIterator = dataset.getOrderedRecordIterator(initialBinding, rightPlan.subjIndex, + rightPlan.predIndex, rightPlan.objIndex, rightPlan.ctxIndex, rightPlan.patternIds, rightPlan.order, + rightBuffers, null, null); + if (rightIterator == null) { + try { + leftIterator.close(); + } catch (Exception ignore) { + } + return evaluateFallback(bindings); + } + + join.setAlgorithm(LmdbIdMergeJoinIterator.class.getSimpleName()); + RecordIterator mergeIterator = new LmdbIdMergeJoinIterator(leftIterator, rightIterator, leftInfo, rightInfo, + mergeVariable, bindingInfo); + return new LmdbIdFinalBindingSetIteration(mergeIterator, bindingInfo, context, bindings, valueStore, + Collections.emptyMap()); + } catch (QueryEvaluationException e) { + throw e; + } + } + + private CloseableIteration evaluateFallback(BindingSet bindings) { + if (fallbackAlgorithmName != null) { + join.setAlgorithm(fallbackAlgorithmName); + } + if (fallbackStep != null) { + return fallbackStep.evaluate(bindings); + } + return new EmptyIteration<>(); + } + + private LmdbEvaluationDataset resolveDataset() { + Optional fromContext = datasetContext.getLmdbDataset(); + if (fromContext.isPresent()) { + return fromContext.get(); + } + return LmdbEvaluationStrategy.getCurrentDataset() + .orElseThrow( + () -> new IllegalStateException("No active LMDB dataset available for merge join evaluation")); + } + + private static long[] createInitialBinding(IdBindingInfo info, BindingSet bindings, ValueStore valueStore) + throws QueryEvaluationException { + long[] binding = new long[info.size()]; + Arrays.fill(binding, LmdbValue.UNKNOWN_ID); + if (bindings == null || bindings.isEmpty()) { + return binding; + } + for (String name : info.getVariableNames()) { + Value value = bindings.getValue(name); + if (value == null) { + continue; + } + long id = resolveId(valueStore, value); + if (id == LmdbValue.UNKNOWN_ID) { + return null; + } + int index = info.getIndex(name); + if (index >= 0) { + binding[index] = id; + } + } + return binding; + } + + private KeyRangeBuffers keyBuffersFor(StatementPattern pattern) { + return patternBuffers.computeIfAbsent(pattern, p -> KeyRangeBuffers.acquire()); + } + + private static long resolveId(ValueStore valueStore, Value value) throws QueryEvaluationException { + if (value == null) { + return LmdbValue.UNKNOWN_ID; + } + if (value instanceof org.eclipse.rdf4j.sail.lmdb.model.LmdbValue) { + org.eclipse.rdf4j.sail.lmdb.model.LmdbValue lmdbValue = (org.eclipse.rdf4j.sail.lmdb.model.LmdbValue) value; + if (lmdbValue.getValueStoreRevision().getValueStore() == valueStore) { + long id = lmdbValue.getInternalID(); + if (id != LmdbValue.UNKNOWN_ID) { + return id; + } + } + } + try { + long id = valueStore.getId(value); + return id; + } catch (IOException e) { + throw new QueryEvaluationException(e); + } + } + + private StatementOrder determineOrder(StatementPattern pattern, LmdbIdJoinIterator.PatternInfo info) { + StatementOrder order = pattern.getStatementOrder(); + if (order != null) { + return order; + } + + if (mergeVariable == null) { + return null; + } + + int mask = info.getPositionsMask(mergeVariable); + if ((mask & (1 << TripleStore.SUBJ_IDX)) != 0) { + return StatementOrder.S; + } + if ((mask & (1 << TripleStore.PRED_IDX)) != 0) { + return StatementOrder.P; + } + if ((mask & (1 << TripleStore.OBJ_IDX)) != 0) { + return StatementOrder.O; + } + if ((mask & (1 << TripleStore.CONTEXT_IDX)) != 0) { + return StatementOrder.C; + } + return null; + } + + private static final class PatternPlan { + private final long[] patternIds; + private final int subjIndex; + private final int predIndex; + private final int objIndex; + private final int ctxIndex; + private final StatementOrder order; + private final StatementPattern pattern; + + private PatternPlan(long[] patternIds, int subjIndex, int predIndex, int objIndex, int ctxIndex, + StatementOrder order, StatementPattern pattern) { + this.patternIds = patternIds; + this.subjIndex = subjIndex; + this.predIndex = predIndex; + this.objIndex = objIndex; + this.ctxIndex = ctxIndex; + this.order = order; + this.pattern = pattern; + } + } + + private static final class RawPattern { + private final long[] patternIds; + private final String subjVar; + private final String predVar; + private final String objVar; + private final String ctxVar; + private final boolean invalid; + private final StatementPattern pattern; + + private RawPattern(long[] patternIds, String subjVar, String predVar, String objVar, String ctxVar, + boolean invalid, StatementPattern pattern) { + this.patternIds = patternIds; + this.subjVar = subjVar; + this.predVar = predVar; + this.objVar = objVar; + this.ctxVar = ctxVar; + this.invalid = invalid; + this.pattern = pattern; + } + + static RawPattern create(StatementPattern pattern, ValueStore valueStore) { + long[] ids = new long[4]; + Arrays.fill(ids, LmdbValue.UNKNOWN_ID); + boolean invalid = false; + + Var subj = pattern.getSubjectVar(); + String subjVar = null; + if (subj != null) { + if (subj.hasValue()) { + long id = constantId(valueStore, subj.getValue(), true, false); + if (id == Long.MIN_VALUE) { + invalid = true; + } else { + ids[TripleStore.SUBJ_IDX] = id; + } + } else { + subjVar = subj.getName(); + } + } + + Var pred = pattern.getPredicateVar(); + String predVar = null; + if (pred != null) { + if (pred.hasValue()) { + long id = constantId(valueStore, pred.getValue(), false, true); + if (id == Long.MIN_VALUE) { + invalid = true; + } else { + ids[TripleStore.PRED_IDX] = id; + } + } else { + predVar = pred.getName(); + } + } + + Var obj = pattern.getObjectVar(); + String objVar = null; + if (obj != null) { + if (obj.hasValue()) { + long id = constantId(valueStore, obj.getValue(), false, false); + if (id == Long.MIN_VALUE) { + invalid = true; + } else { + ids[TripleStore.OBJ_IDX] = id; + } + } else { + objVar = obj.getName(); + } + } + + Var ctx = pattern.getContextVar(); + String ctxVar = null; + if (ctx != null) { + if (ctx.hasValue()) { + long id = constantId(valueStore, ctx.getValue(), true, false); + if (id == Long.MIN_VALUE) { + invalid = true; + } else { + ids[TripleStore.CONTEXT_IDX] = id; + } + } else { + ctxVar = ctx.getName(); + } + } + + return new RawPattern(ids, subjVar, predVar, objVar, ctxVar, invalid, pattern); + } + + PatternPlan toPlan(IdBindingInfo bindingInfo, StatementOrder order) { + return new PatternPlan(patternIds.clone(), indexFor(subjVar, bindingInfo), indexFor(predVar, bindingInfo), + indexFor(objVar, bindingInfo), indexFor(ctxVar, bindingInfo), order, pattern); + } + + private static int indexFor(String varName, IdBindingInfo info) { + return varName == null ? -1 : info.getIndex(varName); + } + + private static long constantId(ValueStore valueStore, Value value, boolean requireResource, + boolean requireIri) { + if (requireResource && !(value instanceof Resource)) { + return Long.MIN_VALUE; + } + if (requireIri && !(value instanceof IRI)) { + return Long.MIN_VALUE; + } + if (value instanceof Resource && ((Resource) value).isTriple()) { + return Long.MIN_VALUE; + } + try { + if (value instanceof org.eclipse.rdf4j.sail.lmdb.model.LmdbValue) { + org.eclipse.rdf4j.sail.lmdb.model.LmdbValue lmdbValue = (org.eclipse.rdf4j.sail.lmdb.model.LmdbValue) value; + if (lmdbValue.getValueStoreRevision().getValueStore() == valueStore) { + long id = lmdbValue.getInternalID(); + if (id != LmdbValue.UNKNOWN_ID) { + return id; + } + } + } + long id = valueStore.getId(value); + if (id == LmdbValue.UNKNOWN_ID) { + id = valueStore.getId(value, true); + } + return id == LmdbValue.UNKNOWN_ID ? Long.MIN_VALUE : id; + } catch (IOException e) { + return Long.MIN_VALUE; + } + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdOrderVerifier.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdOrderVerifier.java new file mode 100644 index 00000000000..22928b7eea5 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdOrderVerifier.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + ******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.util.Arrays; + +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; + +public final class LmdbIdOrderVerifier { + + private LmdbIdOrderVerifier() { + } + + public static RecordIterator wrap(String label, int mergeIndex, RecordIterator delegate) { + if (label == null || label.isEmpty()) { + throw new IllegalArgumentException("Label must be provided"); + } + if (mergeIndex < 0) { + throw new IllegalArgumentException("Merge index must be non-negative"); + } + return new RecordIterator() { + private long position = 0; + private long previous = Long.MIN_VALUE; + private boolean first = true; + + @Override + public long[] next() { + long[] record = delegate.next(); + if (record == null) { + return null; + } + if (mergeIndex >= record.length) { + throw new AssertionError(String.format( + "%s iterator produced record #%d shorter than merge index %d: %s", + label, position, mergeIndex, Arrays.toString(record))); + } + long current = record[mergeIndex]; + if (!first && current < previous) { + throw new AssertionError(String.format( + "%s iterator out of order at position %d: previous=%d current=%d (record=%s)", + label, position, previous, current, Arrays.toString(record))); + } + first = false; + previous = current; + long[] copy = Arrays.copyOf(record, record.length); + position++; + return copy; + } + + @Override + public void close() { + delegate.close(); + } + }; + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/PeekMarkRecordIterator.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/PeekMarkRecordIterator.java new file mode 100644 index 00000000000..61d9f227d84 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/PeekMarkRecordIterator.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; + +/** + * Peek/mark/reset wrapper for {@link RecordIterator}. Mirrors + * {@link org.eclipse.rdf4j.query.algebra.evaluation.iterator.PeekMarkIterator} but operates on primitive long[] + * records. + */ +class PeekMarkRecordIterator { + + private final RecordIterator iterator; + private boolean mark; + private List buffer; + private Iterator bufferIterator = Collections.emptyIterator(); + private long[] next; + + // -1: reset impossible, 0: available once, >0: buffered and ready + private int resetPossible; + + private boolean closed; + + PeekMarkRecordIterator(RecordIterator iterator) { + this.iterator = iterator; + } + + boolean hasNext() { + if (closed) { + return false; + } + calculateNext(); + return next != null; + } + + long[] next() { + if (closed) { + throw new NoSuchElementException("The iteration has been closed."); + } + calculateNext(); + long[] result = next; + next = null; + if (!mark && resetPossible == 0) { + resetPossible--; + } + if (result == null) { + throw new NoSuchElementException(); + } + return result; + } + + long[] peek() { + if (closed) { + return null; + } + calculateNext(); + return next; + } + + void mark() { + if (closed) { + throw new IllegalStateException("The iteration has been closed."); + } + mark = true; + resetPossible = 1; + + if (buffer != null && !bufferIterator.hasNext()) { + buffer.clear(); + bufferIterator = Collections.emptyIterator(); + } else { + buffer = new ArrayList<>(); + } + + if (next != null) { + buffer.add(next); + } + } + + void reset() { + if (closed) { + throw new IllegalStateException("The iteration has been closed."); + } + if (buffer == null) { + throw new IllegalStateException("Mark never set"); + } + if (resetPossible < 0) { + throw new IllegalStateException("Reset not possible"); + } + + if (mark && bufferIterator.hasNext()) { + while (bufferIterator.hasNext()) { + buffer.add(bufferIterator.next()); + } + } + + if (resetPossible == 0) { + assert !mark; + if (next != null) { + buffer.add(next); + next = null; + } + bufferIterator = buffer.iterator(); + } else if (resetPossible > 0) { + next = null; + bufferIterator = buffer.iterator(); + } + + mark = false; + resetPossible = 1; + } + + boolean isMarked() { + return !closed && mark; + } + + boolean isResettable() { + return !closed && (mark || resetPossible >= 0); + } + + void unmark() { + mark = false; + resetPossible = -1; + if (bufferIterator.hasNext()) { + buffer = null; + bufferIterator = Collections.emptyIterator(); + } else if (buffer != null) { + buffer.clear(); + bufferIterator = Collections.emptyIterator(); + } + } + + void close() { + if (!closed) { + closed = true; + buffer = null; + bufferIterator = Collections.emptyIterator(); + next = null; + try { + iterator.close(); + } catch (Exception ignore) { + // RecordIterator#close does not declare checked exceptions + } + } + } + + private void calculateNext() { + if (next != null || closed) { + return; + } + + if (bufferIterator.hasNext()) { + next = bufferIterator.next(); + } else { + if (!mark && resetPossible > -1) { + resetPossible--; + } + if (iterator != null) { + long[] candidate = iterator.next(); + if (candidate != null) { + next = Arrays.copyOf(candidate, candidate.length); + } + } + } + + if (mark && next != null) { + assert resetPossible > 0; + buffer.add(next); + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/package-info.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/package-info.java new file mode 100644 index 00000000000..c64114acf65 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/package-info.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2020 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ + +/** + * @apiNote This feature is for internal use only: its existence, signature or behavior may change without warning from + * one release to the next. + */ + +@InternalUseOnly +package org.eclipse.rdf4j.sail.lmdb.join; + +import org.eclipse.rdf4j.common.annotation.InternalUseOnly; diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbBNode.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbBNode.java index eeab5e74797..178927b9bf2 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbBNode.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbBNode.java @@ -12,6 +12,7 @@ import java.io.ObjectStreamException; +import org.eclipse.rdf4j.model.BNode; import org.eclipse.rdf4j.model.impl.SimpleBNode; import org.eclipse.rdf4j.sail.lmdb.ValueStoreRevision; @@ -110,7 +111,18 @@ public boolean equals(Object o) { } } - return super.equals(o); + if (o instanceof BNode) { + init(); + return super.equals(o); + } + + return false; + } + + @Override + public int hashCode() { + init(); + return super.hashCode(); } protected Object writeReplace() throws ObjectStreamException { diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java index f6c77a0c935..a9e9cf2e508 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java @@ -151,57 +151,53 @@ public void init() { public boolean equals(Object o) { if (this == o) { return true; - } - - if (o == null) { + } else if (o instanceof LmdbIRI) { + return equalsLmdbIRI(((LmdbIRI) o)); + } else if (o instanceof IRI) { + IRI other = (IRI) o; + return stringValue().equals(other.stringValue()); + } else { return false; } - if (o.getClass() == LmdbIRI.class) { - if (internalID == UNKNOWN_ID) { - boolean equals = stringValue().equals(((IRI) o).stringValue()); - if (equals && revision.equals(((LmdbIRI) o).revision)) { - internalID = ((LmdbIRI) o).internalID; - } - return equals; - } - - LmdbIRI otherLmdbURI = (LmdbIRI) o; + } - if (revision.equals(otherLmdbURI.revision)) { - if (otherLmdbURI.internalID == UNKNOWN_ID) { - boolean equals = stringValue().equals(((IRI) o).stringValue()); - if (equals) { - otherLmdbURI.internalID = this.internalID; - } - return equals; + private boolean equalsLmdbIRI(LmdbIRI o) { + if (internalID == UNKNOWN_ID) { + boolean equals = stringValue().equals(o.stringValue()); + if (equals && revision.equals(o.revision)) { + internalID = o.internalID; + } + return equals; + } else if (revision.equals(o.revision)) { + if (o.internalID == UNKNOWN_ID) { + boolean equals = stringValue().equals(o.stringValue()); + if (equals) { + o.internalID = this.internalID; } - + return equals; + } else { // LmdbURI's from the same revision of the same lmdb store, with // both ID's set - boolean equal = internalID == otherLmdbURI.internalID; + boolean equal = internalID == o.internalID; if (equal) { if (iriString == null) { - iriString = otherLmdbURI.iriString; - localNameIdx = otherLmdbURI.localNameIdx; - } else if (otherLmdbURI.iriString == null) { - otherLmdbURI.iriString = iriString; - otherLmdbURI.localNameIdx = localNameIdx; + iriString = o.iriString; + localNameIdx = o.localNameIdx; + } else if (o.iriString == null) { + o.iriString = iriString; + o.localNameIdx = localNameIdx; } } return equal; } + } else { + return stringValue().equals(o.stringValue()); } - - if (!(o instanceof IRI)) { - return false; - } - - return stringValue().equals(((IRI) o).stringValue()); } @Override - public int hashCode() { + public final int hashCode() { if (this.iriString != null) { return this.iriString.hashCode(); } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java index 84e16136bc2..9181e07e55d 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java @@ -14,6 +14,7 @@ import java.util.Optional; import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.base.AbstractLiteral; import org.eclipse.rdf4j.model.base.CoreDatatype; import org.eclipse.rdf4j.sail.lmdb.ValueStoreRevision; @@ -226,8 +227,11 @@ public boolean equals(Object o) { } } - init(); - return super.equals(o); + if (o instanceof Literal) { + init(); + return super.equals(o); + } + return false; } @Override diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyReaders.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyReaders.java new file mode 100644 index 00000000000..b7da9a980e6 --- /dev/null +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyReaders.java @@ -0,0 +1,735 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.util; + +import java.nio.ByteBuffer; + +import org.eclipse.rdf4j.sail.lmdb.Varint; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; + +public final class IndexKeyReaders { + + private static final int SUBJ_IDX = 0; + private static final int PRED_IDX = 1; + private static final int OBJ_IDX = 2; + private static final int CONTEXT_IDX = 3; + + private IndexKeyReaders() { + } + + @FunctionalInterface + public interface KeyToQuadReader { + void read(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad); + } + + public static KeyToQuadReader forFieldSeq(String fieldSeq) { + switch (fieldSeq) { + case "spoc": + return IndexKeyReaders::spoc; + case "spco": + return IndexKeyReaders::spco; + case "sopc": + return IndexKeyReaders::sopc; + case "socp": + return IndexKeyReaders::socp; + case "scpo": + return IndexKeyReaders::scpo; + case "scop": + return IndexKeyReaders::scop; + case "psoc": + return IndexKeyReaders::psoc; + case "psco": + return IndexKeyReaders::psco; + case "posc": + return IndexKeyReaders::posc; + case "pocs": + return IndexKeyReaders::pocs; + case "pcso": + return IndexKeyReaders::pcso; + case "pcos": + return IndexKeyReaders::pcos; + case "ospc": + return IndexKeyReaders::ospc; + case "oscp": + return IndexKeyReaders::oscp; + case "opsc": + return IndexKeyReaders::opsc; + case "opcs": + return IndexKeyReaders::opcs; + case "ocsp": + return IndexKeyReaders::ocsp; + case "ocps": + return IndexKeyReaders::ocps; + case "cspo": + return IndexKeyReaders::cspo; + case "csop": + return IndexKeyReaders::csop; + case "cpso": + return IndexKeyReaders::cpso; + case "cpos": + return IndexKeyReaders::cpos; + case "cosp": + return IndexKeyReaders::cosp; + case "cops": + return IndexKeyReaders::cops; + default: + throw new IllegalArgumentException("Unsupported field sequence: " + fieldSeq); + } + } + + private static void spoc(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + } + + private static void spco(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void sopc(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + } + + private static void socp(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + } + + private static void scpo(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void scop(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + } + + private static void psoc(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + } + + private static void psco(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void posc(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + } + + private static void pocs(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void pcso(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void pcos(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void ospc(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + } + + private static void oscp(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + } + + private static void opsc(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + } + + private static void opcs(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void ocsp(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + } + + private static void ocps(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void cspo(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void csop(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + } + + private static void cpso(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void cpos(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + } + + private static void cosp(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + } + + private static void cops(ByteBuffer key, long subj, long pred, long obj, long context, long[] quad) { + if (context != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[CONTEXT_IDX] = context; + } else { + quad[CONTEXT_IDX] = Varint.readUnsigned(key); + } + if (obj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[OBJ_IDX] = obj; + } else { + quad[OBJ_IDX] = Varint.readUnsigned(key); + } + if (pred != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[PRED_IDX] = pred; + } else { + quad[PRED_IDX] = Varint.readUnsigned(key); + } + if (subj != LmdbValue.UNKNOWN_ID) { + Varint.skipUnsigned(key); + quad[SUBJ_IDX] = subj; + } else { + quad[SUBJ_IDX] = Varint.readUnsigned(key); + } + } +} diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java index bfd81aae41b..fb9a01fde15 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/util/IndexKeyWriters.java @@ -24,12 +24,14 @@ private IndexKeyWriters() { @FunctionalInterface public interface KeyWriter { - void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean shouldCache); + void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean shouldCache, boolean hasPrev, + long prevSubj, long prevPred, long prevObj, long prevContext); } @FunctionalInterface interface BasicWriter { - void write(ByteBuffer bb, long subj, long pred, long obj, long context); + void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext); } @FunctionalInterface @@ -119,6 +121,15 @@ public static KeyWriter forFieldSeq(String fieldSeq) { return new CachingKeyWriter(basic); } + private static boolean writeOrSkip(ByteBuffer bb, long value, long prevValue, boolean rewrite) { + if (rewrite || value != prevValue) { + Varint.writeUnsigned(bb, value); + return true; + } + Varint.advanceUnsigned(bb, value); + return false; + } + // Simple array-based cache keyed by a masked index computed from a hashCode. private static final class CachingKeyWriter implements KeyWriter { @@ -149,9 +160,10 @@ private static final class Entry { } @Override - public void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean shouldCache) { + public void write(ByteBuffer bb, long subj, long pred, long obj, long context, boolean shouldCache, + boolean hasPrev, long prevSubj, long prevPred, long prevObj, long prevContext) { if (!shouldCache) { - basic.write(bb, subj, pred, obj, context); + basic.write(bb, subj, pred, obj, context, hasPrev, prevSubj, prevPred, prevObj, prevContext); return; } @@ -169,7 +181,7 @@ public void write(ByteBuffer bb, long subj, long pred, long obj, long context, b int len = Varint.calcListLengthUnsigned(subj, pred, obj, context); byte[] bytes = new byte[len]; ByteBuffer out = ByteBuffer.wrap(bytes); - basic.write(out, subj, pred, obj, context); + basic.write(out, subj, pred, obj, context, false, 0, 0, 0, 0); out.flip(); bb.put(out); cache[slot] = new Entry(hashCode, subj, pred, obj, context, bytes); @@ -231,172 +243,220 @@ public static MatcherFactory matcherFactory(String fieldSeq) { } } - static void spoc(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, context); - } - - static void spco(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, obj); - } - - static void sopc(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, context); - } - - static void socp(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, pred); - } - - static void scpo(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, obj); - } - - static void scop(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, pred); - } - - static void psoc(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, context); - } - - static void psco(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, obj); - } - - static void posc(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, context); - } - - static void pocs(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, subj); - } - - static void pcso(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, obj); - } - - static void pcos(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, subj); - } - - static void ospc(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, context); - } - - static void oscp(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, pred); - } - - static void opsc(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, context); - } - - static void opcs(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, subj); - } - - static void ocsp(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, pred); - } - - static void ocps(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, subj); - } - - static void cspo(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, obj); - } - - static void csop(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, pred); - } - - static void cpso(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, obj); - } - - static void cpos(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, subj); - } - - static void cosp(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, subj); - Varint.writeUnsigned(bb, pred); - } - - static void cops(ByteBuffer bb, long subj, long pred, long obj, long context) { - Varint.writeUnsigned(bb, context); - Varint.writeUnsigned(bb, obj); - Varint.writeUnsigned(bb, pred); - Varint.writeUnsigned(bb, subj); + static void spoc(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + writeOrSkip(bb, context, prevContext, rewrite); + } + + static void spco(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + writeOrSkip(bb, obj, prevObj, rewrite); + } + + static void sopc(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + writeOrSkip(bb, context, prevContext, rewrite); + } + + static void socp(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + writeOrSkip(bb, pred, prevPred, rewrite); + } + + static void scpo(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + writeOrSkip(bb, obj, prevObj, rewrite); + } + + static void scop(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + writeOrSkip(bb, pred, prevPred, rewrite); + } + + static void psoc(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + writeOrSkip(bb, context, prevContext, rewrite); + } + + static void psco(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + writeOrSkip(bb, obj, prevObj, rewrite); + } + + static void posc(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + writeOrSkip(bb, context, prevContext, rewrite); + } + + static void pocs(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + writeOrSkip(bb, subj, prevSubj, rewrite); + } + + static void pcso(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + writeOrSkip(bb, obj, prevObj, rewrite); + } + + static void pcos(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + writeOrSkip(bb, subj, prevSubj, rewrite); + } + + static void ospc(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + writeOrSkip(bb, context, prevContext, rewrite); + } + + static void oscp(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + writeOrSkip(bb, pred, prevPred, rewrite); + } + + static void opsc(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + writeOrSkip(bb, context, prevContext, rewrite); + } + + static void opcs(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + writeOrSkip(bb, subj, prevSubj, rewrite); + } + + static void ocsp(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + writeOrSkip(bb, pred, prevPred, rewrite); + } + + static void ocps(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + writeOrSkip(bb, subj, prevSubj, rewrite); + } + + static void cspo(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + writeOrSkip(bb, obj, prevObj, rewrite); + } + + static void csop(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + writeOrSkip(bb, pred, prevPred, rewrite); + } + + static void cpso(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + writeOrSkip(bb, obj, prevObj, rewrite); + } + + static void cpos(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + writeOrSkip(bb, subj, prevSubj, rewrite); + } + + static void cosp(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, subj, prevSubj, rewrite); + writeOrSkip(bb, pred, prevPred, rewrite); + } + + static void cops(ByteBuffer bb, long subj, long pred, long obj, long context, boolean hasPrev, long prevSubj, + long prevPred, long prevObj, long prevContext) { + boolean rewrite = !hasPrev; + rewrite = writeOrSkip(bb, context, prevContext, rewrite); + rewrite = writeOrSkip(bb, obj, prevObj, rewrite); + rewrite = writeOrSkip(bb, pred, prevPred, rewrite); + writeOrSkip(bb, subj, prevSubj, rewrite); } static boolean[] spocShouldMatch(long subj, long pred, long obj, long context) { diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/IdJoinRecordIteratorTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/IdJoinRecordIteratorTest.java new file mode 100644 index 00000000000..0550469d547 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/IdJoinRecordIteratorTest.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.rdf4j.sail.lmdb.join.IdJoinRecordIterator; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinIterator; +import org.junit.jupiter.api.Test; + +class IdJoinRecordIteratorTest { + + @Test + void rightIteratorAlreadyIncludesLeftBindings() { + IdBindingInfo leftInfo = bindingInfo(Map.of("a", 0, "b", 2)); + + RecordIterator leftIterator = recordIterator( + new long[] { 1L, 0L, 2L }, + new long[] { 7L, 0L, 8L }); + + IdJoinRecordIterator.RightFactory rightFactory = leftRecord -> { + long a = leftInfo.getId(leftRecord, "a"); + long b = leftInfo.getId(leftRecord, "b"); + if (a == 1L) { + return LmdbIdJoinIterator.emptyRecordIterator(); + } + return recordIterator( + new long[] { a, b, 50L }, + new long[] { a, b, 60L }); + }; + + IdJoinRecordIterator iterator = new IdJoinRecordIterator(leftIterator, rightFactory); + + assertThat(iterator.next()).containsExactly(7L, 8L, 50L); + assertThat(iterator.next()).containsExactly(7L, 8L, 60L); + assertThat(iterator.next()).isNull(); + } + + private static RecordIterator recordIterator(long[]... records) { + return new RecordIterator() { + int index = 0; + + @Override + public long[] next() { + if (index >= records.length) { + return null; + } + return Arrays.copyOf(records[index], records[index++].length); + } + + @Override + public void close() { + // no-op + } + }; + } + + private static IdBindingInfo bindingInfo(Map indexByVar) { + LinkedHashMap indexMap = new LinkedHashMap<>(indexByVar); + Map maskMap = new java.util.HashMap<>(); + indexByVar.forEach((name, position) -> maskMap.put(name, 1 << position)); + return new IdBindingInfo(indexMap, maskMap); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIteratorMultiBlockTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIteratorMultiBlockTest.java new file mode 100644 index 00000000000..7f746349e90 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIteratorMultiBlockTest.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.sail.NotifyingSailConnection; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.io.TempDir; + +/** + * Verifies that LmdbDupRecordIterator returns all duplicates across multiple MDB_GET_MULTIPLE blocks by inserting more + * duplicate values than can fit in a single page-chunk for one (subject,predicate) key. + */ +class LmdbDupRecordIteratorMultiBlockTest { + + @TempDir + File dataDir; + + private LmdbStore store; + private NotifyingSailConnection con; + private final ValueFactory vf = SimpleValueFactory.getInstance(); + + @BeforeEach + void setUp() throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,posc"); + config.setDupsortIndices(true); + config.setDupsortRead(true); + store = new LmdbStore(dataDir, config); + store.init(); + con = store.getConnection(); + } + + @AfterEach + void tearDown() throws Exception { + if (con != null) { + con.close(); + con = null; + } + if (store != null) { + store.shutDown(); + store = null; + } + } + + @Test + @Timeout(20) + void returnsAllDuplicatesAcrossMultipleBlocks() throws Exception { + // Choose a single subject/predicate and generate many distinct objects so that + // duplicates exceed a single MDB_GET_MULTIPLE chunk. + Resource s = vf.createIRI("urn:dup:subject"); + IRI p = vf.createIRI("urn:dup:predicate"); + + int duplicates = 1300; // > 1 page of 16-byte dup values on common 4K/16K pages + + con.begin(); + for (int i = 0; i < duplicates; i++) { + con.addStatement(s, p, vf.createIRI("urn:dup:obj:" + i)); + } + con.commit(); + + int count = 0; + try (var iter = con.getStatements(s, p, null, true)) { + while (iter.hasNext()) { + iter.next(); + count++; + } + } + + assertEquals(duplicates, count, "Iterator must return all duplicates, across multiple LMDB blocks"); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIteratorTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIteratorTest.java new file mode 100644 index 00000000000..a2a3b92c1a9 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbDupRecordIteratorTest.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.sail.NotifyingSailConnection; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.io.TempDir; + +/** + * Focused integration tests that exercise {@link LmdbDupRecordIterator} via the public Sail API. The scenarios reflect + * the assumptions made in the iterator about prefix binding and duplicate handling. + */ +class LmdbDupRecordIteratorTest { + + @TempDir + File dataDir; + + private LmdbStore store; + private NotifyingSailConnection con; + private final ValueFactory vf = SimpleValueFactory.getInstance(); + + @BeforeEach + void setUp() throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,posc"); + config.setDupsortIndices(true); + config.setDupsortRead(true); + store = new LmdbStore(dataDir, config); + store.init(); + con = store.getConnection(); + } + + @AfterEach + void tearDown() throws Exception { + if (con != null) { + con.close(); + con = null; + } + if (store != null) { + store.shutDown(); + store = null; + } + } + + @Test + void predicateAndObjectBoundReturnsAllSubjects() throws Exception { + Resource ctx = vf.createIRI("urn:ctx"); + loadTypeStatements(ctx); + + int count = countStatements(null, RDF.TYPE, RDFS.CLASS, true); + assertEquals(3, count, "Expected duplicate iterator to surface all matching subjects"); + } + + @Test + void predicateObjectAndContextBoundRestrictsToContext() throws Exception { + Resource ctx1 = vf.createIRI("urn:ctx:1"); + Resource ctx2 = vf.createIRI("urn:ctx:2"); + loadTypeStatements(ctx1); + con.begin(); + con.addStatement(vf.createIRI("urn:other"), RDF.TYPE, RDFS.CLASS, ctx2); + con.commit(); + + int countCtx1 = countStatements(null, RDF.TYPE, RDFS.CLASS, true, ctx1); + int countCtx2 = countStatements(null, RDF.TYPE, RDFS.CLASS, true, ctx2); + + assertEquals(2, countCtx1, "Context-specific lookup should include only ctx1 statements"); + assertEquals(1, countCtx2, "Context-specific lookup should include only ctx2 statements"); + } + + @Test + void singleLeadingBindingFallsBackGracefully() throws Exception { + loadTypeStatements(vf.createIRI("urn:ctx:fallback")); + int count = countStatements(null, RDF.TYPE, null, true); + assertEquals(3, count, "Fallback path should still return all type statements"); + } + + @Test + void duplicateIterationDisabledFallsBackToStandardIterator() throws Exception { + tearDown(); + + LmdbStoreConfig config = new LmdbStoreConfig("spoc,posc"); + config.setDupsortIndices(true); + config.setDupsortRead(false); + store = new LmdbStore(dataDir, config); + store.init(); + con = store.getConnection(); + + loadTypeStatements(vf.createIRI("urn:ctx:disabled")); + + int count = countStatements(null, RDF.TYPE, RDFS.CLASS, true); + assertEquals(3, count, "Store should still return complete results when dupsort is disabled"); + } + + @Test + void includeInferredStatementsWhenRequested() throws Exception { + Resource ctx = vf.createIRI("urn:ctx:inf"); + loadTypeStatements(ctx); + + con.begin(); + ((LmdbStoreConnection) con).addInferredStatement(vf.createIRI("urn:dan"), RDF.TYPE, RDFS.CLASS, ctx); + con.commit(); + + int explicitOnly = countStatements(null, RDF.TYPE, RDFS.CLASS, false); + int withInferred = countStatements(null, RDF.TYPE, RDFS.CLASS, true); + + assertEquals(3, explicitOnly, "Explicit view should ignore inferred additions"); + assertEquals(4, withInferred, "Requested inferred statements should be surfaced"); + } + + @Test + void rangePositioningSkipsOverSmallerPrefixes() throws Exception { + Resource ctx = vf.createIRI("urn:ctx:range"); + IRI otherType = vf.createIRI("urn:type:other"); + IRI targetType = vf.createIRI("urn:type:target"); + + con.begin(); + con.addStatement(vf.createIRI("urn:noise"), RDF.TYPE, otherType, ctx); + con.addStatement(vf.createIRI("urn:alpha"), RDF.TYPE, targetType, ctx); + con.addStatement(vf.createIRI("urn:beta"), RDF.TYPE, targetType, ctx); + con.addStatement(vf.createIRI("urn:gamma"), RDF.TYPE, targetType, ctx); + con.commit(); + + int count = countStatements(null, RDF.TYPE, targetType, true, ctx); + assertEquals(3, count, "Iterator should position on the first matching predicate/object prefix"); + } + + private void loadTypeStatements(Resource ctx) throws Exception { + con.begin(); + con.addStatement(vf.createIRI("urn:alice"), RDF.TYPE, RDFS.CLASS); + con.addStatement(vf.createIRI("urn:bob"), RDF.TYPE, RDFS.CLASS, ctx); + con.addStatement(vf.createIRI("urn:carol"), RDF.TYPE, RDFS.CLASS, ctx); + con.commit(); + } + + private int countStatements(Resource subj, IRI pred, org.eclipse.rdf4j.model.Value obj, boolean includeInferred, + Resource... ctx) throws Exception { + int count = 0; + try (var iter = con.getStatements(subj, pred, obj, includeInferred, ctx)) { + while (iter.hasNext()) { + iter.next(); + count++; + } + } + return count; + } + + @Test + @Timeout(5) + public void test() { + + // Add some data to the repository + con.begin(); + con.addStatement(Values.bnode(), RDF.TYPE, RDFS.CLASS); + con.addStatement(Values.bnode(), RDF.TYPE, RDFS.CLASS); + con.addStatement(Values.bnode(), RDF.TYPE, Values.bnode(), Values.bnode()); + con.addStatement(Values.bnode(), RDF.TYPE, Values.bnode(), Values.bnode()); + con.commit(); + + try (CloseableIteration statements = con.getStatements(null, RDF.TYPE, RDFS.CLASS, true)) { + int count = 0; + int max = 100; + while (max-- > 0 && statements.hasNext()) { + count++; + Statement next = statements.next(); + System.out.println(next); + } + assertThat(count).isEqualTo(2); + } + + } + + @Test + void clearRemovesAllStatements() throws Exception { + con.begin(); + for (int i = 0; i < 5; i++) { + con.addStatement(vf.createIRI("urn:s" + i), RDF.TYPE, RDFS.CLASS, vf.createIRI("urn:ctx" + (i % 2))); + } + con.commit(); + + con.begin(); + con.clear(); + con.commit(); + + assertEquals(0, countStatements(null, null, null, true)); + } + + @Test + void clearAfterSnapshotScenario() throws Exception { + // mimic SailIsolationLevelTest.setUp sequence + con.begin(); + con.addStatement(RDF.NIL, RDF.TYPE, RDF.LIST); + con.commit(); + + con.begin(); + con.clear(); + con.commit(); + + assertEquals(0, countStatements(null, null, null, true)); + } + +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationDatasetIteratorReuseTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationDatasetIteratorReuseTest.java new file mode 100644 index 00000000000..d26f4fccadf --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationDatasetIteratorReuseTest.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.junit.jupiter.api.Test; + +class LmdbEvaluationDatasetIteratorReuseTest { + + @Test + void defaultImplementationDoesNotCloseReusableIterator() throws Exception { + RecordingDataset dataset = new RecordingDataset(); + long[] binding = new long[] { 0L }; + long[] patternIds = new long[] { 0L, 0L, 0L, 0L }; + RecordingIterator reusable = new RecordingIterator(); + + dataset.getRecordIterator(binding, -1, -1, -1, -1, patternIds, null, null, null, reusable); + + assertFalse(reusable.closed, "reusable iterator should remain open"); + } + + private static final class RecordingDataset implements LmdbEvaluationDataset { + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds) throws QueryEvaluationException { + return new RecordIterator() { + @Override + public long[] next() { + return null; + } + + @Override + public void close() { + // no-op + } + }; + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, KeyRangeBuffers keyBuffers, long[] bindingReuse, long[] quadReuse) + throws QueryEvaluationException { + return getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings) + throws QueryEvaluationException { + return new RecordIterator() { + @Override + public long[] next() { + return null; + } + + @Override + public void close() { + // no-op + } + }; + } + + @Override + public ValueStore getValueStore() { + return null; + } + } + + private static final class RecordingIterator implements RecordIterator { + private boolean closed; + + @Override + public long[] next() { + return null; + } + + @Override + public void close() { + closed = true; + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdBGPEvaluationTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdBGPEvaluationTest.java new file mode 100644 index 00000000000..cb1166a9a83 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdBGPEvaluationTest.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.SingletonIteration; +import org.eclipse.rdf4j.common.iteration.UnionIteration; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.StatementPattern.Index; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.query.impl.EmptyBindingSet; +import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.sail.base.SailDataset; +import org.eclipse.rdf4j.sail.base.SailDatasetTripleSource; +import org.eclipse.rdf4j.sail.base.SailSource; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdBGPQueryEvaluationStep; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for the LMDB ID BGP evaluation step. + */ +public class LmdbIdBGPEvaluationTest { + + private static final String NS = "http://example.com/"; + + @Test + public void bgpPrefersContextOverlayDataset(@TempDir java.nio.file.Path tempDir) throws IOException { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + // Baseline: only the 'knows' triple exists, not 'likes'. + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + + // unwrap to the top-level Join expression + TupleExpr current = tupleExpr; + if (current instanceof org.eclipse.rdf4j.query.algebra.QueryRoot) { + current = ((org.eclipse.rdf4j.query.algebra.QueryRoot) current).getArg(); + } + if (current instanceof org.eclipse.rdf4j.query.algebra.Projection) { + current = ((org.eclipse.rdf4j.query.algebra.Projection) current).getArg(); + } + if (!(current instanceof Join)) { + // conservative guard; if the test query changes shape, fail clearly + throw new AssertionError("expected Join at root of algebra"); + } + Join join = (Join) current; + + // Flatten the BGP patterns from the Join + List patterns = new ArrayList<>(); + boolean flattened = LmdbIdBGPQueryEvaluationStep.flattenBGP(join, patterns); + assertThat(flattened).isTrue(); + assertThat(patterns).hasSize(2); + + // Prepare a baseline dataset (thread-local) and its triple source + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset baselineDataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + + try { + SailDatasetTripleSource baseTs = new SailDatasetTripleSource(repository.getValueFactory(), baselineDataset); + + // Overlay triple source that returns the extra 'likes' statement in addition to baseline content + Statement overlayStmt = vf.createStatement(alice, likes, pizza); + TripleSource overlayTs = new TripleSource() { + @Override + public CloseableIteration getStatements(Resource subj, IRI pred, Value obj, + Resource... contexts) { + boolean isLikes = pred != null && pred.equals(likes); + boolean subjOk = subj == null || subj.equals(alice); + boolean objOk = obj == null || obj.equals(pizza); + CloseableIteration base = baseTs.getStatements(subj, pred, obj, contexts); + if (isLikes && subjOk && objOk) { + // combine overlay with baseline + return new UnionIteration<>(new SingletonIteration<>(overlayStmt), base); + } + return base; + } + + @Override + public ValueFactory getValueFactory() { + return baseTs.getValueFactory(); + } + + @Override + public java.util.Comparator getComparator() { + // Narrow the wildcard comparator to the interface's return type + @SuppressWarnings("unchecked") + java.util.Comparator cmp = (java.util.Comparator) baseTs.getComparator(); + return cmp; + } + }; + + // Build the overlay dataset and a context carrying it + LmdbEvaluationDataset threadLocal = (LmdbEvaluationDataset) baselineDataset; + LmdbEvaluationDataset overlayDataset = new LmdbOverlayEvaluationDataset(overlayTs, + threadLocal.getValueStore()); + + QueryEvaluationContext ctx = new LmdbQueryEvaluationContext(null, overlayTs.getValueFactory(), + overlayTs.getComparator(), overlayDataset, threadLocal.getValueStore()); + + // Simulate a thread-local dataset reference that points to the baseline dataset + LmdbEvaluationStrategy.setCurrentDataset(threadLocal); + try { + LmdbIdBGPQueryEvaluationStep step = new LmdbIdBGPQueryEvaluationStep(join, patterns, ctx, null); + try (CloseableIteration iter = step.evaluate(EmptyBindingSet.getInstance())) { + List results = org.eclipse.rdf4j.common.iteration.Iterations.asList(iter); + // We expect 1 result because the overlay supplies the missing 'likes' triple. + assertThat(results).hasSize(1); + } + } finally { + LmdbEvaluationStrategy.clearCurrentDataset(); + } + } finally { + baselineDataset.close(); + branch.close(); + repository.shutDown(); + } + } + + @Test + public void bgpUsesIdArrayIterator(@TempDir java.nio.file.Path tempDir) throws IOException { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr current = tupleExpr; + if (current instanceof org.eclipse.rdf4j.query.algebra.QueryRoot) { + current = ((org.eclipse.rdf4j.query.algebra.QueryRoot) current).getArg(); + } + if (current instanceof org.eclipse.rdf4j.query.algebra.Projection) { + current = ((org.eclipse.rdf4j.query.algebra.Projection) current).getArg(); + } + if (!(current instanceof Join)) { + throw new AssertionError("expected Join at root of algebra"); + } + Join join = (Join) current; + + List patterns = new ArrayList<>(); + boolean flattened = LmdbIdBGPQueryEvaluationStep.flattenBGP(join, patterns); + assertThat(flattened).isTrue(); + assertThat(patterns).hasSize(2); + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + + try { + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + RecordingDataset recordingDataset = new RecordingDataset(lmdbDataset); + + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + + QueryEvaluationContext ctx = new LmdbQueryEvaluationContext(null, tripleSource.getValueFactory(), + tripleSource.getComparator(), recordingDataset, lmdbDataset.getValueStore()); + + LmdbIdBGPQueryEvaluationStep step = new LmdbIdBGPQueryEvaluationStep(join, patterns, ctx, null); + + try (CloseableIteration iter = step.evaluate(EmptyBindingSet.getInstance())) { + List results = org.eclipse.rdf4j.common.iteration.Iterations.asList(iter); + assertThat(results).hasSize(1); + } + + assertThat(recordingDataset.wasLegacyApiUsed()).isFalse(); + assertThat(recordingDataset.wasArrayApiUsed()).isTrue(); + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + @Test + public void recordsChosenIndexesForPatterns(@TempDir java.nio.file.Path tempDir) throws IOException { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr current = tupleExpr; + if (current instanceof org.eclipse.rdf4j.query.algebra.QueryRoot) { + current = ((org.eclipse.rdf4j.query.algebra.QueryRoot) current).getArg(); + } + if (current instanceof org.eclipse.rdf4j.query.algebra.Projection) { + current = ((org.eclipse.rdf4j.query.algebra.Projection) current).getArg(); + } + if (!(current instanceof Join)) { + throw new AssertionError("expected Join at root of algebra"); + } + Join join = (Join) current; + + List patterns = new ArrayList<>(); + boolean flattened = LmdbIdBGPQueryEvaluationStep.flattenBGP(join, patterns); + assertThat(flattened).isTrue(); + assertThat(patterns).hasSize(2); + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + + try { + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + + QueryEvaluationContext ctx = new LmdbQueryEvaluationContext(null, tripleSource.getValueFactory(), + tripleSource.getComparator(), lmdbDataset, lmdbDataset.getValueStore()); + + LmdbIdBGPQueryEvaluationStep step = new LmdbIdBGPQueryEvaluationStep(join, patterns, ctx, null); + + try (CloseableIteration iter = step.evaluate(EmptyBindingSet.getInstance())) { + while (iter.hasNext()) { + iter.next(); + } + } + + assertIndexMatchesDataset(lmdbDataset, patterns.get(0)); + assertIndexMatchesDataset(lmdbDataset, patterns.get(1)); + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + private void assertIndexMatchesDataset(LmdbEvaluationDataset dataset, + org.eclipse.rdf4j.query.algebra.StatementPattern pattern) + throws IOException { + long subjId = resolveId(dataset, pattern.getSubjectVar()); + long predId = resolveId(dataset, pattern.getPredicateVar()); + long objId = resolveId(dataset, pattern.getObjectVar()); + long ctxId = resolveId(dataset, pattern.getContextVar()); + + String fieldSeq = dataset.selectBestIndex(subjId, predId, objId, ctxId); + if (fieldSeq == null) { + assertThat(pattern.getIndex()).isNull(); + return; + } + String enumKey = fieldSeq.toUpperCase(Locale.ROOT); + assertThat(pattern.getIndex()).isEqualTo(Index.valueOf(enumKey)); + } + + private long resolveId(LmdbEvaluationDataset dataset, Var var) throws IOException { + if (var == null || !var.hasValue()) { + return LmdbValue.UNKNOWN_ID; + } + return dataset.getValueStore().getId(var.getValue()); + } + + private static final class RecordingDataset implements LmdbEvaluationDataset { + private final LmdbEvaluationDataset delegate; + + RecordingDataset(LmdbEvaluationDataset delegate) { + this.delegate = delegate; + } + + @Override + public RecordIterator getRecordIterator(org.eclipse.rdf4j.query.algebra.StatementPattern pattern, + BindingSet bindings) { + legacyApiUsed = true; + return delegate.getRecordIterator(pattern, bindings); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, + long[] patternIds) throws QueryEvaluationException { + arrayApiUsed = true; + return delegate.getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds); + } + + boolean wasLegacyApiUsed() { + return legacyApiUsed; + } + + boolean wasArrayApiUsed() { + return arrayApiUsed; + } + + @Override + public ValueStore getValueStore() { + return delegate.getValueStore(); + } + + @Override + public boolean hasTransactionChanges() { + return delegate.hasTransactionChanges(); + } + + private boolean legacyApiUsed; + private boolean arrayApiUsed; + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdBGPJoinTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdBGPJoinTest.java new file mode 100644 index 00000000000..238b26767fb --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdBGPJoinTest.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.Projection; +import org.eclipse.rdf4j.query.algebra.QueryRoot; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.explanation.Explanation; +import org.eclipse.rdf4j.query.explanation.GenericPlanNode; +import org.eclipse.rdf4j.query.impl.EmptyBindingSet; +import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.sail.SailConnection; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for nested BGP joins using LMDB ID-only join iterators. + */ +public class LmdbIdBGPJoinTest { + + private static final String NS = "http://example.com/"; + + @Test + public void nestedThreePatternBGP_usesIdJoinChain(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + try (RepositoryConnection conn = repository.getConnection()) { + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI carol = vf.createIRI(NS, "carol"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + IRI pasta = vf.createIRI(NS, "pasta"); + + // Data to satisfy a 3-pattern BGP: ?p knows ?x . ?p likes ?i . ?x likes ?i + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + conn.add(bob, likes, pizza); + + // Some extra data + conn.add(carol, likes, pasta); + } + + String query = "SELECT ?p ?i WHERE {\n" + + " ?p <" + NS + "knows> ?x .\n" + + " ?p <" + NS + "likes> ?i .\n" + + " ?x <" + NS + "likes> ?i .\n" + + "}"; + + try (RepositoryConnection conn = repository.getConnection()) { + // Verify correctness + try (var result = conn.prepareTupleQuery(QueryLanguage.SPARQL, query).evaluate()) { + List list = Iterations.asList(result); + assertThat(list).hasSize(1); + assertThat(list.get(0).hasBinding("p")).isTrue(); + assertThat(list.get(0).hasBinding("i")).isTrue(); + } + + // Verify the top join algorithm is our LMDB ID join once implemented + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + SailRepositoryConnection sailRepoConn = (SailRepositoryConnection) conn; + SailConnection sailConnection = sailRepoConn.getSailConnection(); + sailConnection.explain(Explanation.Level.Optimized, tupleExpr, null, EmptyBindingSet.getInstance(), true, + 0); + + TupleExpr unwrapped = unwrap(tupleExpr); + assertThat(unwrapped).isInstanceOf(Join.class); + Join topJoin = (Join) unwrapped; + // Expect the top join to be marked with our ID join algorithm once nested support is implemented + assertThat(topJoin.getAlgorithmName()).isEqualTo("LmdbIdJoinIterator"); + } finally { + repository.shutDown(); + } + } + + @Test + public void twoPatternBGP_usesIdJoinChain(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + try (RepositoryConnection conn = repository.getConnection()) { + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + } + + String query = "SELECT ?p ?i WHERE {\n" + + " ?p <" + NS + "knows> ?x .\n" + + " ?p <" + NS + "likes> ?i .\n" + + "}"; + + try (RepositoryConnection conn = repository.getConnection()) { + try (var result = conn.prepareTupleQuery(QueryLanguage.SPARQL, query).evaluate()) { + List list = Iterations.asList(result); + assertThat(list).hasSize(1); + } + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + SailRepositoryConnection sailRepoConn = (SailRepositoryConnection) conn; + SailConnection sailConnection = sailRepoConn.getSailConnection(); + sailConnection.explain(Explanation.Level.Optimized, tupleExpr, null, EmptyBindingSet.getInstance(), true, + 0); + + TupleExpr unwrapped = unwrap(tupleExpr); + assertThat(unwrapped).isInstanceOf(Join.class); + Join topJoin = (Join) unwrapped; + assertThat(topJoin.getAlgorithmName()).isEqualTo("LmdbIdJoinIterator"); + } finally { + repository.shutDown(); + } + } + + @Test + public void queryPlanAnnotatesEveryIdJoin(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + try (RepositoryConnection conn = repository.getConnection()) { + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI carol = vf.createIRI(NS, "carol"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + IRI pasta = vf.createIRI(NS, "pasta"); + + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + conn.add(bob, likes, pizza); + conn.add(carol, likes, pasta); + } + + String query = "SELECT ?p ?i WHERE {\n" + + " ?p <" + NS + "knows> ?x .\n" + + " ?p <" + NS + "likes> ?i .\n" + + " ?x <" + NS + "likes> ?i .\n" + + "}"; + + try (RepositoryConnection conn = repository.getConnection()) { + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + + SailRepositoryConnection sailRepoConn = (SailRepositoryConnection) conn; + SailConnection sailConnection = sailRepoConn.getSailConnection(); + Explanation explanation = sailConnection.explain(Explanation.Level.Optimized, tupleExpr, null, + EmptyBindingSet.getInstance(), true, 0); + + GenericPlanNode plan = explanation.toGenericPlanNode(); + List joinNodes = collectJoinNodes(plan); + + assertThat(joinNodes) + .withFailMessage("Expected multi-join BGP to produce at least two join nodes in plan but saw %s", + joinNodes.size()) + .hasSizeGreaterThanOrEqualTo(2); + + joinNodes.forEach(node -> assertThat(node.getAlgorithm()) + .withFailMessage("Plan node %s should show LMDB ID join usage", node.getType()) + .isEqualTo("LmdbIdJoinIterator")); + } finally { + repository.shutDown(); + } + } + + private TupleExpr unwrap(TupleExpr tupleExpr) { + TupleExpr current = tupleExpr; + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + if (current instanceof Projection) { + current = ((Projection) current).getArg(); + } + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + return current; + } + + private static List collectJoinNodes(GenericPlanNode root) { + List nodes = new ArrayList<>(); + collectJoinNodes(root, nodes); + return nodes; + } + + private static void collectJoinNodes(GenericPlanNode node, List out) { + if (node == null) { + return; + } + String type = node.getType(); + if (type != null && type.startsWith("Join")) { + out.add(node); + } + List children = node.getPlans(); + if (children != null) { + for (GenericPlanNode child : children) { + collectJoinNodes(child, out); + } + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinDisableOnChangesTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinDisableOnChangesTest.java new file mode 100644 index 00000000000..5e31fcdf76f --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinDisableOnChangesTest.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.SingletonIteration; +import org.eclipse.rdf4j.common.iteration.UnionIteration; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.Projection; +import org.eclipse.rdf4j.query.algebra.QueryRoot; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategyFactory; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.sail.base.SailDataset; +import org.eclipse.rdf4j.sail.base.SailDatasetTripleSource; +import org.eclipse.rdf4j.sail.base.SailSource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Verifies that the LMDB ID join optimization is disabled when the active transaction contains changes (represented via + * an overlay dataset). + */ +public class LmdbIdJoinDisableOnChangesTest { + + private static final String NS = "http://example.com/"; + + @Test + public void idJoinDisabledWhenOverlayPresent(@TempDir java.nio.file.Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + // Baseline: only the 'knows' triple exists + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr current = tupleExpr; + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + if (current instanceof Projection) { + current = ((Projection) current).getArg(); + } + if (!(current instanceof Join)) { + throw new AssertionError("expected Join at root of algebra"); + } + + // Build an overlay triple source that contributes the missing 'likes' triple + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset baselineDataset = branch + .dataset(org.eclipse.rdf4j.common.transaction.IsolationLevels.SNAPSHOT_READ); + try { + SailDatasetTripleSource baseTs = new SailDatasetTripleSource(repository.getValueFactory(), baselineDataset); + Statement overlayStmt = vf.createStatement(alice, likes, pizza); + TripleSource overlayTs = new TripleSource() { + @Override + public CloseableIteration getStatements(Resource subj, IRI pred, Value obj, + Resource... contexts) { + boolean isLikes = pred != null && pred.equals(likes); + boolean subjOk = subj == null || subj.equals(alice); + boolean objOk = obj == null || obj.equals(pizza); + CloseableIteration base = baseTs.getStatements(subj, pred, obj, contexts); + if (isLikes && subjOk && objOk) { + return new UnionIteration<>(new SingletonIteration<>(overlayStmt), base); + } + return base; + } + + @Override + public ValueFactory getValueFactory() { + return baseTs.getValueFactory(); + } + + @Override + public java.util.Comparator getComparator() { + @SuppressWarnings("unchecked") + java.util.Comparator cmp = (java.util.Comparator) baseTs.getComparator(); + return cmp; + } + }; + + // Prepare evaluation strategy over the baseline triple source + EvaluationStrategyFactory factory = store.getEvaluationStrategyFactory(); + EvaluationStrategy strategy = factory.createEvaluationStrategy(null, baseTs, + store.getBackingStore().getEvaluationStatistics()); + + // Install overlay dataset into the evaluation strategy's thread-local so precompile can see it + LmdbEvaluationDataset overlayDataset = new LmdbOverlayEvaluationDataset(overlayTs, + ((LmdbEvaluationDataset) baselineDataset).getValueStore()); + LmdbEvaluationStrategy.setCurrentDataset(overlayDataset); + try { + // Precompile the Join; since the overlay carries transaction changes the ID join must be disabled + strategy.precompile(current); + + assertThat(current).isInstanceOf(Join.class); + Join join = (Join) current; + assertThat(join.getAlgorithmName()).isNotEqualTo("LmdbIdJoinIterator"); + } finally { + LmdbEvaluationStrategy.clearCurrentDataset(); + } + } finally { + baselineDataset.close(); + branch.close(); + repository.shutDown(); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinEvaluationTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinEvaluationTest.java new file mode 100644 index 00000000000..3444bd728e1 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinEvaluationTest.java @@ -0,0 +1,604 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.List; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.Projection; +import org.eclipse.rdf4j.query.algebra.QueryRoot; +import org.eclipse.rdf4j.query.algebra.SingletonSet; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategyFactory; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext; +import org.eclipse.rdf4j.query.explanation.Explanation; +import org.eclipse.rdf4j.query.impl.EmptyBindingSet; +import org.eclipse.rdf4j.query.impl.MapBindingSet; +import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.sail.SailConnection; +import org.eclipse.rdf4j.sail.base.SailDataset; +import org.eclipse.rdf4j.sail.base.SailDatasetTripleSource; +import org.eclipse.rdf4j.sail.base.SailSource; +import org.eclipse.rdf4j.sail.lmdb.join.LmdbIdJoinQueryEvaluationStep; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class LmdbIdJoinEvaluationTest { + + private static final String NS = "http://example.com/"; + + @Test + public void simpleJoinUsesIdIterator(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + assertThat(store.getEvaluationStrategyFactory().getClass().getSimpleName()) + .isEqualTo("LmdbEvaluationStrategyFactory"); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + conn.add(bob, likes, pizza); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + try (RepositoryConnection conn = repository.getConnection()) { + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + + SailRepositoryConnection sailRepoConn = (SailRepositoryConnection) conn; + SailConnection sailConnection = sailRepoConn.getSailConnection(); + try (CloseableIteration iter = sailConnection.evaluate(tupleExpr, null, + EmptyBindingSet.getInstance(), true)) { + List bindings = Iterations.asList(iter); + assertThat(bindings).hasSize(1); + } + + sailConnection.explain(Explanation.Level.Optimized, tupleExpr, null, EmptyBindingSet.getInstance(), true, + 0); + + TupleExpr joinExpr = unwrap(tupleExpr); + assertThat(joinExpr).isInstanceOf(Join.class); + Join join = (Join) joinExpr; + assertThat(join.getAlgorithmName()) + .withFailMessage("left=%s right=%s", join.getLeftArg().getClass(), join.getRightArg().getClass()) + .isEqualTo("LmdbIdJoinIterator"); + } finally { + repository.shutDown(); + } + } + + @Test + public void mergeJoinRequestsLmdbMergeIterator(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI carol = vf.createIRI(NS, "carol"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + IRI salad = vf.createIRI(NS, "salad"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + conn.add(alice, likes, salad); + conn.add(bob, knows, carol); + conn.add(bob, likes, salad); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + + TupleExpr joinExpr = unwrap(tupleExpr); + assertThat(joinExpr).isInstanceOf(Join.class); + Join join = (Join) joinExpr; + StatementPattern left = (StatementPattern) join.getLeftArg(); + join.setOrder(left.getSubjectVar()); + join.setMergeJoin(true); + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + try { + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + EvaluationStrategyFactory factory = store.getEvaluationStrategyFactory(); + EvaluationStrategy strategy = factory.createEvaluationStrategy(null, tripleSource, + store.getBackingStore().getEvaluationStatistics()); + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + QueryEvaluationContext context = new LmdbQueryEvaluationContext(null, + tripleSource.getValueFactory(), tripleSource.getComparator(), lmdbDataset, + lmdbDataset.getValueStore()); + + QueryEvaluationStep step = strategy.precompile(join, context); + try (CloseableIteration iter = step.evaluate(EmptyBindingSet.getInstance())) { + List bindings = Iterations.asList(iter); + assertThat(bindings).hasSize(3); + } + + assertThat(join.isMergeJoin()).isTrue(); + assertThat(join.getAlgorithmName()) + .withFailMessage("left=%s right=%s", join.getLeftArg().getClass(), join.getRightArg().getClass()) + .isEqualTo("LmdbIdMergeJoinIterator"); + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + @Test + public void mergeJoinUsesArrayDatasetApi(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI carol = vf.createIRI(NS, "carol"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + IRI salad = vf.createIRI(NS, "salad"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + conn.add(alice, likes, salad); + conn.add(bob, knows, carol); + conn.add(bob, likes, salad); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr joinExpr = unwrap(tupleExpr); + assertThat(joinExpr).isInstanceOf(Join.class); + Join join = (Join) joinExpr; + StatementPattern left = (StatementPattern) join.getLeftArg(); + join.setOrder(left.getSubjectVar()); + join.setMergeJoin(true); + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + + try { + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + EvaluationStrategyFactory factory = store.getEvaluationStrategyFactory(); + EvaluationStrategy strategy = factory.createEvaluationStrategy(null, tripleSource, + store.getBackingStore().getEvaluationStatistics()); + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + RecordingDataset recordingDataset = new RecordingDataset(lmdbDataset); + QueryEvaluationContext context = new LmdbQueryEvaluationContext(null, tripleSource.getValueFactory(), + tripleSource.getComparator(), recordingDataset, lmdbDataset.getValueStore()); + + QueryEvaluationStep step = strategy.precompile(join, context); + try (CloseableIteration iter = step.evaluate(EmptyBindingSet.getInstance())) { + List bindings = Iterations.asList(iter); + assertThat(bindings).hasSize(3); + } + + assertThat(recordingDataset.wasLegacyOrderedApiUsed()).isFalse(); + assertThat(recordingDataset.wasArrayOrderedApiUsed()).isTrue(); + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + @Test + public void mergeJoinRespectsQueryBindings(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI painter = vf.createIRI(NS, "Painter"); + IRI painting = vf.createIRI(NS, "Painting"); + IRI paints = vf.createIRI(NS, "paints"); + IRI picasso = vf.createIRI(NS, "picasso"); + IRI guernica = vf.createIRI(NS, "guernica"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(painter, RDF.TYPE, RDFS.CLASS); + conn.add(painting, RDF.TYPE, RDFS.CLASS); + conn.add(picasso, RDF.TYPE, painter); + conn.add(guernica, RDF.TYPE, painting); + conn.add(picasso, paints, guernica); + } + + String query = "SELECT ?X WHERE {\n" + + " ?X a ?Y .\n" + + " ?Y a .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr joinExpr = unwrap(tupleExpr); + assertThat(joinExpr).isInstanceOf(Join.class); + Join join = (Join) joinExpr; + + StatementPattern left = (StatementPattern) join.getLeftArg(); + StatementPattern right = (StatementPattern) join.getRightArg(); + Var joinVar = left.getObjectVar(); + join.setOrder(joinVar); + left.setOrder(joinVar); + right.setOrder(right.getSubjectVar()); + join.setMergeJoin(true); + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + + try { + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + EvaluationStrategyFactory factory = store.getEvaluationStrategyFactory(); + EvaluationStrategy strategy = factory.createEvaluationStrategy(null, tripleSource, + store.getBackingStore().getEvaluationStatistics()); + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + RecordingDataset recordingDataset = new RecordingDataset(lmdbDataset); + QueryEvaluationContext context = new LmdbQueryEvaluationContext(null, + tripleSource.getValueFactory(), tripleSource.getComparator(), recordingDataset, + lmdbDataset.getValueStore()); + + QueryEvaluationStep step = strategy.precompile(join, context); + + MapBindingSet bindings = new MapBindingSet(2); + + try (CloseableIteration iter = step.evaluate(bindings)) { + List results = Iterations.asList(iter); + assertThat(results).hasSize(2); + assertThat(results).extracting(bs -> bs.getValue("X")).containsExactlyInAnyOrder(picasso, guernica); + } + + bindings.addBinding("Y", painter); + try (CloseableIteration iter = step.evaluate(bindings)) { + List results = Iterations.asList(iter); + assertThat(results).hasSize(1); + assertThat(results.get(0).getValue("X")).isEqualTo(picasso); + } + + bindings.addBinding("Z", painting); + try (CloseableIteration iter = step.evaluate(bindings)) { + List results = Iterations.asList(iter); + assertThat(results).hasSize(1); + assertThat(results.get(0).getValue("X")).isEqualTo(picasso); + } + + bindings.removeBinding("Y"); + try (CloseableIteration iter = step.evaluate(bindings)) { + List results = Iterations.asList(iter); + assertThat(results).hasSize(2); + assertThat(results).extracting(bs -> bs.getValue("X")).containsExactlyInAnyOrder(picasso, guernica); + } + + assertThat(recordingDataset.wasArrayOrderedApiUsed()).isTrue(); + assertThat(join.getAlgorithmName()).isEqualTo("LmdbIdMergeJoinIterator"); + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + @Test + public void mergeJoinFallsBackWhenOrderUnsupported(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI ben = vf.createIRI(NS, "ben"); + IRI carol = vf.createIRI(NS, "carol"); + IRI dana = vf.createIRI(NS, "dana"); + IRI erin = vf.createIRI(NS, "erin"); + IRI frank = vf.createIRI(NS, "frank"); + IRI george = vf.createIRI(NS, "george"); + IRI hannah = vf.createIRI(NS, "hannah"); + IRI knows = vf.createIRI(NS, "knows"); + IRI mentors = vf.createIRI(NS, "mentors"); + IRI admires = vf.createIRI(NS, "admires"); + IRI trusts = vf.createIRI(NS, "trusts"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, ben); + conn.add(alice, mentors, ben); + conn.add(carol, knows, dana); + conn.add(carol, mentors, dana); + conn.add(erin, admires, ben); + conn.add(george, trusts, ben); + conn.add(frank, admires, dana); + conn.add(hannah, trusts, dana); + } + + String query = "SELECT ?friend ?person ?fan\n" + + "WHERE {\n" + + " ?person ?predicate ?friend .\n" + + " ?fan ?fanPredicate ?friend .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr joinExpr = unwrap(tupleExpr); + assertThat(joinExpr).isInstanceOf(Join.class); + Join join = (Join) joinExpr; + + StatementPattern left = (StatementPattern) join.getLeftArg(); + StatementPattern right = (StatementPattern) join.getRightArg(); + Var joinVar = left.getObjectVar(); + join.setOrder(joinVar); + left.setOrder(joinVar); + right.setOrder(right.getObjectVar()); + join.setMergeJoin(true); + assertThat(left.getStatementOrder()).isEqualTo(StatementOrder.O); + assertThat(right.getStatementOrder()).isEqualTo(StatementOrder.O); + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + + try { + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + EvaluationStrategyFactory factory = store.getEvaluationStrategyFactory(); + EvaluationStrategy strategy = factory.createEvaluationStrategy(null, tripleSource, + store.getBackingStore().getEvaluationStatistics()); + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + Method chooser = lmdbDataset.getClass() + .getDeclaredMethod("chooseIndexForOrder", StatementOrder.class, + long.class, long.class, long.class, long.class); + chooser.setAccessible(true); + Object chosen = chooser.invoke(lmdbDataset, StatementOrder.O, -1L, -1L, -1L, -1L); + assertThat(chosen).isNull(); + QueryEvaluationContext context = new LmdbQueryEvaluationContext(null, tripleSource.getValueFactory(), + tripleSource.getComparator(), lmdbDataset, lmdbDataset.getValueStore()); + + QueryEvaluationStep step = strategy.precompile(join, context); + try (CloseableIteration iter = step.evaluate(EmptyBindingSet.getInstance())) { + List bindings = Iterations.asList(iter); + assertThat(bindings).hasSize(20); + } + + assertThat(join.getAlgorithmName()).isNotEqualTo("LmdbIdMergeJoinIterator"); + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + @Test + public void nonStatementPatternJoinRejected() { + Join join = new Join(new SingletonSet(), + new StatementPattern(new Var("s"), new Var("p"), new Var("o"))); + + EvaluationStrategy strategy = mock(EvaluationStrategy.class); + QueryEvaluationContext context = new QueryEvaluationContext.Minimal(null); + + assertThatThrownBy(() -> new LmdbIdJoinQueryEvaluationStep(strategy, join, context, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("StatementPattern"); + } + + @Test + public void joinUsesRecordIteratorsForLeftSide(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + SailRepository repository = new SailRepository(store); + repository.init(); + + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI knows = vf.createIRI(NS, "knows"); + IRI likes = vf.createIRI(NS, "likes"); + IRI pizza = vf.createIRI(NS, "pizza"); + + try (RepositoryConnection conn = repository.getConnection()) { + conn.add(alice, knows, bob); + conn.add(alice, likes, pizza); + } + + String query = "SELECT ?person ?item\n" + + "WHERE {\n" + + " ?person ?other .\n" + + " ?person ?item .\n" + + "}"; + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + TupleExpr joinExpr = unwrap(tupleExpr); + assertThat(joinExpr).isInstanceOf(Join.class); + Join join = (Join) joinExpr; + + SailSource branch = store.getBackingStore().getExplicitSailSource(); + SailDataset dataset = branch.dataset(IsolationLevels.SNAPSHOT_READ); + try { + SailDatasetTripleSource tripleSource = new SailDatasetTripleSource(repository.getValueFactory(), dataset); + EvaluationStrategyFactory factory = store.getEvaluationStrategyFactory(); + EvaluationStrategy strategy = factory.createEvaluationStrategy(null, tripleSource, + store.getBackingStore().getEvaluationStatistics()); + LmdbEvaluationDataset lmdbDataset = (LmdbEvaluationDataset) dataset; + QueryEvaluationContext context = new LmdbQueryEvaluationContext(null, + tripleSource.getValueFactory(), tripleSource.getComparator(), lmdbDataset, + lmdbDataset.getValueStore()); + + LmdbIdJoinQueryEvaluationStep step = new LmdbIdJoinQueryEvaluationStep(strategy, join, context, null); + + try (CloseableIteration iteration = step.evaluate(EmptyBindingSet.getInstance())) { + Class iteratorClass = iteration.getClass(); + assertThat(iteratorClass.getSimpleName()).isEqualTo("LmdbIdJoinIterator"); + + Field leftIteratorField = iteratorClass.getDeclaredField("leftIterator"); + leftIteratorField.setAccessible(true); + Object leftIterValue = leftIteratorField.get(iteration); + assertThat(leftIterValue).isInstanceOf(RecordIterator.class); + } + } finally { + dataset.close(); + branch.close(); + repository.shutDown(); + } + } + + private TupleExpr unwrap(TupleExpr tupleExpr) { + TupleExpr current = tupleExpr; + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + if (current instanceof Projection) { + current = ((Projection) current).getArg(); + } + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + return current; + } + + private static final class RecordingDataset implements LmdbEvaluationDataset { + private final LmdbEvaluationDataset delegate; + private boolean legacyOrderedApiUsed; + private boolean arrayOrderedApiUsed; + + private RecordingDataset(LmdbEvaluationDataset delegate) { + this.delegate = delegate; + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings) + throws QueryEvaluationException { + return delegate.getRecordIterator(pattern, bindings); + } + + @Override + public RecordIterator getRecordIterator(StatementPattern pattern, BindingSet bindings, + KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + return delegate.getRecordIterator(pattern, bindings, keyBuffers); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds) throws QueryEvaluationException { + return delegate.getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds); + } + + @Override + public RecordIterator getRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, KeyRangeBuffers keyBuffers, long[] reuse, long[] quadReuse) + throws QueryEvaluationException { + return delegate.getRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, + keyBuffers, reuse, quadReuse); + } + + @Override + public RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, + StatementOrder order) throws QueryEvaluationException { + legacyOrderedApiUsed = true; + return delegate.getOrderedRecordIterator(pattern, bindings, order); + } + + @Override + public RecordIterator getOrderedRecordIterator(StatementPattern pattern, BindingSet bindings, + StatementOrder order, KeyRangeBuffers keyBuffers) throws QueryEvaluationException { + legacyOrderedApiUsed = true; + return delegate.getOrderedRecordIterator(pattern, bindings, order, keyBuffers); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order) throws QueryEvaluationException { + arrayOrderedApiUsed = true; + return delegate.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, + order); + } + + @Override + public RecordIterator getOrderedRecordIterator(long[] binding, int subjIndex, int predIndex, int objIndex, + int ctxIndex, long[] patternIds, StatementOrder order, KeyRangeBuffers keyBuffers, long[] bindingReuse, + long[] quadReuse) throws QueryEvaluationException { + arrayOrderedApiUsed = true; + return delegate.getOrderedRecordIterator(binding, subjIndex, predIndex, objIndex, ctxIndex, patternIds, + order, keyBuffers, bindingReuse, quadReuse); + } + + boolean wasLegacyOrderedApiUsed() { + return legacyOrderedApiUsed; + } + + boolean wasArrayOrderedApiUsed() { + return arrayOrderedApiUsed; + } + + @Override + public ValueStore getValueStore() { + return delegate.getValueStore(); + } + + @Override + public boolean hasTransactionChanges() { + return delegate.hasTransactionChanges(); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinIsolationTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinIsolationTest.java new file mode 100644 index 00000000000..37e60965c02 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbIdJoinIsolationTest.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Stream; + +import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.common.transaction.IsolationLevel; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.algebra.Join; +import org.eclipse.rdf4j.query.algebra.Projection; +import org.eclipse.rdf4j.query.algebra.QueryRoot; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.explanation.Explanation; +import org.eclipse.rdf4j.query.impl.EmptyBindingSet; +import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.sail.SailConnection; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class LmdbIdJoinIsolationTest { + + private static final String NS = "http://example.com/"; + private static final String KNOWS = NS + "knows"; + private static final String LIKES = NS + "likes"; + private static final String CTX1 = NS + "ctx1"; + private static final String CTX2 = NS + "ctx2"; + + private SailRepository repository; + + @BeforeEach + void setUp(@TempDir Path tempDir) { + repository = new SailRepository(new LmdbStore(tempDir.toFile())); + repository.init(); + } + + @AfterEach + void tearDown() { + if (repository != null) { + repository.shutDown(); + } + } + + private static Stream isolationLevels() { + return Stream.of(IsolationLevels.SNAPSHOT_READ, IsolationLevels.SNAPSHOT, IsolationLevels.SERIALIZABLE); + } + + @ParameterizedTest + @MethodSource("isolationLevels") + void joinReflectsCommittedChanges(IsolationLevel isolationLevel) throws Exception { + String query = "SELECT ?person ?liked WHERE {\n" + + " GRAPH <" + CTX1 + "> { ?person <" + KNOWS + "> ?friend . }\n" + + " GRAPH <" + CTX2 + "> { ?person <" + LIKES + "> ?liked . }\n" + + "}"; + + try (SailRepositoryConnection conn1 = repository.getConnection(); + SailRepositoryConnection conn2 = repository.getConnection()) { + conn1.setIsolationLevel(isolationLevel); + conn2.setIsolationLevel(isolationLevel); + + ValueFactory vf = conn1.getValueFactory(); + IRI alice = vf.createIRI(NS, "alice"); + IRI bob = vf.createIRI(NS, "bob"); + IRI pizza = vf.createIRI(NS, "pizza"); + + conn1.begin(isolationLevel); + conn1.add(alice, vf.createIRI(KNOWS), bob, vf.createIRI(CTX1)); + conn1.add(alice, vf.createIRI(LIKES), pizza, vf.createIRI(CTX2)); + conn1.commit(); + + conn2.begin(isolationLevel); + assertThat(Iterations.asList(conn2.getStatements(null, vf.createIRI(KNOWS), null, false, + vf.createIRI(CTX1)))).hasSize(1); + + TupleQuery leftOnly = conn2.prepareTupleQuery(QueryLanguage.SPARQL, + "SELECT ?person WHERE { GRAPH <" + CTX1 + "> { ?person <" + KNOWS + "> ?friend . } }"); + assertThat(Iterations.asList(leftOnly.evaluate())).hasSize(1); + + TupleQuery rightOnly = conn2.prepareTupleQuery(QueryLanguage.SPARQL, + "SELECT ?person WHERE { GRAPH <" + CTX2 + "> { ?person <" + LIKES + "> ?liked . } }"); + assertThat(Iterations.asList(rightOnly.evaluate())).hasSize(1); + + TupleQuery tupleQuery = conn2.prepareTupleQuery(QueryLanguage.SPARQL, query); + List beforeClear; + try (TupleQueryResult result = tupleQuery.evaluate()) { + beforeClear = Iterations.asList(result); + } + assertThat(beforeClear).hasSize(1); + assertThat(beforeClear.get(0).getValue("person")).isEqualTo(alice); + assertThat(beforeClear.get(0).getValue("liked")).isEqualTo(pizza); + conn2.commit(); + + conn1.begin(isolationLevel); + conn1.clear(vf.createIRI(CTX1)); + conn1.commit(); + + conn2.begin(isolationLevel); + List afterClear; + try (TupleQueryResult result = tupleQuery.evaluate()) { + afterClear = Iterations.asList(result); + } + assertThat(afterClear).isEmpty(); + conn2.commit(); + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + SailConnection sailConnection = conn2.getSailConnection(); + sailConnection.explain(Explanation.Level.Optimized, tupleExpr, null, + EmptyBindingSet.getInstance(), true, 0); + Join join = unwrapJoin(tupleExpr); + assertThat(join.getAlgorithmName()) + .withFailMessage("left=%s right=%s", join.getLeftArg().getClass(), join.getRightArg().getClass()) + .isEqualTo("LmdbIdJoinIterator"); + } + } + + private Join unwrapJoin(TupleExpr tupleExpr) { + TupleExpr current = tupleExpr; + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + if (current instanceof Projection) { + current = ((Projection) current).getArg(); + } + if (current instanceof QueryRoot) { + current = ((QueryRoot) current).getArg(); + } + assertThat(current).isInstanceOf(Join.class); + return (Join) current; + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbOrderedTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbOrderedTest.java new file mode 100644 index 00000000000..f2e9a5a6d07 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbOrderedTest.java @@ -0,0 +1,99 @@ +/** + * ****************************************************************************** + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + * ****************************************************************************** + */ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.nio.file.Files; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.rdf4j.common.iteration.CloseableIteration; +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.sail.NotifyingSailConnection; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Verifies that LMDB store exposes statement ordering capabilities needed for merge-join optimization. + */ +public class LmdbOrderedTest { + + private File dataDir; + private LmdbStore store; + + @BeforeEach + public void setup() throws Exception { + dataDir = Files.createTempDirectory("rdf4j-lmdb-ordered-test").toFile(); + // Ensure both S and P primary indexes are available (default is spoc,posc, but set explicitly here) + store = new LmdbStore(dataDir, new LmdbStoreConfig("spoc,posc")); + } + + @AfterEach + public void tearDown() { + if (store != null) { + store.shutDown(); + } + if (dataDir != null) { + deleteRecursively(dataDir); + } + } + + private static void deleteRecursively(File f) { + if (f.isDirectory()) { + File[] files = f.listFiles(); + if (files != null) { + for (File c : files) { + deleteRecursively(c); + } + } + } + // ignore result; best-effort cleanup + f.delete(); + } + + @Test + public void getStatements_orderBySubject_returnsSorted() { + String NS = "http://example.org/"; + try (NotifyingSailConnection conn = store.getConnection()) { + conn.begin(); + conn.addStatement(Values.iri(NS, "d"), RDFS.LABEL, Values.literal("b")); + conn.addStatement(Values.iri(NS, "e"), RDFS.LABEL, Values.literal("a")); + conn.addStatement(Values.iri(NS, "a"), RDFS.LABEL, Values.literal("e")); + conn.addStatement(Values.iri(NS, "c"), RDFS.LABEL, Values.literal("c")); + conn.addStatement(Values.iri(NS, "b"), RDFS.LABEL, Values.literal("d")); + conn.commit(); + + conn.begin(IsolationLevels.NONE); + try (CloseableIteration it = conn.getStatements(StatementOrder.S, null, null, null, + true)) { + List subjects = it.stream() + .map(Statement::getSubject) + .map(v -> (IRI) v) + .map(IRI::getLocalName) + .collect(Collectors.toList()); + // LMDB orders by internal value-id (insertion order), not lexical + assertThat(subjects).isEqualTo(List.of("d", "e", "a", "c", "b")); + } + conn.commit(); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbQueryDebugTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbQueryDebugTest.java new file mode 100644 index 00000000000..2528f86b716 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbQueryDebugTest.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import java.io.File; + +import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.common.transaction.IsolationLevel; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.repository.Repository; +import org.eclipse.rdf4j.repository.RepositoryConnection; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Diagnostic replication of + * {@link org.eclipse.rdf4j.testsuite.repository.RepositoryConnectionTest#testPreparedTupleQuery}. + */ +class LmdbQueryDebugTest { + + @ParameterizedTest + @MethodSource("org.eclipse.rdf4j.testsuite.repository.RepositoryConnectionTest#parameters") + void preparedTupleQuery(IsolationLevel level) throws Exception { + File dataDir = java.nio.file.Files.createTempDirectory("lmdb-debug").toFile(); + Repository repo = new SailRepository(new LmdbStore(dataDir, new LmdbStoreConfig("spoc"))); + repo.init(); + try (RepositoryConnection con = repo.getConnection(); + RepositoryConnection con2 = repo.getConnection()) { + con.setIsolationLevel(level); + con2.setIsolationLevel(level); + + ValueFactory vf = repo.getValueFactory(); + IRI foafName = vf.createIRI("http://xmlns.com/foaf/0.1/name"); + IRI foafMbox = vf.createIRI("http://xmlns.com/foaf/0.1/mbox"); + IRI foafAgent = vf.createIRI("http://xmlns.com/foaf/0.1/Agent"); + + IRI alice = vf.createIRI("urn:alice"); + IRI bob = vf.createIRI("urn:bob"); + IRI ctx1 = vf.createIRI("urn:ctx1"); + IRI ctx2 = vf.createIRI("urn:ctx2"); + + con.begin(level); + con.add(alice, foafName, vf.createLiteral("Alice"), ctx2); + con.add(alice, foafMbox, vf.createLiteral("mailto:alice@example.org"), ctx2); + con.add(ctx2, foafAgent, vf.createLiteral("Alice")); + + con.add(bob, foafName, vf.createLiteral("Bob"), ctx1); + con.add(bob, foafMbox, vf.createLiteral("mailto:bob@example.org"), ctx1); + con.add(ctx1, foafAgent, vf.createLiteral("Bob")); + con.commit(); + + long statements = Iterations.asList(con.getStatements(null, null, null, false)).size(); + long statements2 = Iterations.asList(con2.getStatements(null, null, null, false)).size(); + + String queryBuilder = " PREFIX foaf: \n" + + " SELECT ?name ?mbox \n" + + " WHERE { [] foaf:name ?name; \n" + + " foaf:mbox ?mbox . }"; + TupleQuery query = con.prepareTupleQuery(queryBuilder); + query.setBinding("name", vf.createLiteral("Bob")); + + try (TupleQueryResult result = query.evaluate()) { + System.out.println("Isolation=" + level + " stmt=" + statements + " stmt2=" + statements2 + + " result.size=" + Iterations.asList(result).size()); + } + } finally { + repo.shutDown(); + org.apache.commons.io.FileUtils.deleteDirectory(dataDir); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIteratorLateBindingTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIteratorLateBindingTest.java new file mode 100644 index 00000000000..4f0ce6f9f91 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIteratorLateBindingTest.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class LmdbRecordIteratorLateBindingTest { + + @TempDir + File dataDir; + + private TripleStore tripleStore; + + @BeforeEach + void setUp() throws Exception { + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); + + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 3, 0, true); + tripleStore.storeTriple(1, 5, 6, 0, true); + tripleStore.storeTriple(1, 6, 9, 0, true); + tripleStore.storeTriple(2, 5, 6, 0, true); + tripleStore.commit(); + } + + @AfterEach + void tearDown() throws Exception { + if (tripleStore != null) { + tripleStore.close(); + } + } + + @Test + void subjectObjectPatternStillFiltersWithMatcher() throws Exception { + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + LmdbRecordIterator iter = (LmdbRecordIterator) tripleStore.getTriples(txn, 1, -1, 6, -1, true)) { + assertThat(iter).isInstanceOf(LmdbRecordIterator.class); + assertThat(getBooleanField(iter, "needMatcher")).isTrue(); + + List seen = new ArrayList<>(); + long[] next; + while ((next = iter.next()) != null) { + seen.add(next.clone()); + } + + assertThat(seen).containsExactly(new long[] { 1, 5, 6, 0 }); + assertThat(getField(iter, "groupMatcher")).isNotNull(); + } + } + + private static boolean getBooleanField(Object instance, String name) throws Exception { + Field field = LmdbRecordIterator.class.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(instance); + } + + private static Object getField(Object instance, String name) throws Exception { + Field field = LmdbRecordIterator.class.getDeclaredField(name); + field.setAccessible(true); + return field.get(instance); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIteratorPrefixScanTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIteratorPrefixScanTest.java new file mode 100644 index 00000000000..cd6e87b32e5 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIteratorPrefixScanTest.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class LmdbRecordIteratorPrefixScanTest { + + @TempDir + File dataDir; + + private TripleStore tripleStore; + + @BeforeEach + void setUp() throws Exception { + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); + + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 3, 0, true); + tripleStore.storeTriple(1, 5, 6, 0, true); + tripleStore.storeTriple(1, 7, 8, 1, true); + tripleStore.storeTriple(2, 2, 3, 0, true); + tripleStore.commit(); + } + + @AfterEach + void tearDown() throws Exception { + if (tripleStore != null) { + tripleStore.close(); + } + } + + @Test + void subjectPrefixScanSkipsGroupMatcher() throws Exception { + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + LmdbRecordIterator iter = (LmdbRecordIterator) tripleStore.getTriples(txn, 1, -1, -1, -1, true)) { + assertThat(iter).isInstanceOf(LmdbRecordIterator.class); + assertThat(getBooleanField(iter, "needMatcher")).isFalse(); + + List seen = new ArrayList<>(); + long[] next; + while ((next = iter.next()) != null) { + seen.add(next.clone()); + } + + assertThat(seen) + .containsExactlyInAnyOrder(new long[] { 1, 2, 3, 0 }, new long[] { 1, 5, 6, 0 }, + new long[] { 1, 7, 8, 1 }); + assertThat(getField(iter, "groupMatcher")).isNull(); + } + } + + private static boolean getBooleanField(Object instance, String name) throws Exception { + Field field = LmdbRecordIterator.class.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(instance); + } + + private static Object getField(Object instance, String name) throws Exception { + Field field = LmdbRecordIterator.class.getDeclaredField(name); + field.setAccessible(true); + return field.get(instance); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java index b735074c00c..e9cf348d025 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStoreTest.java @@ -209,6 +209,33 @@ public void testInferredSourceHasEmptyIterationWithoutInferredStatements() throw } } + @Test + public void testTxnFlagClearedOnRollback() throws Exception { + // Acquire backing store for direct dataset access + LmdbStore sail = (LmdbStore) ((SailRepository) repo).getSail(); + LmdbSailStore backingStore = sail.getBackingStore(); + + // Begin a transaction and perform a write to flip the storeTxnStarted flag to true + try (RepositoryConnection conn = repo.getConnection()) { + // Disable isolation so writes go directly to LMDB and flip storeTxnStarted + conn.begin(IsolationLevels.NONE); + conn.add(F.createStatement(F.createIRI("urn:txflag"), RDFS.LABEL, F.createLiteral("tmp"))); + + // While the transaction is open, the dataset should report pending transaction changes + try (SailDataset ds = backingStore.getExplicitSailSource().dataset(IsolationLevels.READ_COMMITTED)) { + assertTrue(((LmdbEvaluationDataset) ds).hasTransactionChanges()); + } + + // Roll back via the backing store API to ensure the store's rollback path is exercised + backingStore.rollback(); + } + + // After rollback, the dataset must no longer report transaction changes + try (SailDataset ds = backingStore.getExplicitSailSource().dataset(IsolationLevels.READ_COMMITTED)) { + assertFalse(((LmdbEvaluationDataset) ds).hasTransactionChanges()); + } + } + @AfterEach public void after() { repo.shutDown(); diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnectionExceptionFlagTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnectionExceptionFlagTest.java new file mode 100644 index 00000000000..3dad6c0e62b --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreConnectionExceptionFlagTest.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.nio.file.Path; +import java.util.function.Supplier; + +import org.eclipse.rdf4j.collection.factory.api.CollectionFactory; +import org.eclipse.rdf4j.collection.factory.impl.DefaultCollectionFactory; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.Dataset; +import org.eclipse.rdf4j.query.QueryEvaluationException; +import org.eclipse.rdf4j.query.QueryLanguage; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy; +import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep; +import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource; +import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver; +import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics; +import org.eclipse.rdf4j.query.impl.EmptyBindingSet; +import org.eclipse.rdf4j.query.parser.ParsedTupleQuery; +import org.eclipse.rdf4j.query.parser.QueryParserUtil; +import org.eclipse.rdf4j.sail.SailException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Verifies that {@link LmdbStoreConnection#evaluateInternal(TupleExpr, Dataset, BindingSet, boolean)} clears the + * connection-change flag when the delegate throws a {@link QueryEvaluationException}. + */ +public class LmdbStoreConnectionExceptionFlagTest { + + private static final String NS = "http://example.com/"; + + @Test + public void popsFlagOnQueryEvaluationException(@TempDir Path tempDir) throws Exception { + LmdbStore store = new LmdbStore(tempDir.toFile()); + store.setEvaluationStrategyFactory(new AlwaysThrowingEvaluationStrategyFactory()); + store.init(); + + try (LmdbStoreConnection connection = (LmdbStoreConnection) store.getConnection()) { + connection.begin(); + + ValueFactory vf = store.getValueFactory(); + connection.addStatement(vf.createIRI(NS, "alice"), vf.createIRI(NS, "knows"), vf.createIRI(NS, "bob")); + + ParsedTupleQuery parsed = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, + "SELECT ?person WHERE { ?person ?p ?o }", null); + TupleExpr tupleExpr = parsed.getTupleExpr(); + + assertFalse(LmdbEvaluationStrategy.hasActiveConnectionChanges(), + "precondition: no active thread-local flag"); + + SailException thrown = assertThrows(SailException.class, + () -> connection.evaluateInternal(tupleExpr, null, EmptyBindingSet.getInstance(), false)); + assertInstanceOf(QueryEvaluationException.class, thrown.getCause()); + + assertFalse(LmdbEvaluationStrategy.hasActiveConnectionChanges(), + "thread-local flag must be cleared after exception"); + + connection.rollback(); + } finally { + store.shutDown(); + } + } + + private static final class AlwaysThrowingEvaluationStrategyFactory extends LmdbEvaluationStrategyFactory { + + private Supplier collectionFactory = DefaultCollectionFactory::new; + + AlwaysThrowingEvaluationStrategyFactory() { + super(null); + } + + @Override + public void setCollectionFactory(Supplier collectionFactory) { + super.setCollectionFactory(collectionFactory); + this.collectionFactory = collectionFactory; + } + + @Override + public EvaluationStrategy createEvaluationStrategy(Dataset dataset, TripleSource tripleSource, + EvaluationStatistics evaluationStatistics) { + ThrowingEvaluationStrategy strategy = new ThrowingEvaluationStrategy(tripleSource, dataset, + getFederatedServiceResolver(), getQuerySolutionCacheThreshold(), evaluationStatistics, + isTrackResultSize()); + getOptimizerPipeline().ifPresent(strategy::setOptimizerPipeline); + strategy.setCollectionFactory(collectionFactory); + return strategy; + } + } + + private static final class ThrowingEvaluationStrategy extends LmdbEvaluationStrategy { + + ThrowingEvaluationStrategy(TripleSource tripleSource, Dataset dataset, FederatedServiceResolver resolver, + long iterationCacheSyncThreshold, EvaluationStatistics evaluationStatistics, boolean trackResultSize) { + super(tripleSource, dataset, resolver, iterationCacheSyncThreshold, evaluationStatistics, trackResultSize); + } + + @Override + public QueryEvaluationStep precompile(TupleExpr expr) { + throw new QueryEvaluationException("forced failure"); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreTest.java index ba1551bdb81..258029a8754 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbStoreTest.java @@ -15,6 +15,7 @@ import java.io.File; import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.model.vocabulary.RDFS; import org.eclipse.rdf4j.sail.NotifyingSail; import org.eclipse.rdf4j.sail.SailException; import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; @@ -60,4 +61,22 @@ public void testGetNamespacePersistence() { assertEquals(RDF.NAMESPACE, con.getNamespace("rdf")); } + + @Test + public void testPredicateObjectDupIteration() throws Exception { + con.begin(); + con.addStatement(painter, RDF.TYPE, RDFS.CLASS); + con.addStatement(painting, RDF.TYPE, RDFS.CLASS); + con.commit(); + + int count = 0; + try (var statements = con.getStatements(null, RDF.TYPE, RDFS.CLASS, true)) { + while (statements.hasNext()) { + statements.next(); + count++; + } + } + + assertEquals(2, count); + } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSubjectPredicateDupOrderTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSubjectPredicateDupOrderTest.java new file mode 100644 index 00000000000..54afd3329a4 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSubjectPredicateDupOrderTest.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Verifies that the subject–predicate dup index returns duplicate values in ascending numeric order for object/context + * ids. This guards against little-endian encoding of dup values, which breaks LMDB's lexicographic ordering and thus + * ORDER BY semantics. + */ +public class LmdbSubjectPredicateDupOrderTest { + + private TripleStore tripleStore; + + @BeforeEach + public void setup(@TempDir File dataDir) throws Exception { + // Ensure dupsort indices are enabled (default true) and pick a simple index set + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); + } + + @AfterEach + public void teardown() throws Exception { + if (tripleStore != null) { + tripleStore.close(); + } + } + + @Test + public void subjectPredicateDuplicateValuesOrderedByObjectAscending() throws Exception { + long s = 100L; + long p = 200L; + long c = 1L; + + // Object ids specifically chosen to expose little-endian vs big-endian lexicographic ordering + long[] objects = new long[] { 1L, 256L, 3L, 255L, 2L }; + + tripleStore.startTransaction(); + for (long o : objects) { + tripleStore.storeTriple(s, p, o, c, true); + } + tripleStore.commit(); + + List observed = new ArrayList<>(); + + try (TxnManager.Txn txn = tripleStore.getTxnManager().createReadTxn(); + RecordIterator it = tripleStore.getTriples(txn, s, p, -1, -1, true)) { + long[] quad; + while ((quad = it.next()) != null) { + // Ensure we only consider the (s,p,?,?) pattern we inserted + if (quad[TripleStore.SUBJ_IDX] == s && quad[TripleStore.PRED_IDX] == p) { + observed.add(quad[TripleStore.OBJ_IDX]); + } + } + } + + long[] expectedOrder = new long[] { 1L, 2L, 3L, 255L, 256L }; + long[] actualOrder = observed.stream().mapToLong(Long::longValue).toArray(); + + // If dup values are little-endian, LMDB will sort lexicographically and return 256 before 1. + // We assert strictly ascending numeric order by object id. + assertArrayEquals(expectedOrder, actualOrder, "Objects must be returned in ascending order by id"); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSupportedOrdersTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSupportedOrdersTest.java new file mode 100644 index 00000000000..22f08e79d0c --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/LmdbSupportedOrdersTest.java @@ -0,0 +1,109 @@ +/** + * ****************************************************************************** + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + * ****************************************************************************** + */ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.nio.file.Files; +import java.util.EnumSet; +import java.util.Set; + +import org.eclipse.rdf4j.common.order.StatementOrder; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Resource; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.sail.NotifyingSailConnection; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for LMDB supported statement orders, used by merge-join optimization. + */ +public class LmdbSupportedOrdersTest { + + private File dataDir; + private LmdbStore store; + + @BeforeEach + public void setup() throws Exception { + dataDir = Files.createTempDirectory("rdf4j-lmdb-supported-orders-test").toFile(); + store = new LmdbStore(dataDir, new LmdbStoreConfig("spoc,posc")); + // Force initialization so that backing store is available + try (NotifyingSailConnection ignored = store.getConnection()) { + // no-op + } + } + + @AfterEach + public void tearDown() { + if (store != null) { + store.shutDown(); + } + if (dataDir != null) { + deleteRecursively(dataDir); + } + } + + private static void deleteRecursively(File f) { + if (f.isDirectory()) { + File[] files = f.listFiles(); + if (files != null) { + for (File c : files) { + deleteRecursively(c); + } + } + } + // ignore result; best-effort cleanup + f.delete(); + } + + @Test + public void noBindings_defaultIndexes_supports_S_and_P() throws Exception { + LmdbSailStore internal = getBackingStore(store); + try (var dataset = internal.getExplicitSailSource().dataset(IsolationLevels.NONE)) { + Set supported = dataset.getSupportedOrders(null, null, null); + assertThat(supported).isEqualTo(EnumSet.of(StatementOrder.S, StatementOrder.P)); + } + } + + @Test + public void predicateBound_supports_P_and_O() throws Exception { + LmdbSailStore internal = getBackingStore(store); + try (var dataset = internal.getExplicitSailSource().dataset(IsolationLevels.NONE)) { + Set supported = dataset.getSupportedOrders(null, RDFS.LABEL, null); + assertThat(supported).isEqualTo(EnumSet.of(StatementOrder.P, StatementOrder.O)); + } + } + + @Test + public void subjectAndPredicateBound_supports_S_P_O() throws Exception { + IRI subj = Values.iri("urn:s"); + LmdbSailStore internal = getBackingStore(store); + try (var dataset = internal.getExplicitSailSource().dataset(IsolationLevels.NONE)) { + Set supported = dataset.getSupportedOrders((Resource) subj, RDFS.LABEL, (Value) null); + assertThat(supported).isEqualTo(EnumSet.of(StatementOrder.S, StatementOrder.P, StatementOrder.O)); + } + } + + private static LmdbSailStore getBackingStore(LmdbStore store) throws Exception { + var field = LmdbStore.class.getDeclaredField("backingStore"); + field.setAccessible(true); + return (LmdbSailStore) field.get(store); + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/MultipleSubselectRegressionTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/MultipleSubselectRegressionTest.java new file mode 100644 index 00000000000..a8dc06a8724 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/MultipleSubselectRegressionTest.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.io.IOUtils; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.query.TupleQuery; +import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.io.TempDir; + +class MultipleSubselectRegressionTest { + + private static final String DATASET_RESOURCE = "benchmarkFiles/datagovbe-valid.ttl"; + private static final String DATASET_IRI = "http://data.gov.be/dataset/brussels/3fded591-0cda-485f-97e7-3b982c7ff34b"; + private static final String MULTIPLE_SUB_SELECT_QUERY; + + static { + try (InputStream query = getResource(MultipleSubselectRegressionTest.class, + "benchmarkFiles/multiple-sub-select.qr")) { + MULTIPLE_SUB_SELECT_QUERY = IOUtils.toString(query, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new ExceptionInInitializerError(e); + } + } + + @Test + @Timeout(10 * 1000) + void lmdbMatchesMemoryForMultipleSubSelect(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + assertEquals(evaluateMultipleSubselectCount(memoryConn), evaluateMultipleSubselectCount(lmdbConn)); + assertEquals(evaluateDatasetLanguages(memoryConn), evaluateDatasetLanguages(lmdbConn)); + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + private static void loadDataset(SailRepositoryConnection connection) throws IOException { + connection.begin(IsolationLevels.NONE); + connection.add(getResource(MultipleSubselectRegressionTest.class, DATASET_RESOURCE), "", RDFFormat.TURTLE); + connection.commit(); + } + + private static long evaluateMultipleSubselectCount(SailRepositoryConnection connection) { + TupleQuery tupleQuery = connection.prepareTupleQuery(MULTIPLE_SUB_SELECT_QUERY); + + tupleQuery.setMaxExecutionTime(10); + + try (TupleQueryResult evaluate = tupleQuery.evaluate()) { + return evaluate + .stream() + .count(); + } + + } + + private static List evaluateDatasetLanguages(SailRepositoryConnection connection) { + String query = String.join("\n", + "PREFIX dct: ", + "SELECT ?lang WHERE {", + " <" + DATASET_IRI + "> dct:language ?lang .", + "}"); + + try (var result = connection.prepareTupleQuery(query).evaluate()) { + return result + .stream() + .map(bs -> bs.getValue("lang").stringValue()) + .sorted() + .collect(Collectors.toList()); + } + } + + private static InputStream getResource(Class anchor, String resourceName) { + InputStream stream = anchor.getClassLoader().getResourceAsStream(resourceName); + if (stream == null) { + throw new IllegalStateException("Missing resource: " + resourceName); + } + return stream; + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java index 3f8b3068fdf..a7c165e84e6 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/QueryBenchmarkTest.java @@ -12,12 +12,15 @@ package org.eclipse.rdf4j.sail.lmdb; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Stream; import org.apache.commons.io.IOUtils; @@ -27,7 +30,10 @@ import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.vocabulary.RDF; import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.TupleQuery; import org.eclipse.rdf4j.query.TupleQueryResult; +import org.eclipse.rdf4j.query.explanation.Explanation; +import org.eclipse.rdf4j.query.explanation.GenericPlanNode; import org.eclipse.rdf4j.repository.sail.SailRepository; import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; import org.eclipse.rdf4j.rio.RDFFormat; @@ -35,6 +41,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.rules.TemporaryFolder; /** @@ -147,39 +154,45 @@ public static void afterClass() { } @Test + @Timeout(30) public void groupByQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; try (var stream = connection.prepareTupleQuery(query1).evaluate().stream()) { count = stream.count(); } - System.out.println(count); + assertEquals(5, count); } } @Test +// @Timeout(30) public void complexQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; + System.out.println(); try (var stream = connection.prepareTupleQuery(query4).evaluate().stream()) { count = stream.count(); } System.out.println("count: " + count); + assertEquals(1485, count); } } @Test + @Timeout(30) public void distinctPredicatesQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; try (var stream = connection.prepareTupleQuery(query_distinct_predicates).evaluate().stream()) { count = stream.count(); } - System.out.println(count); + assertEquals(55, count); } } @Test + @Timeout(30) public void optionalLhsFilterQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; @@ -191,6 +204,7 @@ public void optionalLhsFilterQueryProducesExpectedCount() { } @Test + @Timeout(30) public void optionalRhsFilterQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; @@ -202,6 +216,7 @@ public void optionalRhsFilterQueryProducesExpectedCount() { } @Test + @Timeout(30) public void orderedUnionLimitQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; @@ -213,10 +228,58 @@ public void orderedUnionLimitQueryProducesExpectedCount() { } @Test + @Timeout(30) + public void long_chain() { + try (SailRepositoryConnection connection = repository.getConnection()) { + Explanation explain = connection.prepareTupleQuery(long_chain).explain(Explanation.Level.Executed); + System.out.println(explain); + + long count; + try (var stream = connection.prepareTupleQuery(long_chain).evaluate().stream()) { + count = stream.count(); + } + assertEquals(0, count); + } + } + + @Test + @Timeout(30) + public void long_chain_prefersIdJoinAlgorithms() { + try (SailRepositoryConnection connection = repository.getConnection()) { + TupleQuery tupleQuery = connection.prepareTupleQuery(long_chain); + Explanation explanation = tupleQuery.explain(Explanation.Level.Optimized); + GenericPlanNode root = explanation.toGenericPlanNode(); + + List algorithms = new ArrayList<>(); + collectAlgorithms(root, algorithms); + System.out.println("Optimized join algorithms: " + algorithms); + + boolean hasJoinIterator = algorithms.stream() + .filter(Objects::nonNull) + .anyMatch("JoinIterator"::equals); + + boolean hasLmdbIdAlgorithm = algorithms.stream() + .filter(Objects::nonNull) + .anyMatch(name -> name.startsWith("LmdbId")); + boolean hasMergeJoin = algorithms.stream() + .filter(Objects::nonNull) + .anyMatch("LmdbIdMergeJoinIterator"::equals); + + assertTrue(hasLmdbIdAlgorithm, "Expected LMDB ID-based join algorithms to appear in optimized plan"); + assertTrue(hasMergeJoin, "Expected optimized plan to include LmdbIdMergeJoinIterator"); + assertTrue(!hasJoinIterator, + () -> "Expected optimized plan to avoid generic JoinIterator but found: " + algorithms); + } + } + + @Test + @Timeout(30) public void subSelectQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; - try (var stream = connection.prepareTupleQuery(sub_select).evaluate().stream()) { + TupleQuery tupleQuery = connection.prepareTupleQuery(sub_select); + tupleQuery.setMaxExecutionTime(30); + try (var stream = tupleQuery.evaluate().stream()) { count = stream.count(); } assertEquals(16035L, count); @@ -224,6 +287,7 @@ public void subSelectQueryProducesExpectedCount() { } @Test + @Timeout(30) public void multipleSubSelectQueryProducesExpectedCount() { try (SailRepositoryConnection connection = repository.getConnection()) { long count; @@ -235,6 +299,7 @@ public void multipleSubSelectQueryProducesExpectedCount() { } @Test + @Timeout(30) public void removeByQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { connection.begin(IsolationLevels.NONE); @@ -249,6 +314,7 @@ public void removeByQuery() { } @Test + @Timeout(30) public void removeByQueryReadCommitted() { try (SailRepositoryConnection connection = repository.getConnection()) { connection.begin(IsolationLevels.READ_COMMITTED); @@ -263,6 +329,7 @@ public void removeByQueryReadCommitted() { } @Test + @Timeout(30) public void simpleUpdateQueryIsolationReadCommitted() { try (SailRepositoryConnection connection = repository.getConnection()) { connection.begin(IsolationLevels.READ_COMMITTED); @@ -280,6 +347,7 @@ public void simpleUpdateQueryIsolationReadCommitted() { } @Test + @Timeout(30) public void simpleUpdateQueryIsolationNone() { try (SailRepositoryConnection connection = repository.getConnection()) { connection.begin(IsolationLevels.NONE); @@ -297,13 +365,28 @@ public void simpleUpdateQueryIsolationNone() { } @Test + @Timeout(30) public void ordered_union_limit() { - for (int i = 0; i < 100; i++) { - try (SailRepositoryConnection connection = repository.getConnection()) { - long count = count(connection - .prepareTupleQuery(ordered_union_limit) - .evaluate()); - assertEquals(250L, count); + try (SailRepositoryConnection connection = repository.getConnection()) { + long count = count(connection + .prepareTupleQuery(ordered_union_limit) + .evaluate()); + assertEquals(250L, count); + } + } + + private static void collectAlgorithms(GenericPlanNode node, List algorithms) { + if (node == null) { + return; + } + String algorithm = node.getAlgorithm(); + if (algorithm != null) { + algorithms.add(algorithm); + } + List children = node.getPlans(); + if (children != null) { + for (GenericPlanNode child : children) { + collectAlgorithms(child, algorithms); } } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexDistributionDebugTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexDistributionDebugTest.java new file mode 100644 index 00000000000..d246ac78158 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexDistributionDebugTest.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.vocabulary.DCTERMS; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class SubjectPredicateIndexDistributionDebugTest { + + private static final String DATASET_RESOURCE = "temp.nquad"; + private static final String DATASET_IRI = "http://data.gov.be/dataset/brussels/3fded591-0cda-485f-97e7-3b982c7ff34b"; + + @Test + void debugListLanguages(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + List lmdbLangs = getLanguages(lmdbConn); + List lmdbExplicit = getLanguagesExplicitOnly(lmdbConn); + List memLangs = getLanguages(memoryConn); + + System.out.println("LMDB languages (incl inf): " + lmdbLangs); + System.out.println("LMDB languages (explicit): " + lmdbExplicit); + System.out.println("MEM languages: " + memLangs); + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + @Test + void debugFindFirstMismatch(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository referenceRepository = new SailRepository(new MemoryStore()); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + referenceRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection memoryConn = referenceRepository.getConnection()) { + loadDataset(memoryConn); + } + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection(); + SailRepositoryConnection referenceConn = referenceRepository.getConnection()) { + + try (var statements = referenceConn.getStatements(null, null, null, true)) { + int count = 0; + for (Statement stmt : statements) { + count++; + lmdbConn.begin(IsolationLevels.NONE); + memoryConn.begin(IsolationLevels.NONE); + try { + lmdbConn.add(stmt); + memoryConn.add(stmt); + } finally { + lmdbConn.commit(); + memoryConn.commit(); + } + + long countLmdb = org.eclipse.rdf4j.query.QueryResults + .count(lmdbConn.getStatements(SimpleValueFactory.getInstance().createIRI(DATASET_IRI), + DCTERMS.LANGUAGE, null, true)); + long countMem = org.eclipse.rdf4j.query.QueryResults + .count(memoryConn.getStatements(SimpleValueFactory.getInstance().createIRI(DATASET_IRI), + DCTERMS.LANGUAGE, null, true)); + if (countLmdb != countMem) { + System.out.println("Mismatch after adding statement #" + count + ": " + stmt); + System.out.println("LMDB count=" + countLmdb + ", MEM count=" + countMem); + break; + } + } + } + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + referenceRepository.shutDown(); + } + } + + @Test + void debugListLanguagesPsocOnly(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + List lmdbLangs = getLanguages(lmdbConn); + List memLangs = getLanguages(memoryConn); + + System.out.println("[psoc] LMDB languages: " + lmdbLangs); + System.out.println("[psoc] MEM languages: " + memLangs); + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + @Test + void debugListLanguagesNoDupsortRead(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); + config.setDupsortRead(false); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + List lmdbLangs = getLanguages(lmdbConn); + List memLangs = getLanguages(memoryConn); + + System.out.println("[no-dupsort] LMDB languages: " + lmdbLangs); + System.out.println("[no-dupsort] MEM languages: " + memLangs); + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + private static void loadDataset(SailRepositoryConnection connection) throws IOException { + connection.begin(IsolationLevels.NONE); + try (InputStream data = getResource(DATASET_RESOURCE)) { + connection.add(data, "", RDFFormat.TURTLE); + } + connection.commit(); + } + + private static InputStream getResource(String name) { + InputStream stream = SubjectPredicateIndexDistributionDebugTest.class + .getClassLoader() + .getResourceAsStream(name); + assertNotNull(stream, "Missing resource: " + name); + return stream; + } + + private static List getLanguages(SailRepositoryConnection connection) { + var vf = SimpleValueFactory.getInstance(); + var dataset = vf.createIRI(DATASET_IRI); + try (var iter = connection.getStatements(dataset, DCTERMS.LANGUAGE, null, true)) { + return Iterations.stream(iter) + .map(Statement::getObject) + .map(Value::stringValue) + .sorted() + .collect(Collectors.toList()); + } + } + + private static List getLanguagesExplicitOnly(SailRepositoryConnection connection) { + var vf = SimpleValueFactory.getInstance(); + var dataset = vf.createIRI(DATASET_IRI); + try (var iter = connection.getStatements(dataset, DCTERMS.LANGUAGE, null, false)) { + return Iterations.stream(iter) + .map(Statement::getObject) + .map(Value::stringValue) + .sorted() + .collect(Collectors.toList()); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexDistributionRegressionTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexDistributionRegressionTest.java new file mode 100644 index 00000000000..ad57199e09c --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexDistributionRegressionTest.java @@ -0,0 +1,499 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.rdf4j.common.iteration.Iterations; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Statement; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.vocabulary.DCAT; +import org.eclipse.rdf4j.model.vocabulary.DCTERMS; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.QueryResults; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class SubjectPredicateIndexDistributionRegressionTest { + + private static final String DATASET_RESOURCE = "temp.nquad"; + private static final IRI DATASET_IRI = SimpleValueFactory.getInstance() + .createIRI("http://data.gov.be/dataset/brussels/3fded591-0cda-485f-97e7-3b982c7ff34b"); + private static final String FILTERED_MULTIPLE_SUBSELECT_QUERY = String.join("\n", + "PREFIX ex: ", + "PREFIX owl: ", + "PREFIX rdf: ", + "PREFIX rdfs: ", + "PREFIX sh: ", + "PREFIX xsd: ", + "PREFIX dcat: ", + "PREFIX dc: ", + "PREFIX skos: ", + "PREFIX foaf: ", + "PREFIX dct: ", + "", + "SELECT ?type1 ?type2 ?language2 ?mbox ?count ?identifier2 WHERE {", + " VALUES ?a { <" + DATASET_IRI + "> }", + " {", + " SELECT * WHERE {", + " ?a a ?type2.", + " ?b a ?type1.", + "", + " ?b dcat:dataset ?a.", + "", + " ?a dcat:distribution ?mbox.", + " ?a dct:language ?language.", + " FILTER (?type1 != ?type2)", + " ?a dct:identifier ?identifier.", + " }", + " }", + "", + " {", + " SELECT DISTINCT ?a (COUNT(?dist) AS ?count) ?language2 WHERE {", + " ?a a ?type2.", + " ?a dcat:distribution ?dist.", + " ?a dct:language ?language2.", + " } GROUP BY ?a ?language2 HAVING (?count > 2)", + " }", + "}"); + + @Test + void countLanguage(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + System.out.println("LMDB's count of languages for dataset: "); + String lmdbRes = QueryResults.toString(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true), + "\n"); + System.out.println(lmdbRes); + + System.out.println(); + System.out.println("MemoryStore's count of languages for dataset: "); + String memRes = QueryResults.toString(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true), + "\n"); + System.out.println(memRes); + + long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + + assertEquals(3, countm, "Expected 3 languages for dataset in Memory store"); + assertEquals(3, countl, "Expected 3 languages for dataset in LMDB store"); + + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + +// @Test +// void countLanguage2(@TempDir Path tempDir) throws Exception { +// LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); +// SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); +// SailRepository referenceRepository = new SailRepository(new MemoryStore()); +// SailRepository memoryRepository = new SailRepository(new MemoryStore()); +// +// lmdbRepository.init(); +// referenceRepository.init(); +// memoryRepository.init(); +// +// try (SailRepositoryConnection memoryConn = referenceRepository.getConnection()) { +// loadDataset(memoryConn); +// } +// +// try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); +// SailRepositoryConnection memoryConn = memoryRepository.getConnection(); +// SailRepositoryConnection referenceConn = referenceRepository.getConnection()) { +// +// try (RepositoryResult statements = referenceConn.getStatements(null, null, null, true)) { +// int count = 0; +// for (Statement stmt : statements) { +// lmdbConn.begin(IsolationLevels.NONE); +// memoryConn.begin(IsolationLevels.NONE); +// try { +// lmdbConn.add(stmt); +// memoryConn.add(stmt); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// +// lmdbConn.commit(); +// memoryConn.commit(); +// +// count++; +// +// long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// if (countm != countl) { +// System.out.println("Discrepancy at count " + count); +// assertEquals(countl, countm); +// } +// +// } +// } +// +// +// } finally { +// lmdbRepository.shutDown(); +// memoryRepository.shutDown(); +// } +// } +// +// @Test +// void countLanguage3(@TempDir Path tempDir) throws Exception { +// LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); +// SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); +// SailRepository referenceRepository = new SailRepository(new MemoryStore()); +// SailRepository memoryRepository = new SailRepository(new MemoryStore()); +// +// lmdbRepository.init(); +// referenceRepository.init(); +// memoryRepository.init(); +// +// try (SailRepositoryConnection memoryConn = referenceRepository.getConnection()) { +// loadDataset(memoryConn); +// } +// +// try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); +// SailRepositoryConnection memoryConn = memoryRepository.getConnection(); +// SailRepositoryConnection referenceConn = referenceRepository.getConnection()) { +// +// try (RepositoryResult statements = referenceConn.getStatements(null, null, null, true)) { +// lmdbConn.begin(IsolationLevels.NONE); +// memoryConn.begin(IsolationLevels.NONE); +// +// int count = 0; +// +// for (Statement stmt : statements) { +// +// count++; +// if (count < 18000) { +// continue; +// } +// if (count >= 40000) { +// break; +// } +// +// try { +// if (count % 10000 == 0) { +// lmdbConn.commit(); +// memoryConn.commit(); +// long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// assertEquals(countl, countm, "Discrepancy at count " + count); +// lmdbConn.begin(IsolationLevels.NONE); +// memoryConn.begin(IsolationLevels.NONE); +// } +// lmdbConn.add(stmt); +// memoryConn.add(stmt); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// } +// lmdbConn.commit(); +// memoryConn.commit(); +// +// +// long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// assertEquals(countl, countm); +// +// +// } +// +// +// } finally { +// lmdbRepository.shutDown(); +// memoryRepository.shutDown(); +// } +// } +// +// @Test +// void countLanguage4(@TempDir Path tempDir) throws Exception { +// +// +// LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); +// SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb" + UUID.randomUUID()).toFile(), config)); +// SailRepository referenceRepository = new SailRepository(new MemoryStore()); +// SailRepository memoryRepository = new SailRepository(new MemoryStore()); +// +// lmdbRepository.init(); +// referenceRepository.init(); +// memoryRepository.init(); +// +// try (SailRepositoryConnection memoryConn = referenceRepository.getConnection()) { +// loadDataset(memoryConn); +// } +// +// try (FileOutputStream fileOutputStream = new FileOutputStream("/Users/havardottestad/Documents/Programming/rdf4j/temp.nquad")) { +// +// RDFWriter writer = Rio.createWriter(RDFFormat.NQUADS, fileOutputStream); +// writer.startRDF(); +// +// try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); +// SailRepositoryConnection memoryConn = memoryRepository.getConnection(); +// SailRepositoryConnection referenceConn = referenceRepository.getConnection()) { +// +// try (RepositoryResult statements = referenceConn.getStatements(null, null, null, true)) { +// lmdbConn.begin(IsolationLevels.NONE); +// memoryConn.begin(IsolationLevels.NONE); +// +// int count = 0; +// +// for (Statement stmt : statements) { +// count++; +// if (count < 9349) { +// continue; +// } +// +// +// try { +// writer.handleStatement(stmt); +// lmdbConn.add(stmt); +// memoryConn.add(stmt); +// } catch (Exception e) { +// throw new RuntimeException(e); +// } +// +// if (count > 37983) { +// break; +// } +// } +// +// writer.endRDF(); +// lmdbConn.commit(); +// memoryConn.commit(); +// +// +// long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); +// assertEquals(countl, countm, "Discrepancy at count " + count); +// +// } finally { +// lmdbRepository.shutDown(); +// memoryRepository.shutDown(); +// } +// } +// } +// } + + @Test + void countLanguageDifferentIndexes1(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + + assertEquals(countl, countm); + + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + @Test + void countLanguageDifferentIndexes2(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + + assertEquals(countl, countm); + + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + @Test + void countLanguageDifferentIndexes3(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + + assertEquals(countl, countm); + + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + @Test + void countLanguageDifferentIndexes4(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("ospc,psoc"); + SailRepository lmdbRepository = new SailRepository(new LmdbStore(tempDir.resolve("lmdb").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadDataset(lmdbConn); + loadDataset(memoryConn); + + long countl = QueryResults.count(lmdbConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + long countm = QueryResults.count(memoryConn.getStatements(DATASET_IRI, DCTERMS.LANGUAGE, null, true)); + + assertEquals(countl, countm); + + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + @Test + void lmdbExposesAllLanguagesForSimpleDataset(@TempDir Path tempDir) throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("spoc,ospc,psoc"); + SailRepository lmdbRepository = new SailRepository( + new LmdbStore(tempDir.resolve("lmdb-simple").toFile(), config)); + SailRepository memoryRepository = new SailRepository(new MemoryStore()); + + lmdbRepository.init(); + memoryRepository.init(); + + try (SailRepositoryConnection lmdbConn = lmdbRepository.getConnection(); + SailRepositoryConnection memoryConn = memoryRepository.getConnection()) { + loadSimpleDataset(lmdbConn); + loadSimpleDataset(memoryConn); + + List lmdbLanguages = evaluateLanguages(lmdbConn); + List memoryLanguages = evaluateLanguages(memoryConn); + + Collections.sort(lmdbLanguages); + Collections.sort(memoryLanguages); + + assertEquals(memoryLanguages, lmdbLanguages); + } finally { + lmdbRepository.shutDown(); + memoryRepository.shutDown(); + } + } + + private static void loadDataset(SailRepositoryConnection connection) throws IOException { + connection.begin(IsolationLevels.NONE); + try (InputStream data = getResource(DATASET_RESOURCE)) { + connection.add(data, "", RDFFormat.TURTLE); + } + connection.commit(); + } + + private static InputStream getResource(String name) { + InputStream stream = SubjectPredicateIndexDistributionRegressionTest.class.getClassLoader() + .getResourceAsStream(name); + if (stream == null) { + throw new IllegalStateException("Missing resource: " + name); + } + return stream; + } + + private static List evaluateMultipleSubselect(SailRepositoryConnection connection) { + try (var result = connection.prepareTupleQuery(FILTERED_MULTIPLE_SUBSELECT_QUERY).evaluate()) { + return QueryResults.asList(result) + .stream() + .map(SubjectPredicateIndexDistributionRegressionTest::formatBindingSet) + .collect(Collectors.toList()); + } + } + + private static String formatBindingSet(BindingSet bindingSet) { + return bindingSet.getBindingNames() + .stream() + .sorted() + .map(name -> name + "=" + bindingSet.getValue(name)) + .collect(Collectors.joining(", ")); + } + + private static void loadSimpleDataset(SailRepositoryConnection connection) throws IOException { + var vf = SimpleValueFactory.getInstance(); + var dataset = vf.createIRI("urn:dataset:simple"); + var catalog = vf.createIRI("urn:catalog:simple"); + connection.begin(IsolationLevels.NONE); + connection.add(dataset, org.eclipse.rdf4j.model.vocabulary.RDF.TYPE, DCAT.DATASET); + connection.add(catalog, org.eclipse.rdf4j.model.vocabulary.RDF.TYPE, DCAT.CATALOG); + connection.add(catalog, DCAT.DATASET, dataset); + connection.add(dataset, DCTERMS.LANGUAGE, vf.createIRI("urn:lang:ENG")); + connection.add(dataset, DCTERMS.LANGUAGE, vf.createIRI("urn:lang:FRA")); + connection.add(dataset, DCTERMS.LANGUAGE, vf.createIRI("urn:lang:NLD")); + connection.add(dataset, DCAT.DISTRIBUTION, vf.createIRI("urn:dist:one")); + connection.add(dataset, DCAT.DISTRIBUTION, vf.createIRI("urn:dist:two")); + connection.add(dataset, DCAT.DISTRIBUTION, vf.createIRI("urn:dist:three")); + connection.commit(); + } + + private static List evaluateLanguages(SailRepositoryConnection connection) { + try (var iter = connection.getStatements(SimpleValueFactory.getInstance().createIRI("urn:dataset:simple"), + DCTERMS.LANGUAGE, null, true)) { + return Iterations.stream(iter) + .map(Statement::getObject) + .map(Value::stringValue) + .collect(Collectors.toList()); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexTest.java new file mode 100644 index 00000000000..1e8fe187ea0 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/SubjectPredicateIndexTest.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.lang.reflect.Field; + +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class SubjectPredicateIndexTest { + + @TempDir + File dataDir; + + private TripleStore tripleStore; + + @BeforeEach + void setUp() throws Exception { + LmdbStoreConfig config = new LmdbStoreConfig("posc"); + config.setDupsortIndices(true); + config.setDupsortRead(true); + tripleStore = new TripleStore(dataDir, config, null); + + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 3, 0, true); + tripleStore.storeTriple(1, 2, 4, 0, true); + tripleStore.commit(); + } + + @AfterEach + void tearDown() throws Exception { + if (tripleStore != null) { + tripleStore.close(); + } + } + + @Test + void subjectPredicatePatternUsesDupIterator() throws Exception { + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + RecordIterator iter = tripleStore.getTriples(txn, 1, 2, -1, -1, true)) { + assertThat(iter).isInstanceOf(LmdbDupRecordIterator.class); + + int count = 0; + while (iter.next() != null) { + count++; + } + assertEquals(2, count); + } + } + + @Test + void subjectPredicateDupsortPersistsAcrossRestart() throws Exception { + tripleStore.close(); + + LmdbStoreConfig config = new LmdbStoreConfig("posc"); + config.setDupsortIndices(true); + config.setDupsortRead(true); + tripleStore = new TripleStore(dataDir, config, null); + + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 5, 0, true); + tripleStore.commit(); + + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + RecordIterator iter = tripleStore.getTriples(txn, 1, 2, -1, -1, true)) { + assertThat(iter).isInstanceOf(LmdbDupRecordIterator.class); + + int count = 0; + while (iter.next() != null) { + count++; + } + assertEquals(3, count); + } + } + + @Test + void subjectPredicateDupsortCacheFlushMaintainsIndex() throws Exception { + tripleStore.startTransaction(); + + TxnRecordCache cache = new TxnRecordCache(dataDir); + try { + cache.storeRecord(new long[] { 1, 2, 5, 0 }, true); + Field recordCacheField = TripleStore.class.getDeclaredField("recordCache"); + recordCacheField.setAccessible(true); + recordCacheField.set(tripleStore, cache); + + tripleStore.updateFromCache(); + recordCacheField.set(tripleStore, null); + } finally { + // updateFromCache closes the cache, nothing to do here + } + tripleStore.commit(); + + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + RecordIterator iter = tripleStore.getTriples(txn, 1, 2, -1, -1, true)) { + assertThat(iter).isInstanceOf(LmdbDupRecordIterator.class); + + int count = 0; + while (iter.next() != null) { + count++; + } + assertEquals(3, count); + } + } + + @Test + void predicateObjectPatternFallsBackToStandardIterator() throws Exception { + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + RecordIterator iter = tripleStore.getTriples(txn, -1, 2, 3, -1, true)) { + assertThat(iter).isNotInstanceOf(LmdbDupRecordIterator.class); + + int count = 0; + while (iter.next() != null) { + count++; + } + assertEquals(1, count); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreCardinalityRefactorTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreCardinalityRefactorTest.java new file mode 100644 index 00000000000..1aa5af74f94 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/TripleStoreCardinalityRefactorTest.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class TripleStoreCardinalityRefactorTest { + + private static final long PRED_A = 100; + private static final long PRED_B = 200; + private static final long CONTEXT_ONE = 10; + private static final long CONTEXT_TWO = 20; + + @TempDir + File tempFolder; + + private TripleStore tripleStore; + private int explicitStatements; + + @BeforeEach + void setUp() throws Exception { + File dataDir = new File(tempFolder, "triplestore"); + dataDir.mkdir(); + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); + populateData(); + } + + @AfterEach + void tearDown() throws Exception { + tripleStore.close(); + } + + @Test + void predicateAndContextMatchIteratorCounts() throws Exception { + assertEstimatorBounds(LmdbValue.UNKNOWN_ID, PRED_A, LmdbValue.UNKNOWN_ID, CONTEXT_ONE); + } + + @Test + void subjectAndPredicateMatchIteratorCounts() throws Exception { + assertEstimatorBounds(1, PRED_B, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID); + } + + @Test + void fullScanMatchesTotalTripleCount() throws Exception { + double estimate = tripleStore.cardinality(LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, + LmdbValue.UNKNOWN_ID); + assertEquals(explicitStatements, estimate, 0.0); + } + + @Test + void moreSpecificPatternDoesNotExceedBroaderEstimate() throws Exception { + double general = tripleStore.cardinality(LmdbValue.UNKNOWN_ID, PRED_A, LmdbValue.UNKNOWN_ID, CONTEXT_ONE); + double specific = tripleStore.cardinality(1, PRED_A, LmdbValue.UNKNOWN_ID, CONTEXT_ONE); + assertTrue(specific <= general); + } + + private void populateData() throws IOException { + tripleStore.startTransaction(); + explicitStatements = 0; + for (long subj = 1; subj <= 3; subj++) { + for (int i = 0; i < 120; i++) { + long pred = (i % 2 == 0) ? PRED_A : PRED_B; + long obj = subj * 1000 + i; + long context = (i % 3 == 0) ? CONTEXT_ONE : CONTEXT_TWO; + storeTriple(subj, pred, obj, context); + } + } + tripleStore.commit(); + } + + private void storeTriple(long subj, long pred, long obj, long context) throws IOException { + tripleStore.storeTriple(subj, pred, obj, context, true); + explicitStatements++; + } + + private void assertEstimatorBounds(long subj, long pred, long obj, long context) throws Exception { + double estimate = tripleStore.cardinality(subj, pred, obj, context); + int expected = count(subj, pred, obj, context, true) + count(subj, pred, obj, context, false); + assertTrue(estimate >= expected, + () -> "Estimator should not undercount actual matches for pattern: " + patternLabel(subj, pred, obj, + context) + " expected=" + expected + " actual=" + estimate); + assertTrue(estimate <= explicitStatements, + () -> "Estimator should not exceed total explicit statements in dataset for pattern: " + + patternLabel(subj, pred, obj, context)); + } + + private int count(long subj, long pred, long obj, long context, boolean explicit) throws Exception { + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + RecordIterator iterator = tripleStore.getTriples(txn, subj, pred, obj, context, explicit)) { + return count(iterator); + } + } + + private int count(RecordIterator iterator) throws IOException { + int count = 0; + while (iterator.next() != null) { + count++; + } + return count; + } + + private String patternLabel(long subj, long pred, long obj, long context) { + return "subj=" + subj + ", pred=" + pred + ", obj=" + obj + ", context=" + context; + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java index fef3bd67313..221b58843f4 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java @@ -129,10 +129,10 @@ public void testVarintList() { long[] expected = new long[4]; System.arraycopy(values, 0, expected, 0, 4); bb.clear(); - Varint.writeListUnsigned(bb, expected); + Varint.writeQuadUnsigned(bb, expected); bb.flip(); long[] actual = new long[4]; - Varint.readListUnsigned(bb, actual); + Varint.readQuadUnsigned(bb, actual); assertArrayEquals("Encoded and decoded value should be equal", expected, actual); } } diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java index 3a9c88ad840..9f961db2f7e 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/QueryBenchmark.java @@ -40,16 +40,13 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; /** * @author Håvard Ottestad */ @State(Scope.Benchmark) -@Warmup(iterations = 5) +@Warmup(iterations = 50, time = 1, timeUnit = TimeUnit.SECONDS) @BenchmarkMode({ Mode.AverageTime }) @Fork(value = 1, jvmArgs = { "-Xms1G", "-Xmx1G" }) //@Fork(value = 1, jvmArgs = {"-Xms1G", "-Xmx1G", "-XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=profile.jfr,method-profiling=max","-XX:FlightRecorderOptions=stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"}) @@ -124,13 +121,21 @@ public class QueryBenchmark { private File file; - public static void main(String[] args) throws RunnerException { - Options opt = new OptionsBuilder() - .include("QueryBenchmark.ordered_union_limit") // adapt to run other benchmark tests - .forks(0) - .build(); - - new Runner(opt).run(); + public static void main(String[] args) throws RunnerException, IOException { + QueryBenchmark queryBenchmark = new QueryBenchmark(); + queryBenchmark.beforeClass(); + queryBenchmark.complexQuery(); + queryBenchmark.afterClass(); + +// +// Options opt = new OptionsBuilder() +// .include("QueryBenchmark.complexQuery") // adapt to run other benchmark tests +// .warmupIterations(0) +// .measurementIterations(10) +// .forks(0) +// .build(); +// +// new Runner(opt).run(); } @Setup(Level.Trial) diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/README.md b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/README.md new file mode 100644 index 00000000000..07926efcc70 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/README.md @@ -0,0 +1,72 @@ +## pool cursors +``` +Benchmark Mode Cnt Score Error Units +QueryBenchmark.complexQuery avgt 5 3.724 ± 0.083 ms/op +QueryBenchmark.different_datasets_with_similar_distributions avgt 5 2.118 ± 0.089 ms/op +QueryBenchmark.groupByQuery avgt 5 0.916 ± 0.019 ms/op +QueryBenchmark.long_chain avgt 5 641.809 ± 6.881 ms/op +QueryBenchmark.minus avgt 5 10.472 ± 0.556 ms/op +QueryBenchmark.multiple_sub_select avgt 5 55.949 ± 3.581 ms/op +QueryBenchmark.nested_optionals avgt 5 171.423 ± 39.898 ms/op +QueryBenchmark.optional_lhs_filter avgt 5 36.728 ± 2.244 ms/op +QueryBenchmark.optional_rhs_filter avgt 5 51.788 ± 2.241 ms/op +QueryBenchmark.ordered_union_limit avgt 5 73.121 ± 4.403 ms/op +QueryBenchmark.pathExpressionQuery1 avgt 5 20.738 ± 0.385 ms/op +QueryBenchmark.pathExpressionQuery2 avgt 5 4.546 ± 0.338 ms/op +QueryBenchmark.query_distinct_predicates avgt 5 47.712 ± 1.803 ms/op +QueryBenchmark.simple_filter_not avgt 5 5.641 ± 0.151 ms/op +QueryBenchmark.sub_select avgt 5 72.550 ± 10.050 ms/op +QueryBenchmarkFoaf.groupByCount avgt 5 721.870 ± 16.816 ms/op +QueryBenchmarkFoaf.groupByCountSorted avgt 5 651.351 ± 19.324 ms/op +QueryBenchmarkFoaf.personsAndFriends avgt 5 213.945 ± 7.394 ms/op +``` + +### dup key +``` +Benchmark Mode Cnt Score Error Units +QueryBenchmark.complexQuery avgt 5 3.451 ± 0.026 ms/op +QueryBenchmark.different_datasets_with_similar_distributions avgt 5 2.523 ± 0.018 ms/op +QueryBenchmark.groupByQuery avgt 5 0.875 ± 0.011 ms/op +QueryBenchmark.long_chain avgt 5 674.397 ± 6.014 ms/op +QueryBenchmark.lots_of_optional avgt 5 295.217 ± 2.850 ms/op +QueryBenchmark.minus avgt 5 10.704 ± 0.640 ms/op +QueryBenchmark.multiple_sub_select avgt 5 57.411 ± 1.345 ms/op +QueryBenchmark.nested_optionals avgt 5 218.365 ± 0.449 ms/op +QueryBenchmark.optional_lhs_filter avgt 5 34.577 ± 0.341 ms/op +QueryBenchmark.optional_rhs_filter avgt 5 65.118 ± 0.355 ms/op +QueryBenchmark.ordered_union_limit avgt 5 68.742 ± 1.475 ms/op +QueryBenchmark.pathExpressionQuery1 avgt 5 20.378 ± 0.232 ms/op +QueryBenchmark.pathExpressionQuery2 avgt 5 5.127 ± 0.031 ms/op +QueryBenchmark.query_distinct_predicates avgt 5 44.293 ± 0.293 ms/op +QueryBenchmark.simple_filter_not avgt 5 5.509 ± 0.061 ms/op +QueryBenchmark.sub_select avgt 5 76.451 ± 0.970 ms/op +QueryBenchmarkFoaf.groupByCount avgt 5 742.100 ± 11.235 ms/op +QueryBenchmarkFoaf.groupByCountSorted avgt 5 602.247 ± 31.896 ms/op +QueryBenchmarkFoaf.personsAndFriends avgt 5 218.583 ± 1.921 ms/op + +``` + + +### Instance of LmdbIRI +```text +Benchmark Mode Cnt Score Error Units +QueryBenchmark.complexQuery avgt 5 3.431 ± 0.027 ms/op +QueryBenchmark.different_datasets_with_similar_distributions avgt 5 2.504 ± 0.008 ms/op +QueryBenchmark.groupByQuery avgt 5 0.865 ± 0.006 ms/op +QueryBenchmark.long_chain avgt 5 670.361 ± 5.933 ms/op +QueryBenchmark.lots_of_optional avgt 5 294.593 ± 3.254 ms/op +QueryBenchmark.minus avgt 5 10.788 ± 0.241 ms/op +QueryBenchmark.multiple_sub_select avgt 5 56.797 ± 0.608 ms/op +QueryBenchmark.nested_optionals avgt 5 217.519 ± 2.129 ms/op +QueryBenchmark.optional_lhs_filter avgt 5 34.818 ± 0.603 ms/op +QueryBenchmark.optional_rhs_filter avgt 5 64.181 ± 0.366 ms/op +QueryBenchmark.ordered_union_limit avgt 5 68.970 ± 0.575 ms/op +QueryBenchmark.pathExpressionQuery1 avgt 5 20.395 ± 0.229 ms/op +QueryBenchmark.pathExpressionQuery2 avgt 5 4.756 ± 0.041 ms/op +QueryBenchmark.query_distinct_predicates avgt 5 49.811 ± 1.072 ms/op +QueryBenchmark.simple_filter_not avgt 5 5.559 ± 0.049 ms/op +QueryBenchmark.sub_select avgt 5 74.768 ± 1.344 ms/op +QueryBenchmarkFoaf.groupByCount avgt 5 707.383 ± 13.236 ms/op +QueryBenchmarkFoaf.groupByCountSorted avgt 5 633.781 ± 159.621 ms/op +QueryBenchmarkFoaf.personsAndFriends avgt 5 214.109 ± 3.490 ms/op +``` diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java index 94b2c42e09e..53632e8a031 100644 --- a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondForceSyncBenchmark.java @@ -11,28 +11,15 @@ package org.eclipse.rdf4j.sail.lmdb.benchmark; -import java.io.File; -import java.io.IOException; import java.util.concurrent.TimeUnit; -import org.apache.commons.io.FileUtils; -import org.assertj.core.util.Files; -import org.eclipse.rdf4j.common.transaction.IsolationLevels; -import org.eclipse.rdf4j.model.vocabulary.RDFS; -import org.eclipse.rdf4j.repository.sail.SailRepository; -import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; -import org.eclipse.rdf4j.sail.lmdb.LmdbStore; -import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; 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 62dffba9ffe..f6c94d30b04 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 @@ -41,6 +41,30 @@ void testThatLmdbStoreConfigParseAndExportValueEvictionInterval(final long value ); } + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testThatLmdbStoreConfigParseAndExportDupsortIndices(final boolean dupsortIndices) { + testParseAndExport( + LmdbStoreSchema.DUPSORT_INDICES, + Values.literal(dupsortIndices), + LmdbStoreConfig::isDupsortIndices, + dupsortIndices, + !dupsortIndices + ); + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testThatLmdbStoreConfigParseAndExportDupsortRead(final boolean dupsortRead) { + testParseAndExport( + LmdbStoreSchema.DUPSORT_READ, + Values.literal(dupsortRead), + LmdbStoreConfig::isDupsortRead, + dupsortRead, + !dupsortRead + ); + } + @ParameterizedTest @ValueSource(booleans = { true, false }) void testThatLmdbStoreConfigParseAndExportAutoGrow(final boolean autoGrow) { @@ -113,4 +137,4 @@ private void testParseAndExport( assertThat(exportedModel.contains(exportImplNode, property, value)) .isEqualTo(expectedContains); } -} \ No newline at end of file +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinLazyMaterializationTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinLazyMaterializationTest.java new file mode 100644 index 00000000000..faa4a000770 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinLazyMaterializationTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.MutableBindingSet; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.evaluation.ArrayBindingSet; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.ValueStore; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +@Isolated +class LmdbIdJoinLazyMaterializationTest { + + private static final String PROPERTY = "rdf4j.lmdb.idJoin.lazyMaterialization"; + + @Test + void eagerMaterializationWhenDisabled() throws Exception { + System.setProperty(PROPERTY, "false"); + try { + ValueStore valueStore = mock(ValueStore.class); + LmdbValue materialized = mock(LmdbValue.class); + when(valueStore.getLazyValue(1L)).thenReturn(materialized); + when(valueStore.getLazyValue(2L)).thenReturn(materialized); + + StatementPattern pattern = new StatementPattern( + new Var("person"), + new Var("predicate", SimpleValueFactory.getInstance().createIRI("http://example.com/p")), + new Var("item")); + LmdbIdJoinIterator.PatternInfo patternInfo = LmdbIdJoinIterator.PatternInfo.create(pattern); + IdBindingInfo info = IdBindingInfo.fromFirstPattern(patternInfo); + + long[] record = new long[] { 1L, 2L }; + MutableBindingSet target = new ArrayBindingSet(new String[] { "person", "item" }); + boolean result = info.applyRecord(record, target, valueStore); + + assertThat(result).isTrue(); + verify(valueStore).getLazyValue(1L); + verify(valueStore).getLazyValue(2L); + verify(materialized, times(2)).init(); + verify(valueStore, never()).getValue(anyLong()); + assertThat(target.getValue("person")).isSameAs(materialized); + assertThat(target.getValue("item")).isSameAs(materialized); + } finally { + System.clearProperty(PROPERTY); + } + } +} diff --git a/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinIteratorTest.java b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinIteratorTest.java new file mode 100644 index 00000000000..affd3f18de1 --- /dev/null +++ b/core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdMergeJoinIteratorTest.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb.join; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Arrays; + +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.algebra.StatementPattern; +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.sail.lmdb.IdBindingInfo; +import org.eclipse.rdf4j.sail.lmdb.RecordIterator; +import org.eclipse.rdf4j.sail.lmdb.model.LmdbValue; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; + +@Isolated +class LmdbIdMergeJoinIteratorTest { + + @Test + void mergeJoinProducesIdRecordsWithoutMaterialization() throws Exception { + StatementPattern leftPattern = new StatementPattern( + new Var("x"), + new Var("pl", SimpleValueFactory.getInstance().createIRI("urn:p:left")), + new Var("ol", SimpleValueFactory.getInstance().createIRI("urn:o:left"))); + StatementPattern rightPattern = new StatementPattern( + new Var("x"), + new Var("pr", SimpleValueFactory.getInstance().createIRI("urn:p:right")), + new Var("or", SimpleValueFactory.getInstance().createIRI("urn:o:right"))); + + LmdbIdJoinIterator.PatternInfo leftInfo = LmdbIdJoinIterator.PatternInfo.create(leftPattern); + LmdbIdJoinIterator.PatternInfo rightInfo = LmdbIdJoinIterator.PatternInfo.create(rightPattern); + IdBindingInfo bindingInfo = IdBindingInfo.combine(IdBindingInfo.fromFirstPattern(leftInfo), rightInfo, null); + + long[] leftRecord = new long[] { 7L, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, 0L }; + long[] rightRecord = new long[] { 7L, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, 0L }; + + RecordIterator leftIterator = new ArrayRecordIterator(leftRecord); + RecordIterator rightIterator = new ArrayRecordIterator(rightRecord); + + LmdbIdMergeJoinIterator iterator = new LmdbIdMergeJoinIterator(leftIterator, rightIterator, leftInfo, rightInfo, + "x", bindingInfo); + try { + Object record = iterator.next(); + assertThat(record).isInstanceOf(long[].class); + long[] ids = (long[]) record; + assertThat(ids.length).isEqualTo(bindingInfo.size()); + int idx = bindingInfo.getIndex("x"); + assertThat(ids[idx]).isEqualTo(7L); + } finally { + iterator.close(); + } + } + + @Test + void nextReturnsNullWhenNoMatches() throws Exception { + StatementPattern leftPattern = new StatementPattern( + new Var("x"), + new Var("pl", SimpleValueFactory.getInstance().createIRI("urn:p:left")), + new Var("ol", SimpleValueFactory.getInstance().createIRI("urn:o:left"))); + StatementPattern rightPattern = new StatementPattern( + new Var("x"), + new Var("pr", SimpleValueFactory.getInstance().createIRI("urn:p:right")), + new Var("or", SimpleValueFactory.getInstance().createIRI("urn:o:right"))); + + LmdbIdJoinIterator.PatternInfo leftInfo = LmdbIdJoinIterator.PatternInfo.create(leftPattern); + LmdbIdJoinIterator.PatternInfo rightInfo = LmdbIdJoinIterator.PatternInfo.create(rightPattern); + IdBindingInfo bindingInfo = IdBindingInfo.combine(IdBindingInfo.fromFirstPattern(leftInfo), rightInfo, null); + + long[] leftRecord = new long[] { 7L, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, 0L }; + long[] rightRecord = new long[] { 8L, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, 0L }; + + RecordIterator leftIterator = new ArrayRecordIterator(leftRecord); + RecordIterator rightIterator = new ArrayRecordIterator(rightRecord); + + LmdbIdMergeJoinIterator iterator = new LmdbIdMergeJoinIterator(leftIterator, rightIterator, leftInfo, rightInfo, + "x", bindingInfo); + try { + assertThat(iterator.next()).isNull(); + } finally { + iterator.close(); + } + } + + @Test + void orderVerifierDetectsDisorder() { + RecordIterator unordered = new ArrayRecordIterator( + new long[] { 1L, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, 0L }, + new long[] { 0L, LmdbValue.UNKNOWN_ID, LmdbValue.UNKNOWN_ID, 0L }); + + RecordIterator verifying = LmdbIdOrderVerifier.wrap("left", 0, unordered); + + assertThatThrownBy(() -> { + while (verifying.next() != null) { + // consume iterator + } + }) + .isInstanceOf(AssertionError.class) + .hasMessageContaining("left"); + } + + private static final class ArrayRecordIterator implements RecordIterator { + private final long[][] data; + private int index; + + private ArrayRecordIterator(long[]... records) { + this.data = Arrays.stream(records) + .map(arr -> Arrays.copyOf(arr, arr.length)) + .toArray(long[][]::new); + } + + @Override + public long[] next() { + if (index >= data.length) { + return null; + } + long[] source = data[index++]; + return Arrays.copyOf(source, source.length); + } + + @Override + public void close() { + // nothing to do + } + } +} diff --git a/core/sail/lmdb/src/test/resources/temp.nquad b/core/sail/lmdb/src/test/resources/temp.nquad new file mode 100644 index 00000000000..8bf1c2505fe --- /dev/null +++ b/core/sail/lmdb/src/test/resources/temp.nquad @@ -0,0 +1,28636 @@ + . + "application/zip" . + . + "Développement Durable" . + . + "Development durable" . + . + "Duurzame ontkwikkeling" . + . + "Economie, Business, SME, Economische groei, tewerkstelling" . + . + "Economy, Business, SME, Economic development, Employment " . + . + "Education, Training, Research" . + . + "Hébergement, Restauration" . + . + "Justice, Sécurité, Police, Crime" . + . + "Justice, Safety, Police, Crime" . + . + "Justitie, Veiligheid, Politie, Criminaliteit" . + . + "Mobilité, Transport" . + . + "Sport, Leisure" . + . + "Toerism" . + . + "Toerisme" . + . + "Tourisme" . + . + "Environment, Cleanliness " . + . + "Milieu, Netheid " . + . + "Tourism" . + . + "Vervoer, Verplaatsingen" . + . + "Développement durable" . + . + "Economie, Business, KMO, Economische ontwikkeling, Tewerkstelling" . + . + "Sports, Leisure" . + . + "Duurzame ontwikkeling" . + . + "Economy, Business, SME, Economic development, Employment" . + . + "Sport, Loisirs" . + . + "Diensten, Sociaal" . + . + "Economie, Business, PME, Développement économique, Emploi" . + . + "Gezondheid" . + . + "Health" . + . + "Santé" . + . + "Spatial planning, Town planning, Buildings, Equipment, Housing" . + . + "Sport, Vrije tijd" . + . + "Onderwijs, Opleiding, Onderzoek" . + . + "Education, Formation, Recherche, Enseignement" . + . + "Milieu, Netheid" . + . + "Environment, Cleanliness" . + . + "Services, Social" . + . + "Mobiliteit, Vervoer" . + . + "Culture, Heritage" . + . + "Ruimtelijke ordening, Stedenbouw, Gebouwen, Uitrusting, Huisvesting" . + . + "Cultuur, Erfgoed" . + . + "Environnement, Propreté" . + . + "Mobility, Transport" . + . + "Aménagement du territoire, Urbanisme, Bâtiments, Equipements, Logement" . + . + "Mobilité, Transports" . + . + "Culture, Patrimoine, Folklore" . + . + "Internet, TIC" . + . + "Themes" . + . + "Internet, ICT" . + . + "Administration, Gouvernement, Finances publiques, Citoyenneté" . + . + "Administratie, Overheid, Openbare financiën, Burgerschap" . + . + "Administration, Government, Public finances, Citizenship" . + . + "Stad Brussel"@nl . + . + "SHP" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "5107cc21da806deb87e7a1634beeef762a1e26e0" . + . + "0fcad777d5be99046fbac97c7bc4fdc3db750fcc" . + . + "fcc241e9ed9828d7413d54db2a7f1225a56e8781" . + . + "11f1c197033749b3a2340eae2eb914c85b121592" . + . + "4b8538199e8dd383005571c00828d251712766c6" . + . + . + "47dbea015352b5e176b9b60e47e02d29461cdd0b" . + . + "c41892aa79702b840540edfaa0eec3088c6343b8" . + . + "a3359296c5404e20bcef17a7759e78f1102b9854" . + . + "5a3a5c99de0505bc20d288724c5ce2065b5f2168" . + . + "b3d102531bb2721ed66895df6d1731bd4b48211e" . + . + "8e3f529944b25923e0c180a8afcb5f0a439755d6" . + . + "f14215623595ad5bd62961bdd6530ecc835c0d1d" . + . + "2757b731d79af1071d7cac6f1a5016ceadbdbd0e" . + . + "ad0d4a982e14731478d88bc1ae77ed7ef98cf182" . + . + . + "562678540037fc0f258afdc0bff365526c2e3706" . + . + "fa92c6bcf5cd3e1551f9d42662b0e075bf10a44f" . + . + "47dbab99715f3b0069359dc77e12ab5eb851612f" . + . + "3469063eca7d8a521a8de5361eccd8483efd046a" . + . + "6bc1579e81e0aa614644b082f8fdd43d3a2a5424" . + . + "c9878d1c45c1035425175305a1686dd788c78c23" . + . + "84add22436b2016752c85cd17a4669a432af3a72" . + . + "33e833c655f7c0326340a5b501e2f9b422573505" . + . + "db126c4b126e067a6721e5afd37a17694a3edc0d" . + . + . + "590203faaef7788a0644ec137980e60de38ca96b" . + . + "8b7592dac816195979cd45d9769278a5a957069a" . + . + "b425a91983145c8142db88bae4f1c1e2d748601c" . + . + "4c240b2723a55b98fb119ae3df4db42d6da7a5b6" . + . + "246a864a1531247c45424f994b5982b2424c62fe" . + . + "07c2aa7ec52df6cb15d995f93e1a2f7080152b3b" . + . + "68a3442d0225f45fcbce82e9eac3ef829f1acb49" . + . + . + "ec5d272181dc23c93648fc1a6ba672ffc4dd37c7" . + . + "62295d03c0f6e44e103fd0d6b6fe9a73bdd7b82c" . + . + "60f4046f87286fd89a860df9e0f4d04cc67bd8a6" . + . + "57073149c983f7c7bccfed6e030541566bf4ca83" . + . + "105a8da5c7d5db560abb2d58c734f4796c661a6f" . + . + "e08eadc380b77b01c3c120325a4ab6926dcf3228" . + . + "1dd47506cc0e8eff267007422022ae660ca16a7b" . + . + "c1f74fb867eeb9e1c199f3952e5a15953f0a1127" . + . + . + "b467975db52df8f29e8756845260eb0a53cb50a2" . + . + "7ecb5a5637747cf274ac4250341e2c7f2c4b1608" . + . + "459ebf80110601eb958d8107f21dfe46e7b2d0f7" . + . + "a1f0297b512a2181896c57dcae2a0a780e0e8962" . + . + "b5d1e0697ea0e6c054310ceafa5ebac57975fc4a" . + . + "d64bd3395cd83b7e96b7ab75e30e1a5039d1614b" . + . + "49aa4cbd76398d3a64661ef109b1873539def12a" . + . + "e4e9fb483e8ff10e555ebec15749abd2cbce72a1" . + . + "5c006b6bdd47284f450a7035bb914c152df8112c" . + . + . + "ef842ad37ac75e44e4690936a4398e2b29b5c213" . + . + "d488bdaeeac3bf552402989bc9d690b992abf9a1" . + . + "58852b6e8eb757549c8b379e498dd203f4ebf78d" . + . + "93b6aa9aec0172cb22baf4ebaf3f32407c466e51" . + . + "19f96a0574b8016f3f24c21122b65306c74e0615" . + . + "c81a271869507bd9e16c262e07032c3cce511688" . + . + "ac636023d8f90365a130d426d82fa19cb61c26a2" . + . + "2c96b2b53cb477532ea6797ae1571e5fec4868ef" . + . + "50bafe3b4edae230ecb9c0619a4e0e5bd9995e41" . + . + "a08d7c6edd11e9507a3a94349e12ebc7ab0acba6" . + . + "c9415c3f1080e2127c5e2f1c6a6837fbe23cd7d7" . + . + "c6ea9791d8d510c22a3ab3823bf9e220e8a7499c" . + . + "b04bc88419a357b16c8d3a7db4849b2cca671459" . + . + "23e6c79e50f01bba65b15b3c4919c2c4757aba0a" . + . + "56250a7e4a1943dacbcaf0a586138675839c647e" . + . + "028555b32f3b7901a4cb58bb8c960d5d34b03d9d" . + . + "9a0d6438769d3b1bff21ff65dfc24a8ed487eb2e" . + . + "1aa069a89344dbe8ef899784af92dffa66e52e6b" . + . + "fa97a2f4a87b49b37f7b8189848eb97af6adc8b1" . + . + "582d264a6adf2535eab01462239e693570e4ad6b" . + . + . + "ff684f7492aa41cdab321b87ae722881818cc27c" . + . + "7d032787e3b74304eededb2b35cebf1f88ca8e63" . + . + "57a9ccf1964d4d5c3f7fbd0682c5a6b6ada73cca" . + . + "7b495cc0fc9e9901662d29d4d007cb6a677ac40e" . + . + "5f56f929bb78368eb475422a99bc8818f91d9028" . + . + "a379880556761392e25dba924eabeaa51f127f27" . + . + "927ca1f7bca1c8fd6fd82454209b395926456e7f" . + . + . + "ef21c4d1a2edaa787d38652e78564e808f5f414e" . + . + "84c2f0f9378dd19a4103c9ba16b2c02030731dee" . + . + "790a36d29b872814ef7d4a771053a4a36d02adc2" . + . + "a2f3682f74e2da9a94a8bc3b9cd41dbe79368b7f" . + . + "fe0ab087922d9bdb1124eda1614d56418232e29f" . + . + "674f7a32ab0e01ceb6bd423c5aef2e73dbe9bf1b" . + . + "50babd0263147d65e1565ad289734028883a8d16" . + . + "4e1b860972463a2b865da80d9590d5b1767a0528" . + . + . + "00d36cc16336f5df22b11655b2335147fc33e3e2" . + . + "525d30ddfc8abaa2cd9c0fa0d6cdd31223299e3f" . + . + "3120b4ecfa3bc0e59b2e0d451daa59a9bb7238f4" . + . + "d7f3340c82fd876031caec032b3bb6518807fad3" . + . + "c06612bcd3f568f2f8da63c1067fbada7cd23ebd" . + . + . + . + "97b4470cd72c4741f9559e7ce67a6356d8519c4a" . + . + . + "66d5b7b2f809201513e5727e64cffe758032f015" . + "4e28d482db0f90d4188aaa80af0a14e985f30eb6" . + . + . + . + . + . + "8aed86a7b78e8e8121a7d419fff35acf8c486135" . + "cdf47b64ba898467eefa343e7bbbcbeb4509e02b" . + "f0a32fc1115a85fc3a15b56c552b1c6713555cc7" . + "0ec69c15538cee6731881f349a9df598b034c5db" . + . + "cee4fa5c5b8e9764a7e554329c9e552859d1dec9" . + "420567f99ce15a3ee52b98e095588982ba14e741" . + "322bb2a486b32adf9072f774b068f03ee01c7422" . + "dba34d59b0ac5693b58aef3f20f34775b4017e4d" . + "b08ba239ee9185b6073ae32a13ba752496f55c9f" . + "0a3666fba0c9e0c2a59ba5e43c2b4579eefcbec8" . + "13a29c688eebea55fdd41d14eef6c6ac6fcf3e12" . + "e65c2afc7963073eb3b8fa5b0e78413fdb69283e" . + "14610116aba15b1e99c5aac06c23e9811e344d6a" . + "b48d2b920a6fe081b9b5c11b7b972243cbb036f6" . + "76f851e0782b1824f3732f7284c24d179181b072" . + . + "7756f3722a9b867309d3a8fa1d1f751db5c98f3d" . + "08f9f2a16cc8f99b7c14fc8244fbf1547d00be22" . + "80f23aeb288bb1a32b1461e6af02b2324f865cdd" . + "07c94f8c7c9c7b73ff7aa8ac46aacf8abc03b285" . + "8a459909848ef8ff879cf992c90029e4c2c6ee67" . + "499bf96929870a48e06d85cebee19252f309eff2" . + . + "657b6972aeb90375119c4aae58beeacd3c4de1bb" . + "1c5deb753852d87715889e3a6eae947515403c47" . + "b18e2a1e7833bd7695a729547aa6a3e9a7179bc6" . + "c573db354ae03a83867d1281ac9505abcc991da0" . + "104d38ae087356e67ea75713772f7035b4096787" . + "116dfe892190f8caacbeb6374feb6e3f82e63dad" . + . + . + "4330576b94282b686dbd12968bb871028d998ded" . + "6adaa5e4f2890323ad32f5752d5482c9a873a75a" . + "13f0b6c1a9062fcc625a1d96a6a29119e6b863e7" . + "b66e2e8790c8b4017efb8da8cc0d9dd2ce1f2030" . + "f7cb794053c41edbad192738459c9cc392b6fa73" . + "0cfbf0d9798fcf2d26214193c8846464b0d3fbd7" . + "69be928bcb14da0c75700c4666b6199d44ea54ca" . + "5f121485c6096617a2c19f5269d296967747c617" . + "0f19689e0b8edddfeafb11478f8a698bd7cffc2c" . + "1b57fc0dfcf98d83d8d9aced8f7ee776635c6931" . + "733f0740477a7329d034a648ebaf2627ba69c652" . + "352e73a340970b0fdf9c8d00081dd8a880654918" . + "8ec94654eb51b1d115284f9bab2d2450d7f6a4bb" . + "39308e935b8c2daf32ff4eaad8435b2d8ec8685a" . + "40a6a9f70bbd222b7a6d44a7585e0945bcd3a54c" . + . + . + "aa8784fdf20ffd7bd021532deb9d14d352b9d688" . + "246e4b8ece8da17f45ce2e265a5564aadbb6475b" . + "f96d982b89d21761f02258aaaf047798646ff07f" . + "691fb879826bc97b0f94803a844c4726ed951922" . + "53f4f6923240996669d0a9d914550bbb8cb44385" . + "30ea99f34b8986d6c08a99198be90b96ba8504ea" . + "709d276a3514abb692135279059268ccfb189a79" . + "c87df5123933a3e2a48f2abf7a5207b7db6e46d3" . + "b50b4e4d29d9c208992e9e17e3bcf3cf0584e1c0" . + "361ab7137795fe0284e5766e2b91b3df473eb07b" . + "8a144f6d4d61746b8da1f7589c7ee69757904359" . + "094b8e3d6a0b4384266830ca1a89c8c891983fa8" . + "e5e79d15c67c4696626fb221fa143ce5e9bc91e9" . + "ae32f132eb983398f9934a13d29ae250e89a33c1" . + "f3576ac35a0091ee444258dc41b1382755428113" . + . + "f65fb26a351aab003edbf6f72159ef1532328bf0" . + "a823c87c48097228ad392ebd5912e51da35fcaa9" . + "bac9447d8c7c7e333a0e08e5515786593ae4864a" . + "b9cb566a906670075cbb326f4799eebd3f3ade7b" . + "23308e20d104492b19b3308209a91d3273e19c7f" . + "0dc21a7d3e51d2df1036483b577925392b878031" . + "acd9b0a7621c6ada0ea8d0b514491d380a2eaaa9" . + "77d71092d0da527307a2731b034ed0794d84b16a" . + "d423c509ce85507c3ad4023a83940373f73a337e" . + "9bd5ae0c8c031f615ea784dc9617deab941e866e" . + . + "d5a9f747cdcd108352aa780364e2fbe83794335c" . + "cc3a164236d0ff5f763b8b88d5147b6b55172fe1" . + "bcfedb366e1f93346603b6e5ec909210d536d322" . + "e41d0abf3779ac80df1fc61f34f5d2ef8abd8ef8" . + "468f7dd21c405c48825100036cf2d0f574cc04d4" . + "2e1cb8ead963a588033b1a41adff633b785bd5c2" . + "e206d8a27aa0a33a171103d6929913fd8ecc8de3" . + "65a84dd1296925c87cfe7a7643968100f76a3401" . + . + "3321419e8dbf526edc7e81d741852c31a62098be" . + "ccd20b9703db67143b2259f5ba9e2b0e9d70897a" . + "8e4b7d8e848e54b82f8cbd8ed723ced6ebaeaa7f" . + "aacf4842385bd45ce85d66d6e0aef83279c4642e" . + "7fa7c870c1b6820b0f699a3e5de73c9518f87223" . + "0a9a180729b70d2cbed8a64f79279d91a6d247de" . + "ca57bfab3eb2541d262f3fd1a87bbc5c3d1dd87f" . + . + "362f815db3603cb7cb9cd87d9f32a1a61c846a4f" . + "ef62bf947eeabb03a54e408e87f6da6171961d62" . + "81941fd777cd784249fd67be55515a8df078489c" . + "a1d6d1b775ceef010c14dc279e850325f46d6f34" . + "bfb84655111b6085236c6b3f0795fe2fcbb59291" . + "10eb4d576fc867d2abd7532f9998500dcf8c2610" . + "620fed53ca08b29e01c50bbd80047fe33f160dc1" . + "b68d432a5fe0919cb5c6fc47783eeb74bf51c437" . + . + . + "6cca77dac09a8bac9fbca2fe98102a99ff43d272" . + "74fff7d3fedbf4654082d620913ae4eca6bfc09b" . + "bf598398d2b87c18d05ae8ca5c07fe0bb592ed47" . + "bca9f5bc92012d7f5fec7b19bb725823a46b935b" . + "ea8e26690db85ae2086077498a2982d4e80ec02c" . + "78cdeea36b6487066381e5201bfc65a9f0cb2543" . + "6d8732d60607eecfcca240eb006725f4560637bb" . + "7b597813f9b1f562f39ebc4069760aedd480afb8" . + "e7a0684df7eeed7c81400df5b6bca14fa818eb95" . + "123d9908c0aa35132d4c7ffa232f4175c9037910" . + "f8175b5ab4d784f09202df3452667940e509c67d" . + "a1eabfdc5dfd2c3d44d84725947ae5f672a70397" . + "aed310348e0c9f00214e4d92cc3433778216b299" . + "71f224b9152d42b8b7c9158030d6ce2affff30f4" . + "9994a82319e5c75080da92988d0673a04d4d94c4" . + "21bcc21a8c2d00e7c4e408cfdc7049833016ee68" . + "fe10a9c4f036922db711c89c11eef63b239729c4" . + "9b5622289aac57a4b526649ba90017858a7f485d" . + "a6dff66857ddc03c78cf996fe0854435f5b84741" . + "a44ea0ba689a95334feeaa0a997a3168f46cb9d0" . + "b325f1dc85b86a4b9b45fba80565d9bc1fc41135" . + "5ec5facef814f3a7680993cd1af7b95820d37985" . + "0de350e324b5023d83e565330622b64de99219a8" . + "340ac6d571d9e64cbc0b7a4dd4b89c7dcb64ab59" . + "075a0d96dedec8da065acedf5e0e047dc8383d8c" . + . + "f6bf6e4febe8de6b91bb14a49de5d6487f1f296c" . + "d702eb6614b449653fac43b753c31aa9da9a2f95" . + "2cf02c4831449436fe2e4fffe0940bb491d480e8" . + "8f53ac2bedd22651d4754f25fe22620428b0ebe2" . + "20cdd600af3d26d80e148ee9025a7cd25bd0f8b9" . + "f7b83271a8db3662b0d0cf67729317acecfcbb3c" . + "7bf4938931836fb9ca758bfcbe208db67915d100" . + "72909a59969a32eaa7ca938c0553e2c06fb22fa6" . + . + "5cb1b3340da05b8e2d4346dc474726f01d051dc1" . + "189499b1a2969a5485f97cafcab599488c42c648" . + "e3c00486d1bd4146f1c26b3bc58528fd343a6fa9" . + "ec0f23e6d7e4559955b3945d9852b2164450153b" . + "fe805acd9232063ff7c89cf432e2b352328c0a09" . + "70f222924a6b0c63e15537bee54577e4937ddeda" . + "c9fb60acd68ef5f27f89c5d2b5ac585bc3012300" . + "0d467ab52b87ec3764984cfc0a3243640f9125a1" . + "26eb726bf82b044e30535d68710341c8c9dba7fb" . + "170eea91f88297fa6b695320b894a3cda6e4ab7a" . + . + "c845631b2f21f55a932d9b2442984212eda8a409" . + "0867f58c385f21726a56f2c3488a87640d46de88" . + "313f60008de21573ae2cc294ba55472c180d96f5" . + "3440a7c9ef9b588af61fb026acd1d6241f7f7c83" . + "c5808431742c2bb36c97b8e0a557be39bcefc512" . + "9d1bb0592c95ce79d1210d59304f81da5a5c3f9d" . + "f91d11c93e8e6a46fa3cd53c1b981a78fd116784" . + "4930c832896544c1ec4c4fbbe834b41c602e98b5" . + "f90c3718697e6321c56a1bd389e06e13880ad07b" . + "a28b00e9e49a4123c386711c773baa021f5aa48c" . + . + "d1980c57e82d08e5e29c9c2ffce1a12366892674" . + "1372e313e4da6dd8042660dc168fdd34e338a2d8" . + "4080126fda642130e1181fd5803e3122a25749cd" . + "46f0d96ca847e58a7dcc16aa17717cd78d864a34" . + "6ef81def1470ab9d002471f4462e4158f096a68d" . + "d1b26c16b08bb35409c5815fd408f86cbeb4dfe2" . + "9f58a33f05cd0bf4eb5356a2f33f7acd6c6754dd" . + "386c130a5684b23d60a79031b3885aa66e8d411a" . + "220a891c88423eb2b2a67e48cc6bdb9b43f522d0" . + "b76a980ffaeb316c059d43f625931b3761aa86e4" . + "c74bc3db955caa649ff7df4f369c22bda80ac067" . + "4feeee64ffa4c9e2e1b72f9837fe100022cbc732" . + "3322cea76966dfad99d02729c6307ba404c495ac" . + . + "f4dc9fff0b1819b86165da3e3c3ee8a10b850dcd" . + "42c0aeb873b4a7a27a350a1740de24e41eaaf038" . + "a7ff2e48c0f01bcb3db6e858b2660cdc4a735db7" . + "42900bc0754b1b7ad45820b0cc0703e01a65b5f4" . + "ac69aa67661b27280d5c576fdab7896936fe3d24" . + "bb10b4a5af962324ae3825eb6bc13d78004edee2" . + "9dc99faca5d4a5985d416e75cce799659554e5db" . + "a0dbfb06652e3b7b93cb4680e456f08a5445f5e6" . + "f66512dd7437697bfea97174e616fbfbf6ba8f98" . + "7a5c1c02bddb164dcd65b4f9ea29e6dc10182862" . + "2227e6e645b431b8338cb6007b46f68b24a0dd75" . + "497d6b3da396bd2f978fc8fa8b012b75144b06cb" . + . + "c4461bd1cb57b97ce84ff79600038323da4d0d2b" . + "53ff4275a7933aac04737007d85d03ffad63acb6" . + "f9bc8c203087ed7bf0d423608e180277758fa266" . + "f54f484f541a0725e40c727c66de8450b602c015" . + "9a5dfdf896078b39dab92334b6a8d4fc81c1f1f5" . + "4b6b743f21129eeb9d43b9a70bc1faddf1631dcb" . + . + "497d3391f2dd851212743bcc7846719e36e64a6f" . + "9e0f5b6488bcd33c248f998e9556fd9f207bc576" . + "d5aca6e9671e5abdaea8b5692cdd9541874d7cf7" . + "0d746fc607e42b95cf0dca4184428fefb80587b5" . + "bbe00b0008216c491ed694f7c90faf74a3395771" . + "5907f9b5c06564b810eb77e5c3032e22a2e35e70" . + "06331d8bd32ea1cec6d94cf4e6755c3b3d4caf31" . + . + "0731094ee46c633281e4cab42dd032c2234a107d" . + "6e4429f9fc921fd113c2d620a9c6ee171225f791" . + "d48f42943932f72d6915c8f891d7fd9f6cea2cc5" . + "4bbe0a0532d450222e5c9ffc7762ab86721e3693" . + "3cc5886d8ad2a05e2fd5475c3af26645f38662c7" . + "7d98d52f551a3c53b48976810ba20e92842fc272" . + "b3fd4247cdf0b7e1309d42d1f85bc82abec89a2f" . + "538a349e5b26134d3273be29002c04edfbcaea5f" . + "1e9725bb0cab3e73325a169022383ddddc571b6a" . + "eb8d5e4c2ab2078ae80d76faf8f6ec02cd2febd9" . + . + "950774e6103d456a528fbb2bfa86acd285414218" . + "34d82c212ee412a1a693e56889d5c3facfb43750" . + "b9c87a26ef840ae99ccd105ed8e6adc4c0cf5718" . + "0894089d12d3fe6e17d7a8a50399c35b876b8280" . + "d10b636a822fe9e7f160483138256e1b9903a454" . + "cde4bef2eac73b81f3e6f1d45ccfd617732ec04f" . + "78052a875d4b03c72dcd21420e0d3d47fc67ee8b" . + "2131db4ae23bcd0805541ec4d57ef4a768fca33d" . + "1b4dd9bf2b6f5b173447da7a4812e6606fda8009" . + "a37039c7e81db540690ab28c82672f933861f415" . + . + "fb99c80b26b751131ae27a2e4a44d1e5a88fb9f3" . + "08c3af99b0950a6eb4363bb12e0d2a645f886360" . + "d4a28fe8ebe5236fe79622c70583a2ef55cfe78b" . + "4114cf32926ec41c624839b2203c85978da09803" . + "327be0af98491c8f046a72bf9d3eb58952eab9f4" . + "26f4e8682ebd6cd9c66d00e0d6128de2f3540634" . + "4793b70282acce6be0a2457f5256f438b2ef7fa1" . + "f75722d6b9bc6be0da96d924c530aa5977d0e586" . + "8391b9d0206e6ecf631e87eeb3f26641253d2910" . + "9f0b75a4bccfaadece3f7b05bcf3705f2df07482" . + . + "bb19b2e3b926fc1fbc076ab130d3acbadaabd4e7" . + "da56949b9ade7bdd658e7a73f4975a667cb4412b" . + "2a4cd219a480c5a6f65c2696f33fb6810772c5e0" . + "02a21e1a4dda8ae883b18c4da2a04fefb0e58e4e" . + "ecc4d7583fb642d22114b3799f06ea9172182263" . + "6b4cc4966156197a098cebaa8bd5a7a4d9474007" . + "1c505ae6edca936ff2c22b9c3187c5d6dfe2c1da" . + "e7754f767ce6ba0f39599d551f5d0783806fa7a6" . + "61a893abd0750f74a5d6d01cf8202ae88dbb199b" . + . + "971d4d1b1de0ec60a020dca263b57d361310c8f1" . + "59cdcbf70fb7b6eaa00e34b5d228005c1236cc1c" . + "60df11e6d4d986a62651f4698f9fa3bbd80c3eae" . + "c1ad24ffa6b454ed2c076bfa679405886799f6bb" . + "b7ce971451ac41ba89fc01c583f2bc9536897e07" . + "f7b2a22b9072c9a45a9d27615ea615df6f62a8e5" . + "d305f64ffe89cda3a0b518740e582e3786cc959e" . + "83e5dde4934a3c64db38c1e3cf7ed4a02b7a86bb" . + "f2fdea759e80e9ac41f18f4f9ba8399201fdc7fa" . + . + "2a7f7b2e69421b3d030ca307b0d6624b8694394d" . + "9ceedbc6993469709f53288920e0ad68a3a97157" . + "a971bf2dd473fd12d1d2be37bf4cf85a75b63fa8" . + "8d177ef06687d4221f767bb5a1f297770fd79c1d" . + "d74be068a3b6d9ba8f01c208a06ab19cffdd3c0a" . + "7dbca29e0d1b7c747a07659257862db5c352c242" . + "2df9a9d8eb089947e9f40a0b443fa90c21fc0263" . + "ddede1695d8ef08b6b1fdcf5681e4437aa63d70a" . + . + "1ab512952ab0ffb30ae736f9f607356042f8d249" . + "fc50658d123b0deef503ee4f204b5c3937c82712" . + "2623cc8e11adb757875323785677a1acab2a6fd1" . + "17f5d18e7270e17734607d2fe6ff12933751bbae" . + "ff87c3d34c299d3f31ac7d22f26fa4528e8d4083" . + "935e0c916189a1676bd5e782d682c9592bd576d2" . + "a028cde1968cf45730c69ec88b89ff61decb27de" . + "556faac71d694b4fd7eeb78604cc546dbf74d70e" . + . + "0c65bee52a0f323a7c6bb97367d303418967d5e6" . + "289d1f9a233f73c8ee806f63edb056290f6e9ba7" . + "659ed3a5e33380dbcf8f41d812b178d654564e04" . + "7f5d9568db769dbf719e0d824d1ce8ea69159bbb" . + "0075c3e8efd8e501c61b15b73b9fdb37cefd7c86" . + "b0d648f8dec2eae7097422876d2d6ef713d501fd" . + "3e2815db52f3e3823281710f6da731f923efebc3" . + "99f68b904142a6dc43cc1ecd2282ca92638c657e" . + "b47f952183273481d3a0f37bca288e09b50b5632" . + . + "7c49b9eaf36ffa7c549ac3e62b4b44d35199610d" . + "52fd66fb111ed782852fde8898d8796379e17091" . + "5857413ef77fd73201e59b24e9dbac37114900db" . + "ef86c0bb8239cc4f724e83d75520f6788e32bbda" . + "fe591a1a69a26582045037da37c1eaefadfd6e4a" . + "7b5b2f66a842add6af40feab6e9f180a7aeb05de" . + "d3be7dc9bc08a51aeb9f36a2db10180807417423" . + "d60cc0b97f113b6b25334bcdec3a39a1039e5118" . + "fcffca136faf1787ef1ebdae7652d998fc46c01e" . + "7ea34c85ee3938b8a56cbd674ea69291f04e7feb" . + "524dac0689ba6bec232d5e0feac0d9f802aebe78" . + "d38d25f59ba3beb9946756865ad4f10aacd11345" . + "54177001c2be72e0b2161beff073d6ffad7aab63" . + . + "91b92f6270aa64618d5ced1214226c57eb2219ed" . + "9bef49d85bf79842fa67bac904121c706636de47" . + "ec5531edc3a6c2f12c08f8b8c5ad67dcdd6019a8" . + "afaf8a3d1d4cdb769d2b649ea81b68df368eec83" . + "4712fc758a8b5412ae7bd1f9fa7593b869b8afc4" . + "849da5a1222f9c64d996341d8df1261e9045ba04" . + "7151f168884deca2bf6d6654efd281710c0b4e32" . + "0a03e8dc628f39c9ecc00eb2e190e5de56a3cd4c" . + "7d6c9eedfc84238e6c36ee10d5659bebfc0b8b14" . + "39fbfe4a1359c0562740013e33773b39379ba9b5" . + . + "04c61e7393dabb68f4ca915cc95812b8dab039f7" . + "647d0a4f2538590b2d4a9c91a9df700d4bb46d20" . + "0bec2d98f600a6b7566d419a54a4354887603570" . + "4e1f75dda05e33a3636e0415c6ee6229056f3d45" . + "a0c1d9c0e9d2f4da7c608445d385bd41a65e9920" . + "44e146d9e1017572d98775de5095b3674c7a26a3" . + "77ef032a75e2e842996bf96d0fdeab3d8addcec1" . + "e7fbd98e5888160a4c8dee5ab611cc4248ef2826" . + "a6c8448916df36cddb4fb8202fb35e6dc8995344" . + "a6d4ae16784ed69d1e9d2638ceac9e32ab5103b2" . + . + "40f9e448b33623fa693066e017fcda155cecf59c" . + "464608f09beb022785e60b4f50283ac55d2e1df0" . + "52381f484f89a4a974fe21ab0508bfdc43131d31" . + "68acc5ca026b5d235411f8fa8157c934468cc359" . + "b19492fd4bcdb9ec4dc3eda7e1ec99fe37e5d67c" . + "b6e28c40e59c6a3b7164d20c278fa3604f83dc1d" . + "470f7fdb1a1a05221bf77a64135587dd01137370" . + "36c19caed531ff2078e344f84fe30285ac0cde40" . + "8bbd543c71f1bc9b43e059276238fe1b17565262" . + "7f74bb2057947f2dd0257d2434e95159fd615a82" . + . + "aa298dae303a8a08fcb7c8d222757b8811ca320f" . + "cbf8bdb7d611d70d462ef58b32cb1e8223427529" . + "52099237df3bf57a2512df040e62e6ba137affc0" . + "eeea96b306730a75d14e76f21f2d3804cca2e03f" . + "1a6de0ca23f2c0b2a8d6e1ee218928fc16f51f63" . + "3a666652904a545ec912520637cd316f7d8bfba3" . + "e382f28b3cf4fef9ac401f295408b7e71d13731a" . + "e3c667e032c84e1ec00f0feefd99555e7f0f9de6" . + "ee0d0851067f1509db2d87b3e5c790b0d707af1a" . + "7cdf54f1fd9fad6bacad619d5d7c07cf11abdb6b" . + "7ee1a6203b039056a27b0f83f9ce9c922b3bcd52" . + "705aee776a4b1a4f65b876493cc894cedd98dca2" . + "81aeb999adb3ab179d6b2cc9b16ae65260eb71de" . + "8a94d5a59de3258fa2ebf3c45798bd6ff71ff90f" . + "9b0a84a2b086ba31a337fcb75ac36ad88d00b566" . + . + "186d0ef9d8d1ea65577e3308442e2570b9459753" . + "e17f0c1e517574a3a865965d170e48fe6ac498d8" . + "df5f3c352cddede86ed0bda8023cff91b3492d2c" . + "567c83032c6be8fbb0f1ec06869d8604094719c9" . + "ba010c830a1899b2b9b00f60e64046c140dda557" . + "51d95bf50bf064dfc1900412c3edd90bc9ece2e6" . + "8fa1609bb66114c3bbb3dab47bf8771be9af6dbe" . + "0c11492cf79ca49ba49558631961e7fdacec1e59" . + . + "9cbedcdb5d10828e27862833c010b8be95b9adc9" . + "50bba3c61976351b4c857f18b32615d69d38cb85" . + "14c654bd05b0b7b915c95d855299c7a8a8a1bfdf" . + "ce8e798a8eb4c8951e88172209e849250c0344e3" . + "7411b4bf4013eeb5ef192033fcaa8a7f234d3670" . + "76a16a4a6630e47e28a682d7184f5e4fd67d6a0c" . + "5bc1848e656070b805aa299cdeb06e4daac6194c" . + "487154fbc9e3c3fdb864563f98b5ddf72ec59ad8" . + . + "5eb369d21aeb32623519f104e8551cafdf952ce9" . + "f932898ac44bbb78b3e6adf7e6417dbafcbbf970" . + "c3f425b8075c4c0363c56eb1b6f035dee97129ca" . + "1d7119f0ff60cb2b8df170e9171274c499ad7055" . + "f4ffac172642af2bf026e909ae611424ed6bd38a" . + "f52ad2a1fc792d18df7193d184a3eb51dbbee110" . + "64d65ae359bb19c3a4494ee975f65f08e89497b9" . + "b6ac603576f2e9c41c173303b429076c71f5384c" . + "a331d005cb258e60521bd8a79961aaa3be80855d" . + . + "29a98ef55521baaab9d7c6e0594684e4db5077b3" . + "4d5ea872e41da732508a30b9b1df562a19f485f1" . + "80fa050e7a9ced568ce957aacf9bc8fb11af11f5" . + "a115859fc0671d4001130dab1f3aad1813d34dfe" . + "ca5fb4639bbaf6b18ad3258629b3e7cc85c95c51" . + "4a2a7f5edd1b7c471d093d086f7a6e9950f49a70" . + "bd778e3ff3d937ea943dab3818bb6ae524821ceb" . + "91a2f3a32b13455548b7c5b08c783ed119078a25" . + "12fc4af5ac48c7b9081a9d65414ab4fb5dd4ae9c" . + . + "f1f33c93b6b7ad344fccd7deafd6d4ee6713c215" . + "b45cd8c0109bc9839cf956db90d82b56d2c520c9" . + "2abbc2884df2679f0b30aaeead4a237afe9afbce" . + "ad2d691b9b3b5a6590509b9354fa7ffeb8fdd05f" . + "bc67886007786b782dbe43d54543b43bf25c679c" . + "40d0201566c222bd3af5e72effd124c994952fa2" . + "0fc59415fd5fe2d9e4d56ca2686c3968ceed65ce" . + "92d19c98d36d298d3242e4f1b4634c08790f1682" . + . + "c46f644a8f75197a7506ef0010b7b0576de807ed" . + "f2d638528cbef258146124b52b7455047f9c6ff9" . + "5dd371b54079ba7cc56d927b1596dff666c08ba0" . + "c01d03cfa90e82a4c239792d96ff0d4453e59a54" . + "710c457a6dca727701371a359caf88784e58ef4e" . + "eb260d1c1f710207fbaf2fda0d8b40bc56e0a518" . + . + "b40f683da0f156d6d23c7365b07df382baf4557c" . + "adf7ec853eae4499b266a893d8596f4349e21659" . + "7c99bf3a3f827c12c4ea2fef4123c5528b69d5d5" . + "74ff6aeb5ad53a2cb6b20da48c34564ff52d720e" . + "5f9983dbdb75fd7448cfb4eea96c1a10452275f9" . + "b79225464a299d2928fdccf09706a7d6858b6d97" . + "8fae743370338a16b53b8897a418016d2b57dbd9" . + "930858780449bc523a7fc644f9479f5df4111fab" . + . + "6b206db2682f5e7c6f8a7e731af9a445e81ebeb7" . + "f4e6329387191ebb12fe9e93e9f37fd2d5ac4782" . + "b94f633cc3c2f2408e2e70daf053166841e48d79" . + "495e859336ebd35d38fad447334a4474c755a702" . + "49199140fca61cb8af966a1b9c63dfca0c9ccf57" . + "9cc096604edf11d0a3dd0fa131b1fffd276de564" . + "c5e1f57bf6731dd443a4c858f95b8d99b127a4d0" . + "1edf2232e4a0e06eff9ec1f4f364c4a63c40cd1e" . + "01216d16e490070ac70d64464864fea17da4843b" . + . + "c22dcc21307f96fba3faede090756b32a9a6c380" . + "39cc9869b1fa63781b03610d4102a1c776c78b4f" . + "ade229aa4946d06d69c6a407c133ac49611ebf27" . + "127d4178090de356e2fd82b64044841d3067377f" . + . + "2456c3f299535694202ef4b61b92ba05b2024213" . + . + "62147979701bf798616009732b000547abc77704" . + . + "9f6dad47c2d211e34113ea0f40750300301da649" . + . + "a4ee33ed313689d446d8bdf1b236b2af8d553ea1" . + . + "c81d1646870da9f635bb090bff19c8d750647a04" . + . + "1dc164c4b87b5a8acf8c1aff192604a5ecaccee7" . + . + "53643131c0130d808637a4202712d1c122cfa503" . + . + "3d62fc7129b0ccf2ddbadca122172e981fbfe3c9" . + . + "8913459415cc4e18a8a74e87f7a58cd82119bdac" . + . + "66146d0f13b25f09f3f6ec5faf5c8af2d410001a" . + . + "97ce01d9b8f7d8d774a7c384da25bfbd2cf2e05e" . + . + "f21fff69fc9f779e80578657f1c6da8a13a192a0" . + . + "0a9c1cef036ee5e77fe9b6198b90399eb349583e" . + . + "44fda6e64b25629cb11c77eaec655f5dbd51dd43" . + . + "18b5f43c8a3320b1acd1e62bb2c6d365c7b9718a" . + "ce1f52d554ed305647579423d72c586189dd74e0" . + "90cd1c6e35355e253764afecd5a233b98569dd80" . + "2dd6b41a93576594b4a1bbb1bc6ce24f2a58d521" . + "8339d64726ec54d7210325e861532492540a19dd" . + "54ee3246b54ab9509d1193af66d623becbf24227" . + "c5bc3ed7af370982f2b533e9feae3472aa4e8b99" . + "ad9ba3d264b3549b4643fee31b38bf78b0e517c4" . + "cca20ae1b54758ea953e01bfebd950e76740d333" . + "0e6bc1a2b6df5886f259efcee444d351656788d6" . + "94d9e0666c5f0228b907a251caff8374ec0a3c0d" . + "2575cb95bb1da012122b48584cc099d365baf204" . + "c1ac6df6b539e6d02c412fadd818df23279b6d7c" . + "13b251ea66947303bab62274c3c1e03571c2c524" . + "38919331d52c5d76cd59d8ce6620b19e98d97162" . + "1c87ea0b7dc7fa9a9da1107506e6196eb30d11ce" . + "4d494a22e2873f6161eea03cad0673680ad83f7c" . + "ce713c2ffd8168ffe02ab6fa3bef0b446545d34b" . + "77f9a122d872189b5e7c502f6632ad4c35392ef4" . + "31a31ae055777d8ba6dca65e5dff56263c05580c" . + "1a637d83c0b5e4624feff54edf27d7898e7e919d" . + "9cfab05459f7f9e291505621057fb9766f1bc53e" . + "739ac65df7ed2791df429a5023c8e524ed278d53" . + "8f652489c9f07c67d961312f1db0b9c0eace3c2c" . + "87ca5409e835323557aa2cf793e9cdb9d0a0a8ae" . + "e44f8761f67eb2b0ee51c25c9f2e8e9c208b4881" . + "3d85a79dfa88c3e385a229767d493888c558910e" . + "064c903608cb7bff2d49ee93faa25c28ee1cf8d0" . + "5469dcfe126132705aef6a4b523949668a570469" . + "6904a3530aa7679f372bc96c0014cd5fe294a305" . + "57ac8a74ef81346ee268d71451d706d3045180e7" . + "4ec29e42f26e5e4c3aec489bb330201cfc5671db" . + "342bb5ab482757ce5ce2c6f02291ee144daf42ee" . + "eed60886fb9025d9ec37b211b7f1d7e5ace3678a" . + "df67c44a003fa87ea4fa174450ad53b1d48219e8" . + "a46a00437815608c1255cf376f52f71c3913f715" . + "003b30cb5757c87651ab8bd5ff69069304c1fbfc" . + "6874391a5214fbda5e06bf6e717d65d8e3b7b96e" . + "72282f5b75188716bd30af82eaac801e639a217e" . + "1deec599fd7674c0aec5222582b769719a8b9e71" . + "046bd5d7032fde032562cdf08cbf311de6af3a01" . + "1e841bcac4d381f07bc232483e6ece7dfcca5c32" . + "72a83f224f22b1e9a9a5deb464fb6e8421af5f1c" . + "7d95f54fb8473f3c275ceded6a761ae950940af1" . + "3d8cd39765a201f84b200012064b8f51fad3e3e2" . + "f8d98b8247d93f371d1cf5e5c0c07c956b37a032" . + "1ec3f6915f744d5ec274e3345bb6a36018c4b162" . + "ee59551dfc5084d1088d43dff0352568159fb0fc" . + "b6edce9f8b40d2231324a52bdf0923faa9b29f88" . + "8ce9c1beca50fba9a23854e5b28ccecc88caa188" . + "e682c8283dd99dcb3a595519a2ed38903e39d0b3" . + "c06dfe1c1d6bd485ea10488fc2d02cfa052074af" . + "d1f99739fdc64b8a0721a64dc63de7df3ac93987" . + "6a0b28f1686fff4c3ea1e073a35ee5ae76f40092" . + "6f424906a211410964a21f1d89c2862661a26b1e" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Aankledingen van Manneken-Pis"@nl . + "Aanplakborden"@nl . + "Aanplakzuilen"@nl . + "Bezoeken & bezoekers website Stad Brussel"@nl . + "Aanvragen voor een bouwvergunning"@nl . + "Aanvragen voor geleverde milieuvergunningen"@nl . + "Administratief centrum en verbindingsbureaus"@nl . + "Afval"@nl . + "Agenda van de dag"@nl . + "Android-applicatie"@nl . + "Apotheken"@nl . + "Apotheken"@nl . + "Begraafplaatsen"@nl . + "Bezochte webpagina's van de Stad (per URL) in 2013"@nl . + "Bezochte webpagina's van de Stad (per titel) in 2013"@nl . + "Bezochte webpagina's van de Stad (per titel) in 2014"@nl . + "Bezochte webpagina's van de Stad (per titel) in 2015"@nl . + "Bezochte webpagina's van de Stad (per URL) in 2014"@nl . + "Bezochte webpagina's van de Stad (per URL) in 2015"@nl . + "Bioscopen"@nl . + "Burgemeesters"@nl . + "Buurthuizen"@nl . + "Consultaties voor kinderen bij K&G"@nl . + "Consultaties voor kinderen bij ONE"@nl . + "Covers van de Brusseleir"@nl . + "Culturele plaatsen"@nl . + "Dataset van de datasets"@nl . + "Demografische statistieken"@nl . + "Initiatieven voor duurzame ontwikkeling : Elkaar helpen"@nl . + "Energieverbruik"@nl . + "Ereburgers"@nl . + "Initiatieven voor duurzame ontwikkeling : Eten en drinken"@nl . + "Europese instellingen"@nl . + "Europese mannelijke bevolking in Brussel"@nl . + "Europese vrouwelijke bevolking in Brussel"@nl . + "Facebookpagina's Stad Brussel"@nl . + "Fietstrommels"@nl . + "Fonteinen met drinkbaar water"@nl . + "Food trucks"@nl . + "Franstalige bibliotheken"@nl . + "Franstalige scholen"@nl . + "​Gebruikers van de Nederlandstalige bibliotheken"@nl . + "Geldautomaten"@nl . + "Gemeentelijke actualiteit"@nl . + "Gemeentelijke basisscholen"@nl . + "Gemeentelijke kleuterscholen"@nl . + "Gemeentelijke lagere scholen"@nl . + "Gemeentelijke secundaire scholen"@nl . + "Gemeenteraadsverkiezingen (aantal stemmen)"@nl . + "Gemeenteraadsverkiezingen (procenten)"@nl . + "Gemeenteraadsverkiezingen (zetels)"@nl . + "Gemotoriseerde tweewielers"@nl . + "Geografische oorsprong (Belgische gemeenten) van de bezoeken aan de website van de Stad (2015)"@nl . + "Geografische oorsprong (Belgische gemeenten) van de bezoeken aan de website van de Stad (2014)"@nl . + "Geografische oorsprong (Belgische gemeenten) van de bezoeken aan de website van de Stad (2013)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2009)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2010)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2011)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2012)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2013)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2014)"@nl . + "Geografische oorsprong van de bezoeken aan de website van de Stad (2015)"@nl . + "Gewestwegen"@nl . + "Glasbollen"@nl . + "Haltes MIVB"@nl . + "Initiatieven voor duurzame ontwikkeling : Zich verplaatsen"@nl . + "Initiatieven voor duurzame ontwikkeling : Herstellen, hergebruiken, recycleren, composteren"@nl . + "Hondentoiletten"@nl . + "Huizen voor het Kind"@nl . + "​Informatiedragers in de Nederlandstalige bibliotheken"@nl . + "Ingediende aanvragen voor milieuvergunningen"@nl . + "Invasieve exotische planten"@nl . + "Jeugdhotels"@nl . + "Kinderdagverblijven & peutertuinen"@nl . + "Louise - Bois de la Cambre quarter"@nl . + "Mannelijke voornamen 2000"@nl . + "Mannelijke voornamen 2001"@nl . + "Mannelijke voornamen 2002"@nl . + "Mannelijke voornamen 2003"@nl . + "Mannelijke voornamen 2004"@nl . + "Mannelijke voornamen 2005"@nl . + "Mannelijke voornamen 2006"@nl . + "Mannelijke voornamen 2007"@nl . + "Mannelijke voornamen 2008"@nl . + "Mannelijke voornamen 2009"@nl . + "Mannelijke voornamen 2010"@nl . + "Mannelijke voornamen 2011"@nl . + "Mannelijke voornamen 2012"@nl . + "Mannelijke voornamen (2013)"@nl . + "Mannelijke voornamen 2013"@nl . + "Mannelijke voornamen 2014"@nl . + "​Mannelijke voornamen 2015"@nl . + "Mannelijke voornamen 2015"@nl . + "Markten"@nl . + "Middenklassewoningen (met inkomensvoorwaarden) van de Grondregie (2014)"@nl . + "Middenklassewoningen van de Grondregie (2014)"@nl . + "Monumenten 1914-1918"@nl . + "Musea in Brussel"@nl . + "Museums"@nl . + "Nederlandstalige bibliotheken"@nl . + "Nederlandstalige scholen"@nl . + "Nestkasten"@nl . + "Netheidscomités"@nl . + "NMBS-stations"@nl . + "Noordoostwijk"@nl . + "Noordwijk"@nl . + "Openbare aanbestedingen"@nl . + "Openbare computerruimtes (OCR)"@nl . + "Openbare ziekenhuizen"@nl . + "Opmerkelijke bomen"@nl . + "Outdoor multisportvelden"@nl . + "Parkeerplaatsen voor gehandicapten"@nl . + "Parken"@nl . + "Parkings"@nl . + "Parkings voor reisbussen"@nl . + "Petanquebanen"@nl . + "Plaatsen met openbare internettoegang (POIT)"@nl . + "Politiekantoren"@nl . + "Pollumeter"@nl . + "Bevolking van Brussel"@nl . + "Rusthuizen voor ouderen"@nl . + "Sectoren bewonerskaart"@nl . + "Seniorenpaviljoenen"@nl . + "Slimme vuilnisbakken"@nl . + "Sociale netwerken"@nl . + "Speeltuinen"@nl . + "Sportzalen en stadions"@nl . + "Stations Cambio"@nl . + "Stations Zen Car"@nl . + "Stembureaus"@nl . + "Street Art"@nl . + "Striproute"@nl . + "Tellingen op het kruispunt Van der Weyden - Stalingrad"@nl . + "Standplaatsen taxi's"@nl . + "Theaters"@nl . + "Toerismekantoren"@nl . + "Toiletten"@nl . + "​Uitleningen en raadplegingen in de Nederlandstalige bibliotheken"@nl . + "Updates van de datasets"@nl . + "Urinoirs"@nl . + "Vacatures"@nl . + "Vastgoed (woningen) van de Grondregie (2014)"@nl . + "Verkeer bij evenementen & werken"@nl . + "Verkeersintensiteit"@nl . + "Verwachte evolutie van de bevolking van Brussel (met aanwas)"@nl . + "Verwachte evolutie van de Brusselse bevolking naar leeftijdsgroepen"@nl . + "Verwachte evolutie van de Brusselse seniorenbevolking"@nl . + "Villo!-stations & beschikbaarheid in real time"@nl . + "Volgers van de Twitter account"@nl . + "Vrouwelijke voornamen 2000"@nl . + "Vrouwelijke voornamen 2001"@nl . + "Vrouwelijke voornamen 2002"@nl . + "Vrouwelijke voornamen 2003"@nl . + "Vrouwelijke voornamen 2004"@nl . + "Vrouwelijke voornamen 2005"@nl . + "Vrouwelijke voornamen 2006"@nl . + "Vrouwelijke voornamen 2007"@nl . + "Vrouwelijke voornamen 2008"@nl . + "Vrouwelijke voornamen 2009"@nl . + "Vrouwelijke voornamen 2010"@nl . + "Vrouwelijke voornamen 2011"@nl . + "Vrouwelijke voornamen 2012"@nl . + "Vrouwelijke voornamen (2013)"@nl . + "Vrouwelijke voornamen 2013"@nl . + "Vrouwelijke voornamen 2014"@nl . + "Vrouwelijke voornamen 2015"@nl . + "Vrouweljke voornamen 2015"@nl . + "Webcams"@nl . + "Wedstrijden"@nl . + "Wifi"@nl . + "Wijken"@nl . + "Wijken"@nl . + "Woningen binnen wijkcontracten van de Grondregie (2014)"@nl . + "Woningen van de grondregie"@nl . + "Initiatieven voor duurzame ontwikkeling : Zich ontspannen"@nl . + "Zwembaden"@nl . + "Zwembaden"@nl . + "Manneken-Pis"@nl . + "folklore"@nl . + "aanplak"@nl . + "afiche"@nl . + "poster"@nl . + "aanplak"@nl . + "affiche"@nl . + "poster"@nl . + "Internet"@nl . + "web"@nl . + "website"@nl . + "stedenbouw"@nl . + "vergunning"@nl . + "aanvraag"@nl . + "milieu"@nl . + "milieuvergunning"@nl . + "administratie"@nl . + "gemeente"@nl . + "afval"@nl . + "netheid"@nl . + "Android"@nl . + "app"@nl . + "applicatie"@nl . + "smartphone"@nl . + "apotheek"@nl . + "gezondheid"@nl . + "aptheek"@nl . + "gezondheid"@nl . + "pharmacie"@nl . + "santé"@nl . + "begraafplaats"@nl . + "kerkhof"@nl . + "bioscoop"@nl . + "cinema"@nl . + "buurthuis"@nl . + "K&G"@nl . + "Kind & Gezin"@nl . + "consultatie"@nl . + "gezondheid"@nl . + "kind"@nl . + "ONE"@nl . + "consultatie"@nl . + "gezondheid"@nl . + "kind"@nl . + "magazine"@nl . + "Cultuur"@nl . + "ur"@nl . + "dataset"@nl . + "echtscheiding"@nl . + "geboorte"@nl . + "huwelijk"@nl . + "overlijden"@nl . + "scheiding"@nl . + " ruil"@nl . + " uitwisseling "@nl . + "Burger"@nl . + "Burgerinitiatieven"@nl . + "Buurtcomité"@nl . + "Diensten"@nl . + "Lokaal ruilsysteem"@nl . + "Netwerk voor kennisuitwisseling"@nl . + "Netwerken "@nl . + "Netwerken voor uitwisseling en ruil"@nl . + "Verenigingsleven"@nl . + "Vzw"@nl . + "initiatieve"@nl . + "elektriciteit"@nl . + "energie"@nl . + "gas"@nl . + "water"@nl . + "BUURTWINKELS"@nl . + "Biowinkel"@nl . + "HORECA"@nl . + "Kruidenwinkel"@nl . + "Markt"@nl . + "Moestuin"@nl . + "SAGAL"@nl . + "STEDELIJKE LANDBOUW"@nl . + "Stadsboerderij"@nl . + "café"@nl . + "drinken"@nl . + "eten"@nl . + "restaurant"@nl . + "slowfood"@nl . + "Europa"@nl . + "internationaal"@nl . + "Europa"@nl . + "bevolking"@nl . + "man"@nl . + "Europa"@nl . + "bevolking"@nl . + "vrouw"@nl . + "Facebook"@nl . + "sociale netwerken"@nl . + "fontein"@nl . + "water"@nl . + "bib"@nl . + "bibliotheek"@nl . + "boek"@nl . + "franstalig"@nl . + "school"@nl . + "ATM"@nl . + "actualiteit"@nl . + "nieuws"@nl . + "basischool"@nl . + "onderwijs"@nl . + "school"@nl . + "kleuterschool"@nl . + "onderwijs"@nl . + "school"@nl . + "lager"@nl . + "onderwijs"@nl . + "school"@nl . + "middelbaar"@nl . + "onderwijs"@nl . + "school"@nl . + "secundair"@nl . + "gemeenteraad"@nl . + "stem"@nl . + "verkiezing"@nl . + "gemeenteraad"@nl . + "verkiezing"@nl . + "gemeenteraad"@nl . + "verkiezing"@nl . + "moto"@nl . + "parkeerplaats"@nl . + "parkeren"@nl . + "Internet"@nl . + "site web"@nl . + "website"@nl . + "weg"@nl . + "wegenis"@nl . + "container"@nl . + "glas"@nl . + "MIVB"@nl . + "bus"@nl . + "halte"@nl . + "metro"@nl . + "station"@nl . + "tram"@nl . + "Collecto"@nl . + "Herstelling "@nl . + "Taxi"@nl . + "Waterbus"@nl . + "fiets"@nl . + "Afvalinzameling"@nl . + "Compost"@nl . + "Container"@nl . + "Herstelling "@nl . + "Kleding"@nl . + "Kledingcontainer"@nl . + "Kringloopwinkel"@nl . + "Kurken"@nl . + "Plastic potten "@nl . + "Proxy chimik"@nl . + "Schoenmaker"@nl . + "Tweedehands"@nl . + "Weggeefmarkt"@nl . + "canisite"@nl . + "chien"@nl . + "propreté"@nl . + "enfance"@nl . + "enfant"@nl . + "milieu"@nl . + "milieuv"@nl . + "herberg"@nl . + "hotel"@nl . + "jeugd"@nl . + "toerisme"@nl . + "Cambre"@nl . + "Louise"@nl . + "district"@nl . + "quarter"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "jongen"@nl . + "voornaam"@nl . + "mannelijk"@nl . + "voornaam"@nl . + "handel"@nl . + "markt"@nl . + "Grondregie"@nl . + "woning"@nl . + "Gronregie"@nl . + "woning"@nl . + "14-18"@nl . + "1914-1918"@nl . + "monument"@nl . + "cultuur"@nl . + "musea"@nl . + "museum"@nl . + "tentoonstelling"@nl . + "bibliotheek"@nl . + "boek"@nl . + "school"@nl . + "nest"@nl . + "nestkast"@nl . + "vogel"@nl . + "vogelhuis"@nl . + "comité"@nl . + "NMBS"@nl . + "station"@nl . + "trein"@nl . + "ICT"@nl . + "Internet"@nl . + "OCR"@nl . + "hospitaal"@nl . + "ziekenhuis"@nl . + "boom"@nl . + "sport"@nl . + "terrein"@nl . + "auto"@nl . + "handicap"@nl . + "parkeren"@nl . + "natuur"@nl . + "park"@nl . + "auto"@nl . + "parkeerplaats"@nl . + "parking"@nl . + "bus"@nl . + "reisbus"@nl . + "toerisme"@nl . + "ICT"@nl . + "Internet"@nl . + "POIT"@nl . + "politie"@nl . + "veiligheid"@nl . + "Brussels Hoofdstedelijk Gewest"@nl . + "luchtkwaliteit"@nl . + "pollumeter"@nl . + "administratie"@nl . + "bevolking"@nl . + "demografie"@nl . + "OCMW"@nl . + "rusthuis"@nl . + "senior"@nl . + "verzorgingstehuis"@nl . + "auto"@nl . + "bewonerskaart"@nl . + "mobiliteit"@nl . + "parkeerkaart"@nl . + "parkeerplaats"@nl . + "smart city"@nl . + "Facebook"@nl . + "Flickr"@nl . + "Google+"@nl . + "LinkedIn"@nl . + "Pinterest"@nl . + "Twitter"@nl . + "social network"@nl . + "speeltuin"@nl . + "sport"@nl . + "sportzaal"@nl . + "stadion"@nl . + "Cambio"@nl . + "auto"@nl . + "auto"@nl . + "stembureau"@nl . + "verkiezing"@nl . + "Street Art"@nl . + "art"@nl . + "kunst"@nl . + "street art"@nl . + "Cultuur"@nl . + "fresco"@nl . + "muurtekening"@nl . + "personage"@nl . + "strip"@nl . + "stripmuren"@nl . + "stripmuur"@nl . + "telling"@nl . + "voertuig"@nl . + "taxi"@nl . + "schouwburg"@nl . + "theater"@nl . + "information"@nl . + "tourisme"@nl . + "touriste"@nl . + "bibliotheek"@nl . + "Open Data"@nl . + "RSS"@nl . + "job"@nl . + "vacature"@nl . + "werk"@nl . + "Grondregie"@nl . + "woning"@nl . + "mobiliteit"@nl . + "verkeer"@nl . + "verkeer"@nl . + "bevolking"@nl . + "demografie"@nl . + "emigratie"@nl . + "geboorte"@nl . + "immigratie"@nl . + "sterfte"@nl . + "bevolking"@nl . + "demografie"@nl . + "leeftijd"@nl . + "bevolking"@nl . + "senior"@nl . + "Villo!"@nl . + "fiets"@nl . + "Twitter"@nl . + "follower"@nl . + "volger"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "meisje"@nl . + "voornaam"@nl . + "voornaam"@nl . + "voornaam"@nl . + "webcam"@nl . + "RSS"@nl . + "wedstrijd"@nl . + "Internet"@nl . + "hotspot"@nl . + "wi-fi"@nl . + "wifi"@nl . + "quartier"@nl . + "quartier"@nl . + "Grondregie"@nl . + "woning"@nl . + "Informatiecentrum "@nl . + "Jeugdcentra"@nl . + "Jeugdhuizen "@nl . + "jongeren "@nl . + "zwembad"@nl . + "sport"@nl . + "zwembad"@nl . + "Actualités communales"@fr . + "Agenda du jour"@fr . + "Aires de jeux"@fr . + "Aires de stationnement taxi"@fr . + "Appels d'offres"@fr . + "Application Android"@fr . + "Arrêts STIB"@fr . + "Box à vélos"@fr . + "Affichage public"@fr . + "Arbres remarquables"@fr . + "Auberges de jeunesse et hôtels pour jeunes"@fr . + "Bibliothèques francophones"@fr . + "Bulles à verre"@fr . + "Bureaux de police"@fr . + "Bureaux de tourisme"@fr . + "Canisites"@fr . + "Centre administratif et bureaux de liaison"@fr . + "Cinémas"@fr . + "Colonnes d'affichage communal"@fr . + "Concours communaux"@fr . + "Distributeurs bancaires"@fr . + "Ecoles francophones"@fr . + "Bibliothèques néerlandophones"@fr . + "Elèves dans les écoles communales francophones"@fr . + "Plantes exotiques invasives"@fr . + "Installations sportives de la Ville de Bruxelles"@fr . + "Points d'accès publics à l'Internet"@fr . + "Institutions européennes"@fr . + "Lieux culturels"@fr . + "Maisons de quartier"@fr . + "Musées"@fr . + "Offres d'emploi"@fr . + "Parcours BD"@fr . + "Parcs et jardins publics"@fr . + "Parkings pour personnes handicapées"@fr . + "Parkings publics"@fr . + "Rues par secteur pour les cartes de riverain"@fr . + "Pages Facebook"@fr . + "Théâtres"@fr . + "Toilettes publiques"@fr . + "Top 100 des livres empruntés par bibliothèque (2013)"@fr . + "Urinoirs publics"@fr . + "Wifi"@fr . + "Bureaux de vote"@fr . + "Bus scolaires"@fr . + "Bourgmestres"@fr . + "Consommation énergétique"@fr . + "Projection de l'évolution de la population bruxelloise par tranches d'âge"@fr . + "Statistiques démographiques"@fr . + "Fontaines d'eau potable"@fr . + "Food trucks"@fr . + "Prénoms masculins (2013)"@fr . + "Logement moyen à la Régie foncière en 2014"@fr . + "Visites & visiteurs du site web"@fr . + "Pharmacies"@fr . + "Centres de jeunes de Bravvo"@fr . + "Cimetières"@fr . + "​Citoyens d'honneur"@fr . + "Collections des bibliothèques francophones en 2013"@fr . + "Collections des bibliothèques francophones en 2014"@fr . + "Collections des bibliothèques francophones en 2015"@fr . + "Comités Propreté"@fr . + "Comptages au carrefour van der Weyden - Stalingrad"@fr . + "Consultations pour enfants de K&G"@fr . + "Consultations pour enfants de l'ONE"@fr . + "Costumes de Manneken-Pis"@fr . + "Couvertures du Brusseleir"@fr . + "Crèches & prégardiennats"@fr . + "Déchets"@fr . + "Demandes de permis d'environnement délivrés"@fr . + "Demandes de permis d'environnement introduits"@fr . + "Demandes de permis d'urbanisme"@fr . + "Deux-roues motorisés"@fr . + "Ecoles artistiques francophones communales"@fr . + "Ecoles de promotion sociale francophones communales"@fr . + "Ecoles maternelles francophones communales"@fr . + "Ecoles néerlandophones"@fr . + "Ecoles primaires francophones communales"@fr . + "Ecoles secondaires francophones communales"@fr . + "Ecoles spécialisées francophones communales"@fr . + "Ecoles supérieures francophones communales"@fr . + "Elections communales (nombre de votes)"@fr . + "Elections communales (pourcentages)"@fr . + "Elections communales (sièges)"@fr . + "Espaces publics numériques (EPN)"@fr . + "Evénements trafic & travaux"@fr . + "Fiets/Vélo: fietszone in park / zones cyclables dans les parcs"@fr . + "Fiets/Vélo: gemarkeerde fietspaden – pistes cyclables marquées"@fr . + "Fiets/Vélo: verhoogde fietspaden - pistes cyclables surélevées"@fr . + "Fiets/Vélo : voetgangerszone-fietszone / piétonnier - zone cyclable"@fr . + "Fontaines d'eau potable"@fr . + "Gares SNCB"@fr . + "Guérites et bulles des Petits Riens"@fr . + "Habillages de Manneken-Pis"@fr . + "Hôpitaux publics"@fr . + "Logements contrats de quartier à la Régie foncière (2014)"@fr . + "Logements de la Régie foncière"@fr . + "Logements moyens (avec conditions de revenus) de la Régie foncière (2014)"@fr . + "Maisons de repos pour personnes âgées"@fr . + "Maisons des Enfants"@fr . + "Carte des ressources durables : Manger et boire"@fr . + "Manneken-Pis en Photos"@fr . + "Marchés de plein air"@fr . + "​Mise à jour des jeux de données"@fr . + "Monuments commémoratifs de la guerre de 1914-1918"@fr . + "Monuments funéraires du cimetière de Laeken"@fr . + "Musées à Bruxelles"@fr . + "Nichoirs"@fr . + "Niveaux de service de trafic"@fr . + "Origine géographique (communes belges) des visites du site web de la Ville (2014)"@fr . + "Origine géographique (communes belges) des visites du site web de la Ville (2015)"@fr . + "Origine géographique (communes belges) des visites du site web de la Ville (2013)"@fr . + "Origine géographique des visites du site web de la Ville (2009)"@fr . + "Origine géographique des visites du site web de la Ville (2010)"@fr . + "Origine géographique des visites du site web de la Ville (2011)"@fr . + "Origine géographique des visites du site web de la Ville (2012)"@fr . + "Origine géographique (pays) des visites du site web de la Ville (2013)"@fr . + "Origine géographique (pays) des visites du site web de la Ville (2014)"@fr . + "Pages du site web de la Ville (par titre) visitées en 2014"@fr . + "Pages du site web de la Ville (par titre) visitées en 2013"@fr . + "Pages du site web de la Ville (par titre) visitées en 2015"@fr . + "Pages du site web (par URL) de la Ville visitées en 2013"@fr . + "Pages du site web (par URL) de la Ville visitées en 2014"@fr . + "Pages du site web (par URL) de la Ville visitées en 2015"@fr . + "Parkings pour autocars touristiques"@fr . + "Patrimoine (logements) de la Régie foncière (2014)"@fr . + "Pavillons Seniors"@fr . + "Pharmacies"@fr . + "Piscines"@fr . + "Pollumètre"@fr . + "Population"@fr . + "Population bruxelloise"@fr . + "Population européenne masculine à Bruxelles"@fr . + "Population européenne féminine à Bruxelles"@fr . + "Poubelles intelligentes"@fr . + "PPAS"@fr . + "Prénoms féminins 2000"@fr . + "Prénoms féminins 2001"@fr . + "Prénoms féminins 2002"@fr . + "Prénoms féminins 2003"@fr . + "Prénoms féminins 2004"@fr . + "Prénoms féminins 2005"@fr . + "Prénoms féminins 2006"@fr . + "Prénoms féminins 2007"@fr . + "Prénoms féminins 2008"@fr . + "Prénoms féminins 2009"@fr . + "Prénoms féminins 2010"@fr . + "Prénoms féminins 2011"@fr . + "Prénoms féminins 2012"@fr . + "Prénoms féminins (2013)"@fr . + "Prénoms féminins 2013"@fr . + "Prénoms féminins 2014"@fr . + "Prénoms féminins 2015"@fr . + "Prénoms féminins 2015"@fr . + "Prénoms masculins 2000"@fr . + "Prénoms masculins 2001"@fr . + "Prénoms masculins 2002"@fr . + "Prénoms masculins 2003"@fr . + "Prénoms masculins 2004"@fr . + "Prénoms masculins 2005"@fr . + "Prénoms masculins 2006"@fr . + "Prénoms masculins 2007"@fr . + "Prénoms masculins 2008"@fr . + "Prénoms masculins 2009"@fr . + "Prénoms masculins 2010"@fr . + "Prénoms masculins 2011"@fr . + "Prénoms masculins 2012"@fr . + "Prénoms masculins 2013"@fr . + "Prénoms masculins 2014"@fr . + "Prénoms masculins 2015"@fr . + "Prenoms masculins 2015"@fr . + "Prêts dans les bibliothèques francophones en 2013"@fr . + "Prêts dans les bibliothèques francophones en 2014"@fr . + "Prêts dans les bibliothèques francophones en 2015"@fr . + "Projection de l'évolution de la population bruxelloise (avec soldes)"@fr . + "Projection de l'évolution de la population des seniors bruxellois"@fr . + "Quartiers"@fr . + "Références - Hôpitaux"@fr . + "Carte des ressources durables: Réparer, réutiliser, recycler, composter"@fr . + "Réseaux sociaux"@fr . + "Réservations dans les bibliothèques francophones en 2013"@fr . + "Réservations dans les bibliothèques francophones en 2014"@fr . + "Réservations dans les bibliothèques francophones en 2015"@fr . + "Jeu de données des jeux de données"@fr . + "Carte des ressources durables : Se déplacer (réparation de vélos, collecto & waterbus)"@fr . + "Carte des ressources durables: Centres de jeunes et centres d'informations des jeunes"@fr . + "Carte des ressources durables : S'entraider"@fr . + "None"@fr . + "Stationnement des poids lourds interdit"@fr . + "Stations Cambio"@fr . + "Stations Villo! & disponibilités en temps réel"@fr . + "Stations Zen Car"@fr . + "Street Art"@fr . + "Suiveurs des comptes Twitter"@fr . + "Terrains de pétanque"@fr . + "​Terrains multisports extérieurs"@fr . + "Fiets/Vélo: busbanen/fietsstroken - bandes bus/bandes cyclables"@fr . + "Top 100 de la Bibliothèque principale de Bruxelles 1 en 2013"@fr . + "Top 100 des livres empruntés par bibliothèque (2014)"@fr . + "Top 100 des livres empruntés par bibliothèque (2015)"@fr . + "None"@fr . + "Voetgangerszone"@fr . + "Voiries régionales"@fr . + "Webcams"@fr . + "None"@fr . + "None"@fr . + "actualité"@fr . + "actualités"@fr . + "information"@fr . + "agenda"@fr . + "annonce"@fr . + "information"@fr . + "enfant"@fr . + "jeu"@fr . + "taxi"@fr . + "appel d'offre"@fr . + "marché public"@fr . + "Android"@fr . + "app"@fr . + "application"@fr . + "smartphone"@fr . + "arrêt"@fr . + "bus"@fr . + "métro"@fr . + "station"@fr . + "tram"@fr . + "affichage"@fr . + "affiche"@fr . + "publicité"@fr . + " arbre"@fr . + " environnement"@fr . + " vert"@fr . + "nature"@fr . + "auberge de jeunesse"@fr . + "hôtel"@fr . + " lecture publique"@fr . + " livre"@fr . + "bibliothèque"@fr . + "bulle à verre"@fr . + "container"@fr . + "conteneur"@fr . + "déchet"@fr . + "commissariat"@fr . + "police"@fr . + "information"@fr . + "tourisme"@fr . + "touriste"@fr . + "canisite"@fr . + "chien"@fr . + "propreté"@fr . + "Centre administratif"@fr . + "administratif"@fr . + "bureau de liaison"@fr . + "cinéma"@fr . + "affichage"@fr . + "affiche"@fr . + "publicité"@fr . + "concours"@fr . + "ATM"@fr . + "argent"@fr . + "distributeur bancaire"@fr . + "finances"@fr . + "guichet automatique"@fr . + "enseignement"@fr . + "francophone"@fr . + "école"@fr . + "enseignement"@fr . + "néerlandophone"@fr . + "école"@fr . + "école"@fr . + "élève"@fr . + "étudiant"@fr . + "plante"@fr . + "Internet"@fr . + "PAPI"@fr . + "Europe"@fr . + "centre culturel"@fr . + "culture"@fr . + "musée"@fr . + "théâtre"@fr . + "maison de quartier"@fr . + "art"@fr . + "histoire"@fr . + "musée"@fr . + "emploi"@fr . + "job"@fr . + "travail"@fr . + " BD"@fr . + " bande dessinée"@fr . + " fresque"@fr . + " parcours BD"@fr . + "mur BD"@fr . + "espace vert"@fr . + "jardin public"@fr . + "nature"@fr . + "parc"@fr . + "PMR"@fr . + "handicap"@fr . + "handicapé"@fr . + "stationnement"@fr . + "voiture"@fr . + "parking"@fr . + "voiture"@fr . + "riverain"@fr . + "rue"@fr . + "stationnement"@fr . + "voiture"@fr . + "Facebook"@fr . + "réseaux sociaux"@fr . + "culture"@fr . + "théâtre"@fr . + "propreté"@fr . + "toilette"@fr . + "bibliothèque"@fr . + "lecture"@fr . + "livre"@fr . + "propreté"@fr . + "toilette"@fr . + "urinoir"@fr . + "Internet"@fr . + "wi-fi"@fr . + "wifi"@fr . + "vote"@fr . + "élection"@fr . + "autobus"@fr . + "bus"@fr . + "mobilité"@fr . + "parking"@fr . + "scolaire"@fr . + "école"@fr . + "bourgmestre"@fr . + "mayorat"@fr . + "eau"@fr . + "gaz"@fr . + "électricité"@fr . + "énergie"@fr . + "démographie"@fr . + "population"@fr . + "divorce"@fr . + "décès"@fr . + "mariage"@fr . + "naissance"@fr . + "séparation"@fr . + "eau"@fr . + "fontaine"@fr . + "homme"@fr . + "prénom"@fr . + "Régie foncière"@fr . + "logement"@fr . + "Google Analytics"@fr . + "Internet"@fr . + "TIC"@fr . + "site Internet"@fr . + "site web"@fr . + "pharmacie"@fr . + "santé"@fr . + "Bravvo"@fr . + "centre de jeunes"@fr . + "jeune"@fr . + "cimetière"@fr . + "citoyen d'honneur"@fr . + "bibliothèque"@fr . + "lecture publique"@fr . + "livre"@fr . + "bibliothèque"@fr . + "lecture publique"@fr . + "livre"@fr . + "bibliothèque"@fr . + "lecture publique"@fr . + "livre"@fr . + "citoyen"@fr . + "comité"@fr . + "propreté"@fr . + "comptage"@fr . + "mobilité"@fr . + "véhicule"@fr . + "enfant"@fr . + "santé"@fr . + "enfant"@fr . + "santé"@fr . + "Manneken-Mis"@fr . + "folklore"@fr . + "musée"@fr . + "magazine"@fr . + "revue"@fr . + "crèche"@fr . + "milieu d'accueil"@fr . + "petite enfance"@fr . + "prégardiennat"@fr . + "déchet"@fr . + "propreté"@fr . + "permis"@fr . + "urbanisme"@fr . + "permis"@fr . + "urbanisme"@fr . + "permis"@fr . + "urbanisme"@fr . + "moto"@fr . + "parking"@fr . + "stationnement"@fr . + "art"@fr . + "artistique"@fr . + "enseignement"@fr . + "école"@fr . + "enseignement"@fr . + "promotion sociale"@fr . + "école"@fr . + "enseignement"@fr . + "maternelle"@fr . + "école"@fr . + "enseignement"@fr . + "néerlandophone"@fr . + "école"@fr . + "enseignement"@fr . + "primaire"@fr . + "école"@fr . + "enseignement"@fr . + "secondaire"@fr . + "école"@fr . + "enseignement"@fr . + "maternel"@fr . + "primaire"@fr . + "spécial"@fr . + "spécialisé"@fr . + "école"@fr . + "enseignement"@fr . + "supérieure"@fr . + "école"@fr . + "conseil communal"@fr . + "élection"@fr . + "conseil communal"@fr . + "élection"@fr . + "conseil communal"@fr . + "élection"@fr . + "EPN"@fr . + "Internet"@fr . + "chantier"@fr . + "circulation"@fr . + "trafic"@fr . + "travaux"@fr . + "cycliste"@fr . + "fiets"@fr . + "vélo"@fr . + "cycliste"@fr . + "fiets"@fr . + "vélo"@fr . + "cycliste"@fr . + "fiets"@fr . + "vélo"@fr . + "WC"@fr . + "eau"@fr . + "fontaine"@fr . + "urinoir"@fr . + "SNCB"@fr . + "gare"@fr . + "train"@fr . + "Manneken-Pis"@fr . + "folklore"@fr . + "hôpital"@fr . + "santé"@fr . + "Régie foncière"@fr . + "logement"@fr . + "Régie foncière"@fr . + "logement"@fr . + "Régie foncière"@fr . + "logement"@fr . + "CPAS"@fr . + "maison de repos"@fr . + "maison de repos et de soins"@fr . + "senior"@fr . + "enfance"@fr . + "enfant"@fr . + "DEEE"@fr . + "Donnerie"@fr . + "bouchon"@fr . + "collecte"@fr . + "composter"@fr . + "don"@fr . + "donner"@fr . + "déchet"@fr . + "encombrant"@fr . + "informatique"@fr . + "offrir"@fr . + "oxfam"@fr . + "pile"@fr . + "propreté"@fr . + "proxy chimique"@fr . + "recycler"@fr . + "repair café"@fr . + "ressourcerie"@fr . + "retouche"@fr . + "réparer"@fr . + "réutiliser"@fr . + "vélo"@fr . + "vêtement"@fr . + "commerce"@fr . + "marché"@fr . + "Open Data"@fr . + "RSS"@fr . + "14-18"@fr . + "1914-1918"@fr . + "Première Guerre mondiale"@fr . + "buste"@fr . + "histoire"@fr . + "monument"@fr . + "patrimoine"@fr . + "statue"@fr . + "cimetière"@fr . + "funéraire"@fr . + "monument"@fr . + "tombe"@fr . + "tombeau"@fr . + "nichoir"@fr . + "nid"@fr . + "oiseau"@fr . + "circulation"@fr . + "route"@fr . + "trafic"@fr . + "site web"@fr . + "visite"@fr . + "site Internet"@fr . + "site web"@fr . + "site Internet"@fr . + "site web"@fr . + "site Internet"@fr . + "site web"@fr . + "site Internet"@fr . + "site web"@fr . + "site Internet"@fr . + "site web"@fr . + "site Internet"@fr . + "site web"@fr . + "autocar"@fr . + "tourisme"@fr . + "Régie foncière"@fr . + "logement"@fr . + "pharmacie"@fr . + "santé"@fr . + "natation"@fr . + "piscine"@fr . + "Région de Bruxelles-Capitale"@fr . + "pollumètre"@fr . + "pollution"@fr . + "habitant"@fr . + "population"@fr . + "administration"@fr . + "population"@fr . + "Europe"@fr . + "habitant"@fr . + "population"@fr . + "Europe"@fr . + "habitant"@fr . + "population"@fr . + "poubelle"@fr . + "propreté"@fr . + "smart city"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "femme"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "prénom"@fr . + "fille"@fr . + "féminin"@fr . + "naissance"@fr . + "prénom"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "prénom"@fr . + "garçon"@fr . + "masculin"@fr . + "prénom"@fr . + "masculin"@fr . + "prénom"@fr . + "bibliothèque"@fr . + "livre"@fr . + "prêt"@fr . + " lecture publique"@fr . + "bibliothèque"@fr . + "livre"@fr . + "prêt"@fr . + " lecture publique"@fr . + "bibliothèque"@fr . + "livre"@fr . + "prêt"@fr . + "décès"@fr . + "immigration"@fr . + "naissance"@fr . + "population"@fr . + "émigration"@fr . + "démographie"@fr . + "population"@fr . + "senior"@fr . + "quartier"@fr . + "GIAL"@fr . + "achat"@fr . + "marché"@fr . + "Cordonnerie"@fr . + "DEEE"@fr . + "bouchon"@fr . + "collecte"@fr . + "composter"@fr . + "don"@fr . + "donner"@fr . + "donnerie"@fr . + "déchet"@fr . + "encombrant"@fr . + "informatique"@fr . + "offrir"@fr . + "oxfam"@fr . + "pile"@fr . + "propreté"@fr . + "proxy chimique"@fr . + "recycler"@fr . + "repair café"@fr . + "ressourcerie"@fr . + "retouche"@fr . + "réparer"@fr . + "réutiliser"@fr . + "seconde main"@fr . + "vélo"@fr . + "vêtement"@fr . + "Facebook"@fr . + "Flickr"@fr . + "Google+"@fr . + "LinkedIn"@fr . + "Pinterest"@fr . + "Twitter"@fr . + "YouTube"@fr . + "réseau social"@fr . + "réseaux sociaux"@fr . + "bibliothèque"@fr . + "livre"@fr . + " lecture publique"@fr . + "bibliothèque"@fr . + "livre"@fr . + " lecture publique"@fr . + "bibliothèque"@fr . + "livre"@fr . + "jeu de données"@fr . + "bouger"@fr . + "collecto"@fr . + "déplacer"@fr . + "réparation"@fr . + "taxi"@fr . + "vélo"@fr . + "water bus"@fr . + "waterbus"@fr . + "aide"@fr . + "centre d'information des jeunes"@fr . + "centre de jeunes"@fr . + "information"@fr . + "jeune"@fr . + "maison de jeunes"@fr . + "musée"@fr . + "théâtre"@fr . + "ASBL"@fr . + "RES"@fr . + "SEL"@fr . + "accompagnement"@fr . + "accueil"@fr . + "aide"@fr . + "association"@fr . + "associative"@fr . + "auberge"@fr . + "comité de quatier "@fr . + "entraide"@fr . + "hebergement"@fr . + "initiative"@fr . + "initiative citoyenne"@fr . + "réseaux d'échange de savoir"@fr . + "service"@fr . + "soutien"@fr . + "système d'échange local"@fr . + "camion"@fr . + "mobilité"@fr . + "poids lourds"@fr . + "transport"@fr . + "Cambio"@fr . + "auto"@fr . + "voiture"@fr . + "Villo!"@fr . + "vélo"@fr . + "voiture"@fr . + "art"@fr . + "street art"@fr . + "Twitter"@fr . + "follower"@fr . + "sport"@fr . + "terrain de sport"@fr . + "cycliste"@fr . + "fiets"@fr . + "vélo"@fr . + "bibliothèque"@fr . + "lecture"@fr . + "livre"@fr . + "prêt"@fr . + " lecture publique"@fr . + "bibliothèque"@fr . + "lecture"@fr . + "livre"@fr . + " lecture publique"@fr . + "bibliothèque"@fr . + "lecture"@fr . + "livre"@fr . + "caméra"@fr . + "webcam"@fr . + "Administrative centre and liaison offices"@en . + "Advertising columns"@en . + "Agenda of the day"@en . + "Android application"@en . + "Applications for a planning permit"@en . + "Applications for a supplied environmental permit"@en . + "ATMs"@en . + "Bicycle boxes"@en . + "Billboards"@en . + "Geographical origin of the visits to the website of the City of Brussels (2009)"@en . + "Urinals"@en . + "Cambio stations"@en . + "Cemeteries"@en . + "Children's Homes"@en . + "Cinemas"@en . + "Cleanliness Committees"@en . + "Comic book route"@en . + "Community centres"@en . + "Consultations for children at K&G"@en . + "Consultations for children at ONE"@en . + "Counts at the crossroad Van der Weyden - Stalingrad"@en . + "Covers of the Brusseleir"@en . + "Cultural places"@en . + "Data set of the data sets"@en . + "Demographic statistics"@en . + "Dog toilets"@en . + "Dressings of Manneken-Pis"@en . + "Dutch-language libraries"@en . + "Energy consumption"@en . + "European female population in Brussels"@en . + "European institutions"@en . + "European male population in Brussels"@en . + "Expected evolution of the Brussels population by age"@en . + "Expected evolution of the Brussels seniors population"@en . + "Expected evolution of the population of Brussels (with growth)"@en . + "Facebook pages"@en . + "Female first names 2000"@en . + "Female first names 2001"@en . + "Female first names 2002"@en . + "Female first names 2003"@en . + "Female first names 2004"@en . + "Female first names 2005"@en . + "Female first names 2006"@en . + "Female first names 2007"@en . + "Female first names 2008"@en . + "Female first names 2009"@en . + "Female first names 2010"@en . + "Female first names 2011"@en . + "Female first names 2012"@en . + "Female first names (2013)"@en . + "Female first names 2013"@en . + "Female first names 2014"@en . + "Female first names 2015"@en . + "Female first names 2015"@en . + "Food trucks"@en . + "French-language libraries"@en . + "French-language schools"@en . + "Geographical origin (belgian municipalities) of the visits to the website of the City (2015)"@en . + "Geographical origin (belgian municipalities) of the visits to the website of the City (2014)"@en . + "Geographical origin (belgian municipalities) of the visits to the website of the City (2013)"@en . + "Geographical origin of the visits to the website of the City of Brussels (2011)"@en . + "Geographical origin of the visits to the website of the City of Brussels (2012)"@en . + "Geographical origin of the visits to the website of the City of Brussels (2010)"@en . + "Geographical origin of the visits to the website of the City of Brussels (2013)"@en . + "Geographical origin of the visits to the website of the City of Brussels (2014)"@en . + "Geographical origin of the visits to the website of the City of Brussels (2015)"@en . + "Glass containers"@en . + "Haren quarter"@en . + "Homes of the Property Management Agency"@en . + "Honorary citizens"@en . + "Invasive exotic plants"@en . + "Job offers"@en . + "Laeken quarter"@en . + "Male first names 2000"@en . + "Male first names 2001"@en . + "Male first names 2002"@en . + "Male first names 2003"@en . + "Male first names 2004"@en . + "Male first names 2005"@en . + "Male first names 2006"@en . + "Male first names 2007"@en . + "Male first names 2008"@en . + "Male first names 2009"@en . + "Male first names 2010"@en . + "Male first names 2011"@en . + "Male first names 2012"@en . + "Male first names (2013)"@en . + "Male first names 2013"@en . + "Male first names 2014"@en . + "Male first names 2015"@en . + "Male first names 2015"@en . + "Markets"@en . + "Mayors"@en . + "Motorized two-wheelers"@en . + "Municipal contests"@en . + "Municipal elections (number of votes)"@en . + "Municipal elections (percentages)"@en . + "Municipal elections (seats)"@en . + "Municipal News"@en . + "Municipal news"@en . + "Museums in Brussels"@en . + "Museums"@en . + "Neder-over-Heembeek quarter"@en . + "Nest boxes"@en . + "North quarter"@en . + "Northeast quarter"@en . + "Nurseries & kindergartens"@en . + "Nursing homes for elderly"@en . + "Outdoor multisports grounds"@en . + "Parking spaces for disabled"@en . + "Parkings for coaches"@en . + "Parks"@en . + "Pentagon quarter"@en . + "Petanque courts"@en . + "Pharmacies"@en . + "Pharmacies"@en . + "Playgrounds"@en . + "Police stations"@en . + "Polling stations"@en . + "Pollumeter"@en . + "Population of Brussels"@en . + "​Public computer rooms"@en . + "Public hospitals"@en . + "Public Internet access points"@en . + "Public parkings"@en . + "Public toilets"@en . + "Regional roads"@en . + "Remarkable trees"@en . + "Right bank of the canal quarter"@en . + "Government contracts"@en . + "Sectors of the local resident card"@en . + "Seniors Pavilions"@en . + "Smart waste bins"@en . + "SNCB stations"@en . + "Social networks"@en . + "Sports halls and stadiums"@en . + "STIB stops"@en . + "Street Art"@en . + "Submitted applications for an environmental permit"@en . + "Swimming pools"@en . + "Taxi stands"@en . + "Theatres"@en . + "Tourist offices"@en . + "Traffic during events & works"@en . + "Traffic volume"@en . + "Twitter followers"@en . + "Updates of the data sets"@en . + "Villo! stations & availability in real time"@en . + "Visited web pages of the City (by title) in 2013"@en . + "Visited web pages of the City (by title) in 2014"@en . + "Visited web pages of the City (by title) in 2015"@en . + "Visited web pages of the City (by URL) in 2014"@en . + "Visited web pages of the City (by URL) in 2015"@en . + "Visited web pages of the City (by URL) in 2013"@en . + "Visitors & visits City of Brussels website"@en . + "Waste"@en . + "Webcams"@en . + "Wifi"@en . + "World War I memorials"@en . + "Youth hostels"@en . + "Zen Car stations"@en . + "administration"@en . + "municipality"@en . + "office"@en . + "advertisement"@en . + "advertising"@en . + "billboard"@en . + "culture"@en . + "poster"@en . + "Android"@en . + "app"@en . + "application"@en . + "smartphone"@en . + "permit"@en . + "planning"@en . + "environment"@en . + "permit"@en . + "billboard"@en . + "culture"@en . + "information"@en . + "poster"@en . + "Internet"@en . + "website"@en . + "Cambio"@en . + "car"@en . + "cemetery"@en . + "church yard"@en . + "churchyard"@en . + "graveyard"@en . + "enfance"@en . + "enfant"@en . + "cinema"@en . + "film"@en . + "leisure"@en . + "movie"@en . + "theatre"@en . + "citizen"@en . + "cleanliness"@en . + "committee"@en . + "comic"@en . + "culture"@en . + "strip"@en . + "tourisme"@en . + "Community centre"@en . + "K&G"@en . + "child"@en . + "health"@en . + "child"@en . + "health"@en . + "mobility"@en . + "vehicle"@en . + "magazine"@en . + "revue"@en . + "culture"@en . + "museum"@en . + "theatre"@en . + "dataset"@en . + "cleanliness"@en . + "dog"@en . + "toilet"@en . + "Manneken-Pis"@en . + "folklore"@en . + "book"@en . + "library"@en . + "electricity"@en . + "energy"@en . + "gas"@en . + "water"@en . + "Europe"@en . + "population"@en . + "woman"@en . + "Europe"@en . + "commission"@en . + "institution"@en . + "parliament"@en . + "Europe"@en . + "man"@en . + "population"@en . + "age"@en . + "demography"@en . + "population"@en . + "demography"@en . + "population"@en . + "senior"@en . + "birth"@en . + "death"@en . + "emigration"@en . + "immigration"@en . + "migration"@en . + "population"@en . + "Facebook"@en . + "social networks"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "first name"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "girl"@en . + "female"@en . + "first name"@en . + "voornaam"@en . + "female"@en . + "first name"@en . + "book"@en . + "culture"@en . + "library"@en . + "reading"@en . + "school"@en . + "container"@en . + "glass"@en . + "Haren"@en . + "district"@en . + "quarter"@en . + "town"@en . + "Régie foncière"@en . + "environment"@en . + "nature"@en . + "plant"@en . + "employment"@en . + "job"@en . + "Laeken"@en . + "district"@en . + "quarter"@en . + "town"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "first name"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "birth"@en . + "first name"@en . + "male"@en . + "first name"@en . + "male"@en . + "commerce"@en . + "market"@en . + "moto"@en . + "parking"@en . + "contest"@en . + "election"@en . + "conseil communal"@en . + "élection"@en . + "election"@en . + "information"@en . + "news"@en . + "news"@en . + "culture"@en . + "heritage"@en . + "museum"@en . + "Neder-Over-Heembeek"@en . + "district"@en . + "quarter"@en . + "town"@en . + "bird"@en . + "birdhouse"@en . + "nest"@en . + "nestbox"@en . + "district"@en . + "quarter"@en . + "district"@en . + "quarter"@en . + "kindergarten"@en . + "nursery"@en . + "CPAS"@en . + "rest home"@en . + "retirement"@en . + "senior"@en . + "ground"@en . + "multisport"@en . + "sport"@en . + "accessibility"@en . + "disabled"@en . + "handicap"@en . + "autocar"@en . + "bus"@en . + "parking"@en . + "tourisme"@en . + "green"@en . + "nature"@en . + "park"@en . + "public garden"@en . + "Pentagon"@en . + "district"@en . + "quarter"@en . + "health"@en . + "pharmacy"@en . + "health"@en . + "pharmacy"@en . + "child"@en . + "playground"@en . + "police"@en . + "safety"@en . + "security"@en . + "election"@en . + "poll"@en . + "vote"@en . + "Région de Bruxelles-Capitale"@en . + "pollumètre"@en . + "pollution"@en . + "administration"@en . + "demography"@en . + "population"@en . + "ICT"@en . + "computer"@en . + "health"@en . + "hospital"@en . + "ICT"@en . + "Internet"@en . + "PIAP"@en . + "car"@en . + "park"@en . + "parking"@en . + "tree"@en . + "canal"@en . + "district"@en . + "quarter"@en . + "contract"@en . + . + "car"@en . + "mobility"@en . + "parking"@en . + "senior"@en . + . + "smart city"@en . + "waste"@en . + "waste bin"@en . + "SNCB"@en . + "station"@en . + "train"@en . + "Facebook"@en . + "Flickr"@en . + "Google+"@en . + "LinkedIn"@en . + "Pinterest"@en . + "Twitter"@en . + "YouTube"@en . + "social network"@en . + "STIB"@en . + "bus"@en . + "metro"@en . + "station"@en . + "stop"@en . + "tram"@en . + . + "Street Art"@en . + "art"@en . + "street art"@en . + "environment"@en . + "permit"@en . + "swimming pool"@en . + "taxi"@en . + . + "theater"@en . + "theatre"@en . + "information"@en . + "touriste"@en . + "traffic"@en . + "road"@en . + "traffic"@en . + "Twitter"@en . + "follower"@en . + "Open Data"@en . + "RSS"@en . + "Villo!"@en . + "bike"@en . + "Internet"@en . + "visit"@en . + "visitor"@en . + "web"@en . + "website"@en . + . + "cleanliness"@en . + "waste"@en . + "webcam"@en . + "Internet"@en . + "wi-fi"@en . + "wifi"@en . + . + "hostel"@en . + "tourism"@en . + "tourist"@en . + "youth"@en . + "auto"@en . + "car"@en . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Bourgmestres de la Ville de Bruxelles avec la mention des dates de naissance et de décès, les dates du début de mandat et d''entrée en fonction de bourgmestre (ff), les dates des arrêtés royaux de nomination, des installations, des fins de fonction ou démissions.\n"@fr . + "Consommation énergétique de la Ville de Bruxelles depuis 2008.\n"@fr . + "Sur la base de la population bruxelloise en 2012, une projection de l'évolution de la population de la Ville de Bruxelles jusqu'en 2024 par tranches d'âge.\n"@fr . + "Statistiques relatives aux naissances, mariages, divorces et séparations, décès à la Ville de Bruxelles\n"@fr . + "Fontaines d'eau potable sur le territoire de la Ville de Bruxelles recensées par Infirmiers de Rue ASBL.\n"@fr . + "Localisation des emplacements pour les food trucks sélectionnés par la Ville de Bruxelles.\n"@fr . + "Prénoms des hommes en Région de Bruxelles-Capitale en 2013.\n"@fr . + "Situation du logement moyen de la Régie foncière au 31 décembre 2014.\n"@fr . + "Visites et visiteurs du site web de la Ville de Bruxelles (www.bruxelles.be).\n"@fr . + "Localisation des pharmacies sur le territoire de la Ville de Bruxelles à partir d'OpenStreetMap (OSM).\n"@fr . + "Localisation des centres de jeunes du réseau géré par Bravvo et s'adressant aux 12-18 ans.\n"@fr . + "Cimetières de la Ville de Bruxelles avec la localisation de l'entrée, les jours et heures d'ouverture et les coordonnées téléphoniques.\n"@fr . + "Personnalités auxquelles la Ville de Bruxelles a décerné le titre de citoyen d'honneur (avec mention de la date de la remise de la distinction).\n"@fr . + "Etat des collections (par types de documents) des bibliothèques francophones de la Ville de Bruxelles au 31 décembre 2013.\n"@fr . + "Etat des collections (par types de documents) des bibliothèques francophones de la Ville de Bruxelles au 31 décembre 2014.\n"@fr . + "Etat des collections (par types de documents) des bibliothèques francophones de la Ville de Bruxelles au 31 décembre 2015.\n"@fr . + "Informations sur les Comités Propreté de la Ville de Bruxelles (nom du comité, de la structure d'accueil, adresse).\n"@fr . + "Comptages manuels des mouvements (de véhicules légers, de poids lourds, de bus ou autocars, de motos, de vélos ou en équivalents véhicules personnels) au carrefour de la rue Roger van der Weyden et de l'avenue de Stalingrad.\n"@fr . + "Localisation des consultations pour enfants (0-3 ans) de K&G (Kind & Gezin) sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des consultations pour enfants (0-6 ans) de l'ONE (Office de la Naissance et de l'Enfance - francophone) sur le territoire de la Ville de Bruxelles.\n"@fr . + "L'inventaire des costumes de Manneken-Pis conservés au Musée de la Ville de Bruxelles avec date ou période de remise du costume.\n"@fr . + "Couvertures du magazine de la Ville de Bruxelles, \"Le Brusseleir\".\n"@fr . + "Localisation des crèches & prégardiennats de la Ville de Bruxelles avec mention de l'organisme de reconnaissance (ONE, Kind & Gezin), du nombre de place, des heures d'ouverture.\n"@fr . + "Nombres de tonnes de déchets évacués par le service Propreté publique de la Ville de Bruxelles pour être versés en décharge publique, recyclés ou incinérés.\n"@fr . + "Les demandes de permis d'environnement délivrés par le département Urbanisme de la Ville de Bruxelles depuis 2010.\nIl est à noter que les permis délivrés concernent souvent des demandes introduites au cours des années précédentes. \nLes chiffres retenus ici concernent les permis délivrés résultant de demandes introduites au cours des 3 années précédentes.\n"@fr . + "Les demandes de permis d'environnement introduits auprès du département Urbanisme de la Ville de Bruxelles depuis 2010.\nLes permis délivrés concernent souvent des demandes introduites au cours des années précédentes. Les chiffres retenus ici concernent les permis délivrés résultant de demandes introduites au cours des 3 années précédentes.\n"@fr . + "Les demandes de permis d'urbanisme à la Ville de Bruxelles par destination du bien depuis 2003.\n"@fr . + "Emplacements pour le stationnement des deux-roues motorisés sur le territoire de la Ville de Bruxelles.\n"@fr . + "Etablissement d'enseignement artistique francophone du réseau scolaire de la Ville de Bruxelles.\n"@fr . + "Ecoles de promotion sociale francophones du réseau scolaire de la Ville de Bruxelles.\n"@fr . + "Ecoles maternelles francophones du réseau scolaire de la Ville de Bruxelles.\n"@fr . + "Localisation des écoles néerlandophones de la Ville de Bruxelles.\n"@fr . + "Enseignement primaire francophone de la Ville de Bruxelles.\n"@fr . + "Ecoles secondaires francophones du réseau scolaire de la Ville de Bruxelles.\n"@fr . + "Enseignement spécialisé maternel et primaire francophone de la Ville de Bruxelles.\n"@fr . + "Ecoles supérieures francophones du réseau scolaire de la Ville de Bruxelles.\n"@fr . + "Résultat des élections communales à Bruxelles depuis 2000 (nombre de votes par liste).\n"@fr . + "Résultat des élections communales à Bruxelles depuis 2000 (pourcentages des votes par liste).\n"@fr . + "Résultat des élections communales à Bruxelles depuis 2000 (nombre de sièges par liste)\n"@fr . + "Localisation des espaces publics numériques (EPN) de la Ville de Bruxelles.\n"@fr . + "Evénements trafic (incidents, files...), ainsi que les chantiers perturbants en Région de Bruxelles-Capitale.\n"@fr . + "Fietsgemarkeerde fietspadenVélo: pistes cyclables marquées\n\n\n"@fr . + "Fiets: verhoogde fietspaden - Vélo : pistes cyclables surélevées\n\n\n"@fr . + "Fiets: voetgangerszone-fietszoneVélo : piétonnier - zone cyclable\n\n"@fr . + "Localisation des fontaines d'eau potable, WC et urinoirs gratuits.\n"@fr . + "Gares de chemins de fer (SNCB) avec les gares de départ et de destination.\n"@fr . + "Guérites et bulles mises en place par Les Petits Riens pour la collecte de textiles usagés (vêtements en bon état).\n"@fr . + "Jours et heures d'habillage de Manneken-Pis (avec le nom du costume, sa référence, le contexte de cet habillage).\n"@fr . + "Hôpitaux publics (membres du réseau IRIS) sur le territoire de la Ville de Bruxelles.\n"@fr . + "Situation du logement contrats de quartier de la Régie foncière au 31 décembre 2014.\n"@fr . + "Localisation et répartition par quartier des logements de la Régie foncière avec le nombre d'unités par logement.\n"@fr . + "Situation du logement moyen (avec conditions de revenus) de la Régie foncière au 31 décembre 2014\n"@fr . + "Localisations et informations sur les maisons de repos (MR) et les maisons de repos et de soins (MRS) du Centre Public d'Action Sociale (CPAS de Bruxelles.\n"@fr . + "Localisation des Maisons des Enfants à destination des jeunes Bruxellois (6-12 ans).\n"@fr . + "Localisation des différents lieux qui promeuvent une alimentation durable  (restaurants, marchés, potagers, fermes, magasins, Gasap, etc.) sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des marchés de plein air sur le territoire de la Ville de Bruxelles.\n"@fr . + "Flux RSS avec la mise à jour des jeux de données de la\nplateforme opendata.bruxelles.be : opendata.bruxelles.be/api/datasets/1.0/search?format=rss&sort;=modified\n"@fr . + "Monuments de la Ville de Bruxelles commémorant la Première Guerre mondiale.\n"@fr . + "Quelques monuments et tombeaux du cimetière de Laeken à Bruxelles.\n"@fr . + "Localisation des musées présents sur le territoire de la Ville de Bruxelles (y compris les musées de la Ville de Bruxelles).\n"@fr . + "Localisation des nichoirs installés par la Ville de Bruxelles.\n"@fr . + "Niveaux de service de trafic sur certaines parties du réseau routier de la Région de Bruxelles-Capitale.\n"@fr . + "Villes (communes) d'origine des visiteurs du site web de la Ville de Bruxelles en provenance de Belgique, déterminées par leur adresse iP. Source: Google Analytics.\n"@fr . + "Villes (communes) d'origine des visiteurs du site web de la Ville de Bruxelles en provenance de Belgique, déterminées par leur adresse iP. Source: Google Analytics.\n"@fr . + "Villes (communes) d'origine des visiteurs du site web de la Ville de Bruxelles en provenance de Belgique, déterminées par leur adresse iP. Source: Google Analytics.\n"@fr . + "Pays et territoires des visiteurs du site web de la Ville de Bruxelles (2009) déterminés par la zone géographique associée à leur adresse IP. Source: Google Analytics.\n"@fr . + "Pays et territoires des visiteurs du site web de la Ville de Bruxelles (2010) déterminés par la zone géographique associée à leur adresse IP. Source: Google Analytics.\n"@fr . + "Pays et territoires des visiteurs du site web de la Ville de Bruxelles (2011) déterminés par la zone géographique associée à leur adresse IP. Source: Google Analytics.\n"@fr . + "Pays et territoires des visiteurs du site web de la Ville de Bruxelles déterminés par la zone géographique associée à leur adresse IP (2012). Source: Google Analytics.\n"@fr . + "Pays et territoires des visiteurs du site web de la Ville de Bruxelles (2013) déterminés par la zone géographique associée à leur adresse IP. Source: Google Analytics.\n"@fr . + "Pays et territoires des visiteurs du site web de la Ville de Bruxelles (2014) déterminés par la zone géographique associée à leur adresse IP. Source: Google Analytics.\n"@fr . + "Statistiques de fréquentation des pages du site web de la Ville de Bruxelles (en 2014) avec le titre de la page, le nombre de pages vues, le nombre de consultations uniques, le temps moyen passé sur la page. Source: Google Analytics.\nAttention: certaines pages du site web de la Ville ont pu être rendues invisibles.\n"@fr . + "Statistiques de fréquentation des pages du site web de la Ville de Bruxelles (en 2013) avec le titre de la page, le nombre de pages vues, le nombre de consultations uniques, le temps moyen passé sur la page. Source: Google Analytics.\nAttention: certaines pages du site web de la Ville ont pu être rendues invisibles.\n"@fr . + "Statistiques de fréquentation des pages du site web de la Ville de Bruxelles (en 2015) avec le titre de la page, le nombre de pages vues, le nombre de consultations uniques, le temps moyen passé sur la page. Source: Google Analytics.\nAttention: certaines pages du site web de la Ville ont pu être rendues invisibles.\n"@fr . + "Statistiques de fréquentation des pages du site web de la Ville de Bruxelles avec l'adresse de la page (suivant www.bruxelles.be, le nombre de pages vues, le nombre de consultations uniques, le temps moyen passé sur la page. Source: Google Analytics.\nChaque page du site web de la Ville de Bruxelles est identifiée par 4 chiffres figurant dans son adresse. Une recherche sur ces 4 chiffres permet d'obtenir les statistiques relatives à la page considérée.\n"@fr . + "Statistiques de fréquentation des pages du site web de la Ville de Bruxelles (en 2014) avec l'adresse de la page (suivant www.bruxelles.be, le nombre de pages vues, le nombre de consultations uniques, le temps moyen passé sur la page. Source: Google Analytics.\nChaque page du site web de la Ville de Bruxelles est identifiée par 4 chiffres figurant dans son adresse. Une recherche sur ces 4 chiffres permet d'obtenir les statistiques relatives à la page considérée.\n"@fr . + "Statistiques de fréquentation des pages du site web de la Ville de Bruxelles (en 2015) avec l'adresse de la page (suivant www.bruxelles.be, le nombre de pages vues, le nombre de consultations uniques). Source: Google Analytics.\nChaque page du site web de la Ville de Bruxelles est identifiée par 4 chiffres figurant dans son adresse. Une recherche sur ces 4 chiffres permet d'obtenir les statistiques relatives à la page considérée.\n"@fr . + "Localisation des emplacements de stationnement pour les autocars de tourisme sur le territoire de la Ville de Bruxelles.\n"@fr . + "Patrimoine (logements) de la Régie foncière au 31 décembre 2014\n"@fr . + "Localisation et horaires des Pavillons Seniors de la Ville de Bruxelles.\n"@fr . + "Pharmacies sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des piscines de la Ville de Bruxelles.\n"@fr . + "Pollumètre de la Région de Bruxelles-Capitale permettant de suivre en permanence l'évolution de la qualité de l'air avec un indice global calculé en rassemblant les données mesurées par toutes les stations pour l'ozone (O3), le dioxyde d'azote (NO2), le dioxyde de soufre (SO2) et les poussières (PM10 ): www.ibgebim.be/Pollumetre/dynamic_index.txt. \nBruxelles Environnement demande d'effectuer la récupération des données en rapatriant l'information périodiquement (à l'heure 35) et non en venant la chercher à chaque ouverture de la page du site contenant le pollumètre).\n"@fr . + "Population résidente de droit à Bruxelles au 1er janvier, par année.\n"@fr . + "Chiffres totaux (depuis 1921) de la population de la Ville de Bruxelles tels qu'enregistrés par le département Démographie.\n"@fr . + "Evolution de la population originaire des différents pays d'Europe (hommes) à Bruxelles (Ville de Bruxelles) pour la période 2009-2014. \n"@fr . + "Evolution de la population originaire des différents pays d'Europe (femmes à Bruxelles (Ville de Bruxelles) pour la période 2009-2014. \n"@fr . + "Poubelles intelligentes de la Ville de Bruxelles.\n"@fr . + "Plans particuliers d'affectation du sol (PPAS) de la Ville de Bruxelles\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2000.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2001.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2002.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2003.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2004.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2005.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2006.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2007.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2008.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2009.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2010.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2011.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2012.\n"@fr . + "Prénoms des femmes en Région de Bruxelles-Capitale en 2013.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2013.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2014.\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2015 (jusqu'au 27 octobre).\n"@fr . + "Prénoms féminins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2015.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2000.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2001.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2002.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2003.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2004.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2005.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2006.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2007.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2008.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2009.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2010.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2011.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2012.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2013.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2014.\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2015 (jusqu'au 27 octobre).\n"@fr . + "Prénoms masculins enregistrés auprès de la Ville de Bruxelles au cours de l'année 2015.\n"@fr . + "Statistiques des prêts dans les bibliothèques francophones de la Ville de Bruxelles en 2013.\n"@fr . + "Statistiques des prêts dans les bibliothèques francophones de la Ville de Bruxelles en 2014.\n"@fr . + "Statistiques des prêts dans les bibliothèques francophones de la Ville de Bruxelles en 2015.\n"@fr . + "Sur la base de la population bruxelloise en 2012, une projection de l'évolution de la population de la Ville de Bruxelles jusqu'en 2024, tenant compte du solde naturel (naissances - décès) et du solde migratoire (immigration - émigration).\n"@fr . + "Sur la base de la population bruxelloise en 2012, une\nprojection de l'évolution de la population de la Ville de Bruxelles jusqu'en\n2024 par tranches d'âge.\n"@fr . + "Carte des quartiers de Bruxelles (Pentagone, Haren, Laeken, Louise, Neder-Over-Heembeek, Nord, Nord-Est, Rive droite du canal).\n"@fr . + "Références de la centrale d'achat et de marchés informatiques de GIAL, centre informatique de la Ville de Bruxelles.\n"@fr . + "Localisation des différents lieux de collecte d'objets réutilisables (vêtements, informatique, etc.); de déchets ( d'encombrants, DEE,  produits chimiques, etc.); de composts; de ventes de seconde main et de réparation de toutes sortes sur le territoire de la Ville de Bruxelles.\n"@fr . + "Cadastre (non officiel) des comptes et pages de la Ville de Bruxelles (Ville, départements, services, bibliothèques, écoles, autres institutions, ASBL, actions, évènements) sur les réseaux sociaux (Facebook, Twitter, Google+, LinkedIn, Pinterest, Flickr, YouTube).\n"@fr . + "Le classement des réservations de documents dans les bibliothèques francophones de la Ville de Bruxelles en 2013.\n"@fr . + "Le classement des réservations de documents dans les bibliothèques francophones de la Ville de Bruxelles en 2014.\n"@fr . + "Le classement des réservations de documents dans les bibliothèques francophones de la Ville de Bruxelles en 2015.\n"@fr . + "Jeu de données des jeux de données de la plateforme Open Data de la Ville de Bruxelles.\n"@fr . + "Localisation des différents arrêts collecto, waterbus ainsi que les lieux de réparation de vélos sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des maisons de jeunes, centres de jeunes et centre d'informations des jeunes présents sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des différents comités de quatier, ASBL, réseaux d'échange de savoir, systémes d'échange locaux, initiatives citoyennes et accompagnements durables sur le territoire de la Ville de Bruxelles. \n"@fr . + "Localisation des stations de voitures partagées Cambio.\n"@fr . + "Localisation des stations Villo! (vélo partagé) en Région de Bruxelles-Capitale avec l'indication des disponibilités (vélos, emplacements) en temps réel.\n"@fr . + "Localisation des stations de voitures partagées électriques Zen Car sur le territoire de la Ville de Bruxelles.\n"@fr . + "Initiatives Street Art soutenues par la Ville de Bruxelles.\n"@fr . + "\n Suiveurs (followers) des comptes Twitter Ville de Bruxelles en français, néerlandais et anglais.\n"@fr . + "Terrains de pétanque de la Ville de Bruxelles.\n"@fr . + "Terrains multisports extérieurs de la Ville de Bruxelles (avec adresse et description de l'équipement).\n"@fr . + "Fiets: busbanen/fietsstrokenVélo : bandes bus/bandes cyclables\n\n\n"@fr . + "Classement des prêts de la Bibliothèque principale de Bruxelles 1 (Riches Claires) en 2013.\n"@fr . + "Classement par bibliothèque des livres les plus empruntés dans les bibliothèques de la Ville de Bruxelles en 2014.\n"@fr . + "Classement par bibliothèque des livres les plus empruntés dans les bibliothèques de la Ville de Bruxelles en 2015.\n"@fr . + "Voiries gérées par la Région de Bruxelles-Capitale. Certaines voiries (chemins, rues, chaussées, avenues, boulevards, places…) sont situées sur le territoire de la Ville de Bruxelles (BXL); d'autres peuvent le jouxter.\n"@fr . + "Localisation des webcams permanentes accessibles depuis le site web de la Ville de Bruxelles.\n"@fr . + "Location of the Administrative centre and liaison offices of the City of Brussels.\n"@en . + "Location of advertising columns of the City of Brussels.\n"@en . + "Announcement of the activities of the day published on the website of the City of Brussels.\n"@en . + "Statistics of the number of downloads of the Android\napplication of the City of Brussels. A new version of the Android application of the City of Brussels was presented in March 2015.\n"@en . + "Applications for a planning permit by the City of\nBrussels by destination of the property and since 2003. \n"@en . + "Applications for a supplied environmental permit provided\nby the City of Brussels by destination of the property and since 2003. \n"@en . + "Location of ATMS on the territory of the City of Brussels\n"@en . + "Location of bicycle boxes on the territory of the City of Brussels.\n"@en . + "Location of billboards of the City of Brussels\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2011), determined by the geographical area of their IP address.. Source: Google Analytics."@en . + "Location of public urinals (toilets for men) on the territory of the City of Brussels.\n"@en . + "Location of the Cambio carsharing stations. \n"@en . + "Cemeteries of the City of Brussels with the location of the entrance, opening days and hours and telephone numbers.\n"@en . + "Location of the Children's Homes (Maisons des Enfants) intended for young Brussels (6 to 12 years).\n"@en . + "Location of cinemas on the territory of the City of Brussels.\n"@en . + "Information on the Cleanliness committees of the City of Brussels (name of the Cleanliness committee, name of the meeting point, address).\n"@en . + "Location of comic book walls of the City of Brussels (with characters and authors).\n"@en . + "Localisation of the community centres of the CPAS of Brussels\n"@en . + "Location of consultations for children (0-3 years) at the Dutch-language K&G (Kind & Gezin) on the territory of the City of Brussels.\n"@en . + "Location of consultations for children (0-6 years) at the French-language ONE (Office de la Naissance et de l'Enfance) on the territory of the City of Brussels.\n"@en . + "Manual counts of movements (light vehicles, trucks, buses or coaches, motorcycles, bicycles or equivalent personal vehicles) at the intersection of the Rue Roger van der Weyden and the Avenue de Stalingrad.\n"@en . + "Covers of the magazine of the City of Brussels, the ‘Brusseleir’.\n"@en . + "Location of the cultural places of the City of Brussels.\n"@en . + "Data set of the data sets of the Open Data platform of\nthe City of Brussels.\n"@en . + "Statistics on births, marriages, divorces and separations, deaths in the City of Brussels.\n"@en . + "Location of dog toilets on the territory of the City of Brussels.\n"@en . + "Days and hours of the dressings of Manneken-Pis (with the name of the costume, the reference and the context of the dressing). \n"@en . + "Location of Dutch-language libraries of the City of Brussels.\n"@en . + "Energy consumption of the City of Brussels since 2008. \n"@en . + "Evolution of the population from the different countries of Europe (women) for the 2009-2014 period.\n"@en . + "Location of the European institutions on the territory of the City of Brussels.\n"@en . + "Evolution of the population from the different countries of Europe (men) for the 2009-2014 period.\n"@en . + "On the basis of the population of Brussels in 2012, a\nprojection of the evolution of the population of the City until 2024 by age\ngroup.\n"@en . + "On the basis of the population of Brussels in 2012, a\nprojection of the evolution of the senior population in Brussels until 2024.\n"@en . + "On the basis of the population of Brussels in 2012, a\nprojection of the evolution of the population of the City until 2024, taking\ninto account the natural increase (births - deaths) and net migration (immigration\n- emigration).\n"@en . + "\n Statistics of the Facebook pages City of Brussels (number of 'likes') in French, Dutch and English.\n"@en . + "Female first names registered at the City of Brussels in 2000.\n"@en . + "Female first names registered at the City of Brussels in 2001.\n"@en . + "Female first names registered at the City of Brussels in 2002.\n"@en . + "Female first names registered at the City of Brussels in 2003.\n"@en . + "Female first names registered at the City of Brussels in 2004.\n"@en . + "Female first names registered at the City of Brussels in 2005.\n"@en . + "Female first names registered at the City of Brussels in 2006.\n"@en . + "Female first names registered at the City of Brussels in 2007.\n"@en . + "Female first names registered at the City of Brussels in 2008.\n"@en . + "Female first names registered at the City of Brussels in 2009.\n"@en . + "Female first names registered at the City of Brussels in 2010.\n"@en . + "Female first names registered at the City of Brussels in 2011.\n"@en . + "Female first names registered at the City of Brussels in 2012.\n"@en . + "Names of women in the Brussels Capital Region in 2013.\n"@en . + "Female first names registered at the City of Brussels in 2013.\n"@en . + "Female first names registered at the City of Brussels in 2014.\n"@en . + "Female first names registered at the City of Brussels in 2015 (until 27 October).\n"@en . + "Female first names registered at the City of Brussels in 2015.\n"@en . + "Location of the food trucks selected by the City of Brussels.\n"@en . + "Location of French-language libraries of the City of Brussels and social networks.\n"@en . + "Location of French-language schools of the City of Brussels.\n"@en . + "Cities (municipalities) of origin of visitors to the website of the City of Brussels from Belgium, determined by their IP address. Source: Google Analytics.\n"@en . + "Cities (municipalities) of origin of visitors to the website of the City of Brussels from Belgium, determined by their IP address. Source: Google Analytics.\n"@en . + "Cities (municipalities) of origin of visitors to the website of the City of Brussels from Belgium, determined by their IP address. Source: Google Analytics.\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2011), determined by the geographical area of their IP address. Source: Google Analytics.\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2012), determined by the geographical area of their IP address. Source: Google Analytics.\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2010), determined by the geographical area of their IP address. Source: Google Analytics.\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2013), determined by the geographical area of their IP address. Source: Google Analytics.\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2014), determined by the geographical area of their IP address. Source: Google Analytics.\n"@en . + "Countries and territories of the visitors of the website of the City of Brussels (2015), determined by the geographical area of their IP address. Source: Google Analytics.\n"@en . + "Location of glass containers on the territory of the City of Brussels.\n"@en . + "(Old town of) Haren quarter of Brussels.\n"@en . + "Location and distribution by quarter of the homes of the Property Management Agency with the number of units per home.\n"@en . + "Eminent people to whom the City of Brussels has awarded the title of honorary citizen (indicating the name or pseudonym, the function, the date of the granting of the title).\n"@en . + "Location of invasive exotic plants on the territory of the City of Brussels.\n"@en . + "RSS feeds of the job offers on the website of the City of Brussels: http://www.brussels.be/artdet.cfm?function=rss&id=6308\n"@en . + "(Old town of) Laeken quarter of Brussels.\n"@en . + "Male first names registered at the City of Brussels in 2000.\n"@en . + "Male first names registered at the City of Brussels in 2001.\n"@en . + "Male first names registered at the City of Brussels in 2002.\n"@en . + "Male first names registered at the City of Brussels in 2003.\n"@en . + "Male first names registered at the City of Brussels in 2004.\n"@en . + "Male first names registered at the City of Brussels in 2005.\n"@en . + "Male first names registered at the City of Brussels in 2006.\n"@en . + "Male first names registered at the City of Brussels in 2007.\n"@en . + "Male first names registered at the City of Brussels in 2008.\n"@en . + "Male first names registered at the City of Brussels in 2009.\n"@en . + "Male first names registered at the City of Brussels in 2010.\n"@en . + "Male first names registered at the City of Brussels in 2011.\n"@en . + "Male first names registered at the City of Brussels in 2012.\n"@en . + "Names of men in the Brussels Capital Region in 2013.\n"@en . + "Male first names registered at the City of Brussels in 2013.\n"@en . + "Male first names registered at the City of Brussels in 2014.\n"@en . + "Male first names registered at the City of Brussels in 2015 (until 27 October).\n"@en . + "Male first names registered at the City of Brussels in 2015.\n"@en . + "Location of the markets on the territory of the City of Brussels.\n"@en . + "Mayors of the City of Brussels, with indication of the date of birth and date of death, the date of the start of term of the Mayor, the dates of the royal decrees for the appointment, the installation, the end of the term or the dismissal.\n"@en . + "Parking spaces for motorized two-wheelers in the City of Brussels\n"@en . + "RSS feeds of the municipal contests on the website of the City of Brussels: http://www.brussel.be/artdet.cfm?function=rss&id=6314\n"@en . + "Results of the local elections in Brussels since 2000\n(number of votes per list). \n"@en . + "Results of the local elections in Brussels since 2000\n(number of votes per list). \n"@en . + "Results of the local elections in Brussels since 2000\n(number of seats per list).\n"@en . + "RSS feeds of the municipal news on the website of the City of Brussels: http://www.brussels.be/artdet.cfm?function=rss&id=4694\n"@en . + "News published on the website of the City of Brussels.\n"@en . + "Location of the museums on the territory of the City of Brussels (including the museums of the City of Brussels).\n"@en . + "Location of the museums of the City of Brussels\n"@en . + "(Old town of) Neder-Over-Heembeek (NOH) quarter of Brussels.\n"@en . + "Location of the nest boxes installed at the City of Brussels.\n"@en . + "North quarter of Brussels.\n"@en . + "Northeast quarter of Brussels.\n"@en . + "Location of the nurseries and kindergartens of the\nCity specifying the accrediting institution (ONE, Kind & Gezin), the number\nof places, opening hours.\n"@en . + "Location and information of the rest homes (MR) and retirement and nursing homes (MRS)of the Public Welfare Centre (CPAS) of Brussels.\n"@en . + "Outdoor multisports grounds of the City of Brussels (with address and description of the equipment).\n"@en . + "\n Location of parking spaces for disabled on the territory of the City of Brussels.\n\n"@en . + "Location of the parking zones for coaches (tourist buses) on the territory of the City of Brussels.\n"@en . + "Location of the parks and gardens on the territory of the City of Brussels.\n"@en . + "Pentagon quarter (city centre) of Brussels.\n"@en . + "Petanque courts of the City of Brussels.\n"@en . + "Location of the pharmacies on the territory of the City of Brussels from OpenStreetMap (OSM).\n"@en . + "Pharmacies on the territory of the City of Brussels.\n"@en . + "Playgrounds of the City of Brussels indicating the age category and description of the playsets.\n"@en . + "Location of the police offices on the territory of the City of Brussels.\n"@en . + "Location of the polling stations on the territory of the\nCity of Brussels.\n"@en . + "The Pollumeter of the Brussels Capital Region follows\nthe evolution of the air quality. The overall index is calculated on the basis\nof measurements at all places for oxygen (O3), nitrogen dioxide (NO2), sulfur\ndioxide (SO2) and particulate matter (PM10): www.ibgebim.be/Pollumetre/dynamic_index.txt.\nBruxelles Environnement - Leefmilieu Brussel requests\nto periodically retrieve the data (at 35 past the hour), and not to search each\ntime at a web page with the Pollumeter.\n"@en . + "Total number (since 1921) of the Brussels population\nas recorded by the Department of Demography. \n"@en . + "Public computer rooms (PCR) of the City of Brussels.\n"@en . + "Public hospitals (members of the IRIS network) on the territory of the City of Brussels.\n"@en . + "Location of the Public Internet access points of the City of Brussels.\n"@en . + "Location of public paying parking lots on the territory of the City of Brussels. (source: http://opendatastore.brussels/en )\n"@en . + "Location of the public toilets on the territory of the City of Brussels.\n"@en . + "Roads managed by the Brussels Capital Region. Some roads (streets, highways, avenues, boulevards, squares,...) are located on the territory of the City; other roads may be adjacent.\n"@en . + "Location of the remarkable trees on the territory of the City of Brussels.\n"@en . + "Right bank of the canal quarter of Brussels.\n"@en . + "RSS feeds of the government contracts on the website of the City of Brussels: www.brussels.be/artdet.cfm?function=rss&id=5477\n"@en . + "List of street names of each sector of the local resident (parking) card on the territory of the City of Brussels.\n"@en . + "Location of the Seniors Pavilions of the City.\n"@en . + "Smart waste bins on the territory of the City of Brussels.\n"@en . + "Train stations (SNCB - NMBS) with departure and arrival stations.\n"@en . + "Register (unofficial) of accounts and pages of the City of Brussels (City, departments, services, libraries, schools, other institutions, non-profit associations, actions, events) on social networks (Facebook, Twitter, Google+, LinkedIn, Pinterest, Flickr, YouTube).\n"@en . + "Location of sports halls and stadiums of the City of Brussels.\n"@en . + "Location of the STIB stops (stations).\n"@en . + "Street Art supported by the City of Brussels.\n"@en . + "Submitted applications for environmental permits\nby the City of Brussels by destination of the property and since 2003.\n"@en . + "Location of the swimming pools of the City of Brussels.\n"@en . + "Location of the Taxi stands on the terrotory of the Region of Brussels Capital. (Source:  http://opendatastore.brussels/en/)\n"@en . + "Location of theatres on the territory of the City of Brussels\n"@en . + "Location of the tourist offices on the territory of the City of Brussels.\n"@en . + "Traffic (incidents,\ncongestion,...) , as well as disturbing works in the Brussels Capital Region.\n"@en . + "Traffic intensity at different parts of the regional road network.\n"@en . + "\n Followers for Twitter accounts City of Brussels in French, Dutch or English.\n"@en . + "RSS feed with updated data sets of the\nopendata.brussels.be platform :opendata.brussels.be/api/datasets/1.0/search?format=rss&sort;=modified\n"@en . + "Location of the Villo! stations in the Brussels Capital Region with indication of the availability (bikes, bike stands) in real time.\n"@en . + "Visitor statistics of the pages of the website of the City of Brussels with the title of the page, the number of pages viewed, the number of unique visits, average time spent on the page. Source: Google Analytics.\n"@en . + "Visitor statistics of the pages of the website of the City of Brussels (2014) with the title of the page, the number of pages viewed, the number of unique visits, average time spent on the page. Source: Google Analytics.\n"@en . + "Visitor statistics of the pages of the website of the City of Brussels (2015) by title, the number of pages viewed, the number of unique visits. Source: Google Analytics.\n"@en . + "Visitor statistics of the pages of the website of the City of Brussels with the address of the page (after www.brussels.be), the number of pages viewed, the number of unique visits. Source: Google Analytics.\n"@en . + "Visitor statistics of the pages of the website of the City of Brussels with the address of the page (after www.brussels.be), the number of pages viewed, the number of unique visits, average time spent on the page. Source: Google Analytics.\n"@en . + "Visitor statistics of the pages of the website of the City of Brussels with the address of the page (after www.brussels.be), the number of pages viewed, the number of unique visits, average time spent on the page. Source: Google Analytics.\n"@en . + "Number of visitors ands visits City of Brussels website.\n"@en . + "Tons of waste removed by the Cleanliness Service of the City of Brussels\nto be burned, landfilled or recycled."@en . + "Location of the permanent webcams that are accessible on the website of the City of Brussels.\n"@en . + "Location of free wifi on the territory of the City of Brussels\n"@en . + "Monuments of the City of Brussels commemorating World War I.\n"@en . + "Location of the youth hotels on the territory of the City of Brussels.\n"@en . + "Location of the stations of the Zen Car electric carsharing system on the territory of the City of Brussels.\n"@en . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aankledingen-van-manneken-pis" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aankledingen-van-manneken-pis" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aanplakborden" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aanplakborden" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aanplakzuilen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aanplakzuilen" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aantal-bezoekers-website-stad-brussel" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aantal-bezoekers-website-stad-brussel" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aanvragen-voor-een-bouwvergunning" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aanvragen-voor-een-bouwvergunning" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aanvragen-voor-geleverde-milieuvergunningen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aanvragen-voor-geleverde-milieuvergunningen" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/actualites-ville-de-bruxelles" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/actualites-ville-de-bruxelles" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/administratief-centrum-en-verbindingsbureaus" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/administratief-centrum-en-verbindingsbureaus" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/administratief-centrum-en-verbindingsbureaus" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/administratief-centrum-en-verbindingsbureaus" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/administrative-centre-and-liaison-offices" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/administrative-centre-and-liaison-offices" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/administrative-centre-and-liaison-offices" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/administrative-centre-and-liaison-offices" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/advertising-columns" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/advertising-columns" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/afval" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/afval" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/agenda" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/agenda" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/agenda-of-the-day" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/agenda-of-the-day" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/agenda-van-de-dag" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/agenda-van-de-dag" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aires-de-jeux" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aires-de-jeux" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/aires-de-stationnements-taxi" . + . + "Brussels OpenData License" . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/aires-de-stationnements-taxi" . + . + "Brussels OpenData License" . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/aires-de-stationnements-taxi" . + . + "Brussels OpenData License" . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/aires-de-stationnements-taxi" . + . + "Brussels OpenData License" . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/android-applicatie" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/android-applicatie" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/android-application" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/android-application" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken0" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken0" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken0" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/apotheken0" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/appels-doffres" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/appels-doffres" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/application-android" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/application-android" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/applications-for-a-planning-permit" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/applications-for-a-planning-permit" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/applications-for-a-supplied-environmental-permit" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/applications-for-a-supplied-environmental-permit" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/arrets-stib" . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/arrets-stib" . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/arrets-stib" . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/arrets-stib" . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/atms" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/atms" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/atms" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/atms" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/begraafplaatsen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/begraafplaatsen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/begraafplaatsen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/begraafplaatsen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-in-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-in-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-titel-in-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-titel-in-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-titel-in-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-titel-in-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-titel-in-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-titel-in-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-url-in-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-url-in-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-url-in-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bezochte-webpaginas-van-de-stad-per-url-in-2015" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bicycle-boxes" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bicycle-boxes" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bicycle-boxes" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bicycle-boxes" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/billboards" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/billboards" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bioscopen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bioscopen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bioscopen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bioscopen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/box-a-velos" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/box-a-velos" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/box-a-velos" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/box-a-velos" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_affichage_public" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_affichage_public" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_arbres_remarquables" . + . + "Open Data Ville de Bruxelles" . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_arbres_remarquables" . + . + "Open Data Ville de Bruxelles" . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_auberges_de_jeunesse_et_hotels_pour_jeunes" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_auberges_de_jeunesse_et_hotels_pour_jeunes" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_auberges_de_jeunesse_et_hotels_pour_jeunes" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_auberges_de_jeunesse_et_hotels_pour_jeunes" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bibliotheques_francophones" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bibliotheques_francophones" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bibliotheques_francophones" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bibliotheques_francophones" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bulles_a_verre" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bulles_a_verre" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bulles_a_verre" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bulles_a_verre" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_police" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_police" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_police" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_police" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_tourisme" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_tourisme" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_tourisme" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_bureaux_de_tourisme" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_canisite" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_canisite" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_canisite" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_canisite" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_centre_administratif_et_bureaux_de_liaison" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_centre_administratif_et_bureaux_de_liaison" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_centre_administratif_et_bureaux_de_liaison" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_centre_administratif_et_bureaux_de_liaison" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_cinemas" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_cinemas" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_cinemas" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_cinemas" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_colonnes_affichage_communal" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_colonnes_affichage_communal" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_concours_communaux" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_concours_communaux" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_distributeurs_bancaires" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_distributeurs_bancaires" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_distributeurs_bancaires" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_distributeurs_bancaires" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_francophones" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_francophones" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_francophones" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_francophones" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_neerlandophones" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_neerlandophones" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_neerlandophones" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_ecoles_neerlandophones" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_eleves_ecoles_francophones" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_eleves_ecoles_francophones" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_especes_de_plantes_exotiques_invasives" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_especes_de_plantes_exotiques_invasives" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_especes_de_plantes_exotiques_invasives" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_especes_de_plantes_exotiques_invasives" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_installations_sportives_de_la_ville_de_bruxelles" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_installations_sportives_de_la_ville_de_bruxelles" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_installations_sportives_de_la_ville_de_bruxelles" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_installations_sportives_de_la_ville_de_bruxelles" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_internet_public" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_internet_public" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_internet_public" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_internet_public" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_l_europe_a_bruxelles" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_l_europe_a_bruxelles" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_l_europe_a_bruxelles" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_l_europe_a_bruxelles" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_lieux_culturels" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_lieux_culturels" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_lieux_culturels" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_lieux_culturels" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_maisons_de_quartier" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_maisons_de_quartier" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_maisons_de_quartier" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_maisons_de_quartier" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_musees" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_musees" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_musees" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_musees" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_offres_d_emploi" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_offres_d_emploi" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcours_bd" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcours_bd" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcours_bd" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcours_bd" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcs_et_jardins" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcs_et_jardins" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcs_et_jardins" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parcs_et_jardins" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_pour_personnes_handicapees" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_pour_personnes_handicapees" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_pour_personnes_handicapees" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_pour_personnes_handicapees" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_publics" . + . + "Brussels OpenData License" . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_publics" . + . + "Brussels OpenData License" . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_publics" . + . + "Brussels OpenData License" . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_parkings_publics" . + . + "Brussels OpenData License" . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_rues_par_secteur_pour_les_cartes_de_riverain" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_rues_par_secteur_pour_les_cartes_de_riverain" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_statistiques_facebook_francais" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_statistiques_facebook_francais" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_theatres" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_theatres" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_theatres" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_theatres" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_toilettes_publiques" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_toilettes_publiques" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_toilettes_publiques" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_toilettes_publiques" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_top_100_livres_empruntes_par_bibilotheque" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_top_100_livres_empruntes_par_bibilotheque" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_urinoirs_publics" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_urinoirs_publics" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_urinoirs_publics" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_urinoirs_publics" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_wifi" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_wifi" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_wifi" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bruxelles_wifi" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bureaux-de-vote0" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bureaux-de-vote0" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bureaux-de-vote0" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bureaux-de-vote0" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/burgemeesters" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/burgemeesters" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/busscolaires" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/busscolaires" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/busscolaires" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/busscolaires" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/buurthuizen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/buurthuizen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/buurthuizen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/buurthuizen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_bourgmestres" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_bourgmestres" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_consommation_energetique" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_consommation_energetique" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_demo_evolutionpop_tranchesage" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_demo_evolutionpop_tranchesage" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_demographie_140306_annee" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_demographie_140306_annee" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_fontaines" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_fontaines" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_fontaines" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_fontaines" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_food_trucks" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_food_trucks" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_food_trucks" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_food_trucks" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_prenoms_masculins_2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_prenoms_masculins_2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_regiefonciere_logementsmoyens2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_regiefonciere_logementsmoyens2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_siteweb_origines_visiteurs2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_siteweb_origines_visiteurs2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_siteweb_visites_visiteursfr" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_siteweb_visites_visiteursfr" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_urinals" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_urinals" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_urinals" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/bxl_urinals" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cambio-stations" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cambio-stations" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cambio-stations" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cambio-stations" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cartographie-openstreetmap-des-pharmacies-a-bruxelles" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cartographie-openstreetmap-des-pharmacies-a-bruxelles" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cartographie-openstreetmap-des-pharmacies-a-bruxelles" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cartographie-openstreetmap-des-pharmacies-a-bruxelles" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cemeteries" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cemeteries" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cemeteries" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cemeteries" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/centres-de-jeunes-de-bravvo" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/centres-de-jeunes-de-bravvo" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/centres-de-jeunes-de-bravvo" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/centres-de-jeunes-de-bravvo" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/childrens-homes" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/childrens-homes" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/childrens-homes" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/childrens-homes" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cimetieres" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cimetieres" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cimetieres" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cimetieres" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cinemas" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cinemas" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cinemas" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cinemas" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/citoyens-dhonneur" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/citoyens-dhonneur" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cleanliness-committees" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cleanliness-committees" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cleanliness-committees" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cleanliness-committees" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/collections-des-bibliotheques-francophones" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/collections-des-bibliotheques-francophones" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/collections-des-bibliotheques-francophones-en-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/collections-des-bibliotheques-francophones-en-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/collections-des-bibliotheques-francophones-en-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/collections-des-bibliotheques-francophones-en-2015" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/comic-book-route" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/comic-book-route" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/comic-book-route" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/comic-book-route" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/comites-proprete" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/comites-proprete" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/comites-proprete" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/comites-proprete" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/community-centres" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/community-centres" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/community-centres" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/community-centres" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/comptages-au-carrefour-van-der-weyden-stalingrad" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/comptages-au-carrefour-van-der-weyden-stalingrad" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-kg" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-kg" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-kg" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-kg" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-one" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-one" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-one" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/consultaties-voor-kinderen-bij-one" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-kg" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-kg" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-kg" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-kg" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-one" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-one" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-one" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-for-children-at-one" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-kg" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-kg" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-kg" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-kg" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-lone" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-lone" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-lone" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/consultations-pour-enfants-de-lone" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/costumes-de-manneken-pis" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/costumes-de-manneken-pis" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/counts-at-the-crossroad-van-der-weyden-stalingrad" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/counts-at-the-crossroad-van-der-weyden-stalingrad" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/couvertures-du-brusseleir" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/couvertures-du-brusseleir" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/covers-of-the-brusseleir" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/covers-of-the-brusseleir" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/covers-van-de-brusseleir" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/covers-van-de-brusseleir" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/creches-pregardiennats" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/creches-pregardiennats" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/creches-pregardiennats" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/creches-pregardiennats" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/cultural-places" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/cultural-places" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/cultural-places" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/cultural-places" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/culturele-plaatsen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/culturele-plaatsen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/culturele-plaatsen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/culturele-plaatsen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/data-set-of-the-data-sets" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/data-set-of-the-data-sets" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/dataset-van-de-datasets" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/dataset-van-de-datasets" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/dechets" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/dechets" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/demandes-de-permis-denvironnement-delivres" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/demandes-de-permis-denvironnement-delivres" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/demandes-de-permis-denvironnement-introduits" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/demandes-de-permis-denvironnement-introduits" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/demandes-de-permis-durbanisme" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/demandes-de-permis-durbanisme" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/demografische-statistieken" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/demografische-statistieken" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/demographic-statistics" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/demographic-statistics" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/deux-roues-motories" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/deux-roues-motories" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/deux-roues-motories" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/deux-roues-motories" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/dog-toilets" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/dog-toilets" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/dog-toilets" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/dog-toilets" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/dressings-of-manneken-pis" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/dressings-of-manneken-pis" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/dutch-language-libraries" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/dutch-language-libraries" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/dutch-language-libraries" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/dutch-language-libraries" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-artistiques-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-artistiques-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-artistiques-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-artistiques-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-de-promotion-sociale-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-de-promotion-sociale-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-de-promotion-sociale-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-de-promotion-sociale-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-maternelles-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-maternelles-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-maternelles-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-maternelles-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-neerlandophones" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-neerlandophones" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-neerlandophones" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-neerlandophones" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-primaires-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-primaires-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-primaires-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-primaires-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-secondaires-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-secondaires-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-secondaires-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-secondaires-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-specialisees-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-specialisees-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-specialisees-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-specialisees-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-superieures-francophones-communales" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-superieures-francophones-communales" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-superieures-francophones-communales" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ecoles-superieures-francophones-communales" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/elections-communales" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/elections-communales" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/elections-communales-pourcentages" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/elections-communales-pourcentages" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/elections-communales-sieges" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/elections-communales-sieges" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/elkaar-helpen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/elkaar-helpen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/elkaar-helpen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/elkaar-helpen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/energieverbruik" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/energieverbruik" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/energy-consumption" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/energy-consumption" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ereburgers1" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ereburgers1" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/espaces-publics-numeriques-epn" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/espaces-publics-numeriques-epn" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/espaces-publics-numeriques-epn" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/espaces-publics-numeriques-epn" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/eten-en-drinken" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/eten-en-drinken" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/eten-en-drinken" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/eten-en-drinken" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/european-female-population-in-brussels" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/european-female-population-in-brussels" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/european-institutions" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/european-institutions" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/european-institutions" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/european-institutions" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/european-male-population-in-brussels" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/european-male-population-in-brussels" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-instelligen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-instelligen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-instelligen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-instelligen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-mannelijke-bevolking-in-brussel" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-mannelijke-bevolking-in-brussel" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-vrouwelijke-bevolking-in-brussel" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/europese-vrouwelijke-bevolking-in-brussel" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/evenements-trafic-travaux" . + . + "contacter Bruxelles Mobilité" . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/evenements-trafic-travaux" . + . + "contacter Bruxelles Mobilité" . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/evenements-trafic-travaux" . + . + "contacter Bruxelles Mobilité" . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/evenements-trafic-travaux" . + . + "contacter Bruxelles Mobilité" . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/expected-evolution-of-the-brussels-population-by-age" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/expected-evolution-of-the-brussels-population-by-age" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/expected-evolution-of-the-brussels-seniors-population" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/expected-evolution-of-the-brussels-seniors-population" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/expected-evolution-of-the-population-of-brussels-with-growth" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/expected-evolution-of-the-population-of-brussels-with-growth" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/facebook-pages" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/facebook-pages" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/facebookpagina-stad-brussel" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/facebookpagina-stad-brussel" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2000" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2000" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2001" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2001" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2002" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2002" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2003" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2003" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2004" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2004" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2005" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2005" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2006" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2006" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2007" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2007" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2008" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2008" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2010" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2010" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-20130" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-20130" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-20150" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/female-first-names-20150" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fietstrommels" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fietstrommels" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fietstrommels" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fietstrommels" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-fietspaden-in-park-bandes-cyclables-dans-les-parcs" . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-fietspaden-in-park-bandes-cyclables-dans-les-parcs" . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-fietspaden-in-park-bandes-cyclables-dans-les-parcs" . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-fietspaden-in-park-bandes-cyclables-dans-les-parcs" . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-gemarkeerde-fietspaden-pistes-cyclables-marquees" . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-gemarkeerde-fietspaden-pistes-cyclables-marquees" . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-gemarkeerde-fietspaden-pistes-cyclables-marquees" . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-gemarkeerde-fietspaden-pistes-cyclables-marquees" . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-verhoogde-fietspaden-pistes-cyclables-surelevees" . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-verhoogde-fietspaden-pistes-cyclables-surelevees" . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-verhoogde-fietspaden-pistes-cyclables-surelevees" . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-verhoogde-fietspaden-pistes-cyclables-surelevees" . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-voetgangerszone-fietszone-pietonnier-zone-cyclable" . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-voetgangerszone-fietszone-pietonnier-zone-cyclable" . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-voetgangerszone-fietszone-pietonnier-zone-cyclable" . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fietsvelo-voetgangerszone-fietszone-pietonnier-zone-cyclable" . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fontaines-deau-potable" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fontaines-deau-potable" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fontaines-deau-potable" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fontaines-deau-potable" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/fonteinen-met-drinkbaar-water" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/fonteinen-met-drinkbaar-water" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/fonteinen-met-drinkbaar-water" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/fonteinen-met-drinkbaar-water" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks-copie" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks-copie" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks-copie" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/food-trucks-copie" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-bibliotheken" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-bibliotheken" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-bibliotheken" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-bibliotheken" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-scholen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-scholen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-scholen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/franstalige-scholen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-libraries" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-libraries" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-libraries" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-libraries" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-schools" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-schools" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-schools" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/french-language-schools" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gares-sncb" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/gares-sncb" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gares-sncb" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/gares-sncb" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gebruikers-van-de-nederlandstalige-bibliotheken" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gebruikers-van-de-nederlandstalige-bibliotheken" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geldautomaten" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/geldautomaten" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geldautomaten" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/geldautomaten" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijk-actualiteit" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijk-actualiteit" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-basisscholen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-basisscholen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-basisscholen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-basisscholen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-kleuterscholen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-kleuterscholen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-kleuterscholen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-kleuterscholen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-lagere-scholen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-lagere-scholen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-lagere-scholen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-lagere-scholen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-secundaire-scholen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-secundaire-scholen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-secundaire-scholen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeentelijke-secundaire-scholen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeenteraadsverkiezingen-aantal-stemmen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeenteraadsverkiezingen-aantal-stemmen" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeenteraadsverkiezingen-procenten" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeenteraadsverkiezingen-procenten" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeenteraadsverkiezingen-zetels" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemeenteraadsverkiezingen-zetels" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gemotoriseerde-tweewielers" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/gemotoriseerde-tweewielers" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gemotoriseerde-tweewielers" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/gemotoriseerde-tweewielers" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-belgische-gemeenten-van-de-bezoeken-aan-de-website-van-d0" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-belgische-gemeenten-van-de-bezoeken-aan-de-website-van-d0" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-belgische-gemeenten-van-de-bezoeken-aan-de-website-van-de" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-belgische-gemeenten-van-de-bezoeken-aan-de-website-van-de" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-gemeenten-van-de-bezoeken-aan-de-website-van-de-stad-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-gemeenten-van-de-bezoeken-aan-de-website-van-de-stad-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2010" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2010" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geografische-oorsprong-van-de-bezoeken-aan-de-website-van-de-stad-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-belgian-municipalities-of-the-visits-to-the-website-of-the-0" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-belgian-municipalities-of-the-visits-to-the-website-of-the-0" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-belgian-municipalities-of-the-visits-to-the-website-of-the-c" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-belgian-municipalities-of-the-visits-to-the-website-of-the-c" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-municipalities-of-the-visits-to-the-website-of-the-city-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-municipalities-of-the-visits-to-the-website-of-the-city-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-20130" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-20130" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/geographical-origin-of-the-visits-to-the-website-of-the-city-of-brussels-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/gewestelijke-wegen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/gewestelijke-wegen" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/glasbollen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/glasbollen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/glasbollen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/glasbollen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/glass-containers" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/glass-containers" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/glass-containers" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/glass-containers" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/guerites-et-bulles-des-petits-riens" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/guerites-et-bulles-des-petits-riens" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/guerites-et-bulles-des-petits-riens" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/guerites-et-bulles-des-petits-riens" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/habillages-de-manneken-pis" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/habillages-de-manneken-pis" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/haltes-mivb" . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/haltes-mivb" . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/haltes-mivb" . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/haltes-mivb" . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/haren-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/haren-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/haren-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/haren-quarter" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren0" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren0" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren0" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/herstellen-hergebruiken-recycleren-composteren0" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/homes-of-the-property-management-agency" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/homes-of-the-property-management-agency" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/homes-of-the-property-management-agency" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/homes-of-the-property-management-agency" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/hondentoiletten" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/hondentoiletten" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/hondentoiletten" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/hondentoiletten" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/honorary-citizens1" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/honorary-citizens1" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/hopitaux-publics" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/hopitaux-publics" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/hopitaux-publics" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/hopitaux-publics" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/huizen-voor-het-kind" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/huizen-voor-het-kind" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/huizen-voor-het-kind" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/huizen-voor-het-kind" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/informatiedragers-in-de-nederlandstalige-bibliotheken" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/informatiedragers-in-de-nederlandstalige-bibliotheken" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ingediende-aanvragen-voor-milieuvergunningen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ingediende-aanvragen-voor-milieuvergunningen" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/invasieve-exotische-planten" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/invasieve-exotische-planten" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/invasieve-exotische-planten" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/invasieve-exotische-planten" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/invasive-exotic-plants" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/invasive-exotic-plants" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/invasive-exotic-plants" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/invasive-exotic-plants" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/jeugdhotels" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/jeugdhotels" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/jeugdhotels" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/jeugdhotels" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/job-offers" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/job-offers" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/kinderdagverblijven-peutertuinen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/kinderdagverblijven-peutertuinen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/kinderdagverblijven-peutertuinen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/kinderdagverblijven-peutertuinen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/laeken-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/laeken-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/laeken-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/laeken-quarter" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-contrats-de-quartier-a-la-regie-fonciere-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-contrats-de-quartier-a-la-regie-fonciere-2014" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-de-la-regie-fonciere" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-de-la-regie-fonciere" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-de-la-regie-fonciere" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-de-la-regie-fonciere" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-moyens-avec-conditions-de-revenus-de-la-regie-fonciere-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/logements-moyens-avec-conditions-de-revenus-de-la-regie-fonciere-2014" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/louise-bois-de-la-cambre-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/louise-bois-de-la-cambre-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/louise-bois-de-la-cambre-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/louise-bois-de-la-cambre-quarter" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-de-repos-pour-personnes-agees" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-de-repos-pour-personnes-agees" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-de-repos-pour-personnes-agees" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-de-repos-pour-personnes-agees" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-des-enfants" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-des-enfants" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-des-enfants" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/maisons-des-enfants" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2000" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2000" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2002" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2002" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20020" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20020" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2003" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2003" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2004" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2004" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2005" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2005" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2006" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2006" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2007" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2007" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2008" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2008" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2010" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2010" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20130" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20130" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20140" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20140" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20150" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/male-first-names-20150" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/manger-et-boire-durablement" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/manger-et-boire-durablement" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/manger-et-boire-durablement" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/manger-et-boire-durablement" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/manneken-pis" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/manneken-pis" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2000" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2000" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2001" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2001" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2002" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2002" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2003" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2003" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2004" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2004" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2005" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2005" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2006" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2006" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2007" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2007" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2008" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2008" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2010" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2010" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-20130" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-20130" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-20150" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mannelijke-voornamen-20150" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/marches" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/marches" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/marches" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/marches" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/markets" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/markets" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/markets" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/markets" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/markten" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/markten" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/markten" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/markten" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mayors" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mayors" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/middenklassewoningen-met-inkomensvoorwaarden-van-de-grondregie-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/middenklassewoningen-met-inkomensvoorwaarden-van-de-grondregie-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/middenklassewoningen-van-de-grondregie-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/middenklassewoningen-van-de-grondregie-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/mise-a-jour-des-jeux-de-donnees" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/mise-a-jour-des-jeux-de-donnees" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/monumenten-1914-1918" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/monumenten-1914-1918" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/monumenten-1914-1918" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/monumenten-1914-1918" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-commemoratifs-de-la-guerre-de-1914-1918" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-commemoratifs-de-la-guerre-de-1914-1918" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-commemoratifs-de-la-guerre-de-1914-1918" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-commemoratifs-de-la-guerre-de-1914-1918" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-funeraires-du-cimetiere-de-laeken" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-funeraires-du-cimetiere-de-laeken" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-funeraires-du-cimetiere-de-laeken" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/monuments-funeraires-du-cimetiere-de-laeken" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/motos" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/motos" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/motos" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/motos" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-contests" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-contests" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-elections-number-of-votes" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-elections-number-of-votes" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-elections-percentages" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-elections-percentages" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-elections-seats" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-elections-seats" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-news" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-news" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-news0" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/municipal-news0" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/musea-in-brussel" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/musea-in-brussel" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/musea-in-brussel" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/musea-in-brussel" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/musees-a-bruxelles" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/musees-a-bruxelles" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/musees-a-bruxelles" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/musees-a-bruxelles" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/museums" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/museums" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/museums" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/museums" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/museums-in-brussels" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/museums-in-brussels" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/museums-in-brussels" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/museums-in-brussels" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/museums0" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/museums0" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/museums0" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/museums0" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/neder-over-heembeek-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/neder-over-heembeek-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/neder-over-heembeek-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/neder-over-heembeek-quarter" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlandstalige-bibliotheken" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlandstalige-bibliotheken" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlandstalige-bibliotheken" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlandstalige-bibliotheken" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlanstalige-scholen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlanstalige-scholen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlanstalige-scholen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nederlanstalige-scholen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nest-boxes" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nest-boxes" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nest-boxes" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nest-boxes" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nestkasten" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nestkasten" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nestkasten" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nestkasten" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/netheidscomites" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/netheidscomites" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/netheidscomites" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/netheidscomites" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nichoirs" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nichoirs" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nichoirs" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nichoirs" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/niveaux-de-service-de-trafic" . + . + "Voir producteur" . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/niveaux-de-service-de-trafic" . + . + "Voir producteur" . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/niveaux-de-service-de-trafic" . + . + "Voir producteur" . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/niveaux-de-service-de-trafic" . + . + "Voir producteur" . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nmbs-stations" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nmbs-stations" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nmbs-stations" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nmbs-stations" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/noordoostwijk" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/noordoostwijk" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/noordoostwijk" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/noordoostwijk" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/noordwijk" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/noordwijk" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/noordwijk" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/noordwijk" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/north-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/north-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/north-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/north-quarter" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/northeast-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/northeast-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/northeast-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/northeast-quarter" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nurseries-kindergartens" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nurseries-kindergartens" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nurseries-kindergartens" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nurseries-kindergartens" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/nursing-homes-for-elderly" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/nursing-homes-for-elderly" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/nursing-homes-for-elderly" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/nursing-homes-for-elderly" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-aanbestedingen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-aanbestedingen" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-computerruimtes-ocr" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-computerruimtes-ocr" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-computerruimtes-ocr" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-computerruimtes-ocr" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-ziekenhuizen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-ziekenhuizen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-ziekenhuizen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/openbare-ziekenhuizen" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/opmerkelijke-bomen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/opmerkelijke-bomen" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-communes-belges-des-visites-du-site-web-de-la-ville-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-communes-belges-des-visites-du-site-web-de-la-ville-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-communes-belges-des-visites-du-site-web-de-la-ville-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-communes-belges-des-visites-du-site-web-de-la-ville-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-communes-des-visites-du-site-web-de-la-ville-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-communes-des-visites-du-site-web-de-la-ville-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2010" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2010" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-des-visites-du-site-web-de-la-ville-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-pays-des-visites-du-site-web-de-la-ville-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/origine-geographique-pays-des-visites-du-site-web-de-la-ville-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/outdoor-multisports-grounds" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/outdoor-multisports-grounds" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/outdoor-multisportvelden" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/outdoor-multisportvelden" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-par-titre-visitees-en-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-par-titre-visitees-en-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-par-titre-visitees-en-20130" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-par-titre-visitees-en-20130" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-par-titre-visitees-en-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-par-titre-visitees-en-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-visitees-en-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-de-la-ville-visitees-en-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-par-url-de-la-ville-visitees-en-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-par-url-de-la-ville-visitees-en-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-par-url-de-la-ville-visitees-en-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pages-du-site-web-par-url-de-la-ville-visitees-en-2015" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parkeerplaatsen-voor-gehandicapten" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parkeerplaatsen-voor-gehandicapten" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parkeerplaatsen-voor-gehandicapten" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parkeerplaatsen-voor-gehandicapten" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parken" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parken" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parken" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parken" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parking-spaces-for-disabled" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parking-spaces-for-disabled" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parking-spaces-for-disabled" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parking-spaces-for-disabled" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings" . + . + "Licentie Brussels OpenData License" . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings" . + . + "Licentie Brussels OpenData License" . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings" . + . + "Licentie Brussels OpenData License" . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings" . + . + "Licentie Brussels OpenData License" . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-for-coaches" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-for-coaches" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-for-coaches" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-for-coaches" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-pour-autocars-touristiques" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-pour-autocars-touristiques" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-pour-autocars-touristiques" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-pour-autocars-touristiques" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-voor-reisbussen" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-voor-reisbussen" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-voor-reisbussen" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parkings-voor-reisbussen" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/parks" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/parks" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/parks" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/parks" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/patrimoine-logements-de-la-regie-fonciere-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/patrimoine-logements-de-la-regie-fonciere-2014" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pavillons-seniors" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/pavillons-seniors" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pavillons-seniors" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/pavillons-seniors" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pentagon-quarter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/pentagon-quarter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pentagon-quarter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/pentagon-quarter" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/petanque-courts" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/petanque-courts" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/petanquebanen" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/petanquebanen" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies0" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies0" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies0" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies0" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies1" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies1" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies1" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/pharmacies1" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/piscines" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/piscines" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/piscines" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/piscines" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/plaatsen-met-openbare-internettoegang-poit" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/plaatsen-met-openbare-internettoegang-poit" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/plaatsen-met-openbare-internettoegang-poit" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/plaatsen-met-openbare-internettoegang-poit" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/playgrounds" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/playgrounds" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/police-stations" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/police-stations" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/police-stations" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/police-stations" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/politiekantoren" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/politiekantoren" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/politiekantoren" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/politiekantoren" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/polling-stations" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/polling-stations" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/polling-stations" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/polling-stations" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pollumeter" . + . + "contacteer Leefmilieu Brussel " . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pollumeter" . + . + "contacteer Leefmilieu Brussel " . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pollumeter0" . + . + "see producer" . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pollumeter0" . + . + "see producer" . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/pollumetre" . + . + "contactez Bruxelles Environnement" . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/pollumetre" . + . + "contactez Bruxelles Environnement" . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/population" . + . + "Voir SPF Economie" . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/population" . + . + "Voir SPF Economie" . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/population-bruxelloise" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/population-bruxelloise" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/population-bruxelloise-copie" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/population-bruxelloise-copie" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/population-bruxelloise-copie0" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/population-bruxelloise-copie0" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/population-europeenne-a-bruxelles" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/population-europeenne-a-bruxelles" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/population-europeenne-feminine-a-bruxelles" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/population-europeenne-feminine-a-bruxelles" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/poubellesintelligentes" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/poubellesintelligentes" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/poubellesintelligentes" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/poubellesintelligentes" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/ppas" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/ppas" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/ppas" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/ppas" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2000" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2000" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2001" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2001" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2002" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2002" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2003" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2003" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2004" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2004" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2005" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2005" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2006" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2006" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2007" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2007" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2008" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2008" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2009" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2009" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2010" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2010" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2011" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2011" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2012" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2012" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-20130" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-20130" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-20131" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-20131" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-20150" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-feminins-20150" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2000" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2000" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2001" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2001" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2002" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2002" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2003" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2003" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2004" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2004" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2005" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2005" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2006" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2006" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2007" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2007" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2008" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2008" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2009" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2009" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2010" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2010" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2011" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2011" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2012" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2012" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2013" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2013" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2014" . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2014" . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-20150" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prenoms-masculins-20150" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prets-dans-les-bibliotheques-francophones-en-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prets-dans-les-bibliotheques-francophones-en-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prets-dans-les-bibliotheques-francophones-en-2014" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prets-dans-les-bibliotheques-francophones-en-2014" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/prets-dans-les-bibliotheques-francophones-en-2015" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/prets-dans-les-bibliotheques-francophones-en-2015" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/projection-de-levolution-de-la-population-bruxelloise-avec-soldes" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/projection-de-levolution-de-la-population-bruxelloise-avec-soldes" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/projection-de-levolution-de-la-population-des-seniors-bruxellois" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/projection-de-levolution-de-la-population-des-seniors-bruxellois" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/public-computer-rooms" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/public-computer-rooms" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/public-computer-rooms" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/public-computer-rooms" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/public-hospitals" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/public-hospitals" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/public-hospitals" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/public-hospitals" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/public-internet-access-points" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/public-internet-access-points" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/public-internet-access-points" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/public-internet-access-points" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/public-parkings" . + . + "Brussels OpenData License" . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/public-parkings" . + . + "Brussels OpenData License" . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/public-parkings" . + . + "Brussels OpenData License" . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/public-parkings" . + . + "Brussels OpenData License" . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/public-toilets" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/public-toilets" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/public-toilets" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/public-toilets" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/quartiers" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/quartiers" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/quartiers" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/quartiers" . + . + . + . + . + "application/zip" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/references-hopitaux" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/references-hopitaux" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/references-hopitaux" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/references-hopitaux" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/regional-roads" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/regional-roads" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/remarkable-trees" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/remarkable-trees" . + . + . + . + . + "application/json" . + . + . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/reparer-reutiliser-recycler-composter" . + . + . + . + . + "text/csv" . + . + "geojson export of https://opendata.brussels.be/api/v2/catalog/datasets/reparer-reutiliser-recycler-composter" . + . + . + . + . + "application/json" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/reparer-reutiliser-recycler-composter" . + . + . + . + . + "application/json" . + . + "shp export of https://opendata.brussels.be/api/v2/catalog/datasets/reparer-reutiliser-recycler-composter" . + . + . + . + . + "application/zip" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/reseaux-sociaux" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/reseaux-sociaux" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/reservations-dans-les-bibliotheques-francophones-en-2013" . + . + . + . + . + "text/csv" . + . + "json export of https://opendata.brussels.be/api/v2/catalog/datasets/reservations-dans-les-bibliotheques-francophones-en-2013" . + . + . + . + . + "application/json" . + . + . + . + "csv export of https://opendata.brussels.be/api/v2/catalog/datasets/reservations-dans-les-bibliotheques-francophones-en-2014" . + . + . + . + . + "text/csv" . + "Dagen en uren van de aankledingen van Manneken-Pis (met de naam van het kostuum, de referentie en de context van de aankleding).\n"@nl . + "Locatie van de aanplakborden van de Stad Brussel\n"@nl . + "Locatie van de aanplakzuilen van de Stad Brussel.\n"@nl . + "Statistieken van de bezoekersaantallen website Stad Brussel.\n"@nl . + "Aanvragen voor een stedenbouwkundige vergunning bij de\nStad Brussel per bestemming van het goed en sinds 2003. \n"@nl . + "Aanvragen voor milieuvergunningen die geleverd werden door de Stad Brussel per\nbestemming van het goed en sinds 2003.\n"@nl . + "Locatie van het Administratief centrum en de verbindingsbureaus van de Stad Brussel.\n"@nl . + "Aantal ton afval verwijderd door de dienst Netheid van de Stad Brussel om te worden verbrand, gestort op een vuilnisbelt of gerecycleerd.\n"@nl . + "Aankondiging van de activiteiten van de dag gepubliceerd op de website van de Stad Brussel.\n"@nl . + "Statistieken van het aantal downloads van de Android-applicatie van de Stad Brussel. Een nieuwe versie van de Android-applicatie van de Stad Brussel werd aangeboden in maart 2015.\n"@nl . + "Locatie van de apotheken op het grondgebied van de Stad Brussel vanuit OpenStreetMap (OSM).\n"@nl . + "Apotheken op het grondgebied van de Stad Brussel.\n"@nl . + "Kerkhoven van de Stad Brussel met de locatie van de ingang, openingsdagen en -uren en telefoonnummers.\n"@nl . + "Bezoekersstatistieken van de pagina's van de website van de Stad Brussel met het adres van de pagina (volgend op www.brussel.be), het aantal bekeken pagina's, het aantal unieke bezoeken, de gemiddelde tijd besteed aan de pagina. Bron: Google Analytics.\n"@nl . + "Bezoekersstatistieken van de pagina's van de website van de Stad Brussel met de titel van de pagina, het aantal bekeken pagina's, het aantal unieke bezoeken, de gemiddelde tijd besteed aan de pagina. Bron: Google Analytics.\n"@nl . + "Bezoekersstatistieken van de pagina's van de website van de Stad Brussel (2014) met de titel van de pagina, het aantal bekeken pagina's, het aantal unieke bezoeken, de gemiddelde tijd besteed aan de pagina. Bron: Google Analytics.\n"@nl . + "Bezoekersstatistieken van de pagina's van de website van de Stad Brussel (2015) met de titel van de pagina, het aantal bekeken pagina's, het aantal unieke bezoeken, de gemiddelde tijd besteed aan de pagina. Bron: Google Analytics.\n"@nl . + "Bezoekersstatistieken van de pagina's van de website van de Stad Brussel (2014) met het adres van de pagina (volgend op www.brussel.be), het aantal bekeken pagina's, het aantal unieke bezoeken, de gemiddelde tijd besteed aan de pagina. Bron: Google Analytics.\n"@nl . + "Bezoekersstatistieken van de pagina's van de website van de Stad Brussel (2014) met het adres van de pagina (volgend op www.brussel.be), het aantal bekeken pagina's, het aantal unieke bezoeken. Bron: Google Analytics.\n"@nl . + "Locaties van de bioscopen op het grondgebied van de Stad Brussel.\n"@nl . + "Burgemeesters van de Stad Brussel met de vermelding van de geboortedatum en overlijdensdatum, de datum van aanvang van mandat envan de ambtstermijn van de burgemeester (WN), de data van de koninklijke besluiten tot benoeming, de installatie, het einde van de functie of het ontslag.\n"@nl . + "Localisatie van de Buurthuizen van het OCMW Brussel\n"@nl . + "Locatie van de consultaties voor kinderen (0 tot 3 jaar) bij K&G (Kind & Gezin) op het grondgebied van de Stad Brussel."@nl . + "Locatie van de consultaties voor kinderen (0-6 jaar) bij het Franstalige ONE (Office de la Naissance et de l'Enfance) op het grondgebied van de Stad Brussel.\n"@nl . + "Covers van het magazine van de Stad Brussel, ‘De Brusseleir’.\n"@nl . + "Locatie van de culturele plaatsen van de Stad Brussel.\n"@nl . + "Dataset van de datasets van het Open Data platform van de\nStad Brussel.\n"@nl . + "Statistieken over geboorten, huwelijken, echtscheidingen\nen scheidingen, overlijdens in de Stad Brussel.\n"@nl . + "De locatie van de verschillende buurtcomités, vzw’s, netwerken voor kennisuitwisseling en ruilsystemen, burgerinitiatieven en organisaties die begeleiding bieden op het vlak van duurzaamheid op het grondgebied van de Stad Brussel.\n"@nl . + "Energieverbruik van de Stad Brussel sinds 2008. \n"@nl . + "Vooraanstaande personen waaraan de Stad Brussel de titel van ereburger heeft toegekend (met vermelding van de naam of pseudoniem, de functie, de datum van de toekenning van de onderscheiding)."@nl . + "De locatie van de verschillende organisaties die duurzame voeding promoten (restaurants, markten, moestuinen, boerderijen, winkels, SAGAL’s, enz.) op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de Europese instellingen op het grondgebied van de Stad Brussel.\n"@nl . + "Evolutie van de bevolking afkomstig van de verschillende landen van Europa (mannen) voor de periode 2009-2014.\n"@nl . + "Evolutie van de bevolking afkomstig van de verschillende landen van Europa (vrouwen) voor de periode 2009-2014.\n"@nl . + "\n Statistieken van de Facebookpagina's Stad Brussel (aantal 'likes') in het Frans, het Nederlands en het Engels.\n"@nl . + "Locatie van fietstrommels op het grondgebied van de Stad Brussel.\n"@nl . + "Fonteinen met drinkbaar water van Brussel (Straatverplegers VZW).\n"@nl . + "Locatie van de food trucks geselecteerd door de StaD Brussel.\n"@nl . + "Locatie van de Franstalige bibliotheken van de Stad Brussel.\n"@nl . + "Locatie van de Franstalige scholen van de Stad Brussel.\n"@nl . + "Het aantal gebruikers (actieve leden) van de Nederlandstalige bibliotheken van de Stad Brussel (Laken, Neder-Over-Heembeek en Haren).\n"@nl . + "Locatie van geldautomaten op het grondgebied van de Stad Brussel.\n"@nl . + "Actualiteit gepubliceerd op de website van de Stad Brussel.\n"@nl . + "Basisonderwijs van de Stad Brussel.\n"@nl . + "Kleuteronderwijs van de Stad Brussel.\n"@nl . + "Lager onderwijs van de Stad Brussel.\n"@nl . + "Secundair onderwijs van de Stad Brussel.\n"@nl . + "Resultaten van de lokale verkiezingen in Brussel sinds\n2000 (aantal stemmen per lijst). \n"@nl . + "Resultaten van de lokale verkiezingen in Brussel sinds\n2000 (aantal stemmen per lijst). \n"@nl . + "Resultaten van de lokale verkiezingen in Brussel sinds\n2000 (aantal zetels per lijst).\n"@nl . + "Parkeerplaatsen voor gemotoriseerde tweewielers op het grondgebiend van de Stad Brussel.\n"@nl . + "Steden (gemeenten) van herkomst van de bezoekers van de website van de Stad Brussel uit België, bepaald door hun IP-adres. Bron: Google Analytics.\n"@nl . + "Steden (gemeenten) van herkomst van de bezoekers van de website van de Stad Brussel uit België, bepaald door hun IP-adres. Bron: Google Analytics.\n"@nl . + "Steden (gemeenten) van herkomst van de bezoekers van de website van de Stad Brussel uit België, bepaald door hun IP-adres. Bron: Google Analytics.\n"@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2009) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics."@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2010) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics.\n"@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2011) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics.\n"@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2012) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics.\n"@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2013) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics.\n"@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2014) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics.\n"@nl . + "Landen en grondgebieden van de bezoekers van de website van de Stad Brussel (2015) bepaald door de geografische zone van hun IP-adres. Bron: Google Analytics.\n"@nl . + "Wegen beheerd door het Brussels Hoofdstedelijk Gewest. Sommige wegen (straten, steenwegen, lanen, pleinen,...) bevinden zich op het grondgebied van de Stad Brussel; andere kunnen eraan grenzen.\n"@nl . + "Locatie van de glascontainers op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de haltes (stations) van de MIVB.\n"@nl . + "De locatie van de haltes van Collecto en Waterbus en fietsherstelplaatsen op het grondgebied van de Stad Brussel.\n"@nl . + "De locatie van inzamelpunten (kleding, IT-materiaal, enz.); afvalverwerking (grofvuil, kga, enz.); compostlocaties, tweedehandswinkels en plaatsen voor herstellingen die zich op het grondgebied van de Stad Brussel bevinden.\n"@nl . + "Localisatie van hondentoiletten op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de Huizen voor het Kind die bestemd zijn voor jonge Brusselaars (6 tot 12 jaar).\n"@nl . + "Het aantal informatiedragers (boeken, tijdschriften, speelgoed, audiovisuele materialen,...) in de Nederlandstalige bibliotheken van de Stad Brussel (Laken, Neder-Over-Heembeek en Haren).\n"@nl . + "Ingediende\naanvragen voor milieuvergunningen bij de Stad Brussel per bestemming van het\ngoed en sinds 2003.\n"@nl . + "Locatie van invasieve exotische plantensoorten op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de jeugdherbergen en jeugdhotels op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de kinderdagverblijven (crèches) & peutertuinen van de Stad Brussel met vermelding van de erkennende instelling (ONE, Kind & Gezin), het aantal plaatsen, openingstijden.\n"@nl . + "Louise - Bois de la Cambre quarter of Brussels.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2000.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2001.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2002.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2003.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2004.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2005.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2006.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2007.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2008.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2009.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2010.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2011.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2012.\n"@nl . + "Voormannen van mannen in het Brussels Hoofdstedelijk Gewest in 2013\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2013.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2014.\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2015 (tot 27 oktober).\n"@nl . + "Mannelijke voornamen geregistreerd bij de Stad Brussel in 2015.\n"@nl . + "Locatie van de markten op het grondgebied van de Stad Brussel.\n"@nl . + "Situatie van de middenklassewoningen (met inkomensvoorwaarden) van de Grondregie op 31 december 2014\n"@nl . + "Situatie van de middenklassewoningen van de Grondregie op 31 december 2014\n"@nl . + "Monumenten van de Stad Brussel die de Eerste Wereldoorlog herdenken\n"@nl . + "Locatie van de musea op het grondgebied van de Stad Brussel (waaronder de musea van de Stad Brussel).\n"@nl . + "Locatie van de Musea van de Stad Brussel\n"@nl . + "Locatie van de Nederlandstalige bibliotheken van de Stad Brussel\n"@nl . + "Locatie van de Nederlandstalige scholen van de Stad Brussel\n"@nl . + "Locatie van de nestkastjes geplaatst in de Stad Brussel.\n"@nl . + "Informatie over de Netheidscomités van de Stad Brussel (naam Netheidscomité, naam ontvangststructuur, adres).\n"@nl . + "Treinstations (NMBS) met vertrek- en eindstations.\n"@nl . + "Noordoostwijk van Brussel.\n"@nl . + "Noordwijk van Brussel.\n"@nl . + "RSS-feeds van de openbare aanbestedingen op de website van de Stad Brussel: http://www.brussel.be/artdet.cfm?function=rss&id=5477\n"@nl . + "Locatie van de openbare computerruimtes (OCR) van de Stad Brussel\n"@nl . + "Openbare ziekenhuizen (leden van het IRIS-netwerk) op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de opmerkelijke bomen op het grondgebied van de Stad Brussel\n"@nl . + "Outdoor multisportvelden van de Stad Brussel (met adres en de beschrijving van de uitrusting).\n"@nl . + "\n Locatie van de parkeerplaatsen voor gehandicapten op het grondgebied van de Stad Brussel.\n\n"@nl . + "Locatie van de parken op het grondgebied van de Stad Brussel.\n"@nl . + "\n Locatie van de openbare, betalende parkings op het grondgebied van de Stad Brussel. (bron: http://opendatastore.brussels/nl )\n"@nl . + "Locatie van de parkeerplaatsen voor reisbussen (autocars) op het grondgebied van de Stad Brussel.\n"@nl . + "Petanquebanen van de Stad Brussel.\n"@nl . + "Locatie van de Plaatsen met openbare internettoegang (POIT) van de Stad Brussel.\n"@nl . + "Locatie van de politiekantoren op het grondgebied van de Stad Brussel.\n"@nl . + "De Pollumeter van het Brussels Hoofdstedelijk Gewest\nvolgt de evolutie van de luchtkwaliteit. De globale index wordt berekend op\nbasis van metingen in alle meetpunten voor zuurstof (O3), stikstofdioxide\n(NO2), zwaveldioxide (SO2) en fijne stofdeeltjes (PM10): www.ibgebim.be/Pollumetre/dynamic_index.txt. \nLeefmilieu Brussel verzoekt om de data periodiek op te halen (om 35 na\nhet uur) en niet door elke keer te zoeken op een webpagina met de Pollumeter.\n"@nl . + "Totale aantal (sinds 1921) van de Brusselse bevolking\nzoals geregistreerd door het departement Demografie. \n"@nl . + "Locatie en informatie van de rusthuizen en rust- en verzorgingstehuizen van het Openbaar Centrum voor Maatschappelijk Welzijn (OCMW) van Brussel.\n"@nl . + "Lijst met straten per sector van de bewonerskaarten op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie en openingsuren van de Seniorenpaviljoenen van de Stad Brussel.\n"@nl . + "Slimme vuilnisbakken op het grondgebied van de Stad Brussel.\n"@nl . + "Register (officieus) van accounts en pagina's van de Stad Brussel (Stad, departementen, diensten, bibliotheken, scholen, andere instellingen, vzw's, acties, evenementen) op sociale netwerken (Facebook, Twitter, Google+, LinkedIn, Pinterest, Flickr, YouTube).\n"@nl . + "Speeltuinen van de Stad Brussel met vermelding van de leeftijdscategorie en beschrijving van de speeltuigen.\n"@nl . + "Locatie van de sportzalen en stadions van de Stad Brussel.\n"@nl . + "Locatie van de stations van Cambio voor autodelen.\n"@nl . + "Locatie van de stations van het elektrische autodeelsysteem Zen Car op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de stembureaus op het grondgebied van de Stad\nBrussel.\n"@nl . + "Street Art ondersteund door de Stad Brussel.\n"@nl . + "Locatie van de stripmuren van de Stad Brussel (personages & auteurs).\n"@nl . + "Manuele tellingen van bewegingen (lichte voertuigen, vrachtwagens, bussen of autocars, motorfietsen, fietsen of gelijkwaardige persoonlijke voertuigen) op het kruispunt van de Rogier van der Weydenstraat en de Stalingradlaan.\n"@nl . + "Locatie van de standplaatsen\nvoor taxi's op het grondgebied van het Brussels Hoofdstedelijk Gewest. (Bron:  http://opendatastore.brussels/nl/ )\n"@nl . + "Locaties van de theaters op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de toerismekantoren van VisitBrussels op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de openbare toiletten op het grondgebied van de Stad Brussel.\n"@nl . + "Het aantal uitleningen en raadplegingen in de Nederlandstalige bibliotheken van de Stad Brussel (Laken, Neder-Over-Heembeek en Haren).\n"@nl . + "RSS-feed met bijgewerkte datasets van het platform\nopendata.brussel.be. :opendata.brussel.be/api/datasets/1.0/search?format=rss&sort;=modified\n"@nl . + "Locatie van de openbare urinoirs (toiletten voor mannen) op het grondgebied van de Stad Brussel.\n"@nl . + "Dataset van de RSS-feeds van de vacatures op de website van de Stad Brussel (van het departement Personeel, Openbaar onderwijs, paragemeentelijke verenigingen,...): http://www.brussel.be/artdet.cfm?function=rss&id=4108\n"@nl . + "Vastgoed (woningen) van de Grondregie op 31 december 2014.\n"@nl . + "Verkeer (incidenten,\nfiles... ), evenals storende werken in het Brussels Hoofdstedelijk Gewest.\n"@nl . + "Verkeersintensiteit op verschillende delen van het gewestelijk wegennetwerk.\n"@nl . + "Op basis van de bevolking van Brussel in 2012, een\nprojectie van de evolutie van de bevolking van de Stad Brussel tot 2024,\nrekening houdend met de natuurlijke aanwas (geboorte - sterfte) en het\nmigratiesaldo (immigratie - emigratie).\n"@nl . + "Op basis van de bevolking van Brussel in 2012, een projectie van de\nevolutie van de bevolking van de Stad Brussel tot 2024 per leeftijdsgroep.\n"@nl . + "Op basis van de bevolking van Brussel in 2012, een\nprojectie van de evolutie van de seniorenbevolking in Brussel tot 2024.\n"@nl . + "Locatie van de Villo!-stations in het Brussels Hoofdstedelijk Gewest met indicatie van de beschikbaarheid (fietsen, fietspalen) in real time.\n"@nl . + "\n Volgers (followers) van de Twitter account in het Frans, Nederlands en Engels.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2000.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2001.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2002.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2003.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2004.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2005.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2006.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2007.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2008.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2009.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2010.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2011.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2012.\n"@nl . + "Voornamen van vorouwen in het Brussels Hoofdstedelijk Gewest in 2013\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2013.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2014.\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2015 (tot 27 oktober).\n"@nl . + "Vrouwelijke voornamen geregistreerd bij de Stad Brussel in 2015.\n"@nl . + "Locatie van de permanente webcams die toegankelijk zijn op de website van de Stad Brussel.\n"@nl . + "Dataset van de RSS-feeds van de wedstrijden op de website van de Stad Brussel: http://www.brussel.be/artdet.cfm?function=rss&id=6314\n"@nl . + "Locatie van wifi op het grondgebied van de Stad Brussel.\n"@nl . + "Caart van de wijcken van Brussel (Vijfhoek, Haren, Laken, Louiza - Ter Kamerenbos, Neder-Over-Heembeek, Noord, Noord-Oost, Rechterover Kanaal).\n"@nl . + "Kaart van de Brusselse wijken (Vijfhoek, Haren, Laken, Louiza, Neder-Over-Heembeek, Noord, Noord-Oost, Rechteroever van het kanaal).\n"@nl . + "Situatie van de woningen binnen wijkcontracten van de Grondregie op 31 december 2014\n"@nl . + "Locatie en verdeling per wijk van de woningen van de Grondregie met het aantal wooneenheden per woning.\n"@nl . + "De locatie van de jeugdcentra, jeugdhuizen en het informatiecentrum voor jongeren op het grondgebied van de Stad Brussel.\n"@nl . + "Locatie van de zwembaden van de Stad Brussel.\n"@nl . + "Locatie van de zwembaden van de Stad Brussel.\n"@nl . + "Actualités publiées sur le site web de la Ville de Bruxelles.\n"@fr . + "Annonces des activités du jour à l'agenda du site web de la Ville de Bruxelles.\n"@fr . + "Aires de jeux de la Ville de Bruxelles avec mention de la catégorie d'âge visée et descriptif des engins.\n"@fr . + "Localisation des emplacements de taxis sur le territoire de la Région de Bruxelles Capitale. (Source:  http://opendatastore.brussels/fr/)\n"@fr . + "Fil RSS des marchés publics (appels d'offres) proposés sur le site web de la Ville de Bruxelles : http://www.bruxelles.be/artdet.cfm?function=rss&id=5477. Les candidats soumissionnaires peuvent aussi s'enregistrer via l'adresse soumissionnaires.bruxelles.be\n"@fr . + "Nombre de téléchargements de l'application Android de la Ville de Bruxelles. A noter: une nouvelle version de l'application Android de la Ville de Bruxelles a été proposée en mars 2015.\n"@fr . + "Localisation des arrêts (stations) de la STIB.\n"@fr . + "Localisation des box à vélos installés sur le territoire de la Ville de Bruxelles\n"@fr . + "Localisation des panneaux d'affichage public gérés par la Ville de Bruxelles.\n"@fr . + "Localisation des arbres remarquables sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des auberges de jeunesse et des hôtels pour jeunes sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des bibliothèques francophones de la Ville de Bruxelles et présence sur les réseaux sociaux.\n"@fr . + "Localisation des bulles (conteneurs) à verre installées sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des commissariats de police ou services de garde situés sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des bureaux de tourisme de VisitBrussels (organisme régional chargé du tourisme) sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des canisites installés sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation du Centre administratif et des bureaux de liaison de l'administration de la Ville de Bruxelles.\n"@fr . + "Salles de cinéma sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des colonnes d'affichage communal de la Ville de Bruxelles où des manifestations culturelles, sociales ou sportives, peuvent être annoncées, via un affichage par la cellule communale Affichage et Publicité.\n"@fr . + "\n\tFil RSS des concours communaux du site internet de la Ville de Bruxelles: http://www.brussels.be/artdet.cfm?function=rss&id=4694.\n"@fr . + "Localisation des distributeurs (guichets automatiques) bancaires (distributeurs automatiques de billets ou ATM) installés sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des écoles francophones de la Ville de Bruxelles.\n"@fr . + "Localisation des bibliothèques néerlandophones de la Ville de Bruxelles.\n"@fr . + "Evolution du nombre d'élèves ou d'inscriptions dans les écoles francophones de la Ville de Bruxelles.\n"@fr . + "Localisation des espèces de plantes exotiques invasives sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des installations sportives (stades, salles, complexes et terrains de sport) de la Ville de Bruxelles.\n"@fr . + "Localisation des points d'accès publics à l'Internet (PAPI) sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des institutions européennes sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des lieux culturels (organisés ou soutenus par la Ville) sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des maisons de quartier de la Ville de Bruxelles\n"@fr . + "Localisation des musées de la Ville de Bruxelles.\n"@fr . + "Fil RSS des offres d'emploi du site web la Ville de Bruxelles en provenance des départements du Personnel (Ressources humaines), de l'Instruction publique, des associations paracommunales : http://www.bruxelles.be/artdet.cfm?function=rss&id=6308.\n"@fr . + "Localisation des murs (fresques) BD de la Ville de Bruxelles.\n"@fr . + "Localisation des parcs et des jardins publics sur le territoire de la Ville de Bruxelles.\n"@fr . + "\n Localisation des espaces de stationnement (parking sur la voie publique) pour les personnes à mobilité réduite (PMR) sur le territoire de la Ville de Bruxelles.(voiries communales & régionales).\n"@fr . + "\n Localisation des parkings publics payants sur le territoire de la Ville de Bruxelles (sur la base de l'OpenDataStore régional: http://opendatastore.brussels/fr\n\n\n"@fr . + "\n\tLocalisation des rues par secteur pour les détenteurs d'une carte de riverain de stationnement sur le territoire de la Ville de Bruxelles.\n"@fr . + "\n Statistiques des pages Facebook Ville de Bruxelles (nombre de mentions \"J'aime\") en français, néerlandais, anglais.\n"@fr . + "Salles de théâtre sur le territoire de la Ville de Bruxelles.\n"@fr . + "\n Localisation des toilettes publiques sur le territoire de la Ville de Bruxelles.\n\n"@fr . + "Classement par bibliothèque des livres les plus empruntés dans les bibliothèques de la Ville de Bruxelles en 2013.\n"@fr . + "\n Localisation des urinoirs publics (toilettes pour hommes) sur le territoire de la Ville de Bruxelles.\n\n"@fr . + "Localisation des points d'accès gratuits wifi sur le territoire de la Ville de Bruxelles.\n"@fr . + "Localisation des bureaux de vote sur le territoire de la Ville de Bruxelles (Lambert 72).\n"@fr . + "Emplacements de bus scolaires aux abords des écoles de la Ville de Bruxelles.\n"@fr . + "text/html" . + . + "text/html" . + . + "text/html" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/json" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "De begroting van het Brussels Hoofdstedelijk Gewest is een indicator van de middelen waarover de Brusselse regering beschikt om haar beleid uit te voeren.\n\nU vindt een overzicht van de toestand van de overheidsfinanciën van het Brussels Gewest: de ontvangsten en uitgaven van de gewestelijke overheidsdienst Brussel (ex-Ministerie van het Brussels Hoofdstedelijk Gewest), maar ook van de plaatselijke besturen en de pararegionale instellingen, alsook statistieken over de uitstaande schuld van het Gewest."@nl . + "e4587278-2501-48ae-94e7-f31185251f93" . + "2016-11-18T00:00:00"^^ . + "2016-12-01T11:15:23.585263"^^ . + "Finances publiques"@fr . + "Overheidsfinanciën"@nl . + . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "Le budget de la Région de Bruxelles-Capitale est un indicateur des moyens dont dispose le gouvernement de la Région pour mener à bien ses politiques.\n\nVous trouverez un aperçu de la situation des finances publiques de la Région bruxelloise : les recettes et dépenses du Service public régional de Bruxelles (ex- Ministère de la Région de Bruxelles-Capitale), mais également des pouvoirs locaux et des organismes para-régionaux ainsi que des statistiques quant à l’encours de la dette de la Région. \n"@fr . + "Public finance "@en . + "The Brussels-Capital Region budget is an indicator of the means available to the government of the Region to implement its policies.\n\nYou will find an overview of public finance in the Brussels Region: the revenue and expenditure of the Brussels Regional Public Service, the local authorities and para-regional bodies, and the statistics on the outstanding debt of the Region."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC3BA798CD14264694ECBBB701EEE77B7 . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + "Proposal of regional express network for cyclists."@en . + "2016-01-01T00:00:00"^^ . + "Proposition de réseau express régional pour les cyclistes."@fr . + "2017-06-23T12:52:29.275940"^^ . + "FietsGEN"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC05406AF2496A4EC701BBF514E344A0B . + "Het fiets-GEN strekt zich uit over een straal van circa 15 kilometer rond Brussel. Er zijn 15 fietsroutes geselecteerd, die prioritair worden aangelegd. Deze prioritaire routes zijn 280 kilometer lang, waarvan ongeveer 60 procent in het Brussels Gewest. Het nieuwe net wordt hoofdzakelijk gevormd door een combinatie van de al bestaande Gewestelijke Fietsroutes (GRS) in Brussel en het Vlaamse Bovenlokaal Functioneel Fietsnetwerk (BFF). De helft van het fiets-GEN komt voort uit de al bestaande plannen voor die twee."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7292344E623DD1E4E391F4CC0008B127 . + . + "f2d31fbf-f805-4b6e-88ee-0b2834ec87f5" . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "RER-vélo"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB059F53C6CBF020491A269F57C8EFEC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB059F53C6CBF020491A269F57C8EFEC "Hoeck Michèle" . + "Cycling regional express network"@en . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "f1ddc86c-98b5-4899-bfea-f66a91b6f02b" . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Le niveau de vie des ménages peut être approché par leur revenu ou leurs dépenses. Ceux-ci permettent de mesurer le pouvoir d’achat de la population et par conséquent son accès plus ou moins facile aux biens et aux services tels que le logement, les biens d’équipement, l’alimentation, etc.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + "2016-11-18T00:00:00"^^ . + "Household income and expenditure "@en . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Income and expenditure are indications of the living standard of households. It makes it possible to measure the purchasing power of the population and, consequently, whether or not it has easy access to goods and services such as housing, capital goods, food, etc.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/\n"@en . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD14379397DDF8EBC58E29CB474A75CCF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD14379397DDF8EBC58E29CB474A75CCF "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD14379397DDF8EBC58E29CB474A75CCF "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + "Revenus et dépenses des ménages"@fr . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + "De levensstandaard van de huishoudens kan worden benaderd vanuit het standpunt van hun inkomen of hun uitgaven. Op die manier kan de koopkracht van de bevolking worden gemeten en dus ook de mate waarin zij al dan niet makkelijk toegang heeft tot goederen en diensten zoals huisvesting, uitrustingsgoederen, voeding enz.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + "2016-12-01T11:31:16.360362"^^ . + . + . + "Région de Bruxelles-Capitale"@fr . + . + "Inkomens en uitgaven van de huishoudens"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE0FBA649E5A635B967E4A06F6B5B21D7 . + . + . + . + "2016-01-27T08:13:32.460178"^^ . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "ZenCar Vehicles"@en . + "2017-10-30T09:48:28.842066"^^ . + "81834c61-5924-45aa-a3f6-80457f32bece" . + "ZenCar Autos"@nl . + "Véhicules ZenCar"@fr . + "ZenCar Vehicles"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDDF03058F3C31E4CE65A731D8E8DB9A0 . + "ZenCar Autos"@nl . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3D40291E6B142E8C8E57D0D3390644E8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3D40291E6B142E8C8E57D0D3390644E8 "BEW- BEE" . + "Région de Bruxelles-Capitale"@fr . + . + "Véhicules ZenCar"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + "Bicycle pumps"@en . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d594EC1A0600CF3B0B7AAF4449915B4EC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d594EC1A0600CF3B0B7AAF4449915B4EC "Team BruGIS" . + "De fietspompen die openbaar beschikbaar zijn en gekend zijn voor Brussel Mobiliteit."@nl . + "Les pompes à vélo publiques disponibles et connues de Bruxelles Mobilité."@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4472F8AB705A0E73BA04F7D076ABB2B8 . + "Pompes à vélo"@fr . + "Région de Bruxelles-Capitale"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9D93C8B1532C7ADFB5F5DFD3B9942D89 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9D93C8B1532C7ADFB5F5DFD3B9942D89 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9D93C8B1532C7ADFB5F5DFD3B9942D89 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "c9258fb3-5407-45f8-b5a5-39a72c27c2a9" . + "2016-01-01T00:00:00"^^ . + . + . + . + . + "2016-12-07T17:37:19.855610"^^ . + "Fietspompen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d2AA3A9D780D06DF4C8667E425661452F . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Public bicycle pumps known by Brussels Mobility."@en . + . + "ZenCar Stations "@en . + "Stations ZenCar "@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "97f14b34-fbef-4e43-9b34-d2eb24484437" . + "2017-10-30T09:46:57.931523"^^ . + "2016-01-27T08:07:40.814586"^^ . + "ZenCar Stations "@en . + "ZenCar Stations "@nl . + . + . + . + "Stations ZenCar "@fr . + "ZenCar Stations "@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDC19E65FB6FDC5E408907096853822F8 . + "Région de Bruxelles-Capitale"@fr . + . + "2018-01-23T09:36:45.415860"^^ . + "Bâtiments Bruxelles - 3DTiles"@fr . + . + . + "3D Tiles are an open specification for streaming massive heterogeneous 3D geospatial datasets. "@en . + "2017-10-06T00:00:00"^^ . + "3D Tiles are an open specification for streaming massive heterogeneous 3D geospatial datasets. "@nl . + . + . + "3D Tiles are an open specification for streaming massive heterogeneous 3D geospatial datasets. "@fr . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d39CB997EEFDD3014D3250EF9A3B854C8 . + "93b1bcb1-2adb-4cf8-9e5d-381e9904536c" . + "Brussels Buildings - 3DTiles"@en . + "Brussels Gebouwen - 3DTiles"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d70ECFB220485EFD19AEB97CC9E6AC348 . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3CB021CC946B6B8F3930E8A428564D2B . + "Stations d'épuration"@fr . + . + . + "Région de Bruxelles-Capitale"@fr . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE3367817AA96B85EF16C21789A474A66 . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de twee waterzuiveringsstations die het afvalwater en het regenwater zuiveren. Het waterzuiveringsstation Brussel-Noord (AQUIRIS, in dienst genomen in 2007) en het waterzuiveringsstation Brussel-Zuid (in dienst genomen in 2000)."@nl . + . + . + . + . + "http://opendatastore.brussels/catalog.xml?page=1" . + "100"^^ . + "http://opendatastore.brussels/catalog.xml?page=3" . + "http://opendatastore.brussels/catalog.xml?page=2" . + "205"^^ . + . + "{u'fr': u'Easybrussels', u'en': u'Easybrussels', u'nl': u'Easybrussels'}" . + "Brussels-Capital Region: localization of the two treatment stations of wastewater and rainwater. The Brussels-North waste water treatment plant (AQUIRIS commissioned in 2007) and the waste water treatment plant Brussels-South (commissioned in 2000)."@en . + "Waste water treatment plants"@en . + . + . + "2015-09-26T00:00:00"^^ . + "2f2ab1f0-19a2-43fe-940b-8d906ac19cc7" . + "Waterzuiveringsstations"@nl . + . + . + . + . + "Région de Bruxelles-Capitale : localisation des deux stations d'épuration qui traitent les eaux résiduaires et les eaux de pluie. La station d'épuration de Bruxelles-Nord (AQUIRIS, active depuis 2007) et la station d'épuration Bruxelles-Sud (active depuis 2000)."@fr . + "2016-01-22T18:45:38.176382"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d595AD2218C87A24FFC81333B2FCE9D88 . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Tunnelsecties van de tunnels van het Brussels Gewest."@nl . + "Sections de tunnel"@fr . + "2016-01-01T00:00:00"^^ . + "Tunnel sections"@en . + . + "0868986b-3239-414f-9c39-ae24ffac4513" . + "Tunnelsecties"@nl . + "Localisation des sections de tunnel dans la Région de Bruxelles-Capitale."@fr . + "2016-12-07T17:06:57.317807"^^ . + . + . + . + "Locations of tunnels in the Brussels-Capital Region."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3059C15FE07AB4D5AD70DA29E982ABF3 . + . + "2018-02-13T14:14:02.149921"^^ . + "Ce dataset vous donne l'accès aux informations concernant les positions des véhicules en temps réel, qui sont fournies par la société des transports intercommunaux de Bruxelles (STIB) en Belgique."@fr . + "1fe9c5a2-7af3-4b15-a356-07801393b34f" . + . + . + "2017-12-01T00:00:00"^^ . + "Position des véhicules (temps réel)"@fr . + "Positie van de voertuigen (real-time)"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0C0A61ABCDEF1C9E7CD847EF0F953413 . + . + "Deze dataset geeft u toegang tot real-time informatie voor de positie van de voertuigen die voorzien wordt door het Brussels intercommunaal openbaar transportbedrijf in België."@nl . + . + . + . + . + "This dataset gives you acces to the real-time information for the vehicles positions, which is provided by Brussels Intercommunal Transport Company in Belgium."@en . + "Vehicles position (real-time)"@en . + . + "Gemeentelijke website"@nl . + "2018-04-12T13:26:14.382943"^^ . + . + . + . + "Fréquentation et contenu du site internet communal d'Auderghem"@fr . + "Gebruik en inhoud van de gemeentelijke website van Oudergem"@nl . + "d1c4571c-8103-423b-8910-be65d4759913" . + "2018-04-18T07:31:06.931740"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d808A44B6E0581FBAA9774B85368FAC3E . + "Site internet communal"@fr . + . + . + "Orthophotoplan 2012"@en . + "Brussels Hoofdstedelijk Gewest : de orthofoto van het Brussels Hoofdstedelijk Gewest wordt gemaakt op basis van grootschalige luchtfoto's.\nDeze orthofoto omvat het hele grondgebied van het Gewest.\nOrthofoto's beschikbaar van 2004, 2009 en 2012."@nl . + . + . + "Brussels-Capital Region : the orthophotos of the Brussels-Capital are made on the basis of large-scale aerial photographs.\nThis orthophoto covers the whole territory of the region.\nOrthophotos available from 2004, 2009 and 2012."@en . + "2016-01-26T14:38:58.342356"^^ . + "2015-09-28T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d1F5658119AAABFC8EED472A7E0FBCBE3 . + . + . + "Région de Bruxelles-Capitale : les orthophotoplans de la Région de Bruxelles-Capitale sont réalisés sur base de photographies aériennes à grande échelle.\nCet orthophotoplan couvre l'ensemble du territoire de la région.\nLes orthophotos disponibles datent de 2004, 2009 et 2012."@fr . + "db2e0ac3-a8ac-464b-bfef-514dfa37d4e6" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d752518BE235D70A379AC74E67026CE41 . + "Orthophotoplan"@fr . + . + . + . + . + . + "Orthofoto 2009"@nl . + . + "De transitparkings worden beheerd door het Parkeeragentschap en dienen als verbinding met het openbaar vervoer."@nl . + . + "Transit parkings"@en . + "Localisation des parkings de transit.\n"@fr . + "Transit parking locations in the Brussels Capital Region.\n"@en . + "2016-01-01T00:00:00"^^ . + "2016-12-07T17:20:43.860590"^^ . + "a6c9af56-d4f6-4544-98cc-78bc24385099" . + . + . + "Parkings de transit"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d57D7639AD10526460B6E3FBFEC85E525 . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d1727855354562ADA631A0D35DAD8A131 . + . + . + "Transitparkings"@nl . + . + "Estimated number of off-road parking places, aggregated by block.\n(Joint data management with the parking agency)"@en . + "b29980e7-19e5-4873-8fb4-0f672eb021ec" . + "Parking hors voirie (par bloc)"@fr . + "Parking buiten de weg (per blok)"@nl . + "Door het parkeeragentschap van het Brussels Gewest wordt een lijst bijgehouden met de parkingen buiten de weg. De gegevens van deze lijsten worden gekoppeld aan de laag Urbadm_blocks zodat de totalen per huizenblok kunnen worden weergegeven. Parkings die niet binnen een huizenblok gelegen zijn worden gekoppeld aan de dichtstbijzijnde huizenblok in de gemeente."@nl . + "Estimation du nombre de places hors voiries, agrégées par bloc. "@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3AADB07C3EB3FAF22A058D9E16AD998A . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9466C49419BEBA145CD55285209E6095 . + "Parking hors voirie (par block)"@en . + . + . + . + . + "2016-01-01T00:00:00"^^ . + "2017-06-23T12:29:59.839642"^^ . + . + . + "Mobiliteit en vervoer"@nl . + . + . + . + . + . + . + "48f15d6f-43c6-4e42-8c8e-761356bcecb8" . + "Mobilité et transport"@fr . + . + . + . + . + "Mobility and Transport "@en . + . + . + . + . + . + . + . + "The statistics gathered by the BISA relate to road traffic, soft mobility, collective transport and shared mobility, freight transport, road safety and mobility practices.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Les statistiques rassemblées par l’IBSA portent sur la circulation routière, la mobilité douce, le transport collectif et partagé, le transport de marchandises, la sécurité routière et les pratiques de déplacements.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + . + . + . + "2016-11-22T00:00:00"^^ . + . + . + . + . + . + . + . + . + "2016-12-01T11:08:28.897760"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dEE482C133EF852BC112786DE9DEF9456 . + "De statistieken die door het IBSA verzameld werden hebben betrekking op het wegverkeer, zachte mobiliteit, collectief en gedeeld vervoer, vervoer van goederen, verkeersveiligheid en verplaatsingsgewoonten.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + . + . + . + "Localisation des bornes de recharge pour les véhicules électriques dans la région de Bruxelles-Capitale."@fr . + "Locatie van oplaadpunten voor elektrische voertuigen in het Brussels Hoofdstedelijk Gewest."@nl . + . + . + "2016-12-07T17:36:23.675916"^^ . + . + "Bornes de recharge électriques"@fr . + "Location of charging stations for electric vehicles in the Brussels-Capital Region."@en . + "2016-03-23T08:13:23.436495"^^ . + "Elektrische oplaadpunten"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d2382AA13E8EC6194CF4A54A19F437717 . + . + . + "e5b9ff72-7ee6-407d-84d4-fc8dad8fa1a7" . + "Electric charging stations"@en . + . + "Brussels Hoofdstedelijk Gewest : gegevensbestand van de groene ruimten en de recreatieve ruimten in het Brussels Hoofdstedelijk Gewest, toegankelijk voor het publiek"@nl . + "Openbare groene ruimten"@nl . + . + . + . + "Public green spaces"@en . + . + . + . + . + . + "2016-09-14T12:47:48.385384"^^ . + "Brussels-Capital Region : database of green spaces and recreational areas in the Brussels-Capital Region, accessible to the public "@en . + "2015-09-26T00:00:00"^^ . + "Région de Bruxelles-Capitale : inventaire des espaces verts et espaces récréatifs accessibles au public en Région de Bruxelles–Capitale"@fr . + "Espaces verts publics"@fr . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dADB06977A6666F6DFA57903AA82D5A8D . + . + . + "61f41610-beb6-4c0c-bfde-2e7475fda17e" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB10E6C6414C0D257D759555E765A605C . + . + . + . + . + "Brussels-Capital Region : Brussels post box is the cutting postal zones according to municipal boundaries. This division is made by the CIRB based on information provided by the National Register."@en . + "Zones postales communales"@fr . + "3452edb8-641e-4c79-96ba-516d0119291e" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFB7F9D9C3D3194E97F3E0675E89561BA . + . + . + "Brussels Hoofdstedelijk Gewest : de gemeentepostzone stemt met het snijden van de postzones in functie van de gemeentegrenzen overeen. Dit snijden wordt door CIRB op basis van inlichtingen verwezenlijkt die door het Nationale Register worden verstrekt."@nl . + "Région de Bruxelles-Capitale : la zone postale communale correspond au découpage des zones postales en fonction des limites communales. Ce découpage est réalisé par le CIRB sur base d'informations fournies par le Registre National."@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD126B63FEBC4C7D0E03F6240073DDD32 . + . + . + "2016-01-28T13:41:39.556362"^^ . + "Gemeentelijke postzone"@nl . + . + . + . + "2015-09-28T00:00:00"^^ . + "Postal zones"@en . + . + "00975d39-a062-45ee-abeb-ae13a8d80e87" . + "Organisaties actief op het terrein van gendergelijkheid in het Brussels Gewest"@nl . + "Organisations committed to gender equality in the Brussels Region"@en . + "This dataset contains: (1) the list of public services for equal opportunity (gender equality included) in the Brussels Region (at regional and local levels) ; (2) the list of organisations committed to gender equality established in the Brussels Region"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB8D93DF50D9BF460DA0B07D713E84B17 . + . + . + . + . + "Deze dataset bevat: (1) de lijst van de openbare diensten voor gelijke kansen (incl. gendergelijkheid) in het Brussels Gewest (op regionaal niveau en lokaal niveau) ; (2) de lijst van de organisaties actief op het terrein van gendergelijkheid en gevestigd in het Brussels Gewest "@nl . + "Organisations pour l'égalité de genre en Région de Bruxelles-Capitale"@fr . + . + . + . + . + "2016-11-22T00:00:00"^^ . + "2016-11-22T00:00:00"^^ . + "Ce jeu de données contient: (1) la liste des services publics pour l'égalité de genre en Région bruxelloise (aux niveaux régional et local) ; (2) la liste des organisations engagées en faveur de l'égalité de genre et établies en Région bruxelloise"@fr . + . + . + "Geografische inventarisatie van de monumenten en kunstwerken langs de gewestwegen."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA7B82A94F76AC859CA9016D97ED8CC30 . + "Artistic heritage of regional roads"@en . + "1a871ae1-335b-4624-a736-ac1f7dc80816" . + . + . + . + "Recensement georéférencé des fontaines ou pièces d'eau ainsi que des monuments et œuvres d'art situés le long des voiries régionales."@fr . + "Kunstpatrimonium gewestwegen"@nl . + "2016-12-07T17:10:20.204620"^^ . + "2016-01-01T00:00:00"^^ . + "Georeferenced census of fountains or water features as well as monuments and works of art situated along regional roads."@en . + . + "Patrimoine artistique de voirie régionale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d425426E22B86B83FA02F4EF7369C0BFB . + . + . + "Sites Natura 2000"@fr . + "Brussels Hoofdstedelijk Gewest : stations gerangschikt volgens code, in de Natura 2000 gebieden. Info over het type en de naam van de zone, het type en de naam van het station."@nl . + "Région de Bruxelles-Capitale : stations classées selon leur code, dans les sites Natura 2000. On dispose du type et du nom du site, du code et du nom de la station."@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4127D107771FDA7E9F8A00E87D059913 . + . + . + . + "2016-01-22T18:45:42.667313"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dEEB0094A2E7D15DCFDAB95A7B46B9870 . + "Natura 2000 gebieden"@nl . + "natura_2000_stations.xml" . + . + . + . + . + . + . + . + . + "2015-09-26T00:00:00"^^ . + "Natura 2000 sites"@en . + "Brussels-Capital Region : subsites (stations) classified by their code, within the Natura 2000 sites. Information on the nature and name of each site, code and name of euch subsite."@en . + . + . + . + . + "7ffdc37a-525f-4c5b-be3d-932637334e98" . + "2018-04-09T12:51:13.537928"^^ . + "Liste des rues d'Auderghem"@fr . + . + . + "Lijst van de straten in Oudergem"@nl . + "2018-04-18T07:31:55.286162"^^ . + "Rues d'Auderghem"@fr . + "Oudergemse straten"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDD955DC0FDB19D52F2A0CD5975109B99 . + . + "Images caméras"@fr . + "Cameras images visible on the Brussels Mobility portal."@en . + "aa777e51-9c99-4721-b2af-a0f5be104727" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAAE86DBCB526542448A9F90F078EB5AB . + . + . + . + . + . + "Beelden cameras"@nl . + . + . + . + "Images caméras visibles sur le portail Bruxelles Mobilité."@fr . + "2015-12-18T00:00:00"^^ . + "2016-03-02T13:31:17.867721"^^ . + "Cameras images"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dACD91713CF6E6FA67ADB944192B74F7F . + . + . + "Beelden cameras zichtbaar op de portal Brussel Mobiliteit."@nl . + . + . + . + . + . + "691fd4a0-68ab-407a-b6fd-652299ed08f9" . + "2017-08-07T07:44:14.402128"^^ . + "2018-07-12T06:23:28.826805"^^ . + "Begrotingsrekening"@nl . + "Compte budgétaire"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6BFE13A386C35965B19F74BB97AD1F13 . + . + . + . + "Réserves naturelles et forestières"@fr . + "2015-09-26T00:00:00"^^ . + "Nature and forest reserves"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d43ABD3120EC1C1A6813E6AEF7C545BDA . + "Région de Bruxelles-Capitale : données sur les réserves naturelles et forestières en Région de Bruxelles-Capitale. Noms officiels des réserves; date de l'acte de désignation; type de réserve"@fr . + "Brussels Hoofdstedelijk Gewest : gegevens over de natuur- en bosreservaten in het Brussels Hoofdstedelijk Gewest. Officiële naam van de reservaten; datum van het erkenningsbesluit; type reservaat"@nl . + . + . + . + . + . + "fd03abe0-6cfc-4b75-a8cd-36e2c961421e" . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD1884F002E90538E4433E4FD8A97C07D . + . + . + . + . + "Brussels-Capital Region : data on the nature and forest reserves in the Brussels-Capital Region . Official name of the reserve; date of the legal act identifying the reserve; type of reserve"@en . + "2016-01-22T18:45:40.005866"^^ . + "Natuur- en bosreservaten"@nl . + . + "2016-01-22T18:45:44.491667"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d814AEE2B85DA57FA2EE0D221DB90919B . + "Brussels-Capital Region : location of Brussels drinking water catchment drainage gallery operated by VIVAQUA. Each section of the drainage gallery is named. Classified as protection area 1 of water extraction (with the extraction wells)."@en . + . + . + . + . + . + "2015-09-26T00:00:00"^^ . + "Beschermingszone 1 van winningen - drainage galerij"@nl . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d534D37BA97474E37563EB9DD1EF9B643 . + "Brussels Hoofdstedelijk Gewest : Lokalisatie van de drainage galerij voor drinkwaterwinning in Brussel uitgebaat door VIVAQUA. Bevat de naam van elk drainage galerij gedeelte. Gerangschikt als beschermingszone 1 van winningen (met de waterwinningsputten)"@nl . + "Protection area 1 of water extraction - drainage gallery"@en . + . + . + . + . + . + . + "Région de Bruxelles-Capitale : localisation de la galerie drainante de captage d'eau potable de Bruxelles exploitée par VIVAQUA. On dispose du nom de chaque tronçon de la galerie drainante. Classée comme zone 1 de protection de captage (ainsi que les puits captants)"@fr . + "Zone1_protection_captage_galerie.xml" . + "Zone 1 de protection de captage - galerie drainante"@fr . + . + . + "Associations / bike services"@en . + "De fietsverenigingen en fietsdiensten gekend door Brussel Mobiliteit."@nl . + . + "Associations and bike services known by Brussels Mobility."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD937FFEB481A3833A131484187E7A34A . + . + . + "Les associations et services vélos connus de Bruxelles Mobilité."@fr . + "Associations / services vélos"@fr . + "214ef2a2-fc26-4cb8-b896-0079cac3b8ed" . + "2017-06-23T12:28:40.777064"^^ . + "2016-01-01T00:00:00"^^ . + "Fietsverenigingen / -diensten"@nl . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA34DE8F29628F265CF191304EA59869C . + . + "Toponiemen van de openbare ruimtes"@nl . + "2015-09-28T00:00:00"^^ . + "Toponymie des espaces publics"@fr . + . + . + . + "Brussels-Capital Region : toponymy public spaces is the name of :\n- Highways;\n- Areas of water;\n- Green areas;\n- Cemeteries."@en . + "2016-01-28T10:43:43.660131"^^ . + . + . + . + "Région de Bruxelles-Capitale : la toponymie des espaces publics correspond à la dénomination des :\n- Voies publiques ;\n- Zones d’eau ;\n- Zones vertes ;\n- Cimetières."@fr . + "Brussels Hoofdstedelijk Gewest : de toponiemen komen overeen :\n- Openbare wegen ;\n- Waterzones ;\n- Groenzones ;\n- Kerkhoven en begraafplaatsen."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d865BA987312B3780A14D76AA465C04F5 . + "e53dea13-3be1-48dc-8d72-eeefb8f64bd2" . + "Toponymy"@en . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE4EB246A39AC218EDB19CD5276DEB00A . + . + . + . + . + . + . + . + . + "Population"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8451A75AD09223559923F5074B053D2D . + "2016-10-26T00:00:00"^^ . + . + . + . + . + . + . + . + . + "L'IBSA rassemble et produit de nombreuses données statistiques qui apportent un éclairage chiffré sur la population en Région bruxelloise : population totale, structure de la population par groupes d'âges, sexes ou nationalités, structure des ménages, mouvements de population, projections de population...).\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + . + . + . + . + . + . + . + . + . + "The BISA (Brussels Institute for Statistics en Analysis) compiles and produces a range of statistical data to give quantified insights into the population in the Brussels Region: total population, structure of population by age, sex or nationality, structure of households, movements of population, demographic projections etc.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Population"@en . + . + . + . + . + . + . + . + . + . + "Het BISA verzamelt en produceert tal van statistische gegevens die een cijfermatig beeld schetsen van de bevolking van het Brussels Hoofdstedelijk Gewest: totale bevolking, structuur van de bevolking per leeftijdscategorie, geslacht of nationaliteit, structuur van de huishoudens, loop van de bevolking, bevolkingsprojecties.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + . + . + . + "b3e3ff15-4c5c-4e39-acc7-dd5c9f4ffc23" . + "2016-12-01T13:09:36.305045"^^ . + "Bevolking"@nl . + . + . + . + . + "Ce dataset vous donne l'accès aux informations concernant le temps d'attente aux arrêts en temps réel, qui sont fournies par la société des transports intercommunaux de Bruxelles (STIB) en Belgique.\n"@fr . + "2017-01-16T00:00:00"^^ . + "This dataset gives you acces to the real-time information for the next departure time at stop(s), which is provided by Brussels Intercommunal Transport Company in Belgium."@en . + "Le temps d'attente aux arrêts (temps réel)"@fr . + "Waiting time at stops (real-time)"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3771DDD545C7FF833EAADDD0B8C16985 . + "Wachttijden aan de haltes (real-time)"@nl . + "2018-02-13T14:00:58.321650"^^ . + . + . + . + . + "ff05bc1f-73ec-46aa-8251-f167e806d552" . + "Deze dataset geeft u toegang tot real-time informatie voor de volgende vertrektijden aan de halte(s), die voorzien wordt door het Brussels intercommunaal openbaar transportbedrijf in België."@nl . + . + . + . + . + "Taxi stops"@en . + "De locaties van de taxihaltes in het Brussels Hoofdstedelijk Gewest."@nl . + "Arrêts taxis"@fr . + "Taxi stop"@nl . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF01E40D8DF7FD02ED41C805485FED73F . + "Localisation des aires de stationnements de taxis sur le territoire de la Région de Bruxelles-Capitale."@fr . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8A205C93A6B873873D785461591DA0F5 . + "2016-12-07T17:05:12.426289"^^ . + "2016-01-01T00:00:00"^^ . + "The locations of the Taxi stops in the Brussels-Capital Region. "@en . + "7b0e017f-ef5d-4011-8684-fa40598d0079" . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dECD4EDA046583F5D2E84C1FD67988073 . + "2016-03-01T13:34:42.652737"^^ . + "2016-12-07T17:39:05.042470"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5253BC8E88B23C4F192ADBB6729E406F . + "The road network in the Brussels-Capital Region is divided in regional (generally the most structuring traffic axes) and municipal (generally roads of local character) roads. Approximately 20% of the road network is managed by the region."@en . + "fc062424-139c-481f-93c3-ffb6520df3cc" . + . + . + . + "Gewestwegen"@nl . + "Regional roads"@en . + "Le réseau routier de la Région de Bruxelles-Capitale est réparti en voiries régionales (généralement les axes les plus structurants) et communales (généralement les voiries à caractère local). A peu près 20% de la voirie sont gérés par la Région."@fr . + "Voiries régionales"@fr . + . + "SLD" . + . + . + "In het Brussels Hoofdstedelijk Gewest zijn de openbare wegen opgedeeld in gewestelijke (doorgaans de meest structurerende verkeersassen) en gemeentelijke wegen (doorgaans wegen van lokale aard). Ongeveer 20% van het wegennet worden door het gewest beheerd."@nl . + . + "Education "@en . + "Onderwijs"@nl . + . + . + "Enseignement"@fr . + . + . + . + . + . + . + "Onderwijs is een Gemeenschapsbevoegdheid. Het BISA maakt statistieken die de gegevens van de Vlaamse Gemeenschap en de Franse Gemeenschap combineren : schoolbevolking, aantal scholen. Het BISA stelt ook gegevens op over schoolbevolking buiten de gemeenschappen.\n \nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + "4850d3bf-4c4f-4752-bb2b-019224b4957c" . + . + . + . + "Education being a community competence, the BISA provides statistics that combine data from the Flemish Community and the French Community: student population, number of schools… The BISA also provides student population data for education not provided by the communities.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "2016-11-21T00:00:00"^^ . + . + . + . + . + . + . + . + "2016-12-01T11:22:28.048075"^^ . + . + "L’enseignement étant une compétence communautaire, l’IBSA propose des statistiques qui combinent des données de la Communauté flamande et de la Communauté française : population scolaire, nombre d’établissements scolaires…\nL’IBSA présente également des données de population scolaire pour l’enseignement hors communautés.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4455D8966A2B7AD708AC83922EA55488 . + . + . + . + . + "Information from https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin"@en . + . + . + . + . + . + . + "19181ead-b37f-44d8-a708-d9bddd347c3b" . + . + "Information from https://agenda.brussels"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCF726A6A5E46023651ABC5AA084186DB . + . + . + . + . + "Information provenant de https://agenda.brussels"@fr . + "Agenda.brussels"@en . + . + . + "Agenda.brussels"@nl . + . + . + . + "2017-06-13T00:00:00"^^ . + "2017-06-13T00:00:00"^^ . + "Agenda.brussels"@fr . + . + "Parkings à vélo à contrôle d'accès disponibles sur le territoire de la Région Bruxelloise gérés par CycloParking"@fr . + "b8748e7e-87e5-4599-9d0c-669e086a6e80" . + "2017-09-14T15:36:36.841813"^^ . + "CycloParking"@fr . + "2017-09-13T12:43:52.027637"^^ . + "CycloParking"@nl . + "Fietsparkings met toegangscontrole beschikbaar in het Brussels Hoofdstedelijk Gewest beheerd in het kader van het CyloParking project."@nl . + "CycloParking"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5C3F7A2F5F30A786C7D7CC7E62FC8761 . + . + . + . + . + "Bicycle parking with acces control available in Brussels and managed by the CycloParking project."@en . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA0C35FA6E956E4D4CA0CE01DE7452099 . + . + . + . + . + "{u'fr': u'visit.brussels', u'en': u'visit.brussels', u'nl': u'visit.brussels'}" . + . + "Economy "@en . + "U vindt een beeld van de economische activiteit in het Brussels Hoofdstedelijk Gewest in cijfers: bedrijvendemografie, omzetcijfers, toegevoegde waarde, investeringen en export.\nAl deze gegevens zijn opgesplitst per activiteitstak.\nHier staan ook de resultaten van de vertrouwensenquête bij de Brusselse consumenten die elke maand door de Nationale Bank van België uitgevoerd is.\n"@nl . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCBB7115B7FCCD6D72FB4F18708013013 . + . + . + . + . + . + . + "You will find figures on the economic activity in the Brussels-Capital Region: demography of companies, company turnover, value added, investments and exports.\nAll this data is split according to sectors of activity.\nYou will also find the results regarding the consumer survey realized each month by the National Bank of Belgium."@en . + "2016-12-01T11:18:01.865849"^^ . + "Économie"@fr . + "Vous trouverez un portrait chiffré de l’activité économique en Région de Bruxelles-Capitale: démographie d'entreprises, chiffres d'affaires de celles-ci, valeur ajoutée, investissements et exportations.\nToutes ces données sont réparties par branche d'activité.\nRetrouvez également les résultats de l’enquête de confiance des consommateurs effectuée chaque mois par la Banque Nationale de Belgique.\n"@fr . + . + . + . + . + . + . + . + . + "Economie "@nl . + . + . + . + . + "7b85e211-841a-4b72-9aac-b2c982cc65d6" . + "2016-11-21T00:00:00"^^ . + . + "Brussels-Capital Region : The municipality is the smallest administrative division of the territory of the Federal Belgium. The Brussels Region is composed of nineteen municipalities."@en . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8CA3E4585E2142B0B433B9FE86D089BE . + "Communes"@fr . + "0e1a4a49-52af-4a1d-976a-a7d64f18204e" . + "Municipalities"@en . + "Gemeente"@nl . + . + . + "Brussels Hoofdstedelijk Gewest : de gemeente is de kleinste administratieve indeling van het grondgebied van het federale België. Het Brussels Gewest bestaat uit negentien gemeenten."@nl . + . + . + "2016-01-28T13:48:20.932747"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA93A78FA0ED565530B48133B51C80D47 . + . + . + "TXT" . + "2015-09-28T00:00:00"^^ . + . + . + "Région de Bruxelles-Capitale : la commune est la plus petite division administrative du territoire de la Belgique fédérale. La Région bruxelloise est composée de dix-neuf communes."@fr . + . + "Incidents Fix My Street"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "2018-03-27T00:00:00"^^ . + "Het mobiel en Internet platform Fix My Street laat zowel de burgers als de administraties toe incidenten \nte signaleren binnen de Brusselse openbare ruimte (bepaalde degradaties, verloedering, enz) alsook elke stap van de oplossing van het incident op te volgen.\n\nDe gegevens die ter beschikking worden gesteld zijn :\n•\tIdentificatie van het incident \n•\tCreatie datum\n•\tDatum van de update\n•\tStatus\n•\tLocatie : XY en adres\n•\tInformatie van de verantwoordelijke administratie van het incident : identificatie, naam en type (gemeente, nutsbedrijven, instituten) \n•\tCategorie\n•\tNaam van de verantwoordelijke groep van het incident\n\nAPI beschikbaar op https://api.brussels/store/apis/info?name=fixmystreet&version=1.0.0&provider=admin"@nl . + . + . + . + "The mobile and web platform Fix My Street allows citizens and administrations to flag incidents in the Brussels public space (deterioration, litter, etc.) and to follow its resolution step by step. \nThe available data are: \n•\tIncident identification\n•\tCreation date\n•\tUpdate date\n•\tStatus\n•\tLocation: XY and address\n•\tInformation on the organization responsible for the incident resolution: identification, name and type (municipality, utilities, institution)\n•\tCategory\n•\tName of the group responsible for the incident resolution\n\nAPI available on https://api.brussels/store/apis/info?name=fixmystreet&version=1.0.0&provider=admin "@en . + "01593a26-ed57-498e-bec0-13011a75a773" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC060AC25CC5358540102F99C12D041EC . + "La plateforme internet et mobile Fix My Street permet au citoyen ainsi qu’à l’administration de signaler des incidents dans l’espace public bruxellois (dégradations, malpropreté, etc.) et de suivre chaque étape de résolution de l’incident.\nLes données mises à disposition sont: \n•\tIdentifiant de l’incident\n•\tDate de création\n•\tDate de mise à jour\n•\tStatut \n•\tLocation : XY et l’adresse\n•\tInformation sur l’organisation responsable de l’incident : identifiant, nom et type (commune, impétrant, institution)\n•\tCatégorie\n•\tNom du groupe responsable de l’incident\n\nAPI disponible sur https://api.brussels/store/apis/info?name=fixmystreet&version=1.0.0&provider=admin"@fr . + . + . + "2018-03-27T00:00:00"^^ . + "Incidents Fix My Street"@en . + "Incidents Fix My Street"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d28CE2430A751CE255CD7E68CE15E4DD2 . + . + . + . + . + "Les données rassemblées portent sur le climat, la qualité de l’air, la consommation et la qualité de l’eau, les collectes de déchets, les espaces verts, la biodiversité, la consommation énergétique, etc.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + "De verzamelde gegevens hebben betrekking op het klimaat, de luchtkwaliteit, de consumptie en de kwaliteit van water, ophalingen van afval, groene ruimtes, biodiversiteit, energie, enz.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + . + . + . + "The data compiled relate to climate, air quality, consumption and quality of water, waste collection, green spaces, biodiversity, energy consumption, etc.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Environment and Energy "@en . + "2016-11-22T00:00:00"^^ . + . + "WFS" . + "e8c8d970-e994-4b3b-8a2e-0a416c663342" . + . + . + . + . + . + . + . + "Environnement et énergie"@fr . + . + . + "Milieu en energie"@nl . + . + . + "2016-12-01T11:11:29.746134"^^ . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD1D8D21F78E196C0EAC4F630C62338F3 . + . + . + . + . + . + . + . + "Parce qu’il y en a pour tous les goûts, les idéaux et les budgets, U Talk Freelance a dressé une liste des espaces de coworking à Bruxelles."@fr . + "310f1955-2326-47f4-96f3-5da9899e54bc" . + "2016-09-19T09:23:45.170683"^^ . + "Lijst van coworking in Brussel"@nl . + . + . + "Brussels coworking spaces list"@en . + "Liste des espaces de coworking à Bruxelles"@fr . + "Want er is plaats voor alle smaken, idealen en budgetten, U Talk Freelance een lijst van coworking ruimten in Brussel opgesteld ."@nl . + . + . + . + "Because there is places for all tastes, ideals and budgets, U Talk Freelance compiled a list of coworking spaces in Brussels."@en . + "2018-02-22T13:58:22.043425"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dECF1CA1D321C9F212C6B1265BB43CAFC . + . + . + . + . + . + . + "Verkeersevenementen (incidenten, files...) en wegwerkzaamheden."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d460ACDF20173D1C2522D1EBC6AC6C612 . + "Evénements trafic & travaux"@fr . + "Evénements trafic (incidents, files...), ainsi que les chantiers perturbants."@fr . + . + . + "2016-01-01T00:00:00"^^ . + "Traffic events (incidents, traffic jams, ...) and obtrusive works."@en . + "Verkeersevementen en wegwerkzaamheden"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE8FED21376C255A706D0B32AD51C96C7 . + . + . + . + "915daf6d-2ca5-4ca5-b225-6b0deb935886" . + "2018-01-31T13:23:15.966648"^^ . + "Traffic events and works"@en . + . + "Données sur les parkings publics et leur taux d'occupation en temps réel."@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d971D6B6943E1553498571F3B1806FEC0 . + "2017-01-25T08:33:56.779274"^^ . + "Bezetting Parkings (real time)"@nl . + "Occupation des Parkings (real time)"@fr . + "Parking Occupancy (real time)"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d26462C6E1674C9311F60C22856699190 . + . + "Gegevens over openbare parkings en de bezettingsgraad in real time."@nl . + "486c2e3c-0fb9-48da-841e-2a244db0c39f" . + . + . + "Data on public car parks and their occupancy rate in real-time."@en . + "2018-03-28T07:13:08.042480"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4BA5BF8C8D9081A664F9A09BCB9A332F . + "2016-12-07T17:36:55.636732"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAE9CD16E75EDF652651A76650FB23230 . + "2016-01-01T00:00:00"^^ . + "Deze laag geeft de categorisering van de wegen weer volgens het Iris 2-plan. De hiërarchische indeling maakt een onderscheid tussen autosnelwegen, grootstedelijke wegen, hoofdwegen, verzamelwegen, interwijkwegen en wijkwegen (of lokale wegen). De indeling is geen bestaande toestand van de wegen in Brussel, maar een geplande toestand waarop het beleid moet worden getoetst."@nl . + "Classification hiérarchique des voiries."@fr . + . + . + . + "Hierarchical classification of roads."@en . + "da9e43be-5cf6-47da-9117-5503a6d14ef8" . + "Hiërarchie van wegen"@nl . + "Hiérarchie des voiries"@fr . + "Roads hierarchy"@en . + . + . + . + "a85c8fa5-a178-4000-9d07-3686415c1c7f" . + . + . + "Auderghem - publication des données contenues dans le rapport sur la transparence des rémunérations et avantages des mandataires publics bruxellois"@fr . + "2018-04-09T12:36:49.525381"^^ . + "Rémunérations et avantages des mandataires"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF17445C120E6312AB7E0349C1E2068 . + "Oudergem - gegevens uit het verslag \"ransparantie van bezoldigingen en voordelen van de Brusselse openbare mandatarissen\""@nl . + "Bezoldigingen en voordelen van de mandatarissen"@nl . + . + . + . + "2018-03-05T13:51:11.982825"^^ . + . + . + . + . + "2014-11-03T00:00:00"^^ . + "Région de Bruxelles-Capitale : données sur l'ensemble des 3 masses d'eau de surface de la Région bruxelloise, délimitées dans le cadre de la Directive et de l'Ordonnance Cadre Eau : code européen, code bruxellois et nom de la masse d'eau, district hydrographique."@fr . + "Brussels-Capital Region : data on the 3 Surface Water Bodies of the Area of Brussels, defined under the Directive and the Ordinance Water : code European, code of Brussels and name of the water mass, hydrographic district."@en . + . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD10AA77E877FA891AAA2CDE19EAD3006 . + "Oppervlaktewaterlichamen"@nl . + "Masses d'eau de surface"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d228CEDC4629B1AA6E352033023934C0C . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : gegevens over het geheel van de 3 oppervlaktewaterlichamen in het Brussels Gewest, afgebakend in kader van de Kaderrichtlijn- en Ordonnantie Water : Europese code, Brusselse code en naam van het oppervlaktewaterlichaam, stroomgebiedsdistrict."@nl . + "Surface water bodies"@en . + "80993dc2-303b-4798-b500-e38499d13e4c" . + "2016-01-22T18:45:43.614890"^^ . + . + . + . + "Brussels-Capital Region : the entity \"Rail Block\" (railway area) identifies portions of the territory occupied by the railway. These surfaces not lines representing the rails.\n\nThere are:\n\n1. areas of railway located at [0] (ie ground level \"natural\", abbreviated RB-0):\n- Areas in a physical island\n- Crossing\n2. areas of railway located at [-] (ie ground level, abbreviated RB-M):\n- Parts of railway under a road (invisible from the sky)\n- tunnels\n3. zones thereof located at [+] (ie higher level short RB-P):\n- Parts of railway over a road or a river (visible from the air)\n\nThe contours of the airspaces of the railway were built from the topographic map UrbIS-Topo (photogrammetric surveys).\n\nThe tunnels were designed based on drawings provided by SNCB."@en . + "2016-01-28T13:38:12.005742"^^ . + . + . + "2015-09-28T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4DA7ED9210B6D18176CED31134A30C72 . + "Rail block"@en . + "Spoorweg Zone"@nl . + . + . + "Brussels Hoofdstedelijk Gewest : de entiteit « Rail Block » (spoorwegzone) is dat deel van het grondgebied dat ingenomen wordt door de spoorweg. Het betreft oppervlakken en geen lijnen die de sporen of rails\nvoorstellen.\n\nMen maakt onderscheid tussen:\n\n1. de spoorwegzones gelegen op niveau [0] (dat wil zeggen op het \"maaiveld\", afgekort RB-0):\n- zones in een fysisch huizenblok\n- overwegen\n2. de spoorwegzones gelegen op niveau [-] (dat wil zeggen ondergronds, afgekort RB-M):\n- spoorwegdelen onder een weg (onzichtbaar vanuit de lucht)\n- tunnels\n3. de spoorwegzones gelegen op niveau [+] (dat wil zeggen bovengronds, afgekort RB-P):\n- spoorwegdelen boven een weg of een waterloop (zichtbaar vanuit de lucht)\nDe grenzen van de bovengrondse zones van de spoorweg werden opgebouwd met behulp van de topografische kaart UrbIS-Topo (fotogrammetrische opmetingen). De tunnels werden getekend op basis van de plannen zoals aangeleverd door de NMBS."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE3F6AC9451B0E3655CEA6A54FE63EA51 . + . + . + . + "5a08229f-d2ed-4b75-b588-1b07994aacc8" . + "Région de Bruxelles-Capitale : l'entité « Rail Block » (zone de chemin de fer) identifie les portions du territoire occupées par le chemin de fer. Il s'agit de surfaces et non de lignes représentant les rails. \n\nOn distingue:\n\n1. les zones de chemin de fer situées au niveau [0] (càd niveau du terrain \"naturel\", abrégé RB-0) :\n- zones dans un îlot physique\n- passages à niveau\n2. les zones de chemin de fer situées au niveau [-] (càd niveau souterrain, abrégé RB-M) :\n- parties de chemin de fer sous une voirie (invisibles du ciel)\n- tunnels\n3. les zones de chemin de fer situées au niveau [+] (càd niveau supérieur, abrégé RB-P) :\n- parties de chemin de fer au dessus d’une voirie ou d’un cours d'eau (visibles du ciel)\n\nLes contours des zones aériennes du chemin de fer ont été construits à partir de la carte topographique UrbIS-Topo (levés photogrammétriques). \n\nLes tunnels ont été dessinés sur base de plans fournis par la SNCB."@fr . + "Zones de chemin de fer"@fr . + . + . + . + "2015-03-30T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF2BF65EE86CB1984D6B13DB41966F8CA . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE8E36CBF344E596FDEB640126C17A5ED . + . + . + . + "Waterzone"@nl . + . + . + "Région de Bruxelles-Capitale : l'entité « Water Block » (ou zone d'eau) localise et identifie les différents corps d'eau présents sur le territoire de la Région bruxelloise (canal, étangs, Senne, cours d'eau,...).\n\nOn distingue:\n1. les zones d'eau situées au niveau [0] qui sont visibles du ciel (abrégé WB-0)\n2. les zones d'eau (uniquement pour le canal) situées au niveau [-] sous la voirie (invisibles du ciel) (abrégé WB-M)"@fr . + "Brussels-Capital Region : the entity \"Water Block\" (or water area) locates and identifies various water bodies in the territory of the Brussels-Capital Region (channel, ponds, Senne river, ...).\n\nThere are:\n1. water areas located at [0] which are visible from the air (abbreviated WB-0)\n2. water areas (for the channel) located at [-] on the road (invisible from the sky) (abbreviated WB-M)"@en . + "Zones d'eau"@fr . + . + . + "02a5920e-048c-480e-8ab8-8575731b409e" . + "2016-01-28T13:44:04.455084"^^ . + . + . + . + "Brussels Hoofdstedelijk Gewest : de entiteit « Water Block » (of waterzone) lokaliseert en identificeert de verschillende waterlichamen die aanwezig zijn op het grondgebied van het Brussels Gewest (kanaal, vijvers, Zenne, waterlopen...).\n\nMen maakt onderscheid tussen:\n\n1. de waterzones gelegen op niveau [0], die zichtbaar zijn vanuit de lucht (afgekort WB-0)\n2. de waterzones (alleen voor het kanaal) gelegen op niveau [-] onder de weg, en die onzichtbaar zijn vanuit de lucht (afgekort WB-M)"@nl . + "Water block"@en . + . + . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFB300147DD6B18D8497AD96D31217417 . + . + . + "2015-10-13T00:00:00"^^ . + "Beschermde gebieden op Europees niveau"@nl . + "Brussels-Capital Region : protected sites at the European level in implementation of the following European legislation: Habitats directive-92/43/EEC (Natura 2000 network), Directive concerning urban waste water treatment-91/271/EEC (sensitive areas) and Nitrates Directive-91/676/EEC (vulnerable zones"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d88A7660CEF7FFFA91F61CEF98F83D0A9 . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : zones in het Brussels gewest die een Europees beschermingsstatuut genieten op basis van de volgende richtlijnen : Habitatrichtlijn-92/43/EEC(Natura 2000 netwerk), Richtlijn Stedelijk Afvalwater-91/271/EEC (gevoelig gebied) en Nitraatrichtlijn-91/676/EEC (kwetsbare zone)"@nl . + . + . + "Région de Bruxelles-Capitale : zones bénéficiant d'un statut de protection européen en Région bruxelloise au titre des directives suivantes : Directive habitats-92/43/EEC (réseau Natura 2000), Directive Eaux Résiduaires Urbaines-91/271/EEC (zone sensible) et Directive Nitrates-91/676/EEC (zones vulnérables)"@fr . + "rpa.xml" . + "Zones protégées au niveau européen"@fr . + . + . + "2016-01-22T18:45:37.258450"^^ . + "Protected areas at the European level"@en . + . + . + "2018-04-18T07:31:32.485899"^^ . + . + . + . + "e745bf45-9557-482b-ad97-58aeb268cda8" . + "2018-04-12T13:14:49.417327"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7D085BBA789A8F90DD87B82AA46E764F . + "Oudergem - Bevolking"@nl . + "Données relatives à la Population d'Auderghem"@fr . + "Auderghem - Population"@fr . + . + . + . + "Gegevens over de bevolking van Oudergem"@nl . + . + "This dataset contains:\n* the municipal equal opportunity officers in the Brussels Region (legislature 2012-2018); \n* the Brussels signatory municipalities of the European Charter for Equality of Women and Men role in Local Life (CEMR) "@en . + "Gender equality at local level in the Brussels Region"@en . + . + . + "Deze dataset bevat:\n* de lijst van lokale mandatarissen bevoegd voor Gelijke Kansen in de 19 gemeenten van het Brussels Gewest (legislatuur 2012-2018); \n* de lijst van de Brusselse ondertekenende gemeenten van het Europees charter voor gelijkheid van vrouwen en mannen op lokaal vlak (CEMR)\n"@nl . + . + . + . + . + "Égalité de genre au niveau local en Région bruxelloise"@fr . + "2016-11-22T00:00:00"^^ . + "5230a13a-283f-4e36-a65c-72ad20b9b35a" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d66F755905DD7C8BC678B87D3F51BA727 . + "Gendergelijkheid op lokaal niveau in het Brussels Gewest"@nl . + "Ce jeu de données contient:\n* la liste des mandataires locaux à l'Egalité des Chances en Région bruxelloise (législature 2012-2018); \n* la liste des communes bruxelloises qui ont signé la Charte européenne pour l'égalité des femmes et des hommes dans la vie locale (CCRE)"@fr . + . + . + "{u'fr': u'Bruxelles Urbanisme et Patrimoine', u'en': u'Brussels Planning and Heritage', u'nl': u'Brussel Stedenbouw en Erfgoed'}" . + . + "2016-11-22T00:00:00"^^ . + . + "Cet ensemble de données concerne la représentation des femmes dans l'espace public symbolique en Région bruxelloise"@fr . + "Femmes et espace urbain symbolique (Région bruxelloise)"@fr . + "Deze dataset betreft de vertegenwoordiging van vrouwen in de symbolische openbare ruimte van het Brussels Gewest"@nl . + "Women and symbolic urban space (Brussels Region)"@en . + "2016-11-23T00:00:00"^^ . + . + . + . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d11C9DD3E5B6747E0910EF8D32C58FB41 . + . + . + . + "This dataset focuses on the representation of women in the symbolic public space of the Brussels Region"@en . + "2016-11-23T00:00:00"^^ . + "027694ab-3c73-4f04-b192-8aca009bddb8" . + "Vrouwen en symbolische stedelijke ruimte (Brussels Gewest)"@nl . + . + "dd6ffbbe-7af2-4c60-9a6f-e513a53eb0e3" . + "Région de Bruxelles-Capitale : l'entité SA correspond aux axes des tronçons de rues de la Région de Bruxelles-Capitale"@fr . + "2016-01-28T13:43:38.797472"^^ . + . + . + . + . + "Axes des tronçons de rues"@fr . + "Hoofdlijn van de stukken van straat"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d760861C56057322502B8EBBDEAF93EB2 . + "Street axis"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBC8157FD21DE40C42E8FCA06367923EA . + . + . + . + "Brussels Hoofdstedelijk Gewest : dit entiteit stemt met de hoofdlijnen van de stukken van straat overeen"@nl . + "Brussels-Capital Region : SA entity corresponds to the axes of street sections of the Region of Brussels-Capital"@en . + . + . + "2015-03-30T00:00:00"^^ . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAD7E987C49F350EAC509D704640C79E2 . + "Brussels Hoofdstedelijk Gewest : habitats gerangschikt volgens hun EU-code, in de Natura 2000 gebieden. Info over de code van het station en een beknopte beschrijving van het habitat"@nl . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5C16FC92568D654233E103BE5B50E2D0 . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "Brussels-Capital Region : habitats classified by their EU code, within the Natura 2000 sites. Informations on the code of the station and a brief description of the habitat."@en . + "Région de Bruxelles-Capitale : habitats classés selon leur EU-code, dans les sites Natura 2000. On dispose du code de la station, d'une description sommaire de l'habitat."@fr . + "2015-09-26T00:00:00"^^ . + "Natura 2000 habitats"@en . + . + . + . + . + . + . + "Habitats Natura 2000"@fr . + "2016-01-22T18:45:48.317059"^^ . + "Natura 2000 habitats"@nl . + "natura_2000_habitats.xml" . + . + . + . + "Although early childhood services are a Community competence, they are vitally important for the Brussels-Capital Region. There is a high demand due to the current demographic growth in the Region.\nThe statistics presented relate mainly to the number and accommodation capacity of early childhood facilities in the Brussels Region.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Jonge kinderen"@nl . + . + . + . + "Early childhood"@en . + "Petite enfance"@fr . + . + . + . + "Quoique de compétence communautaire, l’accueil de la petite enfance présente des enjeux essentiels pour la Région de Bruxelles-Capitale. L’essor démographique en cours dans la Région fait peser sur ce service une demande forte.\nLes statistiques présentées portent notamment sur le nombre de milieux d’accueil et la capacité de ceux-ci.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD669ACFEF50FC023209F2B0AE60F7120 . + "Hoewel kinderopvang tot de bevoegdheden van de gemeenschappen behoort, is dit thema zeer belangrijk voor het Brussels Hoofdstedelijk Gewest. De sterke demografische ontwikkeling in het Gewest veroorzaakt een zeer grote vraag naar deze dienst.\nDeze statistieken hebben met name betrekking op het aantal kinderopvangvoorzieningen en de capaciteit.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + . + . + . + "2016-11-18T00:00:00"^^ . + "2016-11-18T16:07:11.980479"^^ . + "461ca649-85cd-4b6a-9eb4-eed94d379845" . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "2016-05-10T20:56:25.585450"^^ . + "Brussels-Capital Region : location of the different groundwater level monitoring sites (or piezometric sites) managed by Brussels Environment (IBGE-BIM) under the surveillance monitoring of the groundwater status in accordance with the Ordonnance and the Water Framework Directive. Indication of the analyzed water body, the European Code and the Brussels code of the monitoring site."@en . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de meetstations (of piëzometers) van het niveau van het grondwater beheerd door Leefmilieu Brussel (BIM) in kader van de monitoringscontrole van de algemene staat van de grondwaterlichamen, conform aan de ordonnantie en de Kaderrichtlijn Water. Met aanduiding van het geanalyseerde grondwaterlichaam, de Europese code en de Brusselse code van het meetstation"@nl . + "Groundwater level monitoring network - European reporting"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d699D4871CF97CED30F05CBAF55BB9BA6 . + . + . + . + "8d65ba4a-2982-434a-9c91-33df3ee666f8" . + . + "{u'fr': u'innoviris', u'en': u'innoviris', u'nl': u'innoviris'}" . + "Région de Bruxelles-Capitale : localisation des stations de mesure du niveau (ou stations piézométriques) des eaux souterraines gérées par Bruxelles Environnement (IBGE), dans le cadre des contrôles de surveillance de l'état général des eaux souterraines, conformément à l'Ordonnance et à la Directive Cadre Eau. Indication de la masse d'eau analysée, du code européen et du code bruxellois de la station de mesure"@fr . + "Monitoring van het niveau van de grondwaterlichamen - Europese rapportering"@nl . + "Réseau de surveillance du niveau des eaux souterraines - reporting européen"@fr . + . + . + "2015-09-26T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFD1F4A91C5C32078059820585BA1C4FA . + . + . + . + "5253ae6b-78b9-4e79-b1e5-b6822ededf14" . + "2016-03-03T14:21:35.946129"^^ . + "Journal du Conseil"@fr . + "Journal du Conseil"@nl . + "Journal du Conseil"@en . + "Journal du Conseil"@en . + "Journal du Conseil"@fr . + "Journal du Conseil"@nl . + "2016-03-03T13:50:22.792222"^^ . + . + . + "Brussels Hoofdstedelijk Gewest : de stroomgebieden (of deelstroomgebieden) in het Brussels Gewest en zijn omgeving"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC3946BF809556E53D66CF2BD28D63A54 . + . + . + . + . + "Sous_bassin_hydro.xml" . + . + . + . + "Sub-drainage-basin (or catchment area) of the rivers"@en . + "Sous bassins versants"@fr . + "Région de Bruxelles-Capitale : étendue des sous bassins hydrographiques (ou sous bassins versants) en Région bruxelloise et sa périphérie"@fr . + "Deelstroomgebieden"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAE0DD8BAFFA0C1A0E67B71CAC6AFD7F3 . + . + . + "Brussels-Capital Region: extent of sub-drainage-basin (or sub-catchment area) of the rivers in the Brussels Region and surrounding area"@en . + "2016-01-22T18:45:36.362311"^^ . + . + . + "2015-09-26T00:00:00"^^ . + . + "aef6dcab-57b3-438b-91e6-9693d33b43b1" . + "2017-01-12T00:00:00"^^ . + "GAN – OPHAALKALENDER HUISAFVAL"@nl . + . + "Ce web service en JSOn renvoie pour une adresse validée UrbIS les jours de collectes entre 2 dates pour les différents flux de produit.\nIl renvoie également les informations relatives aux bulles à verres et les parcs à conteneurs pour une adresse validée UrbIS.\nIl dispose également d’une fonction permettant d’extraire la liste des adresses possibles.\n\nCe web service nécessite la signature d’une convention entre Bruxelles-Propreté et l’entité juridique souhaitant exploiter les données. Cette convention est dérivée de la Licence ouverte du CIRB (Cette licence a été créée à partir de la licence ouverte Etalab, mission sous l'autorité du Premier ministre français chargée de l'ouverture des données publiques et du développement de la plate-forme française Open Data (avec l'autorisation de Etalab). www.etalab.gouv.fr - http://www.etalab.gouv.fr/article-etalab-publie-la-licence-ouverte-open-licence-86708897.html .\n\nToute demande de renseignement ou d’accès doit se faire auprès de Monsieur Vincent Jumeau, Directeur général de Bruxelles-Propreté soit par email : (ict@arp-gan.brussels), soit par courrier avenue de Broqueville 12 à 1150 Bruxelles"@fr . + "ARP – Calendrier de collectes porte-à-porte des ménages"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4C811EFDF1207087A1AA510D02F09958 . + . + . + "Deze JSOn web service verstuurt voor een gevalideerd UrbIS adres de ophaaldagen tussen 2 data voor de verschillende productstromen.\nZe verstuurt ook informatie over glascontainers en containerparken voor een gevalideerd UrbIS adres.\n\nZe beschikt ook over een functie die mogelijke adressen kan selecteren.\n\nDeze web service vereist de handtekening van een overeenkomst tussen Net Brussel en de juridische entiteit die de gegevens wil gebruiken. Deze overeenkomst is afgeleid uit de open Licentie van het CIBG (Deze licentie werd gecreëerd op basis van de open Licentie Etalab, een missie onder gezag van de Franse eerste minister belast met het openbaar maken van publieke gegevens en de ontwikkeling van een Frans Open Data platform me de toestemming van Etalab.\n\nwww.etalab.gouv.fr - http://www.etalab.gouv.fr/article-etalab-publie-la-licence-ouverte-open-licence-86708897.html ).\n\nElke aanvraag voor inlichtingen of toegang moet gebeuren via Vincent Jumeau, algemeen directeur van Net Brussel, ofwel via mail (ict@arp-gan.brussels), ofwel via een brief naar de Broquevillelaan 12 in 1150 Brussel."@nl . + "2012-07-01T00:00:00"^^ . + "This web service in JSOn sends back for each UrbIS validated address the collection days between 2 dates for the different products.\nIt also sends back the information related to glass banks and container parks for any UrbIS validated address.\n\nIt also allows for the extraction of a list containing the possible addresses.\n\nThis web service requires the signature of a convention between Bruxelles-Propreté and the legal entity wishing to exploit the data. This convention is derived from CIRB’s open License (This license is based upon the Etalab open licence, mission under the authority of France’s Prime Minister in charge of opening the public data and developing the French Open Data open platform – with the authorization of Etalab)\n\nwww.etalab.gouv.fr - http://www.etalab.gouv.fr/article-etalab-publie-la-licence-ouverte-open-licence-86708897.html.\n\nAny request for information or access must be made to Mr. Vincent Jumeau, General Manager of Bruxelles-Propreté either by email : ict@arp-gan.brussels, either by courier : avenue de Broqueville 12, 1150 Brussels."@en . + "ARP – COLLECTIONS CALENDAR - HOUSEHOLDS HOUSE-TO-HOUSE COLLECTIONS."@en . + . + . + . + . + "Brussels Hoofdstedelijk Gewest: lokalisatie van de verschillende meetstations voor de luchtkwaliteitsmonitoring. Stations van telemetrisch, niet-telemetrisch (bemonstering met laboratoriumanalyse) en weernetwerken zijn inbegrepen. De verschillende gemeten parameters worden gespecificeerd voor elk station.\nDocumentatie :\nhttp://document.environnement.brussels/opac_css/elecfile/QAir%20Rpt0911%20ssAnn%20B%20C%20D%20E%20bis%20nl"@nl . + "80003a22-2e8b-4dd3-89ea-5b76433e6faf" . + "Monitoring van de luchtkwaliteit"@nl . + "Monitoring network of the air quality"@en . + "2015-10-02T00:00:00"^^ . + . + . + "Brussels Capital Region: location of the different stations measuring the air quality. Stations of the telemetric, non-telemetric (sampling with laboratory analysis) and weather networks are included. The measured parameters are specified for each station.\nNote :\nIn French : \nhttp://documentation.bruxellesenvironnement.be/documents/QAir_Rpt0911_corr_ssAnnexesB_C_D_E_fr.PDF\nIn Dutch :\nhttp://document.environnement.brussels/opac_css/elecfile/QAir%20Rpt0911%20ssAnn%20B%20C%20D%20E%20bis%20nl"@en . + "2016-01-22T18:45:45.479489"^^ . + . + . + . + . + "{u'fr': u'Bruxelles Mobilit\\xe9', u'en': u'Brussels Mobility', u'nl': u'Brussel Mobiliteit'}" . + . + . + "Région de Bruxelles-Capitale : localisation des différentes stations de mesure de la qualité de l'air. Sont reprises les stations du réseau télémétrique, non-télémétriques (échantillonnages avec analyse en laboratoire) et météorologique. Les différents paramètres mesurés sont précisés pour chaque station.\nDocument explicatif :\nhttp://documentation.bruxellesenvironnement.be/documents/QAir_Rpt0911_corr_ssAnnexesB_C_D_E_fr.PDF"@fr . + "Réseau de surveillance de la qualité de l'air"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7E7C0936E56BF82A522F26AE5A43FB59 . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6016774B7208E2622A844DB7CFFDA618 . + . + . + . + "Relevé de la signalisation verticale le long des voiries régionales de la région de Bruxelles-Capitale. Les panneaux sont représentés par leur géométrie (utiliser le fichier de style pour les visualiser dans un outil SIG)."@fr . + "Signalisation verticale"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA291F1876327E0F46C1592CAFC00625A . + . + . + "Locatie van de verticale signalisatie langs de gewestwegen van het Brussels Hoofdstedelijk gewest. De borden worden weergegeven als geometrie. Gebruik het stijlbestand om de juiste kleuren weer te geven in een GIS-omgeving."@nl . + . + "Road signs"@en . + "Verticale signalisatie"@nl . + "c2843c7e-9e1b-454c-b08e-24f8884c6da9" . + . + . + . + "2016-12-07T17:35:59.477262"^^ . + "2016-09-14T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d06DAC25EAEC984A70A02122B89D62E21 . + "Road signs along regional roads of the Brussels-Capital Region. The panels are represented by their geometry (use the style file to view in a GIS tool)."@en . + . + . + . + "Cambio carsharing vous donne accès 24h/24 et 7j/7 à différents modèles de voitures selon le besoin. C'est le plaisir de profiter d'une voiture sans les inconvénients (assurances, entretien, nettoyage). \n\nDe plus, les voitures partagées bénéficient d'avantages considérables en matière de stationnement dans tout Bruxelles. Vous ne payez que lorsque vous vous situez en zones rouges. C'est le moment de devenir membre ! "@fr . + "2016-02-09T00:00:00"^^ . + "2016-02-09T00:00:00"^^ . + . + . + . + "ec5a7b30-3610-4826-b992-3218dedd7dd1" . + "Stations Cambio"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d35CE5C409A4AB7C9FF58AA0B93F26BEF . + "Cambio standplaatsen"@nl . + "Cambio stations"@en . + "Met cambio reserveer je je auto naar keuze, op het moment dat je hem nodig hebt. Het comfort van een eigen wagen, zonder de bijhorende kopzorgen (onderhoud, kuisen, verzekering). Jij rijdt, cambio doet de rest. \n\nBovendien, hoeven cambio-gebruikers geen parkeergeld te betalen in het Brusselse Gewest (met uitzondering van de rode and de gele zones). "@nl . + "Cambio carsharing allows you to book your desired type of car when you need one. We offer you the comfort of a car without the hassle of owning one (maintenance, cleaning, insurance). You enjoy the ride, cambio takes care of the rest. \n\nFurthermore, cambio users are not required to pay parking fees in the Brussels-Capitale region (except in the red and yellow zones). \n"@en . + . + . + "Regional cycle routes"@en . + . + . + "ICR"@fr . + "De gewestelijke fietsroutes zijn aanbevolen fietswegen voor verplaatsingen op middellange en lange afstand die door verschillende gemeenten lopen. In het algemeen volgen deze routes lokale wegen, omdat het verkeer er minder druk is, minder snel verloopt en dus minder stresserend is dan op de hoofdwegen. Vermits bepaalde natuurlijke of kunstmatige obstakels (brug over een dal, een kanaal, een autosnelweg, doorgang onder een spoorweg, enz.) moeten worden genomen, lopen de routes echter soms ook over grotere verkeersaders."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d784D09B24EB57FC440535C124B24A192 . + "2017-09-13T22:40:44.070842"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6EF62B1621AA24B99DD086E71675F4C0 . + . + "The regional cycle routes are suggested paths for travel to medium and long-distance biking."@en . + "2016-01-01T00:00:00"^^ . + "Les itinéraires cyclables régionaux sont des cheminements recommandés pour des déplacements à vélo de moyenne et longue distance à travers plusieurs communes. En règle générale, ces itinéraires empruntent des voiries locales, où le trafic est moins dense, moins rapide, et donc moins stressant que sur les voiries principales. Mais le franchissement de certains obstacles naturels ou artificiels (pont franchissant une vallée, le canal, une autoroute, passage sous une ligne de chemin de fer, etc.) ramène parfois les itinéraires sur les grands axes."@fr . + . + . + . + "a4abf87b-5e18-4b90-9ab6-48d97dc64ff2" . + "GFR"@nl . + . + "Brussels-Capital Region : this directive has as an aim the prevention of the major accidents implying of dangerous substances and the limitation of their consequences for the man and the environment, in order to ensure in a coherent and effective way in all the country of the high levels of protection.\nThe geographical data file SEVESO consists of several classes of objects which make it possible to locate all the SEVESO sites on the territory of the area of Brussels."@en . + "0bacf5ca-c51d-42d2-8a5a-7519cae09564" . + "Seveso sites"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8A9A00C88B7C0DC5107860615AA8D5BA . + . + . + "2016-01-22T18:45:41.797188"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBB56969207EDE5D005DC5CB099E7769C . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest: deze richtlijn heeft als onderwerp de preventie van de belangrijkste ongevallen die gevaarlijke stoffen impliceren en de beperking van hun gevolgen voor de mens en het milieu, teneinde op samenhangende en efficiënte wijze in het heel land van de hoge beschermingsniveau's te waarborgen.\nHet spel van geografische gegevens SEVESO bestaat uit verschillende objectklassen die het mogelijk maken om alle plaatsen SEVESO op het grondgebied van Regio van Brussel te localiseren."@nl . + . + . + "Région de Bruxelles-Capitale : cette directive a pour objet la prévention des accidents majeurs impliquant des substances dangereuses et la limitation de leurs conséquences pour l'homme et l'environnement, afin d'assurer de façon cohérente et efficace dans tout le pays des niveaux de protection élevés.\nLe jeu de données géographiques SEVESO est constitué de plusieurs classes d'objets qui permettent de localiser tous les sites SEVESO sur le territoire de la Région de Bruxelles."@fr . + "Sites Seveso"@fr . + . + . + . + . + "2015-09-26T00:00:00"^^ . + "Seveso sites"@en . + . + . + . + . + "La Région de Bruxelles-Capitale compte une large population au travail ou en âge de travailler et attire également de très nombreux travailleurs habitant dans les deux autres régions du pays.\nCombien de travailleurs compte la Région ? Dans quels secteurs d'activité sont-ils actifs et pour quel salaire ? Où se retrouve la population active occupée bruxelloise ? Qu'en est-il des chiffres du chômage ?\nRetrouvez les statistiques de l'IBSA concernant le marché du travail, organisées par :\nlieu de résidence des actifs (population en âge de travailler, population active occupée, chômage, etc..) et\nlieu de travail (nombre de salariés, indépendants, salaires,...)\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + . + . + "Arbeidsmarkt "@nl . + "Labour market "@en . + "Marché du travail"@fr . + . + . + . + . + . + . + . + . + . + . + . + "In het Brussels Hoofdstedelijk Gewest is een groot deel van de bevolking actief of op arbeidsleeftijd; het Gewest trekt eveneens veel werknemers aan die in de twee andere gewesten wonen.\nHoeveel werknemers telt het Gewest? In welke sectoren werken zij en voor welk loon? Waar concentreert de Brusselse werkende beroepsbevolking zich? Hoe zit het met de werkloosheidscijfers?\nU vindt de statistieken van het BISA over de arbeidsmarkt, georganiseerd per:\n woonplaats van de actieve bevolking (bevolking op arbeidsleeftijd, werkende beroepsbevolking, werkloosheid enz.) en\n werkplaats (aantal loontrekkenden, zelfstandigen, lonen,...).\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + "2016-12-01T11:20:48.433485"^^ . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "df093b9f-a601-4f51-ae92-752e0eba7650" . + . + . + . + . + "2016-11-21T00:00:00"^^ . + "The Brussels-Capital Region has a large working population or population of working age and also attracts a very high number of workers who live in the two other Regions of the country.\nHow many workers does the Region have ? In which sectors are they employed and at which salary ? Where is the employed population in Brussels? What about the unemployment figures ?\nFind BISA statistics on the job market sorted by:\nplace of residence of the labour force (population of working age, working population, unemployment, etc.) and place of work (numbers of employees, self-employed, salaries, etc.).\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + . + . + . + . + . + "{u'fr': u'Bruxelles \\xe9conomie et emploi', u'en': u'Bruxelles \\xe9conomie et emploi', u'nl': u'Brussel economie en werkgelegenheid'}" . + . + "{u'fr': u\"Le r\\xe9gulateur bruxellois pour l'\\xe9nergie\", u'en': u'Brussels energy regulator', u'nl': u'De Brusselse regulator voor energie'}" . + . + . + . + . + . + "{u'fr': u'parking.brussels', u'en': u'parking.brussels', u'nl': u'parking.brussels'}" . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD63D9C34B295EBB7A9984D672FA59E5A . + "Perspectives économiques régionales"@fr . + . + . + "2017-06-23T12:29:02.059642"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFBDE59B26A85712614D2459BF35134B6 . + "5a3ddb52-5465-4703-9557-f315b1e057c6" . + . + . + "Collecto is a collective taxi service available every day between 11 PM and 6 AM everywhere in the Brussels-Capital Region."@en . + "Collecto haltes"@nl . + "2016-01-01T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB30FF5D77D3D091E88D5B3487AF7B7D2 . + "Région de Bruxelles-Capitale"@fr . + . + "Collecto est un service de taxis collectifs disponible 7 jours sur 7 entre 23 heures et 6 heures du matin sur tout le territoire de la Région de Bruxelles-Capitale."@fr . + "Collecto stops"@en . + "Arrêts Collecto"@fr . + "De locaties van de Collecto stopplaatsen in het Brussels Hoofdstedelijk Gewest. Collecto is een collectieve taxidienst die zeven dagen op zeven beschikbaar is tussen 23 uur 's avonds en 6 uur 's ochtends, en dit in het hele Brussels Hoofdstedelijk Gewest. Collecto bedient momenteel meer dan 200 vertrekpunten (gelegen aan de MIVB-haltes).\n"@nl . + . + . + . + "Publicaties over gendergelijkheid in de Brusselse regio"@nl . + "publications"@fr . + "Publications sur l'égalité de genre en région bruxelloise"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF383B1DB8C9D1A1DA642D66F5A9AA438 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF383B1DB8C9D1A1DA642D66F5A9AA438 "Mathieu Tonglet" . + "Deze dataset bevat bibliografische referenties aan ondermeer wetgeving en beleidsteksten, aan lobbydocumenten en aan statistische studies met betrekking tot gendergelijkheid en de situatie van vrouwen in het Brussels Hoofdstedelijk Gewest. Zij zijn afkomstig uit de online catalogus van het Amazone Documentatiecentrum Genderbeleid."@nl . + "2016-11-24T11:33:58.341408"^^ . + "2016-12-23T13:02:41.407536"^^ . + "Région de Bruxelles-Capitale"@fr . + . + "Ce jeu de données contient des références bibliographiques de textes législatifs, documents politiques, notes, communications, rapports statistiques, etc. relatifs à l’égalité de genre et à la situation des femmes et des hommes dans la Région bruxelloise. Elles proviennent du catalogue bibliographique en ligne du Centre de Documentation d’Amazone sur la Politique de Genre. "@fr . + "Publications on gender equality in the Brussels region"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE41F72C47DA8F5682351D2450B0B4EEB . + "0cdaf543-0038-4723-849e-ff1197c54a57" . + "bibliographic resources"@fr . + . + . + "Brussels"@fr . + "gender equality"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d81D532A18EC908CA7102BCBAEE0DF398 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6F96C7ECE63147054BCFA5EA250CB164 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6F96C7ECE63147054BCFA5EA250CB164 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6F96C7ECE63147054BCFA5EA250CB164 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Brussel Mobiliteit heeft het Centre d’études sociologiques (CES) de l'Université Saint-Louis (USL-B) gevraagd een samenvatting te maken van de beschikbare gegevens in Brussel. Deze opdracht gebeurt in nauwe samenwerking met andere onderzoekers (VUB en ULB), zodat uitwisseling en een interdisciplinaire benadering van het mobiliteitsprobleem aangemoedigd worden."@nl . + "2016-03-02T14:10:25.578113"^^ . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7597974B9D32C6C590CB13BBC38C8267 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7597974B9D32C6C590CB13BBC38C8267 "CIRB" . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Bruxelles Mobilité a confié une mission de synthèse des très nombreuses données disponibles au Centre d’études sociologiques (CES) de l'Université Saint-Louis - Bruxelles (USL-B). Ce travail s’effectue en collaboration avec des chercheurs d’autres universités (ULB et VUB), de manière à favoriser les échanges et une approche interdisciplinaire de la problématique des mobilités."@fr . + . + "Katernen van het Kenniscentrum van de mobiliteit"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC420368CAA042F2B405D9E8C7878087C . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Brussels Mobility has entrusted the task of synthesis of many data available to the Centre for Sociological Studies (CES) at the Saint Louis University - Brussels (USL-B). This work is done in collaboration with researchers from other universities (ULB and VUB), in order to promote exchanges and an interdisciplinary approach to the issue of mobility."@en . + "d23ddde7-2451-4444-8821-cf72f3f61b75" . + "2016-01-01T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE5C3AB4885F967E473A5B3181FC3CEF0 . + "Notebooks of the Observatory of mobility"@en . + . + . + . + . + "Cahiers de l’Observatoire de la mobilité"@fr . + . + "Autres formes d'aide sociale"@fr . + "Belgium"@fr . + "Précarité et aide sociale"@fr . + "België"@fr . + "Brussel"@fr . + "Right to social integration"@fr . + "Statistiques"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "In Brussel, net als in de rest van België, wonen veel mensen die over onvoldoende bestaansmiddelen beschikken. Onder bepaalde voorwaarden hebben zij recht op maatschappelijke hulp. Die wordt georganiseerd om de volledige bevolking een minimuminkomen te garanderen.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + "Insecurity and Welfare benefits "@en . + "2016-11-18T00:00:00"^^ . + "Insecurity"@fr . + "Right to social assistance"@fr . + . + . + "À Bruxelles comme dans le reste de la Belgique, de nombreuses personnes disposent de moyens de subsistance insuffisants. Sous certaines conditions, ces personnes peuvent bénéficier d’une aide sociale. Celle-ci est organisée dans le but de garantir un revenu minimum à l'ensemble de la population.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + "52f28221-7542-4abe-9110-5082d49d1f1f" . + "Andere vormen van maatschappelijke hulp"@fr . + "Belgique"@fr . + "Droit à l'aide sociale"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d75AE666460BA239B3A1D79E599C7571F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d75AE666460BA239B3A1D79E599C7571F "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d75AE666460BA239B3A1D79E599C7571F "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Brussels"@fr . + "Other forms of social aid"@fr . + "Droit à l'intégration sociale"@fr . + "Recht op maatschappelijke hulp"@fr . + "Bestaansonzekerheid en sociale bijstand"@nl . + "In Brussels as in the rest of Belgium, many people do not have sufficient means of subsistence. In some circumstances, these people may qualify for welfare benefits. These are aimed at ensuring that all the population has a minimum income.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Geslacht"@fr . + "Recht op maatschappelijke integratie"@fr . + "Bruxelles"@fr . + "Sex"@fr . + "Statistics"@fr . + "Statistieken"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dEF289B5A571AC494ED326B4782E7E8AA . + "2016-12-01T11:29:47.219099"^^ . + "Bestaansonzekerheid"@fr . + "Sexe"@fr . + "Précarité"@fr . + . + . + "Brussels Hoofdstedelijk Gewest : een straatknooppunt staat voor een verzameling van punten op de uiteinden van de assen van het wegennet (« straatas»). Het komt overeen met een kruising van assen of met een asuiteinde."@nl . + "Belgique"@fr . + "Reporting Inspire"@fr . + "Noeuds de carrefours"@fr . + "route"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Brussels-Capital Region : Street nodes designate the set of points at the ends of lines of road network (\"Axe Street\"). It corresponds to an intersection of axes or a shaft end."@en . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8C818FBB8FC590D24B3C9D048F198981 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8C818FBB8FC590D24B3C9D048F198981 "Bruxelles Environnement : Département eau" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4834F417B13589F07B111F4E110059A5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4834F417B13589F07B111F4E110059A5 "POLYGON ((4.2270 50.7504, 4.2270 50.9212, 4.4968 50.9212, 4.4968 50.7504, 4.2270 50.7504))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4834F417B13589F07B111F4E110059A5 "{\"type\" : \"Polygon\", \"coordinates\" : [[[4.227048, 50.750431], [4.227048, 50.9212455], [4.4967503, 50.9212455], [4.4967503, 50.750431], [4.227048, 50.750431]]]}"^^ . + "Région de Bruxelles-Capitale"@fr . + . + "Région de Bruxelles-Capitale : les nœuds de rue désignent l’ensemble des points situés aux extrémités des axes du réseau viaire (« Axe de Rue »). Il correspond à une intersection d’axes ou à une extrémité d’axe."@fr . + "2015-09-28T00:00:00"^^ . + "Straatknooppunt"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8D7CB2C93FE0C4ECC8CD2E7833D3B993 . + "Street node"@en . + "Espace public"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Région de Bruxelles-Capitale"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + "6c5fef0f-81ac-4da9-8cdc-ad3efb945655" . + "2016-01-28T13:39:56.443407"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD0315E997F010592FC0CF37C8DD54505 . + . + . + "Defibrillator"@fr . + "Cardio"@fr . + "Défibrilator"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "2016-04-29T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDBABE43D2541D6DA028A3E1757D4E687 . + "Défibrilateurs"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d307297A6F5D355AAE540F56F7AE70AF6 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d307297A6F5D355AAE540F56F7AE70AF6 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d307297A6F5D355AAE540F56F7AE70AF6 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "2016-04-29T00:00:00"^^ . + . + "AED"@fr . + "DAE"@fr . + "Defibrillator"@nl . + "Défibrillateurs"@en . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "33fb13c2-3cd5-423c-830a-33b508d31b15" . + . + "Lieux et dates des constats."@fr . + "3e9030ac-06fd-4326-bf61-0321a12971a3" . + "2018-04-09T12:41:14.100828"^^ . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Plaats en datum van de vaststellingen"@nl . + "2018-04-18T07:32:09.611475"^^ . + "Sanctions administratives communales"@fr . + "Région de Bruxelles-Capitale"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9C7362DA652CBFC49685E9212D05FDDE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3A2EC9D16B62E3C857B4607EF327074F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3A2EC9D16B62E3C857B4607EF327074F "Corentin Descamps" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE5EFF87757D79631306151414E62BC09 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE5EFF87757D79631306151414E62BC09 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE5EFF87757D79631306151414E62BC09 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Gemeentelijke administratieve sanctie"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d93075D71840A35CAF1F6870376F56F31 . + . + . + "Ce web service en JSOn permet Introduction d’une demande relative à un problème de collecte porte-à-porte des ménages ou de malpropreté dans la région. Toutes les communes de la Région à l’exception d’Auderghem et Woluwé-Saint-Pierre sont connectées à l’application AlloWeb (https://www.arp-gan.be/fr/un-probleme.html).\n\nCe web service nécessite la signature d’une convention entre Bruxelles-Propreté et l’entité juridique souhaitant exploiter la possibilité d’introduire une damande. Cette convention est dérivée de la Licence ouverte du CIRB (Cette licence a été créée à partir de la licence ouverte Etalab, mission sous l'autorité du Premier ministre français chargée de l'ouverture des données publiques et du développement de la plate-forme française Open Data (avec l'autorisation de Etalab).\n\nwww.etalab.gouv.fr - http://www.etalab.gouv.fr/article-etalab-publie-la-licence-ouverte-open-licence-86708897.html.\n\nToute demande de renseignement ou d’accès doit se faire auprès de Monsieur Vincent Jumeau, Directeur général de Bruxelles-Propreté soit par email : (ict@arp-gan.brussels), soit par courrier avenue de Broqueville 12 à 1150 Bruxelles."@fr . + "\nDeze JSOn web service maakt het mogelijk om een vraag te stellen in verband met een probleem rond huisvuilophaling of netheid in de buurt. Alle gemeenten in het Gewest, met uitzondering van Oudergem en Sint-Pieters-Woluwe, zijn verbonden aan de app AlloWeb https://www.arp-gan.be/en/problem.html\n\nDeze web service vergt de handtekening van een overeenkomst tussen GAN en de juridische entiteit die een vraag wil indienen. Deze overeenkomst is afgeleid uit de open Licentie van het CIBG (Deze licentie werd gecreëerd op basis van de open Licentie Etalab, een missie onder gezag van de Franse eerste minister belast met het openbaar maken van publieke gegevens en de ontwikkeling van een Frans Open Data platform me de toestemming van Etalab.\n\nwww.etalab.gouv.fr - http://www.etalab.gouv.fr/article-etalab-publie-la-licence-ouverte-open-licence-86708897.html ).\n\nElke aanvraag voor inlichtingen of toegang moet gebeuren via Vincent Jumeau, algemeen directeur van Net Brussel, ofwel via mail (ict@arp-gan.brussels), ofwel via een brief naar de Broquevillelaan 12 in 1150 Brussel."@nl . + "ARP – Introduction d’une demande relative à un problème de collecte porte-à-porte des ménages ou de malpropreté dans la région"@fr . + "GAN – INDIENING VAN EEN VRAAG IN VERBAND MET EEN PROBLEEM ROND DE HUISVUILOPHALING OF NETHEID IN HET GEWEST."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d67717C4B1928EFB561C32E4B9D146171 . + "propreté"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEC7B63F5F1FF9D5AC5B1BD8442B25C17 . + "Région de Bruxelles-Capitale"@fr . + "2017-02-20T10:49:30.633290"^^ . + "2017-02-20T10:48:54.874995"^^ . + "This web service in JSOn allows for the introduction of a request related to a household house-to-house collection issue or cleanliness in the region. All municipalities of the region excepted Auderghem and Woluwé-Saint-Pierre are connected to the AlloWeb application (https://www.arp-gan.be/en/problem.html).\n\n This web service requires the signature of a convention between Bruxelles-Propreté and the legal entity wishing to exploit the data. This convention is derived from CIRB’s open License (This license is based upon the Etalab open licence, mission under the authority of France’s Prime Minister in charge of opening the public data and developing the French Open Data open platform – with the authorization of Etalab)\n\nwww.etalab.gouv.fr - http://www.etalab.gouv.fr/article-etalab-publie-la-licence-ouverte-open-licence-86708897.html.\n\nAny request for information or access must be made to Mr. Vincent Jumeau, General Manager of Bruxelles-Propreté either by email : ict@arp-gan.brussels, either by courier : avenue de Broqueville 12, 1150 Brussels."@en . + "f7a19002-a3e6-4adc-b090-4fde9dfee117" . + "environnement"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "BRUXELLES-PROPRETÉ – INTRODUCTION OF A REQUEST RELATED TO A HOUSEHOLD HOUSE-TO-HOUSE COLLECTION ISSUE OR CLEANLINESS IN THE REGION."@en . + "déchet"@fr . + . + "Brussel"@fr . + "Brussels"@fr . + "Dit thema biedt zeer uitgebreide informatie over de bodembezetting, de kenmerken van de bestaande bebouwde oppervlakte en de verkoop van vastgoed.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + "Occupation du sol"@fr . + "2016-12-01T11:12:54.906761"^^ . + "Residential and non-residential buildings"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + "Aménagement du territoire et immobilier"@fr . + "Ruimtelijke ordening en vastgoed"@nl . + "Housing subsidies and grants"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2215620C61328A5282FAAF6997F73C19 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2215620C61328A5282FAAF6997F73C19 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2215620C61328A5282FAAF6997F73C19 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "2016-11-22T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA8A5F1A7AF64D58689B3974D2310781D . + "Premies en toelagen voor huisvesting"@fr . + "Bruxelles"@fr . + "België"@fr . + "Land-use planning and real estate"@en . + "Primes et allocations destinées au logement"@fr . + "Residentiële en niet-residentiële gebouwenparken"@fr . + "Statistieken"@fr . + "Parc de bâtiments résidentiels et non-résidentiels"@fr . + "Land occupancy"@fr . + "Industrieparken en -terreinen"@fr . + "Social housing"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Statistiques"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4BD30FA50D5441C983786D6D87FFD238 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4BD30FA50D5441C983786D6D87FFD238 "Bruxelles Environnement" . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Ce thème délivre de nombreuses informations sur l’occupation du sol, les caractéristiques du bâti existant et les ventes immobilières notamment.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + "Industrial sites and parks"@fr . + "11faf7bd-804d-4f52-80d8-793c5ed98531" . + "Parc de logements sociaux"@fr . + "This section provides a wealth of information, for instance on land occupancy, the characteristics of existing constructions and real estate sales.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Parcs et terrains industriels"@fr . + "Sociale woningen"@fr . + "Statistics"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Belgique"@fr . + "Bodembezetting"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8EAA4E7E979B49941A98CD221D199847 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8EAA4E7E979B49941A98CD221D199847 "François Du Mortier" . + "Belgium"@fr . + . + "Recherche et technologie"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD6DA6D78F929A0EA1F6C122736F5CA07 . + "Research and technology"@en . + "Brussel"@fr . + "dd511fc4-16df-48d8-b560-ae2d50f4e7d7" . + "Sex"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "2016-12-01T11:16:24.836990"^^ . + "Belgium"@fr . + "2016-11-22T00:00:00"^^ . + "Onderzoek en technologie"@nl . + "This section presents the statistics for Brussels on both R&D expenditure and staff working in this field.\nThe statistics on the use and equipment with regard to information technology and communication found in Brussels households are also presented in this theme.\n"@en . + "België"@fr . + "Informatie- en communicatietechnologieën"@fr . + "Information and communication technologies"@fr . + "Onderzoek en ontwikkeling"@fr . + "Statistics"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Brussels"@fr . + "Statistiques"@fr . + "Région de Bruxelles-Capitale"@fr . + . + "Bruxelles"@fr . + "Research and development"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d57BC8CC496136D9A959C5A4575945AD8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d57BC8CC496136D9A959C5A4575945AD8 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d57BC8CC496136D9A959C5A4575945AD8 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . + "Geslacht"@fr . + "Cette série présente des données statistiques bruxelloises qui se rapportent aussi bien aux dépenses de R&D qu’au personnel qui travaille dans ce domaine.\nLes statistiques relatives à l’utilisation et l’équipement en technologies de l'information et de la communication des ménages bruxellois sont également présentées sur cette thématique."@fr . + "Dit thema omvat Brusselse statistische gegevens die zowel betrekking hebben op de uitgaven inzake O&O als op het personeel dat in dit domein werkt.\nDe statistieken inzake ICT-gebruik en -uitrusting van de Brusselse huishoudens worden eveneens weergegeven in dit thema."@nl . + "Recherche et développement"@fr . + "Sexe"@fr . + "Statistieken"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Belgique"@fr . + "Technologies Information et Communication"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3A5D09C02632136D9CCA811E02105C8A . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5B2C092F46D53F24E814E00BA19A4B8C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5B2C092F46D53F24E814E00BA19A4B8C "CIRB" . + "Brussels-Capital Region : the territory of the Brussels-Capital Region is divided into six local police zones each including several municipalities."@en . + "Brussels Hoofdstedelijk Gewest : het grondgebied van het Brussels Gewest is opgedeeld in zes politiezones, waarin telkens verschillende gemeenten samengebracht zijn."@nl . + "8d89a535-35b3-4ded-894d-3cf1b4bad93e" . + "2016-01-28T13:49:55.040724"^^ . + "Belgique"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBF12EF78E65F7848216C431443A44B41 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBF12EF78E65F7848216C431443A44B41 "Bruxelles Environnement" . + "Zones de police"@fr . + "Politiezone"@nl . + "Police zone"@en . + "Région de Bruxelles-Capitale : le territoire de la Région bruxelloise est découpé en six zones de police locale rassemblant chacune plusieurs communes."@fr . + "Reporting Inspire"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "2015-09-28T00:00:00"^^ . + "Fond de plan"@fr . + "Harvested"@fr . + "Région de Bruxelles-Capitale"@fr . + "limite administrative"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA1E977449A1E885F69931FC632C07663 . + . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest: De entiteit « Side Walk » (of stoep) stelt de afbakening voor van de wegranden. Deze zones(veelhoeken) bestrijken de verhoogde gedeelten van het openbare domein en de fysische huizenblokken. Als geen stoepen bestaan, komen de omtrekken overeen met de grens van het fysische huizenblok.\nDe stoepen werden getekend op basis van de topografische kaart UrbIS Topo (fotogrammetrische en topografische opmetingen)."@nl . + "2016-01-28T13:36:57.213885"^^ . + "Brussels-Capital Region : The entity \"Side Walk\" (or sidewalk) is the delimitation of roadsides. these areas (polygons) cover raised in the public domain and physical islets parties.\nWhere sidewalks do not exist, the contours correspond to the limit of the physical island. The sidewalks were designed based on the topographic map UrbIS-Topo (raised Photogrammetric and topographic)."@en . + "Stoep"@nl . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale : L’entité « Side Walk » (ou trottoir) représente la délimitation des bords de routes. Ces zones(polygones) couvrent les parties surélevées dans le domaine public et les îlots physiques.\nLorsque les trottoirs n’existent pas, les contours correspondent à la limite de l’îlot physique.\nLes trottoirs ont été dessinés sur base de la carte topographique UrbIS-Topo (levés photogrammétriques et topographiques)."@fr . + "6621ad54-67e1-4ddf-a861-4f309852aa49" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0C32E09AFA9AF06FF3C4C2683A7FA1EF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0C32E09AFA9AF06FF3C4C2683A7FA1EF "Team BruGIS" . + "2015-03-30T00:00:00"^^ . + . + "Région de Bruxelles-Capitale"@fr . + "Trottoirs"@fr . + "Side Walk"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFBE8101373468F4276942802F9891CCA . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD0623B5F4A3F20854EFD0D7D4E75C9F3 . + . + "Belgique"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d1776C4A741C0B5D00EC851419A0CCA68 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2810F7DA2C240527467E62232D1848BD . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2810F7DA2C240527467E62232D1848BD "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2810F7DA2C240527467E62232D1848BD "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0B0E38893F0DE96B847B659D42B3DA3E . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0B0E38893F0DE96B847B659D42B3DA3E "Secrétariat communal" . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "2016-01-01T00:00:00"^^ . + "2016-12-07T17:39:54.229779"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6939BA1EECD7CFC42FBB0632763D1F4D . + "0467e059-7d11-4a63-bd4c-7ba8deb09762" . + "Belangrijkste openbare parkings in het Brussels Gewest. \n(Gemeenschappelijk beheer van de data met het parkeeragentschap)"@nl . + "Principaux parkings publics dans la Région de Bruxelles-Capitale. \n(Gestion conjointe des données avec l'agence de stationnement)"@fr . + "Parkings publics"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Openbare parkeerplaatsen"@nl . + "Main public car parks in the Brussels-Capital Region.\n(Joint management data with the parking agency)"@en . + "Public parkings"@en . + . + "2015-09-28T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3112D776FF578756F8B3A58E9172E268 . + "Straatoppervlak"@nl . + "Espace public"@fr . + "Brussels Hoofdstedelijk Gewest : het straatoppervlak komt overeen met een opdeling van de openbare weg in elementaire oppervlakken, die stukken straat vormen."@nl . + "Région de Bruxelles-Capitale"@fr . + "Région de Bruxelles-Capitale"@fr . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE68D1118D83F2FC08B1F9989E7669360 . + "Harvested"@fr . + "Région de Bruxelles-Capitale : la surface de rue correspond au découpage de la voie publique en surfaces élémentaires représentant des morceaux de rue."@fr . + "2016-03-08T15:17:15.746579"^^ . + . + . + . + "Région de Bruxelles-Capitale : la surface de rue correspond au découpage de la voie publique en surfaces élémentaires représentant des morceaux de rue."@en . + "Fond de plan"@fr . + "Belgique"@fr . + "b0abf800-8362-4d5a-aed7-e0f8b108aafd" . + "Surfaces de rue"@fr . + "Surfaces de rue"@en . + "Reporting Inspire"@fr . + . + "Health care services and senior housing facilities"@fr . + "life expectancy and mortality"@fr . + "Brussels"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Bruxelles"@fr . + "Sex"@fr . + "Naissances"@fr . + "espérance de vie et mortalité"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB68F6E219C7C466D25807D3E2A80FF9F . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C5D761E780A52799F055978589DF99C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C5D761E780A52799F055978589DF99C "Bruxelles Environnement" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4A0511EE8516A81E267E0670BFEF106A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4A0511EE8516A81E267E0670BFEF106A "Hoeck Michèle" . + "2016-11-18T00:00:00"^^ . + "Births"@fr . + "Offre de soins de santé"@fr . + "Gezondheidstoestand en medische praktijken"@fr . + "Sexe"@fr . + "Statistieken"@fr . + "Santé"@fr . + "Statistics"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Aanbod van gezondheidszorg en huisvestingsaanbod voor ouderen"@fr . + "Geslacht"@fr . + "Gezondheid"@nl . + "Belgique"@fr . + "Hoewel gezondheid onder de bevoegdheid van de gemeenschappen valt, maakt het BISA hier toch een analyse van (aanbod van gezondheidsdiensten, levensverwachting enz.) door gegevens van de Vlaamse Gemeenschap, van de Franse Gemeenschap en van de Gemeenschappelijke Gemeenschapscommissie te combineren.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/"@nl . + "Brussel"@fr . + "Si la santé relève des compétences communautaires, l’IBSA présente une sélection d’indicateurs (offre de soins de santé, espérance de vie, etc.), en combinant des données provenant de la Communauté flamande, de la Communauté française et de la Commission communautaire commune.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/"@fr . + "Homes"@fr . + "Statistiques"@fr . + "levensverwachting en sterfte"@fr . + "Health "@en . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Belgium"@fr . + "Although health is a Community competence, the BISA presents a selection of indicators (range of healthcare services on offer, life expectancy, etc.) by combining the data from the Flemish Community, the French Community and the Common Community Commission.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "2016-12-01T11:27:24.506329"^^ . + "België"@fr . + "036a01b4-6c08-4983-9f29-b9b6ddcc0301" . + "État de santé et pratiques médicales"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Geboortes"@fr . + "State of health and medical practices"@fr . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d129BB644EFCFAD8DF344199EFB36EC72 . + "2017-06-23T12:27:21.354036"^^ . + "Feux de signalisation"@fr . + "2016-12-12T10:37:15.284579"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d37682019D85EE4865C7459BA0C60267C . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "b55ffec9-2382-44b5-9d86-5400259c8de8" . + "Traffic lights"@en . + . + "Verkeerslichten"@nl . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF78FD2369E1D412FD3537AD7A087FF93 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF78FD2369E1D412FD3537AD7A087FF93 "Secrétariat communal" . + "Geolocation of kruispunten uitgerust met verkeerslichten beheerd door Brussel Mobiliteit."@nl . + "Geolocation of intersections equipped with traffic lights managed by Brussels Mobility."@en . + "Géolocalisation des carrefours équipés de feux de signalisation gérés par Bruxelles Mobilité."@fr . + "Région de Bruxelles-Capitale"@fr . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCA6F9AAE556630DBEF5AB1FE71402409 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCA6F9AAE556630DBEF5AB1FE71402409 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCA6F9AAE556630DBEF5AB1FE71402409 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Fichier GTFS de la STIB (Bruxelles)"@fr . + . + "2018-02-13T14:22:59.296264"^^ . + "arrets"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5A2450F85D06E0BB420AA39F4BA17189 . + "metro"@fr . + "2016-12-16T00:00:00"^^ . + "Brussels"@fr . + "tram"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC92AC9F4F75EC24B6544AC216A4E9458 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC92AC9F4F75EC24B6544AC216A4E9458 "Team BruGIS" . + "This dataset contains the GTFS file made by the public transport operator for Brussels city in Belgium:\nWhat is a GTFS file?\n\nThe General Transit Feed Specification (GTFS), also known as GTFS static or static transit to differentiate it from the GTFS realtime extension, defines a common format for public transportation schedules and associated geographic information. GTFS \"feeds\" let public transit agencies publish their transit data and developers write applications that consume that data in an interoperable way."@en . + "GTFS File by STIB-MIVB (Brussels)"@en . + "bus"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9D6ECEEC68F4256A0276CC7D01AE8A5D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4E36B081E1F68290399CD29F821A4DD3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4E36B081E1F68290399CD29F821A4DD3 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4E36B081E1F68290399CD29F821A4DD3 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Deze dataset bevat het GTFS-bestand van de stad Brussel in België, aangemaakt door de openbare vervoersmaatschappij MIVB :\n\nWat is een GTFS-bestand?\n\nDe General Transit Feed Specification (GTFS), ook bekend als statische GTFS of statische doorvoer om deze te onderscheiden van de realtime GTFS-uitbreiding, definieert een algemeen formaat voor openbaar vervoersschema's en bijbehorende geografische informatie. GTFS-feeds laten openbare vervoersmaatschappijen toe hun transitgegevens te publiceren en ontwikkelaars hun toepassingen die gegevens gebruiken op een interoperabele manier te ontwikkelen.\n"@nl . + "1bdd4128-9e3e-4b55-9ce1-cf2e4bd62c8a" . + "GTFS-bestand door MIVB (Brussel)"@nl . + "routes"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Ce dataset contient le fichier GTFS créé par la société des transports intercommunaux de Bruxelles (STIB) en Belgique.\n\nQu'est-ce qu'un fichier GTFS?\nGTFS (General Transit Feed Specification), également connu sous le nom de GTFS statique ou de flux statique pour le différencier de l'extension GTFS-realtime, définit un format de fichier commun pour les horaires de transports en commun et les informations géographiques associées. Les \"flux\" GTFS permettent aux agences publiques de publier leurs informations de transports en commun et aux développeurs de créer des applications qui utilisent ces données de manière interopérable.\n"@fr . + "public-transport"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + "Belgium"@fr . + "Brussels Hoofdstedelijk Parlement"@fr . + "Municipal councils"@fr . + "Élections"@fr . + "Parlement Européen"@fr . + "Statistics"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Bruxelles"@fr . + "Brussels"@fr . + "Federal elections"@fr . + "Gemeenteraden"@fr . + "The BISA compiles data on the results of the different elections held in the Brussels-Capital Region.\nYou will find the results of Regional, local, European and legislative elections held in the Brussels-Capital Region.\n\nVisit also the Monitoring des Quartiers website, which offers a range of indicators for the 145 districts within the Brussels-Capital Region ➜ https://monitoringdesquartiers.brussels/"@en . + "Région de Bruxelles-Capitale"@fr . + . + . + "Belgique"@fr . + "Parlement de la Région de Bruxelles-Capitale"@fr . + "België"@fr . + "Elections "@en . + "Parliament of the Brussels-Capital Region"@fr . + "L'IBSA rassemble les données des résultats des différentes élections tenues en Région de Bruxelles-Capitale.\nRetrouvez les résultats des élections régionales, communales, européennes et législatives tenues en Région de Bruxelles-Capitale.\n\nConsultez également le site du Monitoring des Quartiers qui propose une sélection d’indicateurs au niveau des 145 quartiers de la Région de Bruxelles-Capitale ➜ https://monitoringdesquartiers.brussels/\n"@fr . + "Verkiezingen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDCEC352826786DAC7828C69E4D2F24C0 . + "Statistieken"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Het BISA verzamelt de resultaten van de verschillende verkiezingen die gehouden worden in het Brussels Hoofdstedelijk Gewest.\nU vindt de resultaten van de regionale, gemeentelijke, Europese en federale verkiezingen in het Brussels Hoofdstedelijk Gewest.\n\nRaadpleeg ook de site van de Wijkmonitoring die informatie geeft over een reeks indicatoren in de 145 wijken van het Brussels Hoofdstedelijk Gewest ➜ https://wijkmonitoring.brussels/\n"@nl . + "2016-12-01T11:02:55.421508"^^ . + "4169896f-5b80-431a-9a51-b91ffa180ea3" . + "Élections fédérales"@fr . + "Région de Bruxelles-Capitale"@fr . + . + "Statistiques"@fr . + "Europees Parlement"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d66C8CC10117D818ED30650176B6E1DBC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d66C8CC10117D818ED30650176B6E1DBC "Bruxelles Environnement" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB5D2A066B2D1B497ED32707F7660583B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB5D2A066B2D1B497ED32707F7660583B "Andreas Boogaerts" . + "Brussel"@fr . + "Conseils communaux"@fr . + "Federale verkiezingen"@fr . + "European Parliament"@fr . + "2016-11-22T00:00:00"^^ . + "Région de Bruxelles-Capitale"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d21F0720EEB06598DCCDBD33C490D5CE9 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d21F0720EEB06598DCCDBD33C490D5CE9 "Hoeck Michèle" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d65C84745985BCE9A84593A7C8D9312AD . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d65C84745985BCE9A84593A7C8D9312AD "Bruxelles Environnement" . + . + "Brussels Hoofdstedelijk Gewest : België is een Federale Staat die uit drie gewesten bestaat: Vlaams Gewest, Waals Gewest en Brussels Gewest."@nl . + "Région de Bruxelles-Capitale : la Belgique est un Etat fédéral qui se compose de trois régions : la Région wallonne, la Région flamande et la Région bruxelloise. L'objet \"Région bruxelloise\" représente la limite de la Région bruxelloise."@fr . + "55c284c5-f1ea-4465-94a1-fd02f3b9c6a9" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3D14C039A7B9E5FC137156E00F319F9A . + "Brussels Gewest"@nl . + "2016-03-08T15:21:16.773801"^^ . + "Région bruxelloise"@fr . + "Belgique"@fr . + "Reporting Inspire"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + "Brussels-Capital Belgium is a federal state composed of three regions: the Walloon Region, the Flemish Region and the Brussels Region. The object \"Brussels-Capital Region\" represents the limit of the Brussels Region."@en . + "2015-09-28T00:00:00"^^ . + "Brussels-Capital Region"@en . + "Harvested"@fr . + "Fond de plan"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d98701581A576D98DFB69FA664803D106 . + "limite administrative"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7D9312F11CE8E15B0B4126CE264FE0BF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7D9312F11CE8E15B0B4126CE264FE0BF "Team BruGIS" . + "2016-11-22T00:00:00"^^ . + "Belgique"@fr . + "Culture"@fr . + "Statistieken"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Toerisme en cultuur "@nl . + "Tourism and Culture "@en . + "Tourisme et culture"@fr . + "Brussels"@fr . + "Bruxelles"@fr . + "Statistiques"@fr . + "Tourism"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "68c033f4-32aa-4b6e-bd9f-53b34c0a038d" . + "Cultuur"@fr . + "De toeristische statistieken betreffen overnachtingen van personen die in het Brussels Hoofdstedelijk Gewest verbleven hebben.\nVoor het culturele aspect vindt u statistieken over de bioscoopzalen en de voorstellingen.\n"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d1521993328C710C1FB52FC01D713EB61 . + "The statistics on tourism relate to the accommodation of visitors to the Brussels-Capital Region.\nAs regards culture, you will find statistics on cinemas and screenings.\n"@en . + "Statistics"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + "Belgium"@fr . + "Les statistiques relatives au tourisme concernent l’hébergement des personnes qui ont séjourné dans la Région de Bruxelles-Capitale.\nDans le domaine de la culture, vous trouverez des statistiques relatives aux salles de cinéma et aux projections."@fr . + "2016-12-01T11:05:19.438174"^^ . + "België"@fr . + "Brussel"@fr . + "Toerisme"@fr . + "Tourisme"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECF89F39C3C9CE4DA4C603C8367716D8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECF89F39C3C9CE4DA4C603C8367716D8 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECF89F39C3C9CE4DA4C603C8367716D8 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + "Reporting Inspire"@fr . + "Harvested"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3277833AB6D3EF1F582FDFD0E761293E . + "Brussels-Capital Region : location of the different groundwater quality monitoring sites managed by Brussels Environment (IBGE-BIM) and which results are transmitted through the reporting to Europe (under Article 8 of the Water Framework Directive). Indication of the analyzed water body, the European Code and the Brussels code of the monitoring site. Distinction between two types of monitoring: surveillance monitoring for all groundwater bodies (type_monitoring = 1) and operational monitoring for groundwater bodies identified as being at risk of failing to achieve the objectives of good status (type_monitoring = 2)"@en . + "b46e1454-3d1b-4e95-b697-1af015a5a20f" . + "Région de Bruxelles-Capitale"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de verschillende meetstations voor de kwaliteitsmonitoring van het grondwater beheerd door Leefmilieu Brussel (BIM) en waarvan de resultaten doorgegeven zijn in het kader van de Europese rapportering (volgens artikel 8 van de Kaderrichtlijn Water). Met aanduiding van het geanalyseerde grondwaterlichaam, de Europese code en de Brusselse code van het meetstation. 2 monitoring types: controle van de monitoring voor al de grondwaterlichamen (type_monitoring = 1) en operationele controle voor de grondwaterlichamen gerangschikt volgens risico van niet bereikte doelen van de goede staat (type_monitoring = 2)"@nl . + "Région de Bruxelles-Capitale : localisation des différentes stations de mesure de la qualité des eaux souterraines gérées par Bruxelles Environnement (IBGE) et dont les résultats sont transmis dans le cadre du reporting à l'Europe (selon l'article 8 de la Directive Cadre Eau). Indication de la masse d'eau analysée, du code européen et du code bruxellois de la station de mesure. Distinction des 2 types de monitoring : contrôle de surveillance pour toutes les masses d'eau souterraines (type_monitoring = 1) et contrôle opérationnel pour les masses d'eau souterraines classées en risque de non atteinte des objectifs de bon état (type_monitoring = 2)"@fr . + "Groundwater quality monitoring network - European reporting"@en . + "Monitoring van de kwaliteit van de grondwaterlichamen - Europese rapportering"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B4451505D7D8168810BFA33783C81E4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d07084B5259D1F169BC0067EA4B3878FC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d07084B5259D1F169BC0067EA4B3878FC "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d07084B5259D1F169BC0067EA4B3878FC "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Région de Bruxelles-Capitale"@fr . + . + "2016-05-10T23:40:33.847627"^^ . + "Réseau de surveillance de la qualité des eaux souterraines - reporting européen"@fr . + "2015-09-26T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d24770D1DD49A650094161B689ED65A02 . + "Belgique"@fr . + "Région de Bruxelles-Capitale"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + "Zones de gestion, de restriction ou de réglementat"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD55BB21F56FCAF670AFD2E92AD9187E5 . + "environnement"@fr . + "protection des réserves d'eau souterraines"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels-Capital Region : location of groundwater catchment sites protection areas 2 and 3 (i.e. extraction wells and drainage gallery) in the Brussels-Capital Region. Those sites were defined in the Brussels-Capital Government decree of 19 September 2002 delimiting a groundwater catchments protection area at the “Bois de la Cambre” and in the Sonian Forest (Belgian Monitor: June 10, 2008)"@en . + "2016-01-22T18:45:40.914429"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d2A35E2411AE99B9625EF85D4D410FF4B . + "zone protégée de captage d'eau"@fr . + . + . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de beschermingszones 2 en 3 rondom winningen (i.e. waterwinningsputten en drainage galerij) in het Brussels Hoofdstedelijk Gewest. Deze zones werden bepaald in het besluit van de Brusselse Hoofdstedelijke Regering van 19 september 2002 en bakent een beschermingszone rondom grondwaterwinningen af in het Ter Kamerenbos en in het Zoniënwoud (Belgisch Staatsblad : 10 juni 2008)"@nl . + "Région de Bruxelles-Capitale : localisation des zones 2 et 3 de protection des lieux de captage (i.e. puits captants et galerie drainante) en Région de Bruxelles-Capitale. Ces zones ont été définies dans l'arrêté du Gouvernement de la Région de Bruxelles-Capitale du 19 septembre 2002 délimitant une zone de protection des captages d'eau souterraine au Bois de la Cambre et en Forêt de Soignes (Moniteur belge : 10 juin 2008)"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Zones2_et3_protection_captage_Pg.xml" . + "Belgique"@fr . + "Région de Bruxelles-Capitale"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "2015-09-26T00:00:00"^^ . + "Groundwater catchment protection areas 2 and 3"@en . + "Zones 2 et 3 de protection de captage"@fr . + "Harvested"@fr . + "protection de zone de captage de l'eau"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Beschermingszones 2 en 3 van winningen"@nl . + "Reporting Inspire"@fr . + . + "Harvested"@fr . + "Brussels-Capital Region: localization of the extraction wells for drinking water in Brussels, run by VIVAQUA. Ranked as protected area 1 of the water extractions (with the drainage gallery)"@en . + "approvisionnement en eau potable"@fr . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de winningsputten voor drinkwaterwinning in Brussel uitgebaat door VIVAQUA. Gerangschikt als beschermingszone 1 van winningen (met de drainage gallerij)"@nl . + "Région de Bruxelles-Capitale"@fr . + "eaux souterraines"@fr . + "Beschermingszone 1 van winningen - waterwinningsputten"@nl . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBFB2BD9481D9A7A050F3C9C32DEBE83B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBFB2BD9481D9A7A050F3C9C32DEBE83B "Secrétariat communal" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d84547D4D31DB4E4BBEAA4707EA2813E2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d84547D4D31DB4E4BBEAA4707EA2813E2 "Mathieu Tonglet" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCFFC6492D60DC4EE53D84EFA56426256 . + "environnement"@fr . + "alimentation en eau de la ville"@fr . + "dd374aec-3802-4da1-8bad-59004e17460f" . + "protection de zone de captage de l'eau"@fr . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Région de Bruxelles-Capitale : localisation des puits de captage d'eau potable de Bruxelles exploitée par VIVAQUA. Classés comme zone 1 de protection de captage (ainsi que la galerie drainante)"@fr . + "2016-01-22T18:45:46.386154"^^ . + "Reporting Inspire"@fr . + "Zone 1 de protection de captage - puits captants"@fr . + "zone protégée de captage d'eau"@fr . + "2015-09-26T00:00:00"^^ . + "captage d'eau"@fr . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD29DC37AD2ED9FD0DE9BE817229AD25B . + "Belgique"@fr . + "Zones de gestion, de restriction ou de réglementat"@fr . + "Protection area 1 of water extraction - the water extraction wells"@en . + . + "b2aafd9b-d860-4337-bb90-b070f8727484" . + "ShapeFiles by STIB-MIVB (Brussels)"@en . + "tram"@fr . + "De shapefiles (ofwel \"vormbestand\") bevatten per definitie informatie met betrekking tot de geometrie van objecten die in het geval van de MIVB lijnen (reiswegen) en punten (haltes) zijn.\n\nTwee shapefiles worden momenteel verdeeld: \"ACTU_LINES\" en \"ACTU_STOPS\""@nl . + "metro"@fr . + "Région de Bruxelles-Capitale"@fr . + "ShapeFiles de la STIB (Bruxelles)"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d04E7157DE94B76D39C7A8E68066591C4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0B1692FA5D6921FED89D9C2256FA71F8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0B1692FA5D6921FED89D9C2256FA71F8 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0B1692FA5D6921FED89D9C2256FA71F8 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "brussels"@fr . + "ShapeFiles: Les shapefiles (ou ‘fichier de formes’) contiennent par définition l’information liée à la géométrie des objets, qui dans le cas de la STIB sont des lignes (itinéraires) et des points (arrêts).\n\nLes shapefiles actuellement distribués sont au nombre de deux : « ACTU_LINES » et « ACTU_STOPS »"@fr . + "The shapefiles contain information linked to the objects' geometry which, for STIB/MIVB, are lines (itineraries) and dots (stops).\n\nThere are currently two shapefiles being distributed: \"ACTU_LINES\" and \"ACTU_STOPS\""@en . + "bus"@fr . + . + "2018-02-13T14:01:26.548297"^^ . + "shapefiles"@fr . + . + . + "2018-01-25T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDC3F5FCB10BF47B02C7D43558BEB3FC8 . + "MIVB ShapeFiles (Brussel)"@nl . + "transport-public"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4786B3B2A524A1841D7D277A4B5B787F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4786B3B2A524A1841D7D277A4B5B787F "Bruxelles Environnement" . + . + "Statistical district"@en . + "Fond de plan"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d476328F7A9A106802C0F5063583AB220 . + "2016-03-08T15:19:34.965487"^^ . + . + "Région de Bruxelles-Capitale"@fr . + . + "2015-12-22T00:00:00"^^ . + "Région de Bruxelles-Capitale : Le secteur statistique désigne une zone regroupant un ensemble d'adresses défini au niveau communal par l’Institut National de Statistiques (INS) comme unité de base pour le recensement de la population. Ce regroupement est basé sur une analyse géographique de la commune qui tient compte de ses caractéristiques structurelles morphologiques, urbanistiques, sociales et économiques."@fr . + "Belgique"@fr . + "Brussels Hoofdstedelijk Gewest: De statistische sector staat voor een groep adressen, die op gemeentelijk vlak door het Nationaal Instituut voor de Statistiek (NIS) vastgelegd wordt als basiseenheid voor de volkstelling. Deze indeling is gebaseerd op een geografische analyse van de gemeente, die rekening houdt met de structurele, morfologische, stedenbouwkundige, maatschappelijke en economische kenmerken."@nl . + "Brussels-Capital Region : Statistical district means an area containing a set of addresses defined at the municipal level by the National Statistics Institute (INS) as the basic unit for the population census. This grouping is based on a geographic analysis of the municipality that reflects its urban, social and economic structural morphological characteristics."@en . + "Secteurs statistiques"@fr . + "Reporting Inspire"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7CB0731BC5A077DD6F04D041B3EA0953 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7CB0731BC5A077DD6F04D041B3EA0953 "Florine Deladrière" . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6F7EC3F14D0A745F4530A05C21555D32 . + "aca1bd59-f851-43cc-8dcb-250d48b24a46" . + "Statistische sector"@nl . + "Harvested"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Deze laag bevat alle bomenrijen gesitueerd langs de gewestwegen. Deze bomen worden beheerd door de cel Beplantingen, fonteinen en kunstwerken. De databank bevat ongeveer 27000 bomen. De databank bevat zowel data bekomen via luchtfoto's, als data bekomen door terreinwaarnemingen."@nl . + "Inventaire georéférencé des arbres d'alignement situés le long des voiries régionales. Ces arbres sont gérés par la cellule Plantations, fontaines et œuvres d'art. La base de données contient environ 27000 arbres. La base de données contient aussi bien des données provenant de photos aériennes, que des données provenant d'observations de terrain."@fr . + "2016-01-01T00:00:00"^^ . + "Georeferenced inventory of trees along regional roads."@en . + . + "92093e5e-4552-46b9-8f8b-7110ff2f3037" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6969AC30084DF821A73AB1EF015F0EA4 . + "Arbres d'alignement"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d930F0717549000DF4BD2F30F75558791 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE373CAE178BD99E5056F38D325A666E1 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE373CAE178BD99E5056F38D325A666E1 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE373CAE178BD99E5056F38D325A666E1 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Région de Bruxelles-Capitale"@fr . + "2016-12-07T17:40:36.386809"^^ . + "Bomenrijen"@nl . + "Trees along regional roads"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBFCEC5ED9BFD8305D29D26A551B18A04 . + . + . + . + "2018-12-04T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB4DE57D4E43FE07964C515AD04BCB07D . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "In deze dataset heeft u toegang tot de volgende informatie:\n\nDe informatie per halte betreffende geplande werkzaamheden en de wijzigingen in de reisweg. U kunt deze bekomen via de volgende methodes\n\nBoodschappen per lijn : boodschappen per halte voor bepaalde lijnen, via de id(‘s) van de gewenste lijn(en) in de parameters.\n\nBoodschappen per halte : boodschappen per halte via de id(‘s) van de gewenste halte(s) in de paramters."@nl . + "Dans ce jeu de données, vous avez accès aux informations suivantes :\n\nLes informations concernant les travaux planifiés et les changements dans l'itinéraire qui sont liés aux arrêts. Vous pouvez les avoir via les méthodes suivantes :\n\nMessages par ligne : messages liés aux arrêts pour certaines lignes, obtenus en passant le ou les id de la ou des ligne(s) souhaitée(s) dans les paramètres.\n\nMessage par arrêt : messages liés aux arrêts, obtenus en passant le ou les id de la ou des arrêt(s) souhaité(s) dans les paramètres. "@fr . + "In this dataset you’ll find:\n\nInformation about planned works and/or changes in itinerary related stop(s). You can retrieve this information via the following methods:\n\nMessages By Line: contains series of messages related to the stops of specific line(s). These messages can be retrieved by passing line(s) id(s).\n\nMessage By Stop: contains series of messages related to stop(s). These messages can be retrieved by passing stop(s) id(s). "@en . + "95527279-2be7-4a6b-9e00-bd829c504777" . + "2018-05-04T13:41:50.174640"^^ . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4B578D98233B7CD59C58F982A70EDA06 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4B578D98233B7CD59C58F982A70EDA06 "Mathieu Tonglet" . + . + "MESSAGES D'INFO VOYAGEURS (TEMPS-RÉEL)"@fr . + "Information"@fr . + "Messages"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Disruption"@fr . + "STIB-MIVB"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE358BB0B4FC69794FB3C2C9BD3069E4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE358BB0B4FC69794FB3C2C9BD3069E4 "Bruxelles Environnement" . + "REIZIGERSINFORMATIE BOODSCHAPPEN (REAL-TIME)"@nl . + "TRAVELERS INFORMATION (REAL-TIME)"@en . + "real-time"@fr . + . + "Stations Villo (Bruxelles)"@fr . + "bike"@fr . + "villo"@fr . + "Villo Stations (Bruxelles)"@nl . + "vélo"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "2017-10-03T09:18:42.213732"^^ . + "Villo Stations (Brussels)"@en . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2915FE56A67014F452831C4FEA83BAC2 . + . + "Villo stations in the Brussels-Capitale Region"@en . + "Villo stations in the Brussels-Capitale Region"@nl . + "Liste des stations Villo dans la Région Bruxelles-Capitale"@fr . + "fietsen"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD6B533F52631E4183B5737793C7701C1 . + "5836498b-e767-480d-8d0d-c6816213ff83" . + "2016-01-25T14:01:00.551137"^^ . + . + "Région de Bruxelles-Capitale"@fr . + . + . + "d375bb6b-a7aa-4496-9b2e-2b73f989c6db" . + "Accessibilité de cafés bruxellois pour utilisateurs de fauteuils roulants"@fr . + "cafés"@fr . + "Accessibility of Brussels bars list for wheelchair users"@en . + "wheelchairs"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + "Brussels Jazz Marathon wil dat iedereen van de gratis concerten kan genieten. Rolstoelgebruikers kunnen hier nagaan welke indoor locaties (bars & cafés) voor hen toegankelijk zijn en welke faciliteiten beschikbaar zijn."@nl . + "2016-06-05T00:00:00"^^ . + "2016-06-15T00:00:00"^^ . + "bars"@fr . + "disabled"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7998C8E3BA28D8B13FE8D431DADB9675 . + "Région de Bruxelles-Capitale"@fr . + . + . + "Brussels Jazz Marathon wants everyone to enjoy the free concerts. Wheelchair users can check here which indoor locations (bars & cafes) are accessible to them and which facilities are available."@en . + "Toegankelijkheid Brusselse café's voor rolstoelgebruikers"@nl . + "Brussels Jazz Marathon souhaite que tout le monde puisse profiter des concerts gratuits. Les utilisateurs en fauteuils roulants peuvent vérifier les infrastructures indoor (bars et & cafés) qui leur sont accessibles et les installations disponibles."@fr . + "accessibility"@fr . + . + "Région de Bruxelles-Capitale : Les faces de rues sont situées le long des voies publiques. L'entité « Street Side » (ou face de rue) est représentée par une ligne et par un centroïde\n(point à proximité de la ligne).\nLes faces de rues sont dessinées à l’interface entre deux tronçons de rue ou à l’interface entre un tronçon de rue et un îlot. Les faces de rues associées aux galeries et aux voiries locales font exception à cette règle. Elles sont dessinées, à l’intérieur des îlots, le long des surfaces des galeries et de voies locales.\nLes faces de rues sont complétées par deux petits segments de lignes situés à leurs extrémités (= entité géographique \"SILIMIT\" sans aucune information alphanumérique). Ces lignes sont orientées vers l’intérieur des îlots.\nLes centroïdes des faces de rue sont dessinés à l’intérieur des îlots, à proximité de leur milieu.\nIls servent de point d’appui pour les textes des plages d’adresses ."@fr . + "2016-01-28T13:42:58.846417"^^ . + "Street Sides"@en . + "Belgique"@fr . + "Harvested"@fr . + "Straatzijden"@nl . + "Région de Bruxelles-Capitale"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Brussels Hoofdstedelijk Gewest : De straatzijden bevinden zich langs de openbare wegen. De entiteit « Street Side » (of straatzijde) wordt weergegeven met een lijn en een centroïde(punt nabij de lijn).\nZij worden getekend op het raakpunt tussen twee stukken straat of op het raakpunt tussen een stuk straat en een huizenblok. De straatzijden die verbonden zijn met galerijen en lokale wegen zijn uitzonderingen op deze regel. Zij worden binnen de huizenblokken getekend langs stukken galerijen en lokale wegen.\nDe straatzijden worden aangevuld door twee korte lijnsegmenten op de uiteinden ervan (= geografische entiteit \"SILIMIT\" zonder enige alfanumerieke informatie). Deze lijnen zijn naar de binnenkant van de huizenblokken gericht.\nDe centroïden van de straatzijden zijn getekend binnen de huizenblokken, tegen het middelpunt ervan. Zij dienen als steunpunt voor de teksten van de adressenreeksen."@nl . + "05524955-25cf-4a31-ac9c-539b9be709f9" . + "Brussels-Capital Region : The street ends are located along the public roads. The entity \"Streetside\" (or street) is shown with a line and a centroid (point near the line).\nThey are drawn on the interface between two pieces of street or at the interface between a piece of street and a block. The street sides connected with galleries and local roads are exceptions to this rule. They are drawn along pieces galleries and local roads. Within the blocks\nThe road sides are complemented by two short line segments at their ends (= geographical entity \"SILIMIT\" without any alphanumeric information). These lines are directed towards the inside of the housing blocks.\nThe centroids of the street sides are drawn within the blocks, at the center of it. They serve as a focal point for the text of the address ranges."@en . + "Fond de plan"@fr . + "Faces de rue"@fr . + "Espace public"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC66892A1896E5A1E39D95582132ECAED . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC66892A1896E5A1E39D95582132ECAED "Hoeck Michèle" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7885F0EED34D8262E15EE7A62B2A4EE5 . + . + "Brussels Hoofdstedelijk Gewest"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dABF2FEC713E69EA3A8D15199C5F52546 . + "2015-03-30T00:00:00"^^ . + "Reporting Inspire"@fr . + . + . + "2016-01-22T18:45:47.385502"^^ . + "Réseau de mesure du bruit"@fr . + "Harvested"@fr . + "Région de Bruxelles-Capitale"@fr . + "bruit"@fr . + "Région de Bruxelles-Capitale"@fr . + . + "Région de Bruxelles-Capitale : localisation des différentes stations de mesures des niveaux sonores, gérées par Bruxelles Environnement (IBGE). Les stations de mesures sont dédiées au bruit ambiant, routier, ferroviaire et/ou aérien. Indication du nom (correspondant à la localisation) et de la source sonore prépondérante.\nLes résultats des mesures sont accessibles via le module WebNoise sur le site de Bruxelles Environnement."@fr . + "2015-09-26T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d355B4672B9BB90EC7333D8B094F6BD72 . + "bruit ambiant"@fr . + "Netwerkmonitoring van het geluid"@nl . + "bruit routier"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels-Capital Region: location of various monitoring stations sound levels, managed by Brussels Environment (IBGE). The monitoring stations are dedicated to ambient noise, road, rail and/or air.\nIndication of the name (corresponding to the location) and the dominant sound source.\nMeasurement results are available via the module WebNoise on the website of Brussels Environment."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBA01187D8CB6EC6968917EA33FA12AF9 . + "bruit aérien"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "dfcb6396-9c89-4f2f-b125-6695a557a959" . + "Reporting Inspire"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC9940906CA143F5014C12574324BDC68 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC9940906CA143F5014C12574324BDC68 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC9940906CA143F5014C12574324BDC68 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Network monitoring of the noise"@en . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3D63891F27CA83732EC6F76E0C532FA0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3D63891F27CA83732EC6F76E0C532FA0 "Corentin Descamps" . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de verschillende meetstations geluidsniveaus, beheerd door Leefmilieu Brussel (BIM). De meetstations zijn gewijd aan omgevingsgeluid, weg-, spoor- en/of lucht. Vermelding van de naam (die overeenkomt met de lokalisatie) en de dominante geluidsbron.\nMeetresultaten zijn beschikbaar via de module WebNoise op de website van Leefmilieu Brussel."@nl . + "Belgique"@fr . + "environnement"@fr . + "station de surveillance"@fr . + . + . + "Bruit ferroviaire "@fr . + "suveillance du bruit"@fr . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9A7A7A669CEF5DB6A33CF50B16EF4C53 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9A7A7A669CEF5DB6A33CF50B16EF4C53 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9A7A7A669CEF5DB6A33CF50B16EF4C53 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Paved roads"@en . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d813AF87EE3691B319FF77DD20808DC93 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCB8C9776E516BCC67F03250592356723 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d362D1DE2134A3B705FD485B05348DBEE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d362D1DE2134A3B705FD485B05348DBEE "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d362D1DE2134A3B705FD485B05348DBEE "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Roads whose coating consists mostly of paved blocks."@en . + "Routes dont le revêtement est principalement constitué de pavés."@fr . + "Kasseien"@nl . + "Routes pavées"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "b9ab764f-9767-4860-9ec1-632e3fd75272" . + "2016-01-01T00:00:00"^^ . + "2016-12-07T17:31:09.041330"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d24590D32D072DAC1FF8453A0406E9654 . + . + . + "Région de Bruxelles-Capitale"@fr . + . + "De wegen met kasseiverharding zijn geïnventariseerd in het kader van de fietskaart. De arceringen van de fietskaart zijn geprojecteerd op de Urbis Street Sections om een correctere geografische representatie te bekomen. Een aanduiding van kasseien sluit niet uit dat er een vlak voetpad of fietspad aanwezig is."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7D9BA1AE2285E55795F5BC73249D5C55 . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d39BFF1F43BA8374DAA4BCFBA7EE1E5A0 . + "Région de Bruxelles-Capitale"@fr . + "eau de surface"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + "Brussels-Capital Region : location of the different surface water quality monitoring sites under the Water Framework Ordonnance and Directive. Surface waters concerned are surface water bodies: Canal, Senne and Woluwe. Monitoring involves a series of chemical, physico-chemical parameters... The code and the name of the monitoring site are given."@en . + "stationsSW_qualite_phisique_chimique.xml" . + "Réseau de surveillance de la qualité chimique et physico-chimique des eaux de surface"@fr . + "analyse chimique"@fr . + "2015-09-26T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFF54C6CDE330B1EBC1739E32749C1F51 . + "Harvested"@fr . + "Monitoring network of the chemical and physico-chemical quality for surface waters"@en . + "Région de Bruxelles-Capitale : localisation des différentes stations de mesure pour le monitoring qualitatif des eaux de surface, dans le cadre de la Directive et de l'Ordonnance Cadre Eau. Les eaux de surface concernées sont les masses d'eau de surface : Canal, Senne et Woluwe. Le monitoring porte sur une série de paramètres chimiques, physico-chimiques... On dispose du code et du nom de la station de mesure"@fr . + "Chemische en fysisch-chemische kwaliteitsmonitoring van de oppervlaktewateren"@nl . + "analyse physico-chimique"@fr . + "Reporting Inspire"@fr . + "2016-01-22T18:45:53.132299"^^ . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de verschillende meetstations voor de kwaliteitsmonitoring van de oppervlaktewateren in het kader van Kaderrichtlijn- en Ordonnantie Water. De betreffende oppervlaktewateren zijn de wateroppervlakte lichamen : Kanaal, Zenne en Woluwe. De monitoring bestaat uit verschillende chemische, fysisch-chemische parameters... Info over de code en de naam van het meetstation"@nl . + "cours d'eau"@fr . + "Belgique"@fr . + "environnement"@fr . + "qualité de l'eau"@fr . + "réseau de mesure"@fr . + "station de surveillance"@fr . + "surveillance de l'eau"@fr . + . + . + "9b86c7fa-bbc0-473f-9a78-c8b89de5ab66" . + "Les vélocistes connus de Bruxelles Mobilité."@fr . + "Bicycle shops known by Brussels Mobility."@en . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD37F7889BEF13C2C2B535963EA5A376C . + "bicycle shops"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dEADA5DDC6749E836152AABC65C90DB04 . + "De fietswinkels gekend door Brussel Mobiliteit."@nl . + "2017-06-23T12:22:33.839708"^^ . + "Fietswinkels"@nl . + "Vélocistes"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + "2017-06-12T00:00:00"^^ . + . + "eaux souterraines"@fr . + "ressource en eau"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA0CC1F2B7FDF3770403B89540D78DC94 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA0CC1F2B7FDF3770403B89540D78DC94 " Tonglet Mathieu" . + "Harvested"@fr . + . + "Région de Bruxelles-Capitale"@fr . + "gwb.xml" . + "Brussels-Capital Region : data on the 5 Groundwaterbody of the Area of Brussels, defined under the Directive and the Ordinance Water : code European, code of Brussels and name of the water mass, hydrographic district"@en . + "Groundwaterbody"@en . + "Brussels Hoofdstedelijk Gewest : gegevens over het geheel van de 5 grondwaterlichamen in het Brussels Gewest, afgebakend in kader van de Kaderrichtlijn- en Ordonnantie Water : Europese code, Brusselse code en naam van het grondwaterlichaam, stroomgebiedsdistrict en geologische formatie waarbij het hoort"@nl . + "Région de Bruxelles-Capitale : données sur l'ensemble des 5 masses d'eau souterraine de la Région bruxelloise, délimitées dans le cadre de la Directive et de l'Ordonnance Cadre Eau : code européen, code bruxellois et nom de la masse d'eau, district hydrographique et formation géologique à laquelle elle appartient"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB142802C5B50FDAA41C7D61A8F672670 . + "2016-01-22T18:45:49.324751"^^ . + "Région de Bruxelles-Capitale"@fr . + "nappe phréatique"@fr . + . + . + "Belgique"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d671188421C8CB283BDE6C472372A7AB5 . + "Masses d'eau souterraines"@fr . + "Reporting Inspire"@fr . + "environnement"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "2015-09-26T00:00:00"^^ . + "Grondwaterlichamen"@nl . + . + "Criminality and police personnel"@fr . + "Interventions du SIAMU"@fr . + "Road safety"@fr . + "2016-11-22T00:00:00"^^ . + "Sécurité routière"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "cb64c6b9-494f-4494-8518-117faf01856e" . + "Interventies van de DBDMH"@fr . + "Verkeersveiligheid"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Sécurité"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "Het BISA verzamelt de Brusselse statistieken over de veiligheid van de burger in het dagelijkse leven: verkeersveiligheid, interventies van de Dienst voor Brandbestrijding en Dringende Medische Hulp (DBDMH), vastgestelde criminele feiten en personeelssterkte van de politiezones..."@nl . + "The BISA compiles statistics on the safety of the public in their daily lives: road safety, fire and medical emergency service callouts, crime figures and police staffing, etc."@en . + "Safety "@en . + "Criminalité et effectifs policiers"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF844520D4A221D034F83C2BA8DAEC97C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF844520D4A221D034F83C2BA8DAEC97C "Hoeck Michèle" . + . + "L'IBSA rassemble des données statistiques concernant la sécurité des citoyens dans leur vie quotidienne: sécurité routière, interventions du Service d'Incendie et d'Aide Médicale Urgente (SIAMU), délits constatés et effectifs des zones de police..."@fr . + "2016-12-01T11:06:40.636938"^^ . + "Veiligheid"@nl . + "Criminaliteit en politiepersoneel"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9BB3084511EC83A4BA9DF889770AD96F . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d203888C329EFCDA286B0CCAEEFD862EA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d203888C329EFCDA286B0CCAEEFD862EA "Archiefdienst" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d34D10808BF30DFE1A6234634284FA575 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d34D10808BF30DFE1A6234634284FA575 "Patricia Dhaenens" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5DC62EEA39912626288A135B3526035A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5DC62EEA39912626288A135B3526035A "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5DC62EEA39912626288A135B3526035A "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC6046DEDFB0E49DE1AD8C1707AA3EDCB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD4D7FFA12F6262B33BCEA8F0D163C1D7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD4D7FFA12F6262B33BCEA8F0D163C1D7 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD4D7FFA12F6262B33BCEA8F0D163C1D7 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d27AF0D2E61FE64B61AB9A09D3F235C61 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d27AF0D2E61FE64B61AB9A09D3F235C61 "Team BruGIS" . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8B0CB7C05AB135583138AA768106BEDC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8B0CB7C05AB135583138AA768106BEDC "Team BruGIS" . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD9B6BD2314CE1A58F051691203DFF411 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD9B6BD2314CE1A58F051691203DFF411 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD9B6BD2314CE1A58F051691203DFF411 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCED86C842A76384DDF882B65DFACB591 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCED86C842A76384DDF882B65DFACB591 "Valérie Wispenninckx" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0F29FFFBEBBFC8606D143D87BCF08DF0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0F29FFFBEBBFC8606D143D87BCF08DF0 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0F29FFFBEBBFC8606D143D87BCF08DF0 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d20746414F0BBF064417566E1E9889C5A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB59437485B0B68D2073D428F2D6D2961 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB59437485B0B68D2073D428F2D6D2961 "Bruxelles Environnement" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d274B4ADCA66AB8397241B609AEA2A9D7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d274B4ADCA66AB8397241B609AEA2A9D7 "Bruxelles Environnement" . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC82FD5C3B36583D6637C3EE942CDF995 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d55C6A1C0C99A775344142EFAB20012AA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d55C6A1C0C99A775344142EFAB20012AA "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d55C6A1C0C99A775344142EFAB20012AA "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B992CA34F29349865C57BCD06B0DED0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B992CA34F29349865C57BCD06B0DED0 "Tonglet Mathieu" . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3E395C4EAF7FFBDFA9916363BF88B3FE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3E395C4EAF7FFBDFA9916363BF88B3FE "Bruxelles Environnement" . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF176D3E6AB2E51480AF1EC9583D49A91 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF176D3E6AB2E51480AF1EC9583D49A91 "Bruxelles Environnement " . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1A04E759091E88CD34D17FAF87544EEE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1A04E759091E88CD34D17FAF87544EEE "Hoeck Michèle" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d71C100175350EC6CB2AEFDCC89B4A868 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d71C100175350EC6CB2AEFDCC89B4A868 "Bruxelles Environnement" . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + "Région de Bruxelles-Capitale"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d601B1BB85C8CBDE07F17FD30746642C4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d601B1BB85C8CBDE07F17FD30746642C4 "CIRB" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0AD7A730011EA64106C15BBE33FFFC33 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0AD7A730011EA64106C15BBE33FFFC33 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0AD7A730011EA64106C15BBE33FFFC33 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d16A640AD7A30FA99BE9AB15566B7AD0B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d16A640AD7A30FA99BE9AB15566B7AD0B "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d16A640AD7A30FA99BE9AB15566B7AD0B "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC4E8C685D3B77B989104CAA288F7BA99 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC4E8C685D3B77B989104CAA288F7BA99 "Team BruGIS" . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d780DB9AE7F1EAB3F75CD6FA7B8656C63 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d780DB9AE7F1EAB3F75CD6FA7B8656C63 "Bruxelles Environnement" . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3AB75DE4BF8A787BD56D082D043C9EF7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3AB75DE4BF8A787BD56D082D043C9EF7 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3AB75DE4BF8A787BD56D082D043C9EF7 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d85112E4C1AFD42DA2821C3015879CF4A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d85112E4C1AFD42DA2821C3015879CF4A "Team BruGIS" . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d56213AB5E3C2ED629E055D687319D1D6 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d56213AB5E3C2ED629E055D687319D1D6 "CIRB" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0386BC6B2D9D1261CE53E69092663780 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0386BC6B2D9D1261CE53E69092663780 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0386BC6B2D9D1261CE53E69092663780 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4409DB53549CE3901C6F13728A2FB331 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4409DB53549CE3901C6F13728A2FB331 "Hoeck Michèle" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d852D7DF5713DC210E684C1414DAC6F84 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d852D7DF5713DC210E684C1414DAC6F84 "Mathieu Tonglet" . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6BDE8001DF2942F1AEF6CC60BDF527F4 . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8ABEB1AB5CA2A24AF103AD140EB3C62E . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8ABEB1AB5CA2A24AF103AD140EB3C62E "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8ABEB1AB5CA2A24AF103AD140EB3C62E "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF0312FCB86A5E16D9E03EFB4720EBB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF0312FCB86A5E16D9E03EFB4720EBB "Hoeck Michèle" . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3E17AF425BC148916597B80F2720C364 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3E17AF425BC148916597B80F2720C364 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3E17AF425BC148916597B80F2720C364 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5AA78B1425BE930523E3A6B870C4F1AF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5AA78B1425BE930523E3A6B870C4F1AF "Mathieu Tonglet" . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d44B94FAEB48CCB3C88E5D18D2AA11EFD . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d44B94FAEB48CCB3C88E5D18D2AA11EFD "Mathieu Tonglet" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCC8C70A633BD3117C9416C76C17FFF5B . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d03EFE23A906885D31F5D2FF3F547B61A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d03EFE23A906885D31F5D2FF3F547B61A "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d03EFE23A906885D31F5D2FF3F547B61A "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d790C843BED6D428E5A6EADE571F8C123 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d790C843BED6D428E5A6EADE571F8C123 "Bruxelles Environnement" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d07A839B5CB898E28C4AEF4C9D3A1DB6F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d07A839B5CB898E28C4AEF4C9D3A1DB6F "Bruxelles Environnement" . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d81ACDBAABB90150F7C85E0DE1D6C74C7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d81ACDBAABB90150F7C85E0DE1D6C74C7 "CIRB" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDB0CB2D98F00E32DFFBE2A2E5CED934B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDB0CB2D98F00E32DFFBE2A2E5CED934B "POLYGON ((4.2358 50.7575, 4.4981 50.7575, 4.4981 50.9251, 4.2358 50.9251, 4.2358 50.7575))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDB0CB2D98F00E32DFFBE2A2E5CED934B "{\"type\": \"Polygon\", \"coordinates\": [[[4.23579760742, 50.7575331421], [4.49809619141, 50.7575331421], [4.49809619141, 50.925074646], [4.23579760742, 50.925074646], [4.23579760742, 50.7575331421]]]}"^^ . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0675422B5FF221F19ADF6782B3C43C8A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0675422B5FF221F19ADF6782B3C43C8A "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0675422B5FF221F19ADF6782B3C43C8A "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7756E19FDDF6ED0DC6AFC24023F6B0E5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7756E19FDDF6ED0DC6AFC24023F6B0E5 "Hoeck Michèle" . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d322B10B6203A86CA015B9F44FD12B8DB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d62A3947A3A310C88D911C8D1D39DAA42 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d62A3947A3A310C88D911C8D1D39DAA42 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d62A3947A3A310C88D911C8D1D39DAA42 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B0A83B6462DEBA8741B6FA7600E0A36 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B0A83B6462DEBA8741B6FA7600E0A36 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B0A83B6462DEBA8741B6FA7600E0A36 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + "Région de Bruxelles-Capitale"@fr . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3B419BB42B9323D97DECCF73C9A8E522 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3B419BB42B9323D97DECCF73C9A8E522 "Team BruGIS" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5890E9EA4B22C403AD5E9E87ACE41839 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5890E9EA4B22C403AD5E9E87ACE41839 "CIRB" . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD903FB770CF839C911725C9A81852268 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD903FB770CF839C911725C9A81852268 "POLYGON ((4.2340 50.7606, 4.4840 50.7606, 4.4840 50.9158, 4.2340 50.9158, 4.2340 50.7606))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD903FB770CF839C911725C9A81852268 "{\"type\": \"Polygon\", \"coordinates\": [[[4.23404418945, 50.760592041], [4.4839831543, 50.760592041], [4.4839831543, 50.9157739258], [4.23404418945, 50.9157739258], [4.23404418945, 50.760592041]]]}"^^ . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d904C2429CA620CB91CE7C139CDC36DDB . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d839AEB4D9933D25447A9793486A08931 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d839AEB4D9933D25447A9793486A08931 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d839AEB4D9933D25447A9793486A08931 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF4AA0E7103612041E59554867E4A2306 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF4AA0E7103612041E59554867E4A2306 "Bruxelles Environnement" . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE3907014DAEF28ABE232BB3B7840BAB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE3907014DAEF28ABE232BB3B7840BAB "Bruxelles Environnement" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d36E341601DFF7A5B5E862A235F79C9CC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d36E341601DFF7A5B5E862A235F79C9CC "Corentin Descamps" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3B6081C4073B928C40D03B078E319E50 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d92B17BB0E4314D1C0B539DE5DF349601 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d92B17BB0E4314D1C0B539DE5DF349601 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d92B17BB0E4314D1C0B539DE5DF349601 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d964B0DAF9CF755F6E5FD8F555D459350 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d964B0DAF9CF755F6E5FD8F555D459350 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d964B0DAF9CF755F6E5FD8F555D459350 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d567EEC4D7A008F925B3D177DE80889B5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d23B8BB7B9ADBD9419C195E57E4CFE089 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d23B8BB7B9ADBD9419C195E57E4CFE089 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d23B8BB7B9ADBD9419C195E57E4CFE089 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d94EDE7F54CE24412D64FFBB70323540A . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1145EF867677852CE553D812ACFA63A5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1145EF867677852CE553D812ACFA63A5 "Hoeck Michèle" . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF59082FE2CE120EF836EB6373C323D8D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d14378FFF0C0BD3C80F90963B3FA4F7A6 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d14378FFF0C0BD3C80F90963B3FA4F7A6 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d14378FFF0C0BD3C80F90963B3FA4F7A6 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8849B003B708E7DD7746598C1F546BB3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCDA61420E8AB9F9A1CEDB119FF9A03C2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCDA61420E8AB9F9A1CEDB119FF9A03C2 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCDA61420E8AB9F9A1CEDB119FF9A03C2 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9C270F8D208124A178E6DB5D4083AA5D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9C270F8D208124A178E6DB5D4083AA5D "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9C270F8D208124A178E6DB5D4083AA5D "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE95269EBDCE3E26F69BC9706ED810A56 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE7EE0B40716784694B036509823F2A5E . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE7EE0B40716784694B036509823F2A5E "Sébastien Defrance" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6B3D9739545C9364D3DF204541FDA29F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6B3D9739545C9364D3DF204541FDA29F "Hoeck Michèle" . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE767622D4D208738EA4C0C970C431D89 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE767622D4D208738EA4C0C970C431D89 "Hoeck Michèle" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d362E839840D38165D85EAE6FCE2B9D71 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d362E839840D38165D85EAE6FCE2B9D71 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d362E839840D38165D85EAE6FCE2B9D71 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + . + . + "Région de Bruxelles-Capitale"@fr . + . + . + . + . + . + . + . + . + . + . + "Réseau hydrographique"@fr . + "Maillage bleu "@fr . + "Région de Bruxelles-Capitale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d842EF014A2B3FBB47E627B76265D1CE4 . + "eau de surface"@fr . + "Harvested"@fr . + "2016-01-22T18:45:32.732102"^^ . + "Région de Bruxelles-Capitale : typologie et codification de l'ensemble des Objets Bleus Unitaires (OBU) - cours d'eau, canal et étangs - de la Région bruxelloise. On dispose également du nom de l'objet, ainsi que pour les cours d'eau, de caractéristiques (à ciel ouvert, en pertuis...)"@fr . + "Belgique"@fr . + "Brussels-Capital Region: typology and codification of all the Unitarian Blue Objects (OBU) - rivers, channel and ponds - of the Brussels Region. We also have the name of the object, the municipality in which it is situated as well as for the rivers, their characteristics (open-air, pertuis)"@en . + "hydrographie"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + "River system"@en . + "Brussels Hoofdstedelijk Gewest : typologie en codificatie van het geheel van de Blauwe Eenheidsvoorwerpen (OBU) - waterlopen, kanaal en vijvers - in het Brussels Gewest. Bevat aanvullende informatie: de naam van het object, en voor de waterlopen de kenmerken van de waterloop (open bedding, overwelfd...)"@nl . + "2015-12-11T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8BED8BFC716A549519F5894A5525DDA5 . + . + . + "Hydrografisch netwerk"@nl . + "Reporting Inspire"@fr . + "environnement"@fr . + "49220e81-638b-4876-a259-dd87f0b270b1" . + "gestion des ressources en eau"@fr . + "occupation du sol"@fr . + "réseau hydrographique"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d635F101DA107D18104171D3AE85E95BF . + "Brussels Hoofdstedelijk Gewest"@nl . + . + . + . + "Région de Bruxelles-Capitale"@fr . + "stationsSW_cyprinicole.xml" . + "réseau de mesure"@fr . + . + "Harvested"@fr . + "Brussels Hoofdstedelijk Gewest"@nl . + "eau de surface"@fr . + "Région de Bruxelles-Capitale"@fr . + "Région de Bruxelles-Capitale : localisation des différentes stations de mesure pour le monitoring qualitatif des eaux piscicoles (cyprinicoles), conformément à l'arrêté du 18 juin 1992 de l'Exécutif de la Région de Bruxelles-Capitale établissant le classement des eaux de surface. Le monitoring porte sur des paramètres chimiques, physico-chimiques et microbiologiques"@fr . + "qualité de l'eau"@fr . + . + "station de surveillance"@fr . + "Brussels-Capital Region : location of different monitoring points for quality monitoring of waters capable of supporting fish life (cyprinid), in accordance with the decree of 18 June 1992 of the Executive of the Brussels-Capital determining the classification of surface water. Monitoring relates to chemical, physico-chemical and microbiological parameters."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE5616D9969FD3042A961F3A38D2AB86E . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d41F1A92353817736E4016E1DA28018BF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB8BECFF6BAF31300DF9D22C2B43500F9 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB8BECFF6BAF31300DF9D22C2B43500F9 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB8BECFF6BAF31300DF9D22C2B43500F9 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . + "2016-01-22T18:45:31.090132"^^ . + "poisson"@fr . + "environnement"@fr . + "cours d'eau"@fr . + . + "Brussels Hoofdstedelijk Gewest"@nl . + "Région de Bruxelles-Capitale"@fr . + . + "Brussels Hoofdstedelijk Gewest : lokalisatie van de verschillende meetstations voor de kwaliteitsmonitoring van de viswateren (karperachtige), conform aan het besluit van 18 juni 1992 van de uitvoerende macht van het Brussels Hoofdstedelijk Gewest naar de rangschikking van de oppervlaktewateren. De monitoring bestaat uit chemische, fysisch-chemische en microbiologische parameters"@nl . + "Réseau de surveillance de la qualité des eaux piscicoles"@fr . + "2015-09-26T00:00:00"^^ . + "Reporting Inspire"@fr . + "Belgique"@fr . + "Région de Bruxelles-Capitale"@fr . + "Monitoring network of the quality of fresh waters capable of supporting fish life"@en . + "Monitoring van de kwaliteit van de viswateren"@nl . + . + . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d39C3C9C48276C1689ABCA24432331B83 . + "2015-10-02T00:00:00"^^ . + "Zones de limitation du bruit des avions"@fr . + "environnement"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d118C647FBE0DB107B93D2D6D8526D1FE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d118C647FBE0DB107B93D2D6D8526D1FE "CIRB" . + "a7bcb16d-bbfb-42b9-939b-351211ef75e8" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d775FADB35013426678BF3391F4AD684B . + "Région de Bruxelles-Capitale : localisation des 3 zones de limitation du bruit des avions définies par l'arrêté du Gouvernement de la Région de Bruxelles-Capitale du 27 mai 1999 relatif à la lutte contre le bruit généré par le trafic aérien.\nDocument explicatif : \nhttp://geoportal.ibgebim.be/pdf/metadata_doc/19990527_agb_LutteBruit_TraficAerien.pdf"@fr . + "bruit"@fr . + "Gebieden van beperking van vliegtuiggeluid"@nl . + "Région de Bruxelles-Capitale"@fr . + "législation en matière de bruit"@fr . + . + . + "Brussels-Capital Region: location of the three areas of limitation of aircraft noise defined by the order of the Government of the Brussels-Capital Region of 27 May 1999 on the fight against the noise generated by air traffic.\nNote :\nhttp://geoportal.ibgebim.be/pdf/metadata_doc/19990527_agb_LutteBruit_TraficAerien.pdf"@en . + "Harvested"@fr . + "nuisance sonore"@fr . + "Reporting Inspire"@fr . + . + . + "Belgique"@fr . + "Areas of limitation of aircraft noise"@en . + "Brussels Hoofdstedelijk Gewest : localisatie van de drie gebieden van de beperking van het vliegtuiggeluid bepaald door het besluit van de Brusselse Hoofdstedelijke Regering van 27 mei 1999 betreffende de bestrijding van geluidshinder voortgebracht door het luchtverkeer."@nl . + "WMS" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d208362642605765D41E97A5F8191E91B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d208362642605765D41E97A5F8191E91B "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d208362642605765D41E97A5F8191E91B "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . + "Zones de gestion, de restriction ou de réglementat"@fr . + "2016-01-22T18:45:35.440291"^^ . + "bruit aérien"@fr . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : de entiteit « Tube Block » (of metrozone) identificeert de delen van het grondgebied die ingenomen worden door de tunnels en de bovengrondse delen (stations inbegrepen) van de metro en de premetro/tram."@nl . + "Tube block"@en . + "2016-01-22T18:45:10.761149"^^ . + "Espace public"@fr . + . + . + "Belgique"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBEE33AE874DC0320C0075AFC23F94BF7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBEE33AE874DC0320C0075AFC23F94BF7 "CIRB" . + "2b78e1a2-8e40-4135-90b2-32d7951e9391" . + "Bruit ferroviaire "@fr . + "2015-03-30T00:00:00"^^ . + . + . + "Reporting Inspire"@fr . + "Région de Bruxelles-capitale : l'entité « Tube Block » (ou zone de métro) identifie les emprises au sol des tunnels et des parties aériennes (stations comprises) du métro et du pré-métro/tram."@fr . + "Mobilité"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9BFC68D6E345B4FA38D2F2D172593DA8 . + "Région de Bruxelles-Capitale"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6DD8823B931E079AA77D967405159BE7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6DD8823B931E079AA77D967405159BE7 "Bruxelles Environnement " . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7A86E59D559480E5063C00374C120C60 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7A86E59D559480E5063C00374C120C60 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7A86E59D559480E5063C00374C120C60 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . + "Metrozone"@nl . + "Brussels-Capital Region : the entity \"Tube Block\" (or metro area) identifies rights-ground tunnels and aerial parts (including stations) metro and pre-metro/tram."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d65CF7FEA19BA8162DC1F8E9C6C815D0E . + "Fond de plan"@fr . + . + . + . + "Tunnels du métro"@fr . + . + . + "Harvested"@fr . + . + . + "95cb54b3-6e5f-4697-ba6d-7b2dc2f3c5c5" . + "Forêt de Soignes"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "Belgique"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7B0F0F53F6C395234D8C495B11644AF8 . + "espaces verts"@fr . + "environnement"@fr . + "forêt"@fr . + "Zones de protection en forêt de Soignes"@fr . + . + . + "Brussels-Capital Region : protected areas in the Brussels Sonian Forest: vulnerable planting or regeneration plots, refuges for wildlife, fragile being recolonized areas. In those areas, walkers must always stay on the paths and dogs must be leashed (Order of 30 March 1995)."@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d502DE09CBA90E4669C531E318331CE7A . + "Brussels Hoofdstedelijk Gewest : beschermingsgebieden in het Brusselse deel van het Zoniënwoud : kwetsbare aanplantingen of regeneratiepercelen, toevluchtsgebieden voor fauna, kwetsbare gebieden waar de vegetatie zich aan het herstellen is. In al deze gebieden moeten de wandelaars op de paden blijven en hun honden aan de leiband houden (Ordonnantie van 30 maart 1995)."@nl . + "protection de la forêt"@fr . + "Beschermingsgebieden in het Zoniënwoud"@nl . + . + . + "2015-09-26T00:00:00"^^ . + . + . + "Région de Bruxelles-Capitale : zones de protection en forêt de Soignes bruxelloise : parcelles de plantation ou de régénération vulnérables, zones refuges pour la faune, zones fragilisées en voie de recolonisation. Dans ces zones, les promeneurs doivent impérativement rester sur les chemins et les chiens doivent être tenus en laisse (Ordonnance du 30 mars 1995)."@fr . + "2016-01-22T18:45:33.810168"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d52C0E83B4FADCB042AF748DA915CB072 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d52C0E83B4FADCB042AF748DA915CB072 "CIRB" . + . + "Harvested"@fr . + "Protected areas in the Sonian Forest"@en . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d682525B6925614F6D76780E080E695DE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d682525B6925614F6D76780E080E695DE "Bruxelles Environnement" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d33DEF9BBC393C26B6F0705312C1B20FC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d33DEF9BBC393C26B6F0705312C1B20FC "Florine Deladrière" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC3BA798CD14264694ECBBB701EEE77B7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC3BA798CD14264694ECBBB701EEE77B7 "IBSA-IBSA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC05406AF2496A4EC701BBF514E344A0B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC05406AF2496A4EC701BBF514E344A0B "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC05406AF2496A4EC701BBF514E344A0B "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7292344E623DD1E4E391F4CC0008B127 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE0FBA649E5A635B967E4A06F6B5B21D7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE0FBA649E5A635B967E4A06F6B5B21D7 "IBSA-BISA" . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDDF03058F3C31E4CE65A731D8E8DB9A0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDDF03058F3C31E4CE65A731D8E8DB9A0 "Bertrand Castillon" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4472F8AB705A0E73BA04F7D076ABB2B8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4472F8AB705A0E73BA04F7D076ABB2B8 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4472F8AB705A0E73BA04F7D076ABB2B8 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2AA3A9D780D06DF4C8667E425661452F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDC19E65FB6FDC5E408907096853822F8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDC19E65FB6FDC5E408907096853822F8 "Bertrand Castillon" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "XLS" . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d39CB997EEFDD3014D3250EF9A3B854C8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d39CB997EEFDD3014D3250EF9A3B854C8 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d39CB997EEFDD3014D3250EF9A3B854C8 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d70ECFB220485EFD19AEB97CC9E6AC348 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d70ECFB220485EFD19AEB97CC9E6AC348 "CIRB" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3CB021CC946B6B8F3930E8A428564D2B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3CB021CC946B6B8F3930E8A428564D2B "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE3367817AA96B85EF16C21789A474A66 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE3367817AA96B85EF16C21789A474A66 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE3367817AA96B85EF16C21789A474A66 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d595AD2218C87A24FFC81333B2FCE9D88 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3059C15FE07AB4D5AD70DA29E982ABF3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3059C15FE07AB4D5AD70DA29E982ABF3 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3059C15FE07AB4D5AD70DA29E982ABF3 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0C0A61ABCDEF1C9E7CD847EF0F953413 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0C0A61ABCDEF1C9E7CD847EF0F953413 "Emad Alsous" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d808A44B6E0581FBAA9774B85368FAC3E . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d808A44B6E0581FBAA9774B85368FAC3E "Secretariat" . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1F5658119AAABFC8EED472A7E0FBCBE3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1F5658119AAABFC8EED472A7E0FBCBE3 "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d752518BE235D70A379AC74E67026CE41 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d752518BE235D70A379AC74E67026CE41 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d752518BE235D70A379AC74E67026CE41 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d57D7639AD10526460B6E3FBFEC85E525 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d57D7639AD10526460B6E3FBFEC85E525 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d57D7639AD10526460B6E3FBFEC85E525 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + "begeleiding"@fr . + "formation"@fr . + "numérique"@fr . + "accompagnement"@fr . + "opleiding"@fr . + "ordinateur"@fr . + "computer"@fr . + "pc"@fr . + . + . + . + "agences de voyages"@fr . + "travel agencies"@fr . + . + . + . + "reisagentschappen"@fr . + "Crèche"@fr . + "enfance"@fr . + "garderie"@fr . + "jeunesse"@fr . + . + . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + . + "couche"@fr . + "base de données"@fr . + "dikte"@fr . + "geology"@fr . + "geologisch model"@fr . + "stratigrafische eenheden"@fr . + . + . + . + "geological model"@fr . + "layer"@fr . + "épaisseur"@fr . + "modèle géologique"@fr . + "hydrogeology"@fr . + "geologie"@fr . + "top"@fr . + . + . + "stratigraphy"@fr . + "thickness"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1727855354562ADA631A0D35DAD8A131 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3AADB07C3EB3FAF22A058D9E16AD998A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9466C49419BEBA145CD55285209E6095 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9466C49419BEBA145CD55285209E6095 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9466C49419BEBA145CD55285209E6095 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + "gegevensbank"@fr . + "géologie"@fr . + "hydrogéologie"@fr . + "database"@fr . + "stratigraphic units"@fr . + "gis"@fr . + "sig"@fr . + "toit"@fr . + "unités stratigraphiques"@fr . + "laag"@fr . + "stratigrafie"@fr . + "stratigraphie"@fr . + . + . + "critère européen"@fr . + "déchets"@fr . + "installations"@fr . + "agenda"@fr . + "tango"@fr . + . + "Fond de plan"@fr . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "maison individuelle"@fr . + . + "centre de demontage"@fr . + "exploitants"@fr . + "véhicules"@fr . + . + . + "cobrace"@fr . + "évaluation"@fr . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEE482C133EF852BC112786DE9DEF9456 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEE482C133EF852BC112786DE9DEF9456 "IBSA-BISA" . + "incidences"@fr . + . + "http://opendatastore.brussels/catalog.xml?page=1" . + "100"^^ . + "http://opendatastore.brussels/catalog.xml?page=3" . + "http://opendatastore.brussels/catalog.xml?page=3" . + "http://opendatastore.brussels/catalog.xml?page=1" . + "205"^^ . + "marché"@fr . + "markt"@fr . + "collete"@fr . + "déchets"@fr . + "traitement"@fr . + "Innovation"@fr . + "Research and development"@fr . + "boek"@fr . + "lezen"@fr . + "lire"@fr . + "livre"@fr . + "fournisseurs"@fr . + "gaz"@fr . + "commerce"@fr . + "marché"@fr . + "counter"@fr . + "bike"@fr . + "compteur"@fr . + "bike count"@fr . + "comptage"@fr . + "counting"@fr . + "fietspaal"@fr . + "fietstelpaal"@fr . + "vélo"@fr . + "fournisseurs"@fr . + "éléctricité"@fr . + "60-7"@fr . + "social economy"@fr . + "sociale economie"@fr . + "économie sociale"@fr . + . + "Bibliothèque"@fr . + "Prêt"@fr . + "média"@fr . + "simplification"@fr . + "simplification administrative"@fr . + "simplify"@fr . + "XLSX" . + . + . + "administratieve vereenvoudiging"@fr . + "easy"@fr . + "football"@fr . + "fitness"@fr . + "omnisport"@fr . + "salle"@fr . + "omnisports"@fr . + "sport"@fr . + "terrain"@fr . + "veld"@fr . + "voetbal"@fr . + "zaal"@fr . + "hotspot"@fr . + "internet access"@fr . + "urbizone"@fr . + "wifi"@fr . + "bâtiments"@fr . + "omnisports"@fr . + "sports"@fr . + "activiteit"@fr . + "activité"@fr . + "culture"@fr . + "loisirs"@fr . + "cultuur"@fr . + "recreatie"@fr . + "récréatif"@fr . + "vrije tijd"@fr . + "Bruxelles Coordination Régionale"@fr . + "Ordonnance relative à la publicité de l'administration"@fr . + "Brussel Gewestelijke Coordinatie"@fr . + "Ordonnantie betreffende de openbaarheid van bestuur"@fr . + "Publicité"@fr . + "Bestuur"@fr . + "Gewestelijke Overheidsdienst Brussel"@fr . + "Administration"@fr . + "Openbaarheid"@fr . + "Registre des études"@fr . + "Service public régional de Bruxelles"@fr . + "Studieregister"@fr . + "SINE"@fr . + "CPAS"@fr . + "commune"@fr . + "centrum algemeen welzijnswerk"@fr . + "caw"@fr . + "gemeente"@fr . + "OCMW"@fr . + "organisation"@fr . + "service"@fr . + "service social"@fr . + "sociale dienst"@fr . + "exploitants"@fr . + "destruction"@fr . + "hors usage"@fr . + "recyclage"@fr . + "type c"@fr . + "véhicules"@fr . + "déchets"@fr . + "entreposage"@fr . + "caveau"@fr . + "cimetière"@fr . + "concession"@fr . + "animaux"@fr . + "déchets"@fr . + "traitement"@fr . + "critère bruxelois"@fr . + "fin de déchets"@fr . + "installations"@fr . + "baby"@fr . + "enfant"@fr . + "bébé"@fr . + "geboorte"@fr . + "kind"@fr . + "naissance"@fr . + "Belgique"@fr . + "Espace public"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "climatisation"@fr . + "formation"@fr . + "véhicules"@fr . + "Belgique"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Topographie"@fr . + "enseignement"@fr . + "klasse"@fr . + "classe"@fr . + "enfant"@fr . + "onderwijs"@fr . + "opvoeding"@fr . + "kind"@fr . + "school"@fr . + "école"@fr . + "éducation"@fr . + "enseignement"@fr . + "école"@fr . + "balançoire"@fr . + "glijbaan"@fr . + "kinderen"@fr . + "enfant"@fr . + "jeu"@fr . + "schommel"@fr . + "spelen"@fr . + "toboggan"@fr . + "numérique"@fr . + "ordinateur"@fr . + "bâtiments"@fr . + "infrastructure"@fr . + "services"@fr . + "Bio"@fr . + "Installations"@fr . + "classées"@fr . + "certificat"@fr . + "destruction"@fr . + "exploitant"@fr . + "hors usqge"@fr . + "véhicules"@fr . + "Belgique"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "limite administrative"@fr . + "conseil"@fr . + "conseiller"@fr . + "echevin"@fr . + "raadslid"@fr . + "schepen"@fr . + "Belgique"@fr . + . + . + "Fond de plan"@fr . + "Harvested"@fr . + "Patrimoine"@fr . + "Reporting Inspire"@fr . + "Tourism"@fr . + "centre"@fr . + "examen"@fr . + "froid"@fr . + "technique"@fr . + "collecteurs"@fr . + "courtiers"@fr . + "déchets dangereux"@fr . + "négociants"@fr . + "sépulture"@fr . + "Agences d'emploi privées"@fr . + "Particuliere bureaus voor arbeidsbemiddeling"@fr . + "aankoop"@fr . + "bestek"@fr . + "achats"@fr . + "cahier des charges"@fr . + "fournisseurs"@fr . + "leveranciers"@fr . + "offerte"@fr . + "offre"@fr . + "boucle"@fr . + "loop"@fr . + "camera"@fr . + "real-time"@fr . + "counter"@fr . + "telling"@fr . + "compteur"@fr . + "tellus"@fr . + "traffic"@fr . + "verkeer"@fr . + . + . + "activiteit"@fr . + "activité"@fr . + "club"@fr . + "sport"@fr . + "boulevard"@fr . + "chaussée"@fr . + "chemin"@fr . + "avenue"@fr . + "binnenhof"@fr . + "clos"@fr . + "rue"@fr . + "passage"@fr . + "laan"@fr . + "steenweg"@fr . + "straat"@fr . + "weg"@fr . + "formation"@fr . + "opleiding"@fr . + "training"@fr . + "Belgique"@fr . + "Espace public"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Maillage vert"@fr . + "Maillage écologique"@fr . + "Reporting Inspire"@fr . + "Fond de plan"@fr . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "altitude"@fr . + "PEB"@fr . + "batiment"@fr . + "certfication"@fr . + "chauffage"@fr . + "climatisation"@fr . + "energie"@fr . + "travaux"@fr . + "Belgique"@fr . + "Espace public"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "België"@fr . + "Bruxelles"@fr . + "Belgique"@fr . + "Finances publiques"@fr . + "Belgium"@fr . + "Brussel"@fr . + "Brussels"@fr . + "Public finance"@fr . + "Overheidsfinanciën"@fr . + "Statistics"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Belgique"@fr . + "Enquête sur le budget des ménages"@fr . + "Brussel"@fr . + "Fiscale statistiek van de inkomens"@fr . + "Brussels"@fr . + "Belgium"@fr . + "Huishoudbudgetonderzoek"@fr . + "Inkomensrekeningen van de huishoudens"@fr . + "Lonen"@fr . + "Salaires"@fr . + "Household income accounts"@fr . + "Comptes des revenus des ménages"@fr . + "Fiscal statistics on income"@fr . + "België"@fr . + "Salaries"@fr . + "Statistics"@fr . + "Bruxelles"@fr . + "Household budget survey"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Statistiques fiscales de revenus"@fr . + "Car Sharing"@fr . + "ZenCar"@fr . + "Car Sharing"@fr . + "ZenCar"@fr . + "3D"@fr . + "Residential and non-residential buildings"@fr . + "Région de Bruxelles-Capitale"@fr . + "UrbIS"@fr . + "eaux usées résiduaires industrielles"@fr . + "Région de Bruxelles-Capitale"@fr . + "Harvested"@fr . + "Belgique"@fr . + "charge en eaux usées"@fr . + "environnement"@fr . + "Reporting Inspire"@fr . + "pollution de l'eau"@fr . + "qualité des eaux usées"@fr . + "station d'épuration des eaux usées"@fr . + "traitement des eaux usées"@fr . + "épuration de l'eau"@fr . + "belgium"@fr . + "brussels"@fr . + "real-time"@fr . + "transport-public"@fr . + "vehicle-position"@fr . + "Belgique"@fr . + "Bruxelles"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "Topographie"@fr . + "Active mobility"@fr . + "Belgique"@fr . + "Collective transport and shared mobility"@fr . + "Freight transport"@fr . + "Belgium"@fr . + "Brussel"@fr . + "België"@fr . + "Actieve mobiliteit"@fr . + "Pratiques de déplacements"@fr . + "Sécurité routière"@fr . + "Statistiques"@fr . + "Verkeersveiligheid"@fr . + "Mobility practices"@fr . + "Statistics"@fr . + "Verplaatsingsgewoonten"@fr . + "Transport de marchandises"@fr . + "Mobilité active"@fr . + "Bruxelles"@fr . + "Brussels"@fr . + "Statistieken"@fr . + "Collectief en gedeeld vervoer"@fr . + "Vehicles and road network"@fr . + "Transport collectif et partagé"@fr . + "Road safety"@fr . + "Vervoer van goederen"@fr . + "Voertuigen en wegennet"@fr . + "Véhicules et réseau routier"@fr . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "classification de l'usage des sols"@fr . + "affectation des sols"@fr . + "environnement"@fr . + "espace vert"@fr . + "occupation du sol"@fr . + "paysage"@fr . + "Belgique"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "limite administrative"@fr . + "civil society organisations"@fr . + "Brussels"@fr . + "gender equality"@fr . + "grassroots organisations"@fr . + "public sector"@fr . + "Reporting Inspire"@fr . + "Belgique"@fr . + "environnement"@fr . + "Région de Bruxelles-Capitale"@fr . + "espace vert"@fr . + "Harvested"@fr . + "conservation des ressources naturelles"@fr . + "faune"@fr . + "flore"@fr . + "législation en matière de préservation de la natur"@fr . + "préservation de la nature"@fr . + "site naturel protégé"@fr . + "Harvested"@fr . + "Mobilité"@fr . + "Bruxelles"@fr . + "Belgique"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "contrôle de la circulation"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "Belgique"@fr . + "environnement"@fr . + "législation en matière de préservation de la natur"@fr . + "protection de la faune et de la flore"@fr . + "protection des espaces naturels"@fr . + "protection des espèces"@fr . + "Harvested"@fr . + "Région de Bruxelles-Capitale"@fr . + "Zones de gestion, de restriction ou de réglementat"@fr . + "approvisionnement en eau potable"@fr . + "Belgique"@fr . + "alimentation en eau de la ville"@fr . + "captage d'eau"@fr . + "Reporting Inspire"@fr . + "eaux souterraines"@fr . + "environnement"@fr . + "protection de zone de captage de l'eau"@fr . + "zone protégée de captage d'eau"@fr . + "Belgique"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "Belgique"@fr . + "Brussels"@fr . + "Brussel"@fr . + "Femmes"@fr . + "Age structure"@fr . + "Households"@fr . + "Nationaliteiten"@fr . + "Ménages"@fr . + "Belgium"@fr . + "Demographic projections"@fr . + "Huishoudens"@fr . + "Projections démographiques"@fr . + "Loop van de bevolking"@fr . + "België"@fr . + "Annual change"@fr . + "Statistieken"@fr . + "Bruxelles"@fr . + "Movement of the population"@fr . + "Mannen"@fr . + "Jaarlijkse evolutie"@fr . + "Statistics"@fr . + "Nationalities"@fr . + "Hommes"@fr . + "Mouvement de la population"@fr . + "Statistiques"@fr . + "Bevolkingsprojecties"@fr . + "Nationalités"@fr . + "Men"@fr . + "Leeftijdsstructuur"@fr . + "Structure par âge"@fr . + "Vrouwen"@fr . + "Women"@fr . + "Évolution annuelle"@fr . + "bus"@fr . + "metro"@fr . + "public-transport"@fr . + "Brussels"@fr . + "realtime"@fr . + "stops"@fr . + "tram"@fr . + "Bruxelles"@fr . + "Geslacht"@fr . + "Belgium"@fr . + "Population scolaire"@fr . + "Origin-destination of pupils"@fr . + "Brussels"@fr . + "Sexe"@fr . + "België"@fr . + "Herkomst-bestemming van leerlingen"@fr . + "School population"@fr . + "Onderwijsinstellingen"@fr . + "Brussel"@fr . + "Schools"@fr . + "Schoolbevolking"@fr . + "Origine et destination des élèves"@fr . + "Statistics"@fr . + "Sex"@fr . + "Belgique"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Établissements"@fr . + "Agenda"@fr . + "Culture"@fr . + "Cinéma"@fr . + "Exposition"@fr . + "Concert"@fr . + "Nightlife"@fr . + "Spectacle"@fr . + "Théatre"@fr . + "contrôle d'accès"@fr . + "fietsparking"@fr . + "parking"@fr . + "vélo"@fr . + "Activité économique"@fr . + "Belgique"@fr . + "Bruxelles"@fr . + "Conjoncture"@fr . + "Companies"@fr . + "Conjunctuur"@fr . + "Economic activity"@fr . + "Economic situation"@fr . + "Regional economic prospects"@fr . + "België"@fr . + "Belgium"@fr . + "Ondernemingen"@fr . + "Economische activiteit"@fr . + "Perspectives économiques régionales"@fr . + "Entreprises"@fr . + "Regionale economische vooruitzichten"@fr . + "Brussels"@fr . + "Brussel"@fr . + "Statistics"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Fond de plan"@fr . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "limite administrative"@fr . + "fix my street"@fr . + "incident"@fr . + "mobilite"@fr . + "mobiliteit"@fr . + "mobility"@fr . + "voirie"@fr . + "Brussel"@fr . + "Energy"@fr . + "Environment and society"@fr . + "Brussels"@fr . + "Belgium"@fr . + "Environnement et société"@fr . + "Environment and territory"@fr . + "Milieu en grondgebied"@fr . + "Energie"@fr . + "Belgique"@fr . + "Bruxelles"@fr . + "Milieu en maatschappij"@fr . + "Statistics"@fr . + "België"@fr . + "Environnement et territoire"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Énergie"@fr . + "brussel"@fr . + "brussels"@fr . + "bruxelles"@fr . + "coworking"@fr . + "espace de travail"@fr . + "freelance"@fr . + "indépendants"@fr . + "u talk freelance"@fr . + "Events"@fr . + "Traffic"@fr . + "Works"@fr . + "Reporting Inspire"@fr . + "Harvested"@fr . + "Région de Bruxelles-Capitale"@fr . + "Belgique"@fr . + "eau de surface"@fr . + "environnement"@fr . + "occupation du sol"@fr . + "ressource en eau"@fr . + "Belgique"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Mobilité"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "Espace public"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Gezondheid"@nl . + "Health"@en . + "Santé"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Environnement"@fr . + "Milieu"@nl . + . + . + "Environment"@en . + "Nutsdiensten en overheidsdiensten"@nl . + "Services d'utilité publique et services publics"@fr . + "Utility and governmental services"@en . + "Environment"@en . + "Environnement"@fr . + "Gebouwen"@nl . + "Habitats en biotopen"@nl . + "Land use"@en . + "Bâtiments"@fr . + "Bodemgebruik"@nl . + "Buildings"@en . + "Habitats and biotopes"@en . + "Habitats et biotopes"@fr . + "Land cover"@en . + "Landgebruik"@nl . + "Milieu"@nl . + "Occupation des terres"@fr . + "Usage des sols"@fr . + "Science and technology"@en . + "Science et technologie"@fr . + "Wetenschap en technologie"@nl . + "Administratieve eenheden"@nl . + "Administrative units"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Unités administratives"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Gezondheid"@nl . + "Health"@en . + "Santé"@fr . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Government and public sector"@en . + "Gouvernement et secteur public"@fr . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Administratieve eenheden"@nl . + "Administrative units"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Unités administratives"@fr . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Installations de suivi environnemental"@fr . + "Environmental monitoring facilities"@en . + "Milieubewakingsvoorzieningen"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Statistical units"@en . + "Statistische eenheden"@nl . + "Unités statistiques"@fr . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Addresses"@en . + "Adressen"@nl . + "Adresses"@fr . + "Gezondheid"@nl . + "Santé"@fr . + . + "{u'fr': u'STIB', u'en': u'STIB-MIVB', u'nl': u'MIVB'}" . + "Health"@en . + "Addresses"@en . + "Adressen"@nl . + "Adresses"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Installations de suivi environnemental"@fr . + "Milieubewakingsvoorzieningen"@nl . + "Environmental monitoring facilities"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Transport"@en . + "Transport networks"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Environmental monitoring facilities"@en . + "Installations de suivi environnemental"@fr . + "Milieubewakingsvoorzieningen"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Geologie"@nl . + "Geology"@en . + "Géologie"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Justice, legal system and public safety"@en . + "Justice, système juridique et sécurité publique"@fr . + "Justitie, rechtsstelsel en openbare veiligheid"@nl . + "Hydrografie"@nl . + "Hydrographie"@fr . + "Hydrography"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Installations de suivi environnemental"@fr . + "Milieubewakingsvoorzieningen"@nl . + "Environmental monitoring facilities"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Régions et villes"@fr . + "Regions and cities"@en . + "Regio's en steden"@nl . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Protected sites"@en . + "Beschermde gebieden"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Sites protégés"@fr . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "{u'fr': u'JC Decaux', u'en': u'JC Decaux', u'nl': u'JC Decaux'}" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2382AA13E8EC6194CF4A54A19F437717 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dADB06977A6666F6DFA57903AA82D5A8D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dADB06977A6666F6DFA57903AA82D5A8D "Equipe Geodata" . + . + . + . + . + . + . + . + . + "{u'fr': u'Ligue Cardiologique Belge / Belgische Cardiologische Liga', u'en': u'Ligue Cardiologique Belge / Belgische Cardiologische Liga', u'nl': u'Ligue Cardiologique Belge / Belgische Cardiologische Liga'}" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "{u'fr': u'Bruxelles-Propret\\xe9', u'en': u'Bruxelles-Propret\\xe9', u'nl': u'Bruxelles-Propret\\xe9'}" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "ZIP" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB10E6C6414C0D257D759555E765A605C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB10E6C6414C0D257D759555E765A605C "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB10E6C6414C0D257D759555E765A605C "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB7F9D9C3D3194E97F3E0675E89561BA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB7F9D9C3D3194E97F3E0675E89561BA "Sébastien DEFRANCE" . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD126B63FEBC4C7D0E03F6240073DDD32 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD126B63FEBC4C7D0E03F6240073DDD32 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD126B63FEBC4C7D0E03F6240073DDD32 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB8D93DF50D9BF460DA0B07D713E84B17 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB8D93DF50D9BF460DA0B07D713E84B17 "Virginie Tumelaire" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA7B82A94F76AC859CA9016D97ED8CC30 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA7B82A94F76AC859CA9016D97ED8CC30 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA7B82A94F76AC859CA9016D97ED8CC30 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d425426E22B86B83FA02F4EF7369C0BFB . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4127D107771FDA7E9F8A00E87D059913 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4127D107771FDA7E9F8A00E87D059913 "Equipe Geodata" . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEEB0094A2E7D15DCFDAB95A7B46B9870 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEEB0094A2E7D15DCFDAB95A7B46B9870 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEEB0094A2E7D15DCFDAB95A7B46B9870 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDD955DC0FDB19D52F2A0CD5975109B99 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDD955DC0FDB19D52F2A0CD5975109B99 "Secretariat" . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAE86DBCB526542448A9F90F078EB5AB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAE86DBCB526542448A9F90F078EB5AB "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAE86DBCB526542448A9F90F078EB5AB "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dACD91713CF6E6FA67ADB944192B74F7F . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6BFE13A386C35965B19F74BB97AD1F13 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6BFE13A386C35965B19F74BB97AD1F13 "Thibault Delforge" . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d43ABD3120EC1C1A6813E6AEF7C545BDA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d43ABD3120EC1C1A6813E6AEF7C545BDA "Equipe Geodata" . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD1884F002E90538E4433E4FD8A97C07D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD1884F002E90538E4433E4FD8A97C07D "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD1884F002E90538E4433E4FD8A97C07D "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d814AEE2B85DA57FA2EE0D221DB90919B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d814AEE2B85DA57FA2EE0D221DB90919B "Equipe Geodata" . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d534D37BA97474E37563EB9DD1EF9B643 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d534D37BA97474E37563EB9DD1EF9B643 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d534D37BA97474E37563EB9DD1EF9B643 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD937FFEB481A3833A131484187E7A34A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA34DE8F29628F265CF191304EA59869C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA34DE8F29628F265CF191304EA59869C "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA34DE8F29628F265CF191304EA59869C "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d865BA987312B3780A14D76AA465C04F5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d865BA987312B3780A14D76AA465C04F5 "POLYGON ((4.2432 50.7607, 4.4822 50.7607, 4.4822 50.9132, 4.2432 50.9132, 4.2432 50.7607))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d865BA987312B3780A14D76AA465C04F5 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24320180859, 50.7607222666], [4.48215444531, 50.7607222666], [4.48215444531, 50.9131575693], [4.24320180859, 50.9131575693], [4.24320180859, 50.7607222666]]]}"^^ . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE4EB246A39AC218EDB19CD5276DEB00A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE4EB246A39AC218EDB19CD5276DEB00A "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8451A75AD09223559923F5074B053D2D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8451A75AD09223559923F5074B053D2D "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3771DDD545C7FF833EAADDD0B8C16985 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3771DDD545C7FF833EAADDD0B8C16985 "Emad Alsous" . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF01E40D8DF7FD02ED41C805485FED73F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF01E40D8DF7FD02ED41C805485FED73F "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF01E40D8DF7FD02ED41C805485FED73F "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8A205C93A6B873873D785461591DA0F5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5253BC8E88B23C4F192ADBB6729E406F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5253BC8E88B23C4F192ADBB6729E406F "Mobigis" . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECD4EDA046583F5D2E84C1FD67988073 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECD4EDA046583F5D2E84C1FD67988073 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECD4EDA046583F5D2E84C1FD67988073 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4455D8966A2B7AD708AC83922EA55488 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4455D8966A2B7AD708AC83922EA55488 "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCF726A6A5E46023651ABC5AA084186DB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCF726A6A5E46023651ABC5AA084186DB "Visit Brussels" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C3F7A2F5F30A786C7D7CC7E62FC8761 . + . + . + . + . + . + . + . + . + . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA0C35FA6E956E4D4CA0CE01DE7452099 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA0C35FA6E956E4D4CA0CE01DE7452099 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA0C35FA6E956E4D4CA0CE01DE7452099 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCBB7115B7FCCD6D72FB4F18708013013 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCBB7115B7FCCD6D72FB4F18708013013 "IBSA-BISA" . + . + . + . + . + . + . + . + . + . + "http://opendatastore.brussels/catalog.xml?page=1" . + "100"^^ . + "http://opendatastore.brussels/catalog.xml?page=3" . + "http://opendatastore.brussels/catalog.xml?page=2" . + "205"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Addresses"@en . + "Adressen"@nl . + "Adresses"@fr . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Coordinate reference systems"@en . + "Bâtiments"@fr . + "Education, culture and sport"@en . + "Buildings"@en . + "Onderwijs, cultuur en sport"@nl . + "Gebouwen"@nl . + "Référentiels de coordonnées"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Éducation, culture et sport"@fr . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Cadastral parcels"@en . + "Kadastrale percelen"@nl . + "Parcelles cadastrales"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Environnement"@fr . + "Environment"@en . + "Geologie"@nl . + "Geology"@en . + "Géologie"@fr . + "Milieu"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Environnement"@fr . + "Environment"@en . + "Overheid en publieke sector"@nl . + "Population and society"@en . + "Bevolking en samenleving"@nl . + "Milieu"@nl . + "Population et société"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Adresses"@fr . + "Addresses"@en . + "Adressen"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Energie"@nl . + "Energy"@en . + "Énergie"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Science and technology"@en . + "Science et technologie"@fr . + "Wetenschap en technologie"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Environnement"@fr . + "Gouvernement et secteur public"@fr . + "Milieu"@nl . + "Government and public sector"@en . + "Bevolking en samenleving"@nl . + "Overheid en publieke sector"@nl . + "Population et société"@fr . + "Population and society"@en . + "Environment"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Bodemgebruik"@nl . + "Economie en financiën"@nl . + "Coordinate reference systems"@en . + "Economy and finance"@en . + "Land cover"@en . + "Occupation des terres"@fr . + "Référentiels de coordonnées"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Économie et finances"@fr . + "Transport"@en . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Government and public sector"@en . + "Gouvernement et secteur public"@fr . + "Overheid en publieke sector"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Coordinate reference systems"@en . + "Nutsdiensten en overheidsdiensten"@nl . + "Onderwijs, cultuur en sport"@nl . + "Education, culture and sport"@en . + "Référentiels de coordonnées"@fr . + "Services d'utilité publique et services publics"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Utility and governmental services"@en . + "Éducation, culture et sport"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Energie"@nl . + "Energy"@en . + "Énergie"@fr . + "Onderwijs, cultuur en sport"@nl . + "Regions and cities"@en . + "Transports"@fr . + "Science and technology"@en . + "Education, culture and sport"@en . + "Population and society"@en . + "Transport"@en . + "Régions et villes"@fr . + "Regio's en steden"@nl . + "Population et société"@fr . + "Bevolking en samenleving"@nl . + "Science et technologie"@fr . + "Vervoer"@nl . + "Wetenschap en technologie"@nl . + "Éducation, culture et sport"@fr . + "Bâtiments"@fr . + "Buildings"@en . + "Education, culture and sport"@en . + "Gebouwen"@nl . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Administrative units"@en . + "Government and public sector"@en . + "Nutsdiensten en overheidsdiensten"@nl . + "Overheid en publieke sector"@nl . + "Administratieve eenheden"@nl . + "Services d'utilité publique et services publics"@fr . + "Unités administratives"@fr . + "Utility and governmental services"@en . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Government and public sector"@en . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Overheid en publieke sector"@nl . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Gouvernement et secteur public"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Government and public sector"@en . + "Gouvernement et secteur public"@fr . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Gouvernement et secteur public"@fr . + "Nutsdiensten en overheidsdiensten"@nl . + "Overheid en publieke sector"@nl . + "Bevolking en samenleving"@nl . + "Population et société"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Population and society"@en . + "Government and public sector"@en . + "Régions et villes"@fr . + "Services d'utilité publique et services publics"@fr . + "Utility and governmental services"@en . + "Energie"@nl . + "Energy"@en . + "Énergie"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Regio's en steden"@nl . + "Régions et villes"@fr . + "Regions and cities"@en . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Agriculture, fisheries, forestry and food"@en . + "Agriculture, pêche, sylviculture et alimentation"@fr . + "Gouvernement et secteur public"@fr . + "Overheid en publieke sector"@nl . + "Landbouw, visserij, bosbouw en voeding"@nl . + "Government and public sector"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Bodemgebruik"@nl . + "Land cover"@en . + "Occupation des terres"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Education, culture and sport"@en . + "Bâtiments"@fr . + "Onderwijs, cultuur en sport"@nl . + "Buildings"@en . + "Coordinate reference systems"@en . + "Gebouwen"@nl . + "Référentiels de coordonnées"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Éducation, culture et sport"@fr . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Coordinate reference systems"@en . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Référentiels de coordonnées"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Éducation, culture et sport"@fr . + "Coordinate reference systems"@en . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Référentiels de coordonnées"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Éducation, culture et sport"@fr . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Statistical units"@en . + "Statistische eenheden"@nl . + "Unités statistiques"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Energie"@nl . + "Energy"@en . + "Énergie"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Buildings"@en . + "Bâtiments"@fr . + "Gebouwen"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Government and public sector"@en . + "Justice, système juridique et sécurité publique"@fr . + "Menselijke gezondheid en veiligheid"@nl . + "Population and society"@en . + "Bevolking en samenleving"@nl . + "Regio's en steden"@nl . + "Justice, legal system and public safety"@en . + "Justitie, rechtsstelsel en openbare veiligheid"@nl . + "Human health and safety"@en . + "Regions and cities"@en . + "Health"@en . + "Population et société"@fr . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Régions et villes"@fr . + "Santé"@fr . + "Onderwijs, cultuur en sport"@nl . + "Santé et sécurité des personnes"@fr . + "Gezondheid"@nl . + "Nutsdiensten en overheidsdiensten"@nl . + "Education, culture and sport"@en . + "Services d'utilité publique et services publics"@fr . + "Utility and governmental services"@en . + "Éducation, culture et sport"@fr . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Bevolking en samenleving"@nl . + "Bâtiments"@fr . + "Population and society"@en . + "Buildings"@en . + "Coordinate reference systems"@en . + "Gebouwen"@nl . + "Population et société"@fr . + "Référentiels de coordonnées"@fr . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Energie"@nl . + "Energy"@en . + "Énergie"@fr . + "Energie"@nl . + "Energy"@en . + "Énergie"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Buildings"@en . + "Gebouwen"@nl . + "Bâtiments"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Land cover"@en . + "Occupation des terres"@fr . + "Bodemgebruik"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Altitude"@fr . + "Elevation"@en . + "Hoogte"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Energy"@en . + "Energie"@nl . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Énergie"@fr . + "Dénominations géographiques"@fr . + "Geografische namen"@nl . + "Geographical names"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Nutsdiensten en overheidsdiensten"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Services d'utilité publique et services publics"@fr . + "Utility and governmental services"@en . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Ortho-imagerie"@fr . + "Orthobeeldvorming"@nl . + "Orthoimagery"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Transport"@en . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Transport networks"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Land cover"@en . + "Bodemgebruik"@nl . + "Occupation des terres"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Administratieve eenheden"@nl . + "Administrative units"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Unités administratives"@fr . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Beschermde gebieden"@nl . + "Regio's en steden"@nl . + "Protected sites"@en . + "Regions and cities"@en . + "Régions et villes"@fr . + "Sites protégés"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Regions and cities"@en . + "Transport networks"@en . + "Transport"@en . + "Regio's en steden"@nl . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Economy and finance"@en . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Economie en financiën"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Économie et finances"@fr . + "Beschermde gebieden"@nl . + "Regio's en steden"@nl . + "Protected sites"@en . + "Regions and cities"@en . + "Régions et villes"@fr . + "Sites protégés"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Transport networks"@en . + "Transport"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Dénominations géographiques"@fr . + "Geografische namen"@nl . + "Geographical names"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Bevolking en samenleving"@nl . + "Population distribution — demography"@en . + "Population and society"@en . + "Population et société"@fr . + "Répartition de la population — démographie"@fr . + "Spreiding van de bevolking — demografie"@nl . + "Régions et villes"@fr . + "Regions and cities"@en . + "Regio's en steden"@nl . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Transport networks"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Education, culture and sport"@en . + "Onderwijs, cultuur en sport"@nl . + "Éducation, culture et sport"@fr . + "Coordinate reference systems"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Population and society"@en . + "Population et société"@fr . + "Référentiels de coordonnées"@fr . + "Régions et villes"@fr . + "Services d'utilité publique et services publics"@fr . + "Transport"@en . + "Systemen voor verwijzing door middel van coördinaten"@nl . + "Bevolking en samenleving"@nl . + "Nutsdiensten en overheidsdiensten"@nl . + "Transports"@fr . + "Utility and governmental services"@en . + "Vervoer"@nl . + "Economie en financiën"@nl . + "Economy and finance"@en . + "Économie et finances"@fr . + "Administratieve eenheden"@nl . + "Regio's en steden"@nl . + "Administrative units"@en . + "Regions and cities"@en . + "Régions et villes"@fr . + "Unités administratives"@fr . + "Gouvernement et secteur public"@fr . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Government and public sector"@en . + "Régions et villes"@fr . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Energie"@nl . + "Energy"@en . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Énergie"@fr . + "Addresses"@en . + "Adressen"@nl . + "Adresses"@fr . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Transport"@en . + "Transport networks"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Réseaux de transport"@fr . + "Transport"@en . + "Transport networks"@en . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Hydrografie"@nl . + "Hydrographie"@fr . + "Hydrography"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Régions et villes"@fr . + "Regions and cities"@en . + "Regio's en steden"@nl . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Land cover"@en . + "Bodemgebruik"@nl . + "Occupation des terres"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Beschermde gebieden"@nl . + "Regio's en steden"@nl . + "Protected sites"@en . + "Regions and cities"@en . + "Régions et villes"@fr . + "Sites protégés"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Regions and cities"@en . + "Regio's en steden"@nl . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport networks"@en . + "Vervoersnetwerken"@nl . + "Beschermde gebieden"@nl . + "Regio's en steden"@nl . + "Protected sites"@en . + "Regions and cities"@en . + "Régions et villes"@fr . + "Sites protégés"@fr . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Environmental monitoring facilities"@en . + "Installations de suivi environnemental"@fr . + "Milieubewakingsvoorzieningen"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Gouvernement et secteur public"@fr . + "Government and public sector"@en . + "Overheid en publieke sector"@nl . + "Hydrographie"@fr . + "Hydrography"@en . + "Hydrografie"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Environment"@en . + "Environnement"@fr . + "Milieu"@nl . + "Nutsdiensten en overheidsdiensten"@nl . + "Services d'utilité publique et services publics"@fr . + "Utility and governmental services"@en . + "Environmental monitoring facilities"@en . + "Installations de suivi environnemental"@fr . + "Milieubewakingsvoorzieningen"@nl . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Transport networks"@en . + "Transport"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Transport"@en . + "Transports"@fr . + "Vervoer"@nl . + "Transport"@en . + "Transport networks"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Faciliteiten voor productie en industrie"@nl . + "Lieux de production et sites industriels"@fr . + "Production and industrial facilities"@en . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Bevolking en samenleving"@nl . + "Population and society"@en . + "Population et société"@fr . + "Transport"@en . + "Transport networks"@en . + "Réseaux de transport"@fr . + "Transports"@fr . + "Vervoer"@nl . + "Vervoersnetwerken"@nl . + "Government and public sector"@en . + "Bevolking en samenleving"@nl . + "Justice, legal system and public safety"@en . + "Overheid en publieke sector"@nl . + "Population and society"@en . + "Population et société"@fr . + "Justitie, rechtsstelsel en openbare veiligheid"@nl . + "Gouvernement et secteur public"@fr . + "Justice, système juridique et sécurité publique"@fr . + "Regio's en steden"@nl . + "Regions and cities"@en . + "Régions et villes"@fr . + "Réseaux de transport"@fr . + "Transport"@en . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "GTFS" . + . + . + . + . + "HTML" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + "application/pdf" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/pdf" . + . + "Kalender van de zittingen van de Gemeenteraad 2018" . + "2018 - Kalender van de zittingen van de Gemeenteraad " . + . + . + "power bi" . + "2016 - Clients protégés et hivernaux.pbix" . + . + "Activiteitenverslag 2017" . + . + . + . + "power bi" . + "2018 - Beschermde en winterklanten.pbix" . + . + "WFS" . + . + "application/pdf" . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + . + "power bi" . + "2018 - T1 - Clientèle sociale/Beschermde klanten.pbix" . + . + . + "Composition du Conseil communal au 21/12/2017 " . + "21/12/2017 " . + . + . + "CSV" . + . + . + . + "application/pdf" . + "Calendrier des séances du conseil communal 2018" . + "2018 - Calendrier des séances du conseil communal " . + . + "lijst_installaties_einde_afvalfase_europese_criteria" . + . + "PDF" . + . + . + "Lijst van de wekelijkse markten 2018 (jaarlijkse bijwerking)" . + "2018 - Wekelijkse markten " . + . + . + "Agenda de toutes les activités liées au tango en Belgique" . + "application/pdf" . + . + "Milonga en Belgique" . + . + "Crèches d'Auderghem FR" . + . + . + "Rapport Annuel 2003 Port de Bruxelles" . + . + . + "Ce tableur présente les 8 écoles issues de l'enseignement obligatoire." . + . + "application/pdf" . + "Ecoles communales" . + . + "Rapport annuel 2016" . + . + "power bi" . + "2017 - T3 - Parts de marché/Maarktaandelen.pbix" . + . + . + "power bi" . + "2018 - T4 - Electricité Verte / Groenestroom.pbix" . + . + "Liste des écoles d'Auderghem" . + "Écoles d'Auderghem" . + . + . + . + "Rapport Annuel 2015 Port de Bruxelles" . + "application/pdf" . + . + "GeoJSON" . + . + . + "Jaarverslag 2016 Haven van Brussel" . + . + . + "Gemeentelijke school - schooljaar 2017/2018 (jaarlijkse bijwerking)" . + "2017/2018 - Gemeentelijke school" . + "application/pdf" . + . + . + "Liste des exploitants d'un centre de démontage de véhicules hors d'usage (type A), enregistrés en Région de Bruxelles-Capitale" . + "liste_exploitants_centre_demontage_vehicules_hors_usage_type_A" . + . + . + "CSV-bestand met gestandardiseerde en geolokaliseerbare informatie over de publieke en private organisaties en diensten die algemene sociale dienstverlening bieden in het Brussels Gewest. Zie ook: https://social.brussels/sector/53.\nLicentie: CC-By\nBevat ook geo-coördinaten Lambert" . + "Algemene sociale dienstverlening" . + . + "application/pdf" . + . + "GeoJSON" . + . + . + "lijst_opdrachthouders_effectenbeoordeling" . + . + . + "wifi.brussels" . + . + "application/pdf" . + . + "Registratie als exploitant van een demonteercentrum voor afgedankte voertuigen (type A) in het Brussels Hoofdstedelijk Gewest" . + "lijst_exploitant_demonteercentrum_afgedankte_voertuigen_type_A" . + . + . + "Jaarverslag 2002 Haven van Brussel" . + . + . + "power bi" . + "2018 - T2 - Clientèle sociale/Beschermde klanten.pbix" . + . + "MapViewer" . + . + . + "lijst_vergunde_inzamel_verwerkingsinrichtingen_afvalstoffen" . + . + . + . + "Infrastructures sportives" . + "application/pdf" . + . + . + "liste_installations_classees" . + . + . + "Activiteitenverslag 2014" . + . + "application/pdf" . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + . + . + "JSON" . + . + . + "article 60 -7 – économie sociale" . + . + . + "API for traffic counts" . + "API" . + "API" . + . + . + "lijst_einde_afvalfase_brusselse_criteria" . + "application/pdf" . + . + . + "power bi" . + "2018 - Actieve vermogenbegrenzers.pbix" . + . + "lijst_studiebureaus_opslaginstallaties" . + . + . + "lijst_elektriciteitsleveranciers" . + . + . + . + "06/12/2018" . + . + "application/pdf" . + "Composition du Conseil communal au 06/12/2018" . + . + "Jaarverslag 2013 Haven van Brussel" . + . + . + "Le rapport annuel 2017 complet est disponible en ligne, sur le site \nhttp://rapportannuel.port.brussels" . + "Rapport Annuel 2017 Port de Bruxelles" . + . + . + "Liste des bibliothèques communales 2018 (mise à jour annuelle)" . + "2018 - Bibliothèques communales" . + "application/pdf" . + . + . + "Jaarverslag 2015 Haven van Brussel" . + . + "Lijst van de vergunde reisagentschappen in het BHG" . + "Lijst van de vergunde reisagentschappen in het BHG" . + . + . + . + "ILDE EI" . + "application/pdf" . + . + "power bi" . + "2018_Aantal leveringspunten per leverancier.pbix" . + . + . + "power bi" . + "2018 - Désactivations sur ordre du juge de paix.pbix" . + . + "Preview" . + . + . + "Arrivals & Overnights - Data History" . + . + . + "power bi" . + "2017 - Actieve vermogenbegrenzers.pbix" . + . + . + . + "liste_charges_evaluation_incidences" . + . + . + "application/pdf" . + "Deze dataset omvat alle door Innoviris geaccepteerde projecten (2015-2017). In een bijkomend tabblad worden enkele afkortingen van programma's verduidelijkt. " . + . + "Liste des crèches communales 2017 (mise à jour annuelle)" . + "2017 - Crèches communales " . + . + "API for bicycle counts" . + . + . + "API" . + . + "power bi" . + "2018 - Limiteurs de puissance actifs.pbix" . + . + "Bike counting poles - CSV" . + . + "application/pdf" . + . + "2017_Parts de marché_Maarktaandeel.xlsx" . + . + "Employeurs Sine" . + . + . + . + "power bi" . + "2019 - Actieve vermogenbegrenzers.pbix " . + . + "JSON" . + . + "application/pdf" . + . + "liste_bureaux_etude_incidences" . + . + . + "lijst_EPB_adviseurs_rechtspersonen" . + . + . + . + "Liste des marchés hebdomadaires 2018 (mise à jour annuelle)" . + "2018 - Marchés hebdomadaires " . + "application/pdf" . + . + "Lijst van sportverenigingen (gesubsidieerd door de gemeente) op 30 april 2018 (jaarlijkse bijwerking)" . + "2018 - Sportverenigingen gesubsidieerd door de gemeente" . + . + . + "Infrastructures sportives" . + . + . + "Preview" . + . + "application/pdf" . + . + "GeoJSON" . + . + . + "Lijst van de gecertificeerde biologische bedrijven in het Brussels Hoofdstedelijk Gewest" . + . + "application/pdf" . + . + "Infrastructures sportives 2017 (mise à jour annuelle)" . + "2017 - Infrastructures sportives " . + . + . + "Calendrier des séances du Conseil communal 2019" . + "2019 - Calendrier des séances du conseil communal" . + . + . + . + "liste_centres_formation_systemes_climatisation_automobiles" . + . + "Jaarverslag 2001 Haven van Brussel" . + . + "application/pdf" . + . + "Rapport Annuel 2008 Port de Bruxelles" . + . + . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + . + "power bi" . + "2017 - T3 - Taux de switches / Switch Percentage.pbix" . + . + . + "Liste des marchés hebdomadaires 2019 (mise à jour annuelle)" . + "2019 - Marchés hebdomadaires" . + "application/pdf" . + . + "2019_points_de_fourniture_leveringspunten.xlsx" . + . + . + "Rapport Annuel 2006 Port de Bruxelles" . + . + . + "Rapport Annuel 2002 Port de Bruxelles" . + . + "application/pdf" . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + . + . + "Rapport Annuel 2013 Port de Bruxelles" . + . + . + "power bi" . + "2018 - T4 - Taux de switches / Switch Percentage.pbix" . + . + . + "Composition du Conseil communal au 27/10/2016\n" . + "27/10/2016" . + "application/pdf" . + . + . + "Rapport d'activités 2016" . + . + "Jaarverslag 2004 Haven van Brussel" . + . + . + . + "power bi" . + "2018 - T3 - Taux de switches / Switch Percentage.pbix" . + . + "Bike - WFS, all layers" . + "application/pdf" . + . + . + "Fonds de formation titres-services - Formations admises" . + . + "Mapviewer" . + . + . + . + "The enclosed file shows the data from a panel of 20 guided tours organisations. \nSee the full interactive barometer via the following link: https://visit.brussels/en/article/tourism-barometer-of-the-brussels-capital-region" . + "Barometer of the Guided Tours" . + "application/pdf" . + . + . + "Liste et descriptions détaillées et géolocalisées des organisations publiques et privées qui offrent de l'aide sociale générale en Région de Bruxelles-Capitale" . + "Aide sociale générale" . + . + "WFS" . + . + . + "application/pdf" . + . + "WFS" . + . + . + "Ateliers du web" . + . + . + "Activiteitenverslag 2013" . + . + . + "Ecoles communales - année scolaire 2017/2018 (mise à jour annuelle)" . + "application/pdf" . + . + "2017/2018 - Ecoles communales" . + . + "Lijst van openbare computerruimtes 2017 (jaarlijkse bijwerking)" . + "2017 - Openbare computerruimtes " . + . + "power bi" . + "2018- Deactiveringen op bevel van de vrederechter.pbix" . + . + . + "Het Brussels agentschap voor administratieve vereenvoudiging en de gewestelijke besturen hebben in 2017 een reeks acties ontwikkeld, toegespitst op het uitvoeren van de doelstellingen van het plan voor administratieve vereenvoudiging 2015-2020. Dit verslag zet de grote werven van administratieve vereenvoudiging uiteen die gemeenschappelijk zijn voor alle besturen, zoals open data, het virtuele loket, de digitale inclusie, de elektronische facturatie, online overheidsopdrachten, de stappencatalogus, enz. Het verslag licht verder specifieke acties van vereenvoudiging toe die in de loop van het jaar tot stand zijn gebracht door specifieke partners van het agentschap. Tot slot, formuleert Easybrussels ook aanbevelingen op basis van de ervaringen en vaststellingen in 2017. " . + "Jaarverslag 2017 Easybrussels" . + . + . + "power bi" . + "2017 - T4 - Parts de marché/Maarktaandelen.pbix" . + . + . + "power bi" . + "2017- Deactiveringen op bevel van de vrederechter.pbix" . + . + "application/pdf" . + . + "Masterplan Horizon 2030" . + . + "liste_centres_examen_technique_froid" . + . + . + "liste_installations_collecte_traitement_dechets" . + . + . + . + "Liste des bibliothèques communales 2017 (mise à jour annuelle)" . + "2017 - Bibliothèques communales " . + "application/pdf" . + . + "power bi" . + "2017 - T3 - Electricité Verte / Groenestroom.pbix" . + . + . + "Sociaal Brussel is een gratis, tweetalig, online platform met gedetailleerde, geactualiseerde en gegeolokaliseerde informatie over de publieke en private diensten actief in de welzijns- en gezondheidssector in het Brussels Hoofdstedelijk Gewest.\n\nSociaal Brussel wordt beheerd door de vzw CMDC-CDCS die hiervoor gemandateerd werd door de Gemeenschappelijke Gemeenschapscommissie.\n\nIn het kader van de open data-politiek en een efficiënt gebruik van overheidsmiddelen worden de gegevens van Sociaal Brussel gratis ter beschikking gesteld onder de licentie CC BY.\n\nhttps://sociaal.brussels/page/over-sociaal-brussel-nl\n" . + "Sociaal Brussel | https://sociaal.brussels" . + . + "power bi" . + "2017 - Clients protégés et hivernaux.pbix" . + . + . + "power bi" . + "2017 - T3 - Clientèle sociale/Beschermde klanten.pbix" . + . + "Opleidingsfonds dienstencheques - Toegelaten opleidingen" . + . + . + . + . + "WFS" . + . + "2018_desactivations_afsluitingen.xlsx" . + "application/pdf" . + . + "This file contains\n- Occupation rate\n- Average price per room\n- Revenue per available room (RevPar)\n" . + "Perfomance Hôtelière" . + . + . + "WFS" . + . + . + . + "WFS" . + "application/pdf" . + . + "Jaarverslag 2006 Haven van Brussel" . + . + . + "lijst_gasleveranciers" . + . + . + . + "Le Service public régional de Bruxelles tient le Registre des études en respect de l'article 7 de l'ordonnance relative à la publicité de l'administration.\nDe Gewestelijke Overheidsdienst Brussel houdt het Studieregister bij in naleving van artikel 7 van de ordonnantie betreffende de openbaarheid van bestuur." . + "Studieregister / Registre des études" . + "application/octet-stream" . + . + "Preview" . + . + . + "Lijst van de wekelijkse markten 2019 (jaarlijkse bijwerking)" . + "2019 - Wekelijkse markten" . + . + . + "liste_bureaux_etude_installations_stockage" . + . + . + "Activiteitenverslag 2015" . + . + . + "Liste des plaines de jeux au 30 juin 2018 (mise à jour semestrielle)\n" . + "2018 - Plaines de jeux " . + . + "application/zip" . + . + "Bibliothèque" . + . + "Preview" . + . + . + "Rapport d'activités 2009-2012" . + . + . + "WFS" . + . + . + "CSV" . + "application/zip" . + . + . + "liste_installations_fin_statut_dechets_criteres_bruxellois" . + . + . + "JSON" . + . + . + "JSON" . + . + "application/zip" . + . + "power bi" . + "2018 - T1 - Parts de marché/Maarktaandelen.pbix" . + . + "JSON" . + . + . + "Bike counting poles - GEOJSON" . + . + . + "Infrastructures communales" . + . + "application/zip" . + . + "2019_Limiteurs de puissance_Vermogenbegrenzers.xlsx" . + . + . + "liste_etablissements_entreposage_dechets_animaux" . + . + . + . + "lijst_ingedeelde_inrichtingen" . + . + "application/zip" . + . + "Liste des associations culturelles (subsidiées par la commune) au 30 septembre 2017 (mise à jour annuelle)" . + "2017 - Associations culturelles subsidiées par la commune" . + . + "Informations standardisées et géolocalisées relatives aux organisations publiques et privées qui offrent de l'aide sociale générale en Région de Bruxelles-Capitale" . + . + . + "Aide sociale générale" . + . + "CSV" . + "application/zip" . + . + "power bi" . + "2018 - T3 - Clientèle sociale/Beschermde klanten.pbix" . + . + . + "Cette archive zip contient 19 rasters (.tif) d'altitudes des toits des Unités Stratigraphiques de la Région Bruxelles-Capitale (US/RBC), allant du socle paléozoïque (inclus) aux formations quaternaires. Les US/RBC quaternaires ne sont pas discrétisées dans ce jeu de données et seul le raster d'altitude du toit de l'unité hydrogéologique quaternaire (UH/RBC 01) est présent (l'élévation de ce toit est confondu avec la topographie). Tous ces rasters ont une résolution de 10x10 m et sont dans le système de coordonnées de référence EPSG 31370 : Lambert Belge 1972. Les valeurs d'élévations sont données en mètres Deuxième Nivellement Général (m-DNG).\n\nLes données contenues dans cette archive étant issues d’un modèle, elles peuvent contenir des erreurs, des imprécisions et des lacunes. Elles doivent être utilisées avec prudence et esprit critique. Elles ne peuvent en aucun cas remplacer une étude de terrain réalisée par un expert.\nDe manière générale, Bruxelles Environnement et le Service Géologique de Belgique ne peuvent, en aucun cas, être tenus pour responsables de dommage, direct ou indirect, résultant de l'utilisation de ces données ou de l'impossibilité de les utiliser pour quelque raison que ce soit.\n\n-------------------------------------------------------------------------------------\n\nDit ziparchief bevat 19 rasters (.tif) van de tophoogtes van de Stratigrafische Eenheden van het Brussels Hoofdstedelijk Gewest (SE/BHG), van de paleozoïsche sokkel (inbegrepen) tot de quartaire formaties. De quartaire SE’s/BHG worden niet gediscretiseerd in deze gegevensverzameling en alleen het aster van de tophoogtes van de quartaire hydrogeologische eenheid (HE/BHG 01) is aanwezig (de tophoogte wordt vermengd met de topografie). Al deze rasters hebben een resolutie van 10 x 10 m en bevinden zich in het referentiecoördinatensysteem EPSG 31370: Belgisch Lambert 1972. De hoogtewaarden worden aangegeven in meter Tweede Algemene Waterpassing (m-TAW).\n\nAangezien de gegevens in dit archief uit een model zijn ontstaan, kunnen ze fouten, onduidelijkheden en lacunes bevatten. Ze moeten omzichtig en met een kritische geest worden gebruikt. Ze mogen in geen geval de plaats innemen van een terreinstudie die wordt uitgevoerd door een expert. Over het algemeen kunnen Leefmilieu Brussel en de Belgische Geologische Dienst in geen geval aansprakelijk worden gesteld voor rechtstreekse of onrechtstreekse schade die het gevolg is van het gebruik van deze gegevens, of van de onmogelijkheid om ze om welke reden ook te gebruiken.\n" . + "Altitudes des toits des US/RBC / Tophoogtes van de SE/BHG" . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + . + . + "WFS" . + . + "Lijst van culturele verenigingen (gesubsidieerd door de gemeente) op 30 september 2017 (jaarlijkse bijwerking)" . + "2017 - Culturele verenigingen gesubsidieerd door de gemeente " . + "application/zip" . + . + . + "GeoJSON" . + . + "lijst_ophalers_dierlijk_afval" . + . + . + . + . + "application/zip" . + "Traffic counts - WFS" . + . + "GeoJSON" . + . + "2019_clientele-sociale-regionale_sociaal-regionaal-clienteel.xlsx" . + . + . + . + "liste_collecteurs_dechets_animaux" . + "text/plain" . + . + . + "Preview" . + . + . + "Sépultures militaires" . + . + . + "Musées et Attractions" . + . + . + "power bi" . + "2017 - T4 - Electricité Verte / Groenestroom.pbix" . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + . + . + . + "power bi" . + "2016 - Limiteurs de puissance actifs.pbix" . + . + . + "power bi" . + "2018 - T4 - Clientèle sociale/Beschermde klanten.pbix" . + . + "liste_fournisseurs_electricite" . + . + "application/xml" . + . + "CSV" . + . + . + "Cette archive zip contient 18 rasters (.tif) d'épaisseurs des Unités Stratigraphiques de la Région Bruxelles-Capitale (US/RBC), allant du socle paléozoïque (exclu) aux formations quaternaires. Les US/RBC quaternaires ne sont pas discrétisées dans ce jeu de données et seul le raster d'épaisseur totale de l'unité hydrogéologique quaternaire (UH/RBC 01) est présent. Tous ces rasters ont une résolution de 10x10 m et sont dans le système de coordonnées de référence EPSG 31370 : Lambert Belge 1972. Les valeurs de profondeurs sont données en mètres (m) depuis la surface topographique.\n\nLes données contenues dans cette archive étant issues d’un modèle, elles peuvent contenir des erreurs, des imprécisions et des lacunes. Elles doivent être utilisées avec prudence et esprit critique. Elles ne peuvent en aucun cas remplacer une étude de terrain réalisée par un expert.\nDe manière générale, Bruxelles Environnement et le Service Géologique de Belgique ne peuvent, en aucun cas, être tenus pour responsables de dommage, direct ou indirect, résultant de l'utilisation de ces données ou de l'impossibilité de les utiliser pour quelque raison que ce soit.\n\n---------------------------------------------------------------------------------------------\n\nDit ziparchief bevat 19 rasters (.tif) van de dikten van de Stratigrafische Eenheden van het Brussels Hoofdstedelijk Gewest (SE/BHG), van de paleozoïsche sokkel (buitengesloten) tot de quartaire formaties. De quartaire SE’s/BHG worden niet gediscretiseerd in deze gegevensverzameling en alleen de raster van de dikte van de quartaire hydrogeologische eenheid (HE/BHG 01) is aanwezig. Al deze rasters hebben een resolutie van 10 x 10 m en bevinden zich in het referentiecoördinatensysteem EPSG 31370: Belgisch Lambert 1972. De dikten worden aangegeven in meter (m).\n\nAangezien de gegevens in dit archief uit een model zijn ontstaan, kunnen ze fouten, onduidelijkheden en lacunes bevatten. Ze moeten omzichtig en met een kritische geest worden gebruikt. Ze mogen in geen geval de plaats innemen van een terreinstudie die wordt uitgevoerd door een expert. Over het algemeen kunnen Leefmilieu Brussel en de Belgische Geologische Dienst in geen geval aansprakelijk worden gesteld voor rechtstreekse of onrechtstreekse schade die het gevolg is van het gebruik van deze gegevens, of van de onmogelijkheid om ze om welke reden ook te gebruiken.\n" . + "Epaisseurs des US/RBC / Dikten van de SE/BHG" . + . + "MapViewer" . + . + . + "Marchés publics 2017 (mise à jour annuelle)" . + "2017 - Marchés publics" . + . + . + "power bi" . + "2018 - Clients protégés et hivernaux.pbix" . + . + . + "http://www.cirb.irisnet.be/fr/nos-solutions/urbis-solutions/urbis-data" . + . + . + "application/xml" . + "Json" . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + . + . + "power bi" . + "2018 - T4 - Parts de marché/Maarktaandelen.pbix" . + . + . + "liste_fournisseurs_gaz" . + . + . + . + . + "application/xml" . + "liste_installations_fin_statut_dechets_criteres_bruxellois" . + . + "Preview" . + . + . + "GeoJSON" . + . + "application/xml" . + . + "Gestandardiseerde informatie over de publieke en private organisaties en diensten die algemene sociale dienstverlening bieden in het Brussels Gewest. Zie ook: https://social.brussels/sector/53" . + "Algemene sociale dienstverlening" . + . + . + "Lijst van gemeentelijke bibliotheken 2017 (jaarlijkse bijwerking)" . + "2017 - Gemeentelijke bibliotheken " . + . + "Activiteitenverslag 2009-2012" . + . + . + . + "WFS" . + "application/xml" . + . + "liste_conseillers_PEB_personnes_morales" . + . + . + . + . + "CSV" . + . + "Jaarverslag 2009 Haven van Brussel" . + "application/xml" . + . + "Composition du Collège des Bourgmestre et Echevins et du Conseil communal sur www.auderghem.be\nVersion tenue à jour." . + "Le Collège des Bourgmestre et Echevins" . + . + . + "power bi" . + "2019 - Beschermde en winterklanten.pbix " . + . + "Lijst van gemeentelijke kinderdagverblijven 2017 (jaarlijkse bijwerking)" . + "2017 - Gemeentelijke kinderdagverblijven " . + . + . + "Kalender van de zittingen van de Gemeenteraad 2019" . + "2019 - Kalender van de zittingen van de Gemeenteraad " . + . + . + "GeoJSON" . + . + "application/xml" . + . + "2018_Parts de marché_Maarktaandeel.xlsx" . + . + "power bi" . + "2019_Volume d'énergie par fournisseur.pbix " . + . + . + "lijst_opleidingscentra_klimaatregelingssystemen_motorvoertuigen" . + . + . + . + "Liste des opérateurs certifiés bio situés en Région de Bruxelles-Capitale" . + "application/xml" . + . + "CSV" . + . + . + "Betaald educatief verlof - erkenningscommissie" . + . + . + "Jaarverslag 2012 Haven van Brussel" . + . + "application/xml" . + . + "CSV" . + . + "GeoJSON" . + . + . + . + "power bi" . + "2019 - Clients protégés et hivernaux.pbix" . + . + . + "application/xml" . + "lijst_verwerkingscentra_dierlijk_afval" . + . + "PIOW IO" . + . + . + "WFS" . + . + . + "Overheidsopdrachten 2017 (jaarlijkse bijwerking)" . + "2017 - Overheidsopdrachten" . + "application/xml" . + . + . + "power bi" . + "2016 - Actieve vermogenbegrenzers.pbix" . + . + "power bi" . + "2018_Volume d'énergie par fournisseur.pbix" . + . + . + "power bi" . + "2019 - Limiteurs de puissance actifs.pbix " . + . + "Rapport d'activités 2015" . + . + . + "2018_points_de_fourniture_leveringspunten.xlsx" . + . + "application/xml" . + . + "WFS" . + . + . + "2019_Parts de marché_Maarktaandeel.xlsx" . + . + "power bi" . + "2019_Aantal leveringspunten per leverancier.pbix " . + . + . + "Sportinfrastructuur 2017 (jaarlijkse bijwerking)" . + "2017 - Sportinfrastructuur" . + . + . + . + "application/xml" . + "Liste des espaces publics numériques 2017 (mise à jour annuelle)" . + "2017 - Espaces publics numériques " . + . + "power bi" . + "2017 - T4 - Clientèle sociale/Beschermde klanten.pbix" . + . + "power bi" . + "2017 - Beschermde en winterklanten.pbix" . + . + . + "Lijst van speelpleinen op 30 juni 2018 (halfjaarlijkse bijwerking)" . + "2018 - Speelpleinen" . + . + "power bi" . + "2018 - T2 - Parts de marché/Maarktaandelen.pbix" . + . + . + "power bi" . + "2017 - Limiteurs de puissance actifs.pbix" . + . + "Cimetières communaux" . + . + . + . + "liste_collecteurs_negociants_courtiers_dechets_dangereux" . + . + "application/xml" . + . + "Cimetières communaux" . + . + "Bike counting poles - WFS" . + . + . + "application/xml" . + . + "description" . + . + "power bi" . + "2017 - T4 - Taux de switches / Switch Percentage.pbix" . + . + . + "Rapport Annuel 2014 Port de Bruxelles" . + . + "Rapport Annuel 2012 Port de Bruxelles" . + . + . + . + "Rapport Annuel 2016 Port de Bruxelles" . + "application/xml" . + . + "Composition du Conseil communal au 28/04/2016" . + "28/04/2016" . + . + . + . + . + "Bibliothèque" . + . + "power bi" . + "2018 - T1 - Electricité Verte / Groenestroom.pbix" . + . + . + "power bi" . + "2016 - Beschermde en winterklanten.pbix" . + . + . + "application/xml" . + "liste_exploitants_centre_destruction_recyclage_vehicules_hors_usage_type_C" . + . + "Preview" . + . + . + "lijst_studiebureaus_effectenstudies" . + . + . + . + "Lijst van gemeentelijke bibliotheken 2018 (jaarlijkse bijwerking)" . + "2018 - Gemeentelijke bibliotheken" . + . + "application/xml" . + . + "power bi" . + "2018 - T3 - Electricité Verte / Groenestroom.pbix" . + . + "liste_exploitants_centre_demontage_vehicules_hors_usage_type_B" . + . + . + "Jaarverslag 2017 Haven van Brussel" . + . + . + "Het volledige verslag 2017 is online beschikbaar, op de website http://jaarverslag.port.brussels/" . + . + "lijst_exploitant_centrum_vernietiging_recycling_afgedankte_voertuigen_type_C" . + "application/xml" . + . + . + "Jaarverslag 2014 Haven van Brussel" . + . + "Jaarverslag 2005 Haven van Brussel" . + . + . + . + "lijst_opslagcentra_dierlijk_afval" . + . + "application/xml" . + . + "MapViewer" . + . + "power bi" . + "2017 - Désactivations sur ordre du juge de paix.pbix" . + . + . + "power bi" . + "2018 - T3 - Parts de marché/Maarktaandelen.pbix" . + . + "2017_points_de_fourniture_leveringspunten.xlsx" . + . + . + . + "Masterplan Horizon 2030" . + "application/xml" . + . + "Liste de associations sportives (subsidiées par la commune) au 30 avril 2018 (mise à jour annuelle)" . + "2018 - Associations sportives subsidiées par la commune" . + . + . + "CSV" . + . + . + "Rapport Annuel 2010 Port de Bruxelles" . + . + "application/xml" . + . + "JSON" . + . + "Bruxelles Social est une plateforme publiant des descriptions détaillées, actualisées et géolocalisées des organisations publiques et privées actifs dans le secteur social-santé en Région de Bruxelles-Capitale. Via cet URL vous trouverez des informations et métadonnées détaillées :\n- Brochure d'information générale\n- L'arborescence thématique construite sur mesure de l'offre sociale-santé bruxelloise\n- Une description détaillée des champs d'information standardisés\n\nhttps://social.brussels/page/a-propos-de-la-carte-sociale" . + "Bruxelles Social | https://social.brussels" . + . + . + "2019_desactivations_afsluitingen.xlsx" . + . + . + "Marchés" . + . + "application/xml" . + . + "Artikel 60-7 - sociale economie" . + . + "Het College van Burgemeester en Schepenen" . + . + . + "Rapport d'activités 2013" . + . + . + "Erkende particuliere bureaus voor arbeidsbemiddeling" . + "application/xml" . + . + . + "Jaarverslag 2011 Haven van Brussel" . + . + "liste_centres_traitement_dechets_animaux" . + . + . + . + "lijst_examencentra_koeltechniek" . + . + "application/xml" . + . + "Modèle Numérique de Terrain (raster) du modèle stratigraphique BRUSTRATI3D V1.1. Ce raster couvrant l'ensemble de la Région Bruxelles-Capitale a une résolution de 10x10 m et a comme système de coordonnées de référence : EPSG 31370 Lambert Belge 1972. Les valeurs d'élévations sont données en mètres Deuxième Nivellement Général (m-DNG). La construction de ce raster a nécessité l'utilisation de données issues de Brussels Urbis : https://cirb.brussels/fr/nos-solutions/urbis-solutions/urbis-data\n\nLes données de ce raster étant issues d’un modèle, elles peuvent contenir des erreurs, des imprécisions et des lacunes. Elles doivent être utilisées avec prudence et esprit critique. Elles ne peuvent en aucun cas remplacer une étude de terrain réalisée par un expert. De manière générale, Bruxelles Environnement et le Service Géologique de Belgique ne peuvent, en aucun cas, être tenus pour responsables de dommage, direct ou indirect, résultant de l'utilisation de ces données ou de l'impossibilité de les utiliser pour quelque raison que ce soit.\n\n--------------------------------------------------------------------------------------------\n\nHet Digitaal Terreinmodel van het stratigrafische model BRUSTRATI3D V1.1. Deze raster heeft een resolutie van 10 x 10 m en bevindt zich in het referentiecoördinatensysteem EPSG 31370: Belgisch Lambert 1972. De hoogtewaarden worden aangegeven in meter Tweede Algemene Waterpassing (m-TAW). Voor de opstelling van deze raster zijn de Brusselse Urbis gegevens gebruikt : https://cibg.brussels/nl/onze-oplossingen/urbis-solutions/urbis-data\n\nAangezien de gegevens in dit archief uit een model zijn ontstaan, kunnen ze fouten, onduidelijkheden en lacunes bevatten. Ze moeten omzichtig en met een kritische geest worden gebruikt. Ze mogen in geen geval de plaats innemen van een terreinstudie die wordt uitgevoerd door een expert. Over het algemeen kunnen Leefmilieu Brussel en de Belgische Geologische Dienst in geen geval aansprakelijk worden gesteld voor rechtstreekse of onrechtstreekse schade die het gevolg is van het gebruik van deze gegevens, of van de onmogelijkheid om ze om welke reden ook te gebruiken." . + "Modèle Numérique de Terrain (MNT) / Digitaal Terreinmodel (DTM) " . + . + . + "Depuis fin 2012, le CIRB offre une représentation tridimensionnelle de l'ensemble des bâtiments présents sur le territoire de la Région bruxelloise, et d'environ 200 ouvrages d'art.\nLes bâtiments et les ouvrages d'arts en 3D sont modélisés individuellement avec un niveau de détail équivalent au LoD 2 (Level of Detail 2), tel qu’il est défini dans la norme d’échange CityGML (City Geography Markup Language – Version 1).\nConcrètement, cela signifie qu'un bâtiment en 3D est représenté sous la forme d'un volume dont la structure du toit est simplifiée." . + . + . + "power bi" . + "2018 - T1 - Taux de switches / Switch Percentage.pbix" . + . + . + "2017_desactivations_afsluitingen.xlsx" . + "application/xml" . + . + "Crèches" . + . + . + "2018_clientele-sociale-regionale_sociaal-regionaal-clienteel.xlsx" . + . + . + "power bi" . + "2019_Volume energie per leverancier.pbix " . + . + . + "power bi" . + "2018_Volume energie per leverancier.pbix" . + . + "application/xml" . + . + "WFS" . + . + "MapViewer" . + . + "2017_clientele-sociale-regionale_sociaal-regionaal-clienteel.xlsx" . + . + . + . + "power bi" . + "2018 - T2 - Taux de switches / Switch Percentage.pbix" . + . + "Rapport Annuel 2001 Port de Bruxelles" . + "application/xml" . + . + . + "Liste des rues d'Evere - Straat lijst van Evere" . + "Rues d'Evere - Straten van Evere" . + . + "Rapport d'activités 2017" . + . + . + . + "CSV" . + "application/xml" . + . + "Jaarverslag 2007 Haven van Brussel" . + . + . + "power bi" . + "2018_Nombre de points de fourniture par founisseurs.pbix" . + . + "MapViewer" . + . + . + . + "GeoJSON" . + "application/xml" . + . + "lijst_inzamelaars_handelaars_makelaars_gevaarlijk_afval" . + . + . + . + . + "2018_Limiteurs de puissance_Vermogenbegrenzers.xlsx" . + . + "Lijst van SINE werkgevers" . + "application/xml" . + . + "WFS" . + . + . + "description" . + . + . + . + "application/xml" . + "Crèches" . + . + "Jaarverslag 2016" . + . + "power bi" . + "2016 - Désactivations sur ordre du juge de paix.pbix" . + . + . + "lijst_exploitant_demonteercentrum_afgedankte_voertuigen_type_B" . + . + . + . + "Agences d'emploi privées reconnues" . + "application/xml" . + . + "power bi" . + "2016- Deactiveringen op bevel van de vrederechter.pbix" . + . + . + "GeoJSON" . + . + "L’agence de simplification administrative bruxelloise et les administrations régionales au sens large ont développé tout au long de l’année 2017 une série d’actions pour la mise en œuvre des objectifs du plan de simplification administrative 2015-2020. Ce rapport détaille les grands chantiers communs de simplification administrative, comme l’Open Data, le guichet virtuel, l’inclusion numérique, la facturation électronique, les marchés publics en ligne, le catalogue des démarches, etc. Il décrit également des actions particulières de simplification menées au cours de l’année par certains partenaires de l’agence, ainsi que les recommandations d’Easybrussels sur base des expériences et constats réalisés en 2017." . + "Rapport annuel 2017 Easybrussels" . + . + . + "application/xml" . + . + "Rapport Annuel 2005 Port de Bruxelles" . + . + "Jaarverslag 2003 Haven van Brussel" . + . + . + "Activiteitenverslag 2016" . + . + . + "power bi" . + "2019_Nombre de points de fourniture par founisseurs.pbix" . + . + . + "Shapefile" . + "application/xml" . + . + "Rapport Annuel 2007 Port de Bruxelles" . + . + . + "power bi" . + "2018 - T2 - Electricité Verte / Groenestroom.pbix" . + . + "Jaarverslag 2010 Haven van Brussel" . + . + . + "application/xml" . + . + "2017_Limiteurs de puissance_Vermogenbegrenzers.xlsx" . + . + "Rapport Annuel 2009 Port de Bruxelles" . + . + . + "Rapport d'activités 2014" . + . + . + "Rapport Annuel 2011 Port de Bruxelles" . + . + "application/xml" . + . + "Jaarverslag 2008 Haven van Brussel" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/xml" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/vnd.ms-excel" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "application/vnd.ms-excel" . + . + . + . + . + . + . + . + "Sessions, utilisateurs, pages vues en 2017." . + "2017 - fréquentation site internet" . + . + . + . + "Ce thème délivre de nombreuses informations sur l’occupation du sol, les caractéristiques du bâti existant et les ventes immobilières notamment." . + "Aménagement du territoire et immobilier" . + "application/vnd.ms-excel" . + . + "bicyclepump - csv - 2016-01-01" . + . + . + . + . + "Liste des rues - lijst van de straten" . + . + "De statistieken die door het IBSA verzameld werden hebben betrekking op het wegverkeer, zachte mobiliteit, collectief en gedeeld vervoer, vervoer van goederen, verkeersveiligheid en verplaatsingsgewoonten." . + "Mobiliteit en vervoer" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "De levensstandaard van de huishoudens kan worden benaderd vanuit het standpunt van hun inkomen of hun uitgaven. Op die manier kan de koopkracht van de bevolking worden gemeten en dus ook de mate waarin zij al dan niet makkelijk toegang heeft tot goederen en diensten zoals huisvesting, uitrustingsgoederen, voeding enz." . + "Inkomens en uitgaven van de huishoudens" . + . + . + "Relevé détaillé des présences en réunion - Collège" . + . + . + "CSV FR" . + . + . + "tunnelsections - geojson - 2016-01-01" . + . + . + "tunnelsections - csv - 2016-01-01" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "parkingpublic - csv - 2016-01-01" . + . + . + . + "WFS" . + . + "traffic_events - json" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "In Brussels as in the rest of Belgium, many people do not have sufficient means of subsistence. In some circumstances, these people may qualify for welfare benefits. These are aimed at ensuring that all the population has a minimum income." . + "Insecurity and Welfare benefits" . + . + . + "The BISA compiles statistics on the safety of the public in their daily lives: road safety, fire and medical emergency service callouts, crime figures and police staffing, etc." . + "Safety " . + . + . + "WFS" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + . + "Preview" . + . + . + . + . + "bikeservice - geojson - 2016-01-01" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Electric charging stations - Mobigis" . + . + "Signalisation verticale - fichier de style" . + . + . + "Transparence des rémunérations et avantages des mandataires publics bruxellois - rapport annuel - Exercice 2017" . + . + . + . + "parkingtransit - wfs" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "WFS" . + . + . + "The statistics presented below relate mainly to the number and accommodation capacity of early childhood facilities in the Brussels Region." . + "Early childhood " . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "WFS" . + . + "Income and expenditure are indications of the living standard of households. It makes it possible to measure the purchasing power of the population and, consequently, whether or not it has easy access to goods and services such as housing, capital goods, food, etc." . + "Household income and expenditure " . + . + "Signalisation verticale - Mobigis" . + . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "WFS" . + . + "traffic lights - CSV - 2017-01-01" . + . + "Dates et lieux des constats effectués en 2017." . + "2017 - sac - gas" . + . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "WFS" . + . + "Zone de police 5343 (Etterbeek, Woluwe-Saint-Pierre et Woluwe-Saint-Lambert)" . + . + . + "Accès aux données via API.Brussels : https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin" . + "Events (Json)" . + . + . + . + "bicyclepump - wfs" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Export du registre de population. Etat civil des personnes enregistrées à la commune d'Auderghem" . + "10/04/218 - Population - Etat civil" . + . + . + "bike_paves_ss - wfs" . + . + . + "Population totale, structure de la population par groupes d'âges, sexes ou nationalités, structure des ménages, mouvements de population, projections de population..." . + "Population" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "parkingtransit - Mobigis" . + . + . + "bikeservice - Mobigis" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Total population, structure of population by age, sex or nationality, structure of households, movements of population, demographic projections etc." . + "Population" . + . + . + "icr - Mobigis" . + . + . + "The \"Shapefiles\" available on the Open Data contain the basic information about the spatial structure of\nthe STIB/MIVB network: route of the lines and position of the stops of the \"commercial\" network, that is\nthe basic itineraries \"to\" and \"from\".\nThe Shapefiles are made available in a zip file of an average size of 1,5 MB. These are updated 2 or 3 times\na year.\n\nLes fichiers « Shapefiles » disponibles sur l’Open Data contiennent les informations de base relatives à la\nstructure spatiale du réseau STIB : tracé des lignes et position des arrêts du réseau « commercial », soit\nles itinéraires « aller » et « retour » de base.\nLes fichiers ShapeFiles sont mis à disposition dans un fichier de format zip d’une taille moyenne de 1,5\nMB. Ils sont mis à jour 2 ou 3 fois par an.\n\nDe \"shapefiles\" beschikbaar op de Open Data bevatten basisinformatie betreffende de ruimtelijke\nstructuur van het MIVB-net: tracé van de lijnen en locatie van de haltes van het \"commerciële\" net,\nnamelijk de basisreiswegen \"heen\" en \"terug\".\nDe shapefiles worden ter beschikking gesteld in een zipbestand van gemiddeld 1,5 MB. Ze worden 2 à 3\nkeer per jaar geüpdatet.\n\n" . + "ShapeFiles" . + . + . + "regional_roads - geojson - 2016-01-01" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "JSON" . + . + . + "Stations Villo (JSON)" . + . + . + . + "collecto_stops - Mobigis" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "rer_velo - wfs" . + . + . + "regional_roads - Mobigis" . + . + . + "Dit thema omvat Brusselse statistische gegevens die zowel betrekking hebben op de uitgaven inzake O&O als op het personeel dat in dit domein werkt.\nDe statistieken inzake ICT-gebruik en -uitrusting van de Brusselse huishoudens worden eveneens weergegeven in dit thema." . + "Onderzoek en technologie" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "Accès aux données via API.Brussels : https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin" . + "Organizers (Json)" . + . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_protectedsite_drinking_water_wells" . + . + "Institutions et services compétents pour l'égalité des chances (y compris l'égalité de genre) en région bruxelloise, aux niveaux régional et local" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "Services pour l'égalité des chances en Région bruxelloise" . + . + "collecto_stops - csv - 2016-01-01" . + . + "How many workers does the Region have ? In which sectors are they employed and at which salary ? Where is the employed population in Brussels? What about the unemployment figures ?" . + . + . + "Labour market " . + . + "Zone de police 5344 (Schaerbeek, Saint-Josse-ten-Noode et Evere)" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "parkingpublic - geojson - 2016-01-01" . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_natura_2000_habitat" . + . + . + . + "Cahier 4 : Le transport de marchandises et la logistique à Bruxelles" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "hierarchy - csv - 2016-01-01" . + . + . + "Het BISA verzamelt de resultaten van de verschillende verkiezingen die gehouden worden in het Brussels Hoofdstedelijk Gewest.\nU vindt de resultaten van de regionale, gemeentelijke, Europese en federale verkiezingen in het Brussels Hoofdstedelijk Gewest." . + "Verkiezingen" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "icr - wfs" . + . + . + "L'IBSA rassemble les données des résultats des différentes élections tenues en Région de Bruxelles-Capitale.\nRetrouvez les résultats des élections régionales, communales, européennes et législatives tenues en Région de Bruxelles-Capitale.\n" . + "Élections" . + . + . + . + "Katern 4: Goederentransport en logistiek in Brussel" . + . + "bicyclestore - csv - 2017-06-12" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Liste des pages créées sur le site internet communal d'Auderghem." . + "Liste des pages - Lijst van paginas" . + . + . + "icr - geojson - 2016-01-01" . + . + . + . + "WFS" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "Although health is a Community competence, the BISA presents a selection of indicators (range of healthcare services on offer, life expectancy, etc.) by combining the data from the Flemish Community, the French Community and the Common Community Commission." . + "Health " . + . + "The operation returns real time vehicle position for given\nline id’s" . + "Vehicles position (VehiclePositionByLine)" . + . + . + . + "bicyclepump - Mobigis" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Preview" . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_monitoring_groundwater_quality" . + . + . + . + "parkingoffroad_blocks - geojson - 2016-01-01" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "WFS" . + . + . + . + . + "Journal du conseil" . + . + "Electric charging stations - geojson - 2016-03-01" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Liste des défibrillateurs CSV" . + . + . + "parkingpublic - Mobigis" . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "Relevé détaillé des présences en réunion - Conseil" . + . + "Preview" . + . + . + "Liste des communes bruxelloises qui ont signé la Charte européenne pour l'égalité des femmes et des hommes dans la vie locale. La liste comprend également les noms des bourgmestres et échevins à l'Égalité des Chances." . + "Communes bruxelloises signataires de la Charte européenne pour l'égalité de genre" . + . + . + . + "The General Transit Feed Specification (GTFS) defines a common format for public transportation schedules\nand associated geographic information. GTFS \"feeds\" allow public transit agencies to publish their static\ntransit data and developers to write applications that consume that data in an interoperable way.\nYou can read more about the GTFS at https://developers.google.com/transit/gtfs/\nThe GTFS files are zipped within a file with a size of about 25 Mb. The GTFS files are updated every two\nweeks. Therefore getting that file once a day is quite enough." . + "GTFS" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "2015 - Compte budgétaire - Begrotingsrekening" . + . + "bikeservice - wfs" . + . + . + . + "Portrait chiffré de l’activité économique en Région de Bruxelles-Capitale: démographie d'entreprises, chiffres d'affaires de celles-ci, valeur ajoutée, investissements et exportations." . + "Économie" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Beeld van de economische activiteit in het Brussels Hoofdstedelijk Gewest in cijfers: bedrijvendemografie, omzetcijfers, toegevoegde waarde, investeringen en export." . + "Economie " . + . + . + . + . + "Relevé détaillé des présences en réunion - Commission" . + . + "Les données rassemblées portent sur le climat, la qualité de l’air, la consommation et la qualité de l’eau, les collectes de déchets, les espaces verts, la biodiversité, la consommation énergétique, etc." . + "Environnement et énergie" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "WFS" . + . + . + . + . + "2016 - Compte budgétaire - Begrotingsrekening" . + . + "parkingoffroad_blocks - Mobigis" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Gewestelijke en gemeentelijke diensten/instellingen voor gelijke kansen (gendergelijkheid incl.) in het Brussels Gewest" . + "Diensten voor gelijke kansen in het Brussels Gewest" . + . + . + "In Brussel, net als in de rest van België, wonen veel mensen die over onvoldoende bestaansmiddelen beschikken. Onder bepaalde voorwaarden hebben zij recht op maatschappelijke hulp. Die wordt georganiseerd om de volledige bevolking een minimuminkomen te garanderen." . + "Bestaansonzekerheid en sociale bijstand" . + . + . + . + "U vindt een overzicht van de toestand van de overheidsfinanciën van het Brussels Gewest: de ontvangsten en uitgaven van de gewestelijke overheidsdienst Brussel (ex-Ministerie van het Brussels Hoofdstedelijk Gewest), maar ook van de plaatselijke besturen en de pararegionale instellingen, alsook statistieken over de uitstaande schuld van het Gewest." . + "Overheidsfinanciën" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "JSON" . + . + . + "UrbIS-Online including a 3DTiles/Cesium viewer to view the 3D buildings of Brussels." . + "UrbIS-Online" . + . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_sewage_treatment" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "parkingtransit - csv - 2016-01-01" . + . + "Milieu en energie" . + . + . + "De verzamelde gegevens hebben betrekking op het klimaat, de luchtkwaliteit, de consumptie en de kwaliteit van water, ophalingen van afval, groene ruimtes, biodiversiteit, energie, enz." . + . + "Preview" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "parkingtransit - geojson - 2016-01-01" . + . + "Land-use planning and real estate " . + . + . + "This section provides a wealth of information, for instance on land occupancy, the characteristics of existing constructions and real estate sales." . + . + "À Bruxelles comme dans le reste de la Belgique, de nombreuses personnes disposent de moyens de subsistance insuffisants. Sous certaines conditions, ces personnes peuvent bénéficier d’une aide sociale. Celle-ci est organisée dans le but de garantir un revenu minimum à l'ensemble de la population. " . + "Précarité et aide sociale" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Cahier 2 : Les pratiques de déplacement à Bruxelles" . + . + . + "hierarchy - Mobigis" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Katern 3: De verplaatsingsgewoonten in Brussel - diepteanalyses" . + . + . + "2014 - Compte budgétaire - Begrotingsrekening" . + . + "L'IBSA rassemble des données statistiques concernant la sécurité des citoyens dans leur vie quotidienne: sécurité routière, interventions du Service d'Incendie et d'Aide Médicale Urgente (SIAMU), délits constatés et effectifs des zones de police..." . + "Sécurité" . + . + . + . + "Export du registre de population. Liste des rues où sont inscrits les habitants de la commune d'Auderghem." . + "10/04/2018 - Population - rues" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "WFS" . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_protectedsite_drinking_water_gallery" . + . + . + "artistic_heritage - wfs" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "URL To the 3DTiles dataset. A 3DTiles viewer is required to view the data.\nAn application to view Brussels buildings in 3D." . + "3DTiles Tileset - 3D tiles for Brussels" . + . + "Relevé de toute réduction opérée sur les rémunérations et avantages de toute nature" . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "bicyclestore - geojson - 2017-06-12" . + . + . + "parkingpublic - wfs" . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_protectedsite_drinking_water" . + . + . + . + "Figures on the economic activity in the Brussels-Capital Region: demography of companies, company turnover, value added, investments and exports." . + "Economy " . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Electric charging stations - CSV - 2016-03-01" . + . + . + "Preview" . + . + . + . + "bike_paves_ss - Mobigis" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Lijst van de lokale mandatarissen bevoegd voor Gelijke Kansen in de 19 gemeenten van het Brussels Gewest. 2 Brusselse gemeenten hebben geen schepen voor Gelijke Kansen aangewezen (legislatuur 2012-2018)" . + "Lokale mandatarissen voor Gelijke kansen in het Brussels Gewest" . + . + . + "collecto_stops - wfs" . + . + . + . + "Export du registre de population. Nationalités et lieux de naissance des personnes enregistrées à la commune d'Auderghem." . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "10/04/2018 - Population - Nationalités et lieux de naissance" . + . + "Electric charging stations - WFS" . + . + . + "XLS" . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Preview" . + . + "This section presents the statistics for Brussels on both R&D expenditure and staff working in this field.\nThe statistics on the use and equipment with regard to information technology and communication found in Brussels households are also presented in this theme." . + "Research and technology " . + . + . + . + "Preview" . + . + "Cette série présente des données statistiques bruxelloises qui se rapportent aussi bien aux dépenses de R&D qu’au personnel qui travaille dans ce domaine.\nLes statistiques relatives à l’utilisation et l’équipement en technologies de l'information et de la communication des ménages bruxellois sont également présentées sur cette thématique.\n" . + "Recherche et technologie" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "Occupancy rate in real time.\nFormat Datex II (http://www.datex2.eu)." . + "Parking Occupancy - Dynamic Data" . + . + . + . + "traffic lights - WFS" . + . + "Rapport_transparence_modele_generique_2017.csv" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + . + "Parkings actuellement gérés dans le cadre du projet CycloParking (nom, coordonnées, type de parking, capacité). Donnée non dynamique." . + "Parkings vélo CycloParking" . + . + "Travelers information" . + . + . + . + "Le niveau de vie des ménages peut être approché par leur revenu ou leurs dépenses. Ceux-ci permettent de mesurer le pouvoir d’achat de la population et par conséquent son accès plus ou moins facile aux biens et aux services tels que le logement, les biens d’équipement, l’alimentation, etc." . + "Revenus et dépenses des ménages" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Export du registre de population. Dates de naissances des personnes enregistrées à la commune d'Auderghem" . + "10/04/2018 - Population - date de naissance" . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_groundwaterbody" . + . + . + . + "Onderwijs is een Gemeenschapsbevoegdheid. Het BISA maakt statistieken die de gegevens van de Vlaamse Gemeenschap en de Franse Gemeenschap combineren : schoolbevolking, aantal scholen. Het BISA stelt ook gegevens op over schoolbevolking buiten de gemeenschappen." . + "Onderwijs" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "* Lijst van organisaties actief op het terrein van gendergelijkheid en gevestigd in het Brussels Gewest. De velden zijn in het Engels, de waarden zijn in het Nederlands en in het Frans. \n* Liste d'organisations engagées en faveur de l'égalité de genre et établies en Région de Bruxelles-Capitale. Les champs sont en anglais, les valeurs sont en français et en néerlandais. \n" . + "Organisations for gender equality in the Brussels Region" . + . + . + "Zone de police 5341 (Anderlecht, Saint-Gilles et Forest)" . + . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "tunnelsections - wfs" . + . + "haljson" . + "Incidents" . + . + "Relevé des Rémunérations – montants bruts" . + . + . + "collecto_stops - geojson - 2016-01-01" . + . + . + . + "Accès aux données via API.Brussels : https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin" . + "Places (xml)" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "taxi_stops - Mobigis" . + . + . + "parkingoffroad_blocks - wfs" . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "bike_paves_ss - csv - 2016-01-01" . + . + "Les statistiques relatives au tourisme concernent l’hébergement des personnes qui ont séjourné dans la Région de Bruxelles-Capitale.\nDans le domaine de la culture, vous trouverez des statistiques relatives aux salles de cinéma et aux projections." . + "Tourisme et culture" . + . + "WFS JSON" . + . + . + . + "regional_roads - wfs" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "approuvé par le conseil communal d'auderghem le 29.06.2018" . + "compte budgétaire 2017" . + . + . + "Katern 1: Het vervoersaanbod in Brussel" . + . + . + "Jonge kinderen" . + . + . + "Evere"@fr . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "Deze statistieken hebben met name betrekking op het aantal kinderopvangvoorzieningen en de capaciteit" . + . + "Carte des stations" . + . + . + . + . + "Evere"@en . + "Evere"@nl . + "Brussels Coworking List " . + . + "Milonga"@en . + "Milonga"@fr . + "Milonga"@nl . + "http://www.utalkbrussels.com/coworking-1" . + . + "Haven van Brussel"@nl . + "Port de Bruxelles"@fr . + "Port of Brussels"@en . + . + "The operation returns the waiting times for the next two vehicles of each line passing through the requested\nstop id’s." . + . + "Brussel Gewestelijke Coördinatoe"@nl . + "Brussels Region Coordination"@en . + "Bruxelles Coordination régionale"@fr . + "Waiting Time (PassingTimeByPoint)" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Auderghem"@fr . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_bodies_surface&SRSNAME=EPSG:31370" . + . + . + . + "Auderghem"@en . + "Oudergem"@nl . + "Zone de police 5339 (Ville de Bruxelles - Ixelles)" . + . + "BRIC"@en . + . + . + "Totale bevolking, structuur van de bevolking per leeftijdscategorie, geslacht of nationaliteit, structuur van de huishoudens, loop van de bevolking, bevolkingsprojecties..." . + "Bevolking " . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "traffic lights - GEOJSON - 2017-01-01" . + . + "2013 - Compte budgétaire - Begrotingsrekening" . + . + . + . + "Vous trouverez un aperçu de la situation des finances publiques de la Région bruxelloise : les recettes et dépenses du Service public régional de Bruxelles (ex- Ministère de la Région de Bruxelles-Capitale), mais également des pouvoirs locaux et des organismes para-régionaux ainsi que des statistiques quant à l’encours de la dette de la Région. " . + "Finances publiques" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "rer_velo - Mobigis" . + . + . + . + "CIBG"@nl . + "CIRB"@fr . + "2012 - Compte budgétaire - Begrotingsrekening" . + . + "Défibrillateur - Utilisation du DEA" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Accès aux données via API.Brussels : https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin" . + "Organizers (xml)" . + . + . + "Education being a community competence, the BISA provides statistics that combine data from the Flemish Community and the French Community: student population, number of schools… The BISA also provides student population data for education not provided by the communities." . + "Education " . + . + . + "Les statistiques rassemblées par l’IBSA portent sur la circulation routière, la mobilité douce, le transport collectif et partagé, le transport de marchandises, la sécurité routière et les pratiques de déplacements." . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "Mobilité et transport" . + . + "Accès aux données via API.Brussels : https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin" . + "Places (Json)" . + . + . + "parkingoffroad_blocks - csv - 2016-01-01" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Preview" . + . + . + "bicyclestore - wfs" . + . + . + . + . + "Saint-Gilles"@fr . + "Lijst van de Brusselse gemeenten die het Europees charter voor gelijkheid van vrouwen en mannen op lokaal vlak* hebben ondertekend, met de namens van de burgemeesters en schepenen voor gelijke kansen (legislatuur 2012-2018).\n*van de Council of European Municipalities and Regions (CEMR)" . + "Ondertekenende Brusselse gemeenten van het Europees charter voor gendergelijkheid " . + . + . + "Het BISA verzamelt de Brusselse statistieken over de veiligheid van de burger in het dagelijkse leven: verkeersveiligheid, interventies van de Dienst voor Brandbestrijding en Dringende Medische Hulp (DBDMH), vastgestelde criminele feiten en personeelssterkte van de politiezones..." . + "Veiligheid" . + "Saint-Gilles"@en . + "Sint-Gillis"@nl . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Center for social documentation and coordination"@en . + "Centre de Documentation et de Coordination Sociales"@fr . + "Centrum voor Maatschappelijke Documentatie en Coördinatie"@nl . + . + . + . + "Brussels Jazz Marathon"@en . + "Brussels Jazz Marathon"@fr . + "Brussels Jazz Marathon"@nl . + "Liste des mandataires locaux qui ont parmi leurs compétences l'égalité des chances (y compris l'égalité de genre). (Législature 2012-2018)" . + "Mandataires locaux pour l'égalité des chances en région bruxelloise" . + . + "IBSA-BISA"@en . + . + . + . + "The data compiled relate to climate, air quality, consumption and quality of water, waste collection, green spaces, biodiversity, energy consumption, etc." . + "Environment and Energy " . + . + "L’enseignement étant une compétence communautaire, l’IBSA propose des statistiques qui combinent des données de la Communauté flamande et de la Communauté française : population scolaire, nombre d’établissements scolaires…\nL’IBSA présente également des données de population scolaire pour l’enseignement hors communautés." . + "Enseignement" . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Accès aux données via API.Brussels : https://api.brussels/store/apis/info?name=agenda.brussels&version=0.0.1&provider=admin" . + "Events (xml)" . + "BISA"@nl . + "IBSA"@fr . + . + . + "CESRBC"@en . + . + . + "U Talk Freelance"@en . + . + "CyCLO"@en . + "CyCLO"@fr . + "CyCLO"@nl . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_air_monitoring_stations" . + . + "Amazone"@fr . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_natural_reserve" . + "Amazone"@en . + "Amazone"@nl . + . + . + "Cambio"@en . + "Cambio"@fr . + "Cambio"@nl . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8CA3E4585E2142B0B433B9FE86D089BE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8CA3E4585E2142B0B433B9FE86D089BE "POLYGON ((4.2437 50.7565, 4.4867 50.7565, 4.4867 50.9226, 4.2437 50.9226, 4.2437 50.7565))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8CA3E4585E2142B0B433B9FE86D089BE "{\"type\": \"Polygon\", \"coordinates\": [[[4.24365722656, 50.756472168], [4.48672973633, 50.756472168], [4.48672973633, 50.9226403809], [4.24365722656, 50.9226403809], [4.24365722656, 50.756472168]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA93A78FA0ED565530B48133B51C80D47 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA93A78FA0ED565530B48133B51C80D47 "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC060AC25CC5358540102F99C12D041EC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC060AC25CC5358540102F99C12D041EC "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC060AC25CC5358540102F99C12D041EC "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d28CE2430A751CE255CD7E68CE15E4DD2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD1D8D21F78E196C0EAC4F630C62338F3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD1D8D21F78E196C0EAC4F630C62338F3 "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECF1CA1D321C9F212C6B1265BB43CAFC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dECF1CA1D321C9F212C6B1265BB43CAFC "Shannon Rowies" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d460ACDF20173D1C2522D1EBC6AC6C612 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d460ACDF20173D1C2522D1EBC6AC6C612 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d460ACDF20173D1C2522D1EBC6AC6C612 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE8FED21376C255A706D0B32AD51C96C7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d971D6B6943E1553498571F3B1806FEC0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d971D6B6943E1553498571F3B1806FEC0 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d971D6B6943E1553498571F3B1806FEC0 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d26462C6E1674C9311F60C22856699190 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4BA5BF8C8D9081A664F9A09BCB9A332F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4BA5BF8C8D9081A664F9A09BCB9A332F "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4BA5BF8C8D9081A664F9A09BCB9A332F "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE9CD16E75EDF652651A76650FB23230 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF17445C120E6312AB7E0349C1E2068 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF17445C120E6312AB7E0349C1E2068 "Secrétariat communal" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD10AA77E877FA891AAA2CDE19EAD3006 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD10AA77E877FA891AAA2CDE19EAD3006 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD10AA77E877FA891AAA2CDE19EAD3006 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d228CEDC4629B1AA6E352033023934C0C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d228CEDC4629B1AA6E352033023934C0C "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4DA7ED9210B6D18176CED31134A30C72 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4DA7ED9210B6D18176CED31134A30C72 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4DA7ED9210B6D18176CED31134A30C72 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE3F6AC9451B0E3655CEA6A54FE63EA51 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE3F6AC9451B0E3655CEA6A54FE63EA51 "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE8E36CBF344E596FDEB640126C17A5ED . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE8E36CBF344E596FDEB640126C17A5ED "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF2BF65EE86CB1984D6B13DB41966F8CA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF2BF65EE86CB1984D6B13DB41966F8CA "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF2BF65EE86CB1984D6B13DB41966F8CA "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB300147DD6B18D8497AD96D31217417 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB300147DD6B18D8497AD96D31217417 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB300147DD6B18D8497AD96D31217417 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d88A7660CEF7FFFA91F61CEF98F83D0A9 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d88A7660CEF7FFFA91F61CEF98F83D0A9 "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7D085BBA789A8F90DD87B82AA46E764F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7D085BBA789A8F90DD87B82AA46E764F "Secrétariat communal" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d66F755905DD7C8BC678B87D3F51BA727 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d66F755905DD7C8BC678B87D3F51BA727 "Virginie Tumelaire" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d11C9DD3E5B6747E0910EF8D32C58FB41 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d11C9DD3E5B6747E0910EF8D32C58FB41 "Virginie Tumelaire" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d760861C56057322502B8EBBDEAF93EB2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d760861C56057322502B8EBBDEAF93EB2 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d760861C56057322502B8EBBDEAF93EB2 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBC8157FD21DE40C42E8FCA06367923EA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBC8157FD21DE40C42E8FCA06367923EA "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAD7E987C49F350EAC509D704640C79E2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAD7E987C49F350EAC509D704640C79E2 "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C16FC92568D654233E103BE5B50E2D0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C16FC92568D654233E103BE5B50E2D0 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C16FC92568D654233E103BE5B50E2D0 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD669ACFEF50FC023209F2B0AE60F7120 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD669ACFEF50FC023209F2B0AE60F7120 "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d699D4871CF97CED30F05CBAF55BB9BA6 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d699D4871CF97CED30F05CBAF55BB9BA6 "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFD1F4A91C5C32078059820585BA1C4FA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFD1F4A91C5C32078059820585BA1C4FA "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFD1F4A91C5C32078059820585BA1C4FA "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC3946BF809556E53D66CF2BD28D63A54 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC3946BF809556E53D66CF2BD28D63A54 "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE0DD8BAFFA0C1A0E67B71CAC6AFD7F3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE0DD8BAFFA0C1A0E67B71CAC6AFD7F3 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE0DD8BAFFA0C1A0E67B71CAC6AFD7F3 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4C811EFDF1207087A1AA510D02F09958 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4C811EFDF1207087A1AA510D02F09958 "Pierre Hotyat" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7E7C0936E56BF82A522F26AE5A43FB59 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7E7C0936E56BF82A522F26AE5A43FB59 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7E7C0936E56BF82A522F26AE5A43FB59 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6016774B7208E2622A844DB7CFFDA618 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6016774B7208E2622A844DB7CFFDA618 "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA291F1876327E0F46C1592CAFC00625A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d06DAC25EAEC984A70A02122B89D62E21 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d06DAC25EAEC984A70A02122B89D62E21 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d06DAC25EAEC984A70A02122B89D62E21 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d35CE5C409A4AB7C9FF58AA0B93F26BEF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d35CE5C409A4AB7C9FF58AA0B93F26BEF "Léonor Rennotte" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d784D09B24EB57FC440535C124B24A192 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6EF62B1621AA24B99DD086E71675F4C0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6EF62B1621AA24B99DD086E71675F4C0 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6EF62B1621AA24B99DD086E71675F4C0 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8A9A00C88B7C0DC5107860615AA8D5BA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8A9A00C88B7C0DC5107860615AA8D5BA "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8A9A00C88B7C0DC5107860615AA8D5BA "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBB56969207EDE5D005DC5CB099E7769C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBB56969207EDE5D005DC5CB099E7769C "Equipe Geodata" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD63D9C34B295EBB7A9984D672FA59E5A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD63D9C34B295EBB7A9984D672FA59E5A "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFBDE59B26A85712614D2459BF35134B6 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFBDE59B26A85712614D2459BF35134B6 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFBDE59B26A85712614D2459BF35134B6 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB30FF5D77D3D091E88D5B3487AF7B7D2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE41F72C47DA8F5682351D2450B0B4EEB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE41F72C47DA8F5682351D2450B0B4EEB "Inge Van der Stighelen" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC420368CAA042F2B405D9E8C7878087C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE5C3AB4885F967E473A5B3181FC3CEF0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE5C3AB4885F967E473A5B3181FC3CEF0 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE5C3AB4885F967E473A5B3181FC3CEF0 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEF289B5A571AC494ED326B4782E7E8AA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEF289B5A571AC494ED326B4782E7E8AA "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8D7CB2C93FE0C4ECC8CD2E7833D3B993 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8D7CB2C93FE0C4ECC8CD2E7833D3B993 "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD0315E997F010592FC0CF37C8DD54505 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD0315E997F010592FC0CF37C8DD54505 "POLYGON ((4.2439 50.7636, 4.4826 50.7636, 4.4826 50.9138, 4.2439 50.9138, 4.2439 50.7636))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD0315E997F010592FC0CF37C8DD54505 "{\"type\": \"Polygon\", \"coordinates\": [[[4.2439, 50.7636], [4.4826, 50.7636], [4.4826, 50.9138], [4.2439, 50.9138], [4.2439, 50.7636]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDBABE43D2541D6DA028A3E1757D4E687 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dDBABE43D2541D6DA028A3E1757D4E687 "Cécile Gasparri " . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d93075D71840A35CAF1F6870376F56F31 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d93075D71840A35CAF1F6870376F56F31 "Secretariat" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d67717C4B1928EFB561C32E4B9D146171 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d67717C4B1928EFB561C32E4B9D146171 "Pierre Hotyat" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA8A5F1A7AF64D58689B3974D2310781D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA8A5F1A7AF64D58689B3974D2310781D "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD6DA6D78F929A0EA1F6C122736F5CA07 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD6DA6D78F929A0EA1F6C122736F5CA07 "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3A5D09C02632136D9CCA811E02105C8A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3A5D09C02632136D9CCA811E02105C8A "POLYGON ((4.2203 50.7565, 4.5032 50.7565, 4.5032 50.9240, 4.2203 50.9240, 4.2203 50.7565))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3A5D09C02632136D9CCA811E02105C8A "{\"type\": \"Polygon\", \"coordinates\": [[[4.2203112793, 50.756472168], [4.50320922852, 50.756472168], [4.50320922852, 50.9240136719], [4.2203112793, 50.9240136719], [4.2203112793, 50.756472168]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA1E977449A1E885F69931FC632C07663 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA1E977449A1E885F69931FC632C07663 "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFBE8101373468F4276942802F9891CCA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFBE8101373468F4276942802F9891CCA "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFBE8101373468F4276942802F9891CCA "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD0623B5F4A3F20854EFD0D7D4E75C9F3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dD0623B5F4A3F20854EFD0D7D4E75C9F3 "DEFRANCE Sébastien" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1776C4A741C0B5D00EC851419A0CCA68 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6939BA1EECD7CFC42FBB0632763D1F4D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6939BA1EECD7CFC42FBB0632763D1F4D "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6939BA1EECD7CFC42FBB0632763D1F4D "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3112D776FF578756F8B3A58E9172E268 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3112D776FF578756F8B3A58E9172E268 "POLYGON ((4.2217 50.7606, 4.4908 50.7606, 4.4908 50.9213, 4.2217 50.9213, 4.2217 50.7606))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3112D776FF578756F8B3A58E9172E268 "{\"type\": \"Polygon\", \"coordinates\": [[[4.22168457031, 50.760592041], [4.49084960938, 50.760592041], [4.49084960938, 50.9212670898], [4.22168457031, 50.9212670898], [4.22168457031, 50.760592041]]]}"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE68D1118D83F2FC08B1F9989E7669360 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE68D1118D83F2FC08B1F9989E7669360 "Sébastien DEFRANCE" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB68F6E219C7C466D25807D3E2A80FF9F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB68F6E219C7C466D25807D3E2A80FF9F "IBSA-BISA" . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d129BB644EFCFAD8DF344199EFB36EC72 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d129BB644EFCFAD8DF344199EFB36EC72 "POLYGON ((4.2412 50.7632, 4.4798 50.7632, 4.4798 50.9133, 4.2412 50.9133, 4.2412 50.7632))"^^ . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d129BB644EFCFAD8DF344199EFB36EC72 "{\"type\": \"Polygon\", \"coordinates\": [[[4.24124261076, 50.7631624449], [4.47975783302, 50.7631624449], [4.47975783302, 50.9133209049], [4.24124261076, 50.9133209049], [4.24124261076, 50.7631624449]]]}"^^ . + . + "Région de Bruxelles-Capitale"@fr . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "Liste des espaces publics numériques (mise à jour annuelle)"@fr . + "Espaces publics numériques "@fr . + "2018-08-16T12:54:53.633058"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dFB059F53C6CBF020491A269F57C8EFEC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dFB059F53C6CBF020491A269F57C8EFEC . + "Maillage bleu"@fr . + "Belgique"@fr . + "Reporting Inspire"@fr . + . + "Reporting Inspire"@fr . + "nitrate"@fr . + "protection de la nature"@fr . + "50512393-709d-4a0b-8e91-caff46687fe6" . + "Lijst van openbare computerruimtes (jaarlijkse bijwerking)"@nl . + "2018-08-16T00:00:00"^^ . + "Openbare computerruimtes "@nl . + "Belgique"@fr . + . + "Een bijkomende erkenning of machtiging is vereist om bepaalde activiteiten uit te oefenen in het Brussels Hoofdstedelijk Gewest. Reisagentschappen vereisen deze erkenning."@nl . + "e4dc9bba-f655-44a0-b9ac-469419372467" . + "2019-02-04T15:56:28.676213"^^ . + "List of authorised travel agencies in BCR"@en . + . + "Liste des agences de voyages autorisées en RBC"@fr . + "environnement"@fr . + "protection des espaces naturels"@fr . + "2018-12-11T00:00:00"^^ . + "Defibrillator - Algoritme voor het gebruik van een AED" . + "pollution des eaux usées"@fr . + "Un agrément ou une autorisation supplémentaire est nécessaire pour exercer certaines activités en Région de Bruxelles-Capitale. Les agences de voyages ont besoin d'une autorisation."@fr . + "An accreditation or additional authorisation is needed for performing certain activities in the Brussels-Capital Region. Travel agencies require an authorisation. "@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD14379397DDF8EBC58E29CB474A75CCF . + "Lijst van vergunde reisagentschappen in het BHG"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3D40291E6B142E8C8E57D0D3390644E8 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3D40291E6B142E8C8E57D0D3390644E8 . + . + "Brussels Hoofdstedelijk Gewest: \"de zone rondom een monument, een geheel, een landschap of en archeologische vindplaats, waarvan de omtrek wordt vastgesteld volgens de vereisten van de vrijwaring van de omgeving van het onroerende erfgoed\" en dat het onderwerp uitmaakt van een beschermingsbesluit."@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d594EC1A0600CF3B0B7AAF4449915B4EC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d594EC1A0600CF3B0B7AAF4449915B4EC . + "Région de Bruxelles-Capitale : \"la zone établie autour d’un monument, d’un ensemble, d’un site ou d’un site archéologique dont le périmètre est fixé en fonction des exigences de la protection des abords du patrimoine en question\" ayant fait l'objet d'un arrêté de classement."@fr . + "Brussels-Capital Region : \"the area established around a monument, a unit, a landscape or an archaeological site whose perimeter is fixed according to the requirements of the protection of the surroundings of the heritage in question\" having been the subject of a decree for its conservation."@en . + "2018-08-24T07:10:29.770141"^^ . + "2018-08-24T07:33:02.583462"^^ . + . + "01838fa3-3a9d-424f-899b-7df9b3629540" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9D93C8B1532C7ADFB5F5DFD3B9942D89 . + "Protection area"@en . + "Vrijwaringszone"@nl . + "Zones de protection"@fr . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_monitoring_river_quality_phch" . + . + "Saint-Gilles dispose de 12 structures d’accueil pour les enfants de 0 à 3 ans. Celles-ci comprennent 9 infrastructures communales agréées et subventionnées par l’ONE. Il s’agit des crèches « Jourdan », « Les Bambins du coin », « Isabelle Blume », « Les Bengalis », « Adèle Hauwel », « Willy Peers », « Gabrielle Petit » et « Marie Janson ».\nLes 3 autres structures sont gérées par le Réseau saint-gillois des mini-crèches à vocation sociale et parentale. Il s’agit des mini-crèches « Ketje », Lily » et « L’Amandoline ».\n"@fr . + "2ea28ecb-563d-42c0-874c-dcf11aafc2ea" . + "Crèches"@fr . + "Harvested"@fr . + "Région de Bruxelles-Capitale"@fr . + "pollution de l'eau"@fr . + . + . + "2018-11-13T00:00:00"^^ . + "2019-01-18T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF383B1DB8C9D1A1DA642D66F5A9AA438 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF383B1DB8C9D1A1DA642D66F5A9AA438 . + "flore"@fr . + . + "On-street parking demand in Brussels-Capital Region"@en . + "b459dfcd-1e5f-4c27-92d4-d41777e08716" . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6F96C7ECE63147054BCFA5EA250CB164 . + "Demande de stationnement en voirie"@fr . + "Op de openbare weg parkeervraag in het Brussels Hoofdstedelijk Gewest"@nl . + . + "Demande de stationnement en voirie en Région de Bruxelles-Capitale"@fr . + "2018-10-23T07:34:43.505102"^^ . + "2017-06-23T11:11:32.813055"^^ . + "On-street parking demand"@en . + "Op de openbare weg parkeervraag"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d81D532A18EC908CA7102BCBAEE0DF398 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d81D532A18EC908CA7102BCBAEE0DF398 . + . + "Preview" . + . + "Brussel-Hoofdstad Gewest : kadastrale percelen van het Brussels Gewest"@nl . + "Région de Bruxelles-Capitale : Parcelles cadastrales de la Région bruxelloise"@fr . + "2015-03-30T00:00:00"^^ . + "Cadastral parcels"@en . + "Kadastrale percelen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d75AE666460BA239B3A1D79E599C7571F . + "58d00d48-daec-41dd-b9bf-14eaf8f552cb" . + "Brussels-Capital Region : cadastral plots of the Brussels Region"@en . + "Parcelles cadastrales"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7597974B9D32C6C590CB13BBC38C8267 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7597974B9D32C6C590CB13BBC38C8267 . + "2019-01-29T10:57:27.645083"^^ . + "faune"@fr . + "zone sensible"@fr . + . + "bike_paves_ss - geojson - 2016-01-01" . + "Brussels"@fr . + "gender equality"@fr . + "local government"@fr . + . + "Het geologische model BRUSTRATI3D v1.1 is een geheel van rasterbestanden die het Brussels Hoofdstedelijk Gewest bestrijken. Deze bestanden bevatten de tophoogten en de dikten van de Stratigrafische Eenheden (SE/BHG), evenals het Digitaal Terreinmodel (DTM) van de topografische oppervlakte die gebruikt werd in dit model. Deze rasters hebben een resolutie van 10 x 10 m en hebben als referentiecoördinatensysteem: EPSG 31370 Belgisch Lambert 1972. \n\nDe documentatie met betrekking tot de opmaak van deze gegevens is beschikbaar op de volgende adressen (enkel in het Frans): \n\n###BRUSTRATI3D V1.1\nhttp://document.environnement.brussels/opac_css/index.php?lvl=notice_display&id=10965\n\n###BRUSTRATI3D V1.0\nhttp://document.environnement.brussels/opac_css/index.php?lvl=notice_display&id=10964"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4834F417B13589F07B111F4E110059A5 . + "Geologisch model van het Brussels Hoofdstedelijk Gewest - BRUSTRATI3D v1.1"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8C818FBB8FC590D24B3C9D048F198981 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8C818FBB8FC590D24B3C9D048F198981 . + "Brussels"@fr . + "gender"@fr . + "public space"@fr . + "representation of women"@fr . + "streets"@fr . + "urban space"@fr . + "women"@fr . + "Belgique"@fr . + "Espace public"@fr . + "Fond de plan"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "route"@fr . + "The geological model BRUSTRATI3D v1.1 is a set of raster files covering the Brussels-Capital Region. These files contain the roof elevations and the thicknesses of the Stratigraphic Units (SU/BCR) and the Digital Terrain Model (DTM) of the topographic surface used in this model. These rasters have a 10x10m resolution and their reference coordinate system is EPSG 31370 Belgian Lambert 31370.\n\nThe documentation relating to the construction oh these data is available at the following addresses (French only) :\n\n###BRUSTRATI3D V1.1\nhttp://document.environnement.brussels/opac_css/index.php?lvl=notice_display&id=10965\n\n###BRUSTRATI3D V1.0\nhttp://document.environnement.brussels/opac_css/index.php?lvl=notice_display&id=10964"@en . + "3a62f9f9-4a3f-433f-9364-3787a0fc6760" . + "2019-03-06T09:12:47.129978"^^ . + "Brussels-Capital Region Geological Model - BRUSTRATI3D v1.1"@en . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "conservation des ressources naturelles"@fr . + "environnement"@fr . + "espace vert"@fr . + "flore"@fr . + "législation en matière de préservation de la natur"@fr . + "préservation de la nature"@fr . + "site naturel protégé"@fr . + "Le modèle géologique BRUSTRATI3D v1.1 est un ensemble de fichiers rasters couvrant la Région Bruxelles-Capitale. Ces fichiers contiennent les élévations des toits et les épaisseurs des Unités Stratigraphiques (US/RBC) ainsi que le Modèle Numérique de Terrain (MNT) de la surface topographique utilisée dans ce modèle. Ces rasters ont une résolution de 10x10 m et ont pour système de coordonnées de référence : EPSG 31370 Lambert Belge 1972. \n\nLa documentation relative à la construction de ces données est disponible aux adresses suivantes : \n\n###BRUSTRATI3D V1.1\nhttp://document.environnement.brussels/opac_css/index.php?lvl=notice_display&id=10965\n\n###BRUSTRATI3D V1.0\nhttp://document.environnement.brussels/opac_css/index.php?lvl=notice_display&id=10964"@fr . + "2019-01-04T00:00:00"^^ . + "Modèle géologique de la Région Bruxelles-Capitale - BRUSTRATI3D v1.1"@fr . + . + "c853d99c-3b2c-4f7a-ab0f-cb5f158cd450" . + "2018-12-14T00:00:00"^^ . + "2018-12-14T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d307297A6F5D355AAE540F56F7AE70AF6 . + "Lijst erkende inschakelingsondernemingen en plaatselijke initiatieven voor de ontwikkeling van werkgelgenheid"@nl . + "List of insertion companies and local initiatives for the development of employment"@en . + "Liste des initiatives locales de développement de l'emploi et des entreprises d'insertion"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9C7362DA652CBFC49685E9212D05FDDE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9C7362DA652CBFC49685E9212D05FDDE . + . + . + "De barometer van de musea en attracties wordt opgemaakt op basis van de verzameling van de gegevens over de bezoekersaantallen (vaste collecties en tijdelijke tentoonstellingen) van een panel van musea en attracties in Brussel. Dit panel evolueert in de tijd, door de toevoeging van nieuwe musea. Alle cijfers en evoluties worden meegedeeld op grond van een vaste perimeter: als een museum in het panel wordt opgenomen, worden ook al zijn vroegere gegevens verwerkt."@nl . + "Le baromètre des musées et attractions est réalisé sur la base de la collecte des données de fréquentation (collections permanentes et expositions temporaires) d’un panel de musées et attractions à Bruxelles. Ce panel évoluant avec le temps suite à l’ajout de nouveaux musées, tous les chiffres et évolutions sont donnés à périmètre constant : quand un musée rentre dans le panel, toutes ses données passées sont également incluses."@fr . + "The barometer of museums and attractions is built from the collection of visitors’ statistics (permanent collections and temporary exhibitions) from a panel of museums and attractions in Brussels. This panel can evolve in time by adding a new museum but all figures and evolutions are done on a constant scope : when a museum enters the panel, its past data are also included in the figures."@en . + "00932042-de14-411e-8da5-9a8755e31546" . + "2017-05-09T08:00:20.613271"^^ . + "2018-11-15T10:40:22.684087"^^ . + . + "Barometer of the museums and attractions"@en . + "Barometer van de musea en attracties"@nl . + "Baromètre des Musées et Attractions"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3A2EC9D16B62E3C857B4607EF327074F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3A2EC9D16B62E3C857B4607EF327074F . + "Preview" . + . + "Périmètres des Contrats de Rénovation urbaine"@fr . + "4ec9b4f2-744f-4c27-8964-05c51fda0ca8" . + "2018-08-23T08:34:32.410347"^^ . + "2018-08-23T10:50:47.481825"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE5EFF87757D79631306151414E62BC09 . + "Contrats de Rénovation urbaine (Programmes)"@fr . + "Stadsvernieuwingscontracten (Programma's)"@nl . + . + "bicyclepump - geojson - 2016-01-01" . + . + "Fietsparkeeraanbod op de openbare weg in het Brussels Hoofdstedelijk Gewest"@nl . + "On-street bicycle parking supply in Brussels-Capital Region"@en . + "Offre de stationnement vélo en voirie en Région de Bruxelles-Capitale"@fr . + "9ae57108-6bc4-4793-bd8e-93c1d28e1183" . + "2017-06-23T10:35:49.219986"^^ . + "2018-10-23T07:35:00.220829"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d2215620C61328A5282FAAF6997F73C19 . + "Arceaux vélo"@fr . + "Bicyle rack"@en . + "Fietsenstallingen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dEC7B63F5F1FF9D5AC5B1BD8442B25C17 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dEC7B63F5F1FF9D5AC5B1BD8442B25C17 . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "trees - Mobigis" . + . + "De installaties die einde afvalfase verkregen hebben volgens de Europese criteria"@nl . + "Installations ayant obtenu la fin de statut de déchets selon les critères européens"@fr . + "336c80cd-37ea-45b2-b512-842e1ffa01a8" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de installaties die einde afvalfase verkregen hebben volgens de Europese criteria"@nl . + "Liste des installations ayant obtenu la fin de statut de déchets selon les critères européens"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4BD30FA50D5441C983786D6D87FFD238 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4BD30FA50D5441C983786D6D87FFD238 . + "faune"@fr . + . + "Brussel"@fr . + "Brussels"@fr . + "Early childhood services"@fr . + "Statistics"@fr . + . + "Images cameras - mobility portal" . + . + "Agenda de toutes les activités liées au tango en Belgique"@fr . + "Agenda of all tango-related activities in Belgium"@en . + "Agenda van alle activiteiten mbt tango in België"@nl . + "c0c72946-a3ae-4b44-9b9e-710335605135" . + "2016-01-27T10:46:24.253624"^^ . + "2018-09-18T07:19:06.634336"^^ . + "Agenda de toutes les activités liées au tango en Belgique"@fr . + "Agenda of all tango-related activities in Belgium"@en . + "Agenda van alle activiteiten mbt tango in België"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8EAA4E7E979B49941A98CD221D199847 . + . + "Brussels-Capital Region : Entity Address Point (ADPT) is the location of the address points of the Brussels-Capital"@en . + "Brussels Hoofdstedelijk Gewest : een adrespunt is de grafische weergave van één of meer huisnummers die binnen gebouwen of percelen geplaatst zijn."@nl . + "12c4b7e5-1d76-4bc1-8157-cabb38bd3c35" . + "Région de Bruxelles-Capitale : l'entité point d'adresses (ADPT) correspond à la localisation des points d'adresses de la Région de Bruxelles-Capitale"@fr . + "2015-09-28T00:00:00"^^ . + "2019-01-29T11:06:40.206838"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d57BC8CC496136D9A959C5A4575945AD8 . + "Address Points"@en . + "Adrespunt"@nl . + "Points d'adresses"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5B2C092F46D53F24E814E00BA19A4B8C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5B2C092F46D53F24E814E00BA19A4B8C . + "Belgique"@fr . + "Belgium"@fr . + "België"@fr . + "Bruxelles"@fr . + "Milieux accueil pour la petite enfance"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Structuren voor kinderopvang"@fr . + . + "Cahier 3 : Les pratiques de déplacement à Bruxelles - analyses approfondies" . + . + "Liste des exploitants d'un centre de démontage de véhicules hors d'usage (type A), enregistrés en Région de Bruxelles-Capitale."@fr . + "Registratie als exploitant van een demonteercentrum voor afgedankte voertuigen (type A)in het Brussels Hoofdstedelijk Gewest"@nl . + "946879f1-7f6f-4a1e-9aa3-e6ed9aefc64a" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Liste des exploitants d'un centre de démontage de véhicules hors d'usage (type A)"@fr . + "Registratie als exploitant van een demonteercentrum voor afgedankte voertuigen (type A)"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBF12EF78E65F7848216C431443A44B41 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBF12EF78E65F7848216C431443A44B41 . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "e505b509-e80e-4fc5-aece-c7ba1904881b" . + "2018-08-24T06:41:53.056477"^^ . + "2018-08-24T06:46:40.046915"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d2810F7DA2C240527467E62232D1848BD . + "Vrijwaringszones UNESCO"@nl . + "Zones de protection UNESCO"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0C32E09AFA9AF06FF3C4C2683A7FA1EF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0C32E09AFA9AF06FF3C4C2683A7FA1EF . + . + "hierarchy - geojson - 2016-01-01" . + . + "Lijst van de scholen in Oudergem"@nl . + "Liste des écoles d'Auderghem"@fr . + "d0bcdbce-045d-4ef9-ae94-289ee2166f2d" . + "2019-02-06T08:16:54.857225"^^ . + "2019-02-12T13:07:11.454692"^^ . + "Scholen in Oudergem"@nl . + "Écoles d'Auderghem"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0B0E38893F0DE96B847B659D42B3DA3E . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d0B0E38893F0DE96B847B659D42B3DA3E . + . + "Bovenop de personen die opgenomen zijn in deze lijst, mag de beoordeling opgesteld conform artikel 2.3.54, §4 van het Brussels Wetboek van Lucht, Klimaat en Energiebeheersing (ordonnantie van 2/05/2013), uitgevoerd worden door personen die erkend zijn als opdrachthouder voor de effectenstudies."@nl . + "En plus des personnes reprises dans cette liste, l'évaluation établie conformément à l'article 2.3.54, §4 du Code bruxellois de l'Air, du Climat et de la Maîtrise de l'Energie (ordonnance du 2/05/2013), peut être exécutée par les personnes qui sont agréées en tant que chargé d'étude d'incidences. "@fr . + "7bf5f5b0-ef38-4f88-b25a-80e92c122cd6" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de opdrachthouders voor de effectenbeoordeling (op het vlak van parkeeraanbod - BWLKE) "@nl . + "Liste des chargés de l'évaluation des incidences (en matière de stationnement - COBRACE)"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5C5D761E780A52799F055978589DF99C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5C5D761E780A52799F055978589DF99C . + . + "Hoeveel werknemers telt het Gewest? In welke sectoren werken zij en voor welk loon? Waar concentreert de Brusselse werkende beroepsbevolking zich? Hoe zit het met de werkloosheidscijfers?" . + "Arbeidsmarkt " . + "Belgique"@fr . + "Espace public"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + . + . + "Lijst van de wekelijkse markten 2018 (jaarlijkse bijwerking)"@nl . + "Liste des marchés hebdomadaires 2018 (mise à jour annuelle)"@fr . + "b02acbb1-7e2b-44a6-bf2c-f1f4f54d221d" . + "2018-08-16T13:14:45.590384"^^ . + "2018-08-16T00:00:00"^^ . + "Marchés hebdomadaires"@fr . + "Wekelijkse markten "@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4A0511EE8516A81E267E0670BFEF106A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4A0511EE8516A81E267E0670BFEF106A . + . + "icr - csv - 2016-01-01" . + . + "Lijsten van de Gemeenteraadsleden"@nl . + "Listes des conseillers communaux"@fr . + "5ed1cc65-e73f-4b23-a903-227ce1142780" . + "2017-08-07T07:15:27.422130"^^ . + "2018-12-14T14:12:27.975815"^^ . + "Conseillers communaux et échevins"@fr . + "Gemeenteraadsleden en Schepenen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF78FD2369E1D412FD3537AD7A087FF93 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF78FD2369E1D412FD3537AD7A087FF93 . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "WFS" . + . + . + "artistic_heritage - geojson - 2016-01-01" . + . + "Espace de Développement Renforcé du Logement et de la Rénovation"@fr . + "Ruimte voor Versterkte Ontwikkeling van de Huisvesting en de Renovatie"@nl . + "9d6fa307-2eed-4015-a7e1-4a16f8c438e1" . + "2018-08-23T10:37:50.884136"^^ . + "2018-08-24T07:53:37.326213"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCA6F9AAE556630DBEF5AB1FE71402409 . + "EDRLR"@fr . + "RVOHR"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC92AC9F4F75EC24B6544AC216A4E9458 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC92AC9F4F75EC24B6544AC216A4E9458 . + . + . + "Brussels Hoofdstedelijk Gewest: Informatie over het aantal vermogensbegrenzers (actief, ingeschakeld, uitgeschakeld en in vervanging), uitgesplitst volgens het vermogen van de begrenzer en het type bescherming. \n\nDe vermogensbegrenzer maakt het mogelijk het elektriciteitsverbruik te beperken volgens 3 vermogensniveaus: \n\n-\t1380 watt (6 ampère): Dit zijn de oude vermogensbegrenzers die werden geplaatst voor de goedkeuring van de wijzigingsordonnantie van 2011 betreffende de organisatie van de elektriciteitsmarkt. De gezinnen kunnen een verhoging vragen tot 10 ampère.\n-\t2300 watt (10 ampère): Dit zijn de vermogensbegrenzers die werden geplaatst in geval van onbetaalde facturen en op aanvraag van de commerciële leverancier. \n-\t4600 watt: Dit is een voorbeeld van een aanvraag tot verhoging van het vermogen van een begrenzer via het OCMW. De wijzigingsordonnantie van 23 juli 2018 voorziet deze maatregel niet meer. Hij werd vervangen door de verwijdering van de begrenzer..\n\nBij drie categorieën huishoudelijke klanten kan een vermogensbegrenzer worden geplaatst: \n\n-\tDe __beschermde klanten__: Het statuut van beschermde klant is een systeem dat werd ingevoerd voor de huishoudelijke klanten die het moeilijk hebben om hun energiefactuur te betalen. Dankzij deze bescherming kunnen gezinnen met betalingsmoeilijkheden de door hun leverancier gevraagde afsluiting vermijden.\nBovendien genieten ze tijdens de duur van de bescherming van een goedkopere energieprijs, die het specifiek sociaal tarief wordt genoemd. Dit tarief is lager dan de tarieven die door de commerciële leveranciers worden toegepast.\n\n-\tDe __winterklanten__: De afsluiting van een huishoudelijke klant, met toelating van de vrederechter, mag niet worden uitgevoerd tussen 1 oktober en 31 maart. Als een commerciële leverancier of de noodleverancier tijdens deze periode de gerechtelijke ontbinding van het contract verkrijgt, moet Sibelga dus de continuïteit van de levering tegen het specifiek sociaal tarief garanderen tot 31 maart, het einde van de winterstop.\nDeze klanten worden bij de “beschermde klanten” gerekend. \n \n-\tDe __niet-beschermde klanten__: In tegenstelling tot de twee voornoemde categorieën, zijn ze niet beschermd tegen afsluiting.\n\nEr zijn 4 soorten labels: \n-\tHet label \"__actief__\" betreft de leveringspunten die over een vermogensbegrenzer in werking beschikken op de laatste dag van de maand. \n\n-\tDe labels \"__ingeschakeld__\" en \"__uitgeschakeld__\": Een vermogensbegrenzer wordt als \"ingeschakeld\" beschouwd als hij in werking (actief) is op het leveringspunt terwijl er voor de voorgaande periode geen begrenzer in werking (actief) was op datzelfde leveringspunt. Omgekeerd wordt een vermogensbegrenzer als \"uitgeschakeld\" beschouwd als hij niet meer in werking (actief) is op het leveringspunt terwijl er voor de voorgaande periode een begrenzer in werking (actief) was op datzelfde leveringspunt.. \n\n-\tHet label “__vervanging van…__\" moet tegelijk met het veld “vermogensbegrenzer” worden gelezen. Zo is een begrenzer met een vermogen van 1,38 kVA in het veld \"vervanging van …\" vervangen door een vermogensbegrenzer van 2,3 kVA of 4,6 kVA zoals aangegeven in het veld \"vermogensbegrenzer”. \n\nVoor het label \"actief\" wordt een momentopname genomen op de laatste dag van de maand. Voor de labels \"ingeschakeld\", \"uitgeschakeld\" en \"vervanging van…\" worden de bewegingen over een periode van de eerste tot de laatste dag van de betrokken maand in aanmerking genomen. \n\nDe gegevens worden geleverd door Sibelga. De meegedeelde gegevens kunnen van rapport tot rapport verschillen. Dat is het gevolg van het rectificatieproces uitgevoerd door de actoren van de energiesector om de kwaliteit van de gegevens te verbeteren.\n"@nl . + "Brussels-Capital Region: Information on the number of power limiters (active, connected, disconnected and being replaced) broken down according to the power of the limiter and the type of protection. \n\nThe power limiter makes it possible to restrict the electricity consumption according to 3 levels of power: \n\n- \t1380 Watts (6 amps): These are the old power limiters installed before the adoption of the modifying decree of 2011 relating to the organisation of the electricity market. Households can request an increase to 10 amps.\n- \t2300 Watts (10 amps): These are the limiters installed in case of unpaid bills and at the request of the commercial supplier. \n- 4600 Watts: This situation used to correspond to a request to increase the power of a limiter issued by the C.P.A.S. (Public Social Services Centre). The amending order of 23 July 2018 no longer provides for this measure. It is in fact replaced by the simple removal of the limiter. \n\nThree categories of residential customers struggling to pay their bills are affected by the installation of a power limiter: \n\n-\t__Protected customers__: Protected customer status is a protection system set up for residential customers struggling to pay their energy bills. This protection allows households experiencing payment difficulties to avoid the power cut that is requested by their supplier.\nMoreover, during the term of protection, they benefit from a lower energy price called the special social tariff. This tariff is lower than the tariffs charged by commercial suppliers.\n\n\n-\t__Winter truce customers__: A residential customer cannot be disconnected between 1 October and 31 March, even if the disconnection is authorised by a judge of the peace. This means that, if a commercial supplier or the supplier of last resort obtains the legal termination of the contract binding it to its customer during this period, Sibelga must ensure continuity of the supply at the special social rate until 31 March, the end of the winter truce.\nThese customers are accounted for together with \"protected customers\". \n \n-\t__Unprotected customers__: They do not benefit from any protection against the disconnection of their energy supply, unlike the two above-mentioned categories.\n\nThere are 4 types of labels: \n-\tThe \"__active__\" label refers to supply points with a power limiter that is in operation on the last day of the month. \n\n-\tThe \"__connected__\" and \"__disconnected__” labels: A power limiter is considered to be \"connected\" if it is in operation (active) at the point of supply, whereas for the previous period no limiter was active at this point of supply. Conversely, a limiter is considered to be \"disconnected\" if a limiter is no longer in operation (no longer active) at the point of supply, whereas for the previous period a limiter was active at this same point of supply. \n\n-\tThe \"__replacement ...__” label should be read in conjunction with the \"power limiter\" field. Thus a limiter with a power of 1.38 kVA under the \"replacement ...\" field is replaced by a 2.3 kVA or 4.6 kVA power limiter as indicated under the \"power limiter\" field. \n\nFor the \"active\" label an image is taken on the last day of the month. For the \"connected”, \"disconnected\" and \"replacement ...” labels, movement accounting is carried out over a period running from the 1st to the last day of the month in question. \n\nData are provided by Sibelga. The data reported may differ from one report to another. This is the result of the process of rectification used by players in the energy sector in order to improve the quality of the data."@en . + "Région de Bruxelles-Capitale : Information sur le nombre de limiteurs de puissance (actifs, branchés, débranchés et en remplacement) ventilée selon la puissance du limiteur et le type de protection. \n\nLe limiteur de puissance permet de restreindre la consommation d’électricité selon 3 niveaux de puissance : \n\n-\t1380 Watts (6 ampères) : Ce sont les anciens limiteurs de puissance posés avant l’adoption de l’ordonnance modificatrice de 2011 relative à l’organisation du marché de l’électricité. Les ménages peuvent demander le rehaussement à 10 ampères.\n-\t2300 Watts (10 ampères) : Ce sont les limiteurs placés en cas d’impayés et à la demande du fournisseur commercial. \n-\t4600 Watts : Ce cas de figure correspondait à une demande de rehaussement de la puissance d’un limiteur de la part du C.P.A.S. L’ordonnance modificatrice du 23 juillet 2018 ne prévoit plus cette mesure. En effet, elle est remplacée par un enlèvement pure et simple du limiteur.\n\n\nTrois catégories de clients résidentiels en difficulté de paiement sont concernées par la pose d’un limiteur de puissance : \n\n-\tLes __clients protégés__ : Le statut de client protégé est un système de protection mis en place pour les clients résidentiels qui ont des difficultés de paiement de leur facture d’énergie. Cette protection permet aux ménages en difficulté de paiement d’éviter une coupure d’énergie demandée par leur fournisseur.\nDe plus, pendant la durée de la protection, ils bénéficient du prix de l’énergie plus avantageux qu’on appelle le tarif social spécifique. Ce tarif est moins élevé que les tarifs appliqués par les fournisseurs commerciaux.\n\n\n-\tLes __clients hivernaux__: La coupure d’un client résidentiel autorisée par le juge de paix ne peut être mise à exécution entre le 1er octobre et le 31 mars. Ainsi, si un fournisseur commercial ou le fournisseur de dernier ressort obtient pendant cette période la résiliation judiciaire du contrat le liant à son client, Sibelga doit assurer la continuité de la fourniture au tarif social spécifique jusqu’au 31 mars, fin de la trêve hivernale.\nCes clients sont comptabilisés avec les « clients protégés ». \n \n-\tLes __clients non protégés__ : Ils ne bénéficient d’aucune protection contre la coupure d’énergie contrairement aux deux catégories précitées.\n\n\nIl y a 4 types de labels : \n-\tLe label \"__actif__\" concerne les points de fourniture disposant d’un limiteur de puissance en fonction au dernier jour du mois. \n\n-\tLes Labels \"__branché__\" et \"__débranché__\" : Un limiteur de puissance est considéré comme \"branché\" s’il est en fonction (actif) sur le point de fourniture alors que pour la période précédente aucun limiteur n’était en fonction (actif) sur ce même point de fourniture. A l'inverse, un limiteur est considéré comme \"débranché\" si un limiteur n’est plus en fonction (n’est plus actif) sur le point de fourniture alors que pour la période précédente, un limiteur était en fonction (actif) pour ce même point de fourniture. \n\n-\tLe label « __remplacement de…__» doit être lu parallèlement au champ « puissance limiteur ». Ainsi, un limiteur d’une puissance de 1.38 kVA sous le champ « remplacement de … » est remplacé par un limiteur de puissance de 2.3 kVA ou de 4.6 kVA tel qu’indiqué sous le champ « puissance limiteur». \n\nPour le label « actif » une image est prise au dernier jour du mois. Pour les labels « branché », « débranché » et en « remplacement de… » une comptabilisation des mouvements est réalisée sur une période allant du 1er au dernier jour du mois considéré. \n\nLes données sont fournies par Sibelga. Les données communiquées peuvent différer d'un rapport à l'autre. Ceci résulte du processus de rectification établi par les acteurs du secteur de l'énergie afin d'améliorer la qualité des données.\n"@fr . + "d6616f03-4f49-4718-9b50-bafbbfbddd28" . + "2017-11-29T00:00:00"^^ . + "2018-04-20T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4E36B081E1F68290399CD29F821A4DD3 . + "Limiteurs de puissance"@fr . + "Power limiters"@en . + "Vermogensbegrenzers"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9D6ECEEC68F4256A0276CC7D01AE8A5D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d9D6ECEEC68F4256A0276CC7D01AE8A5D . + . + "Combien de travailleurs compte la Région ? Dans quels secteurs d'activité sont-ils actifs et pour quel salaire ? Où se retrouve la population active occupée bruxelloise ? Qu'en est-il des chiffres du chômage ?" . + "Marché du travail" . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Brussels Jazz Marathon Wheelchair Users" . + . + . + . + "Cette liste est non exhaustive. Certaines installations de collecte et/ou de traitement de déchets ont un accès limité, nous vous conseillons de contacter l’installation avant de vous y rendre afin de vérifier que vos déchets y seront bien acceptés. "@fr . + "Deze lijst is niet-exhaustief. Sommige inzamel- en verwerkingsinrichtingen hebben een beperkte toegang, dus we raden u aan vooraf de inrichting te contacteren zodat u weet of uw afval geaccepteerd zal worden."@nl . + "a8f330c9-0426-49a1-b9c3-04ef3bc3679c" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de vergunde inzamel- en verwerkingsinrichtingen van afvalstoffen in BHG "@nl . + "Liste des installations de collecte et de traitement de déchets (autorisées en RBC)"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d66C8CC10117D818ED30650176B6E1DBC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d66C8CC10117D818ED30650176B6E1DBC . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "bassin hydrographique"@fr . + "bassin versant"@fr . + "eau de surface"@fr . + . + . + "Ce fichier comprend tous les projets d'innovation acceptés par Innoviris et situés dans la période 2015-2017. Les variables suivantes ont été ajoutées: année, programme, titre du projet, nom du bénéficiaire, et montant."@fr . + "Deze dataset omvat alle door Innoviris geaccepteerde innovatieprojecten gesitueerd in de tijdsperiode 2015-2017. De volgende variabelen zijn toegevoegd: jaar, programma, titel project, naam begunstigde, en bedrag."@nl . + "This dataset includes all Innoviris' accepted innovation projects situated in the time period 2015-2017. The following variables are added: year, program, title project, name beneficiary, and amount. "@en . + "98ec97dc-7e6f-43aa-90b2-3519ae5a58b7" . + "2018-12-20T00:00:00"^^ . + "2018-12-20T09:43:54.475046"^^ . + . + "Innoviris - Accepted projects 2015-2017"@en . + "Innoviris - Geaccepteerde projecten 2015-2017"@nl . + "Innoviris - Projets acceptés 2015-2017"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB5D2A066B2D1B497ED32707F7660583B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB5D2A066B2D1B497ED32707F7660583B . + "CSV NL" . + . + "Lijst van gemeentelijke bibliotheken (jaarlijkse bijwerking)"@nl . + "Liste des bibliothèques communales (mise à jour annuelle)"@fr . + "5ce66361-5745-4b6a-a20a-d5feb443e6c5" . + "2018-08-16T12:45:54.906345"^^ . + "2018-08-16T00:00:00"^^ . + "Bibliothèques communales"@fr . + "Gemeentelijke bibliotheken"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d21F0720EEB06598DCCDBD33C490D5CE9 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d21F0720EEB06598DCCDBD33C490D5CE9 . + . + "regional_roads - csv - 2016-01-01" . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "environnement"@fr . + "hydrographie"@fr . + . + "rer_velo - csv - 2016-01-01" . + . + "De gasleveranciers in het Brussels Hoofdstedelijk Gewest "@nl . + "Liste des fournisseurs de gaz en Région de Bruxelles-Capitale "@fr . + "2cc25d50-5029-4ad7-bd06-6d0252195d09" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de gasleveranciers in het Brussels Hoofdstedelijk Gewest "@nl . + "Liste des fournisseurs de gaz en Région de Bruxelles-Capitale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d65C84745985BCE9A84593A7C8D9312AD . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d65C84745985BCE9A84593A7C8D9312AD . + "déchet"@fr . + "environnement"@fr . + . + "Projets des Contrats de Quartiers Durables."@fr . + "119df370-a159-46c8-a627-ffdcbab68e9e" . + "2018-08-23T08:23:23.176587"^^ . + "2018-08-23T11:24:33.311874"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dECF89F39C3C9CE4DA4C603C8367716D8 . + "Contrats de Quartiers Durables (Projets)"@fr . + "Duurzame Wijkcontracten (Projecten)"@nl . + "Sustainable Neighbourhood Contracts (Projects)"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7D9312F11CE8E15B0B4126CE264FE0BF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7D9312F11CE8E15B0B4126CE264FE0BF . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + . + . + "Parkeren voor inrij van eigendom in het Brussels Hoofdstedelijk Gewest"@nl . + "Parking in front of access road in Brussels-Capital Region"@en . + "Stationnement devant accès carrossable en Région de Bruxelles-Capitale"@fr . + "3602f144-3119-4241-b9ad-d21223541321" . + "2018-10-23T07:31:00.107756"^^ . + "2018-10-23T07:34:25.922507"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d07084B5259D1F169BC0067EA4B3878FC . + "Parkeren voor inrij van eigendom"@nl . + "Parking in front of access road"@en . + "Stationnement devant accès carrossable"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7B4451505D7D8168810BFA33783C81E4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B4451505D7D8168810BFA33783C81E4 . + . + "Lijst van de Kinderopvangplaatsen in Oudergem"@nl . + "Liste des crèches d'Auderghem"@fr . + "f4f60a03-b60d-444e-b69e-452ab966b3ac" . + "2019-02-06T07:58:13.829117"^^ . + "2019-02-12T13:07:30.221799"^^ . + "Crèches d'Auderghem"@fr . + "Kinderopvangplaatsen in Oudergem"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBFB2BD9481D9A7A050F3C9C32DEBE83B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBFB2BD9481D9A7A050F3C9C32DEBE83B . + . + "Bibliographic references on gender equality in Brussels. \nFrom the library catalogue of Amazone, Crossroads for Gender Equality" . + "JSON File" . + . + "La commune de Saint-gilles vit au rythme de divers marchés hebdomadaires répartis sur le territoire de la commune. Les marchés du Midi, du Parvis, de la Place Van Meenen et le marché de la Place Horta présentent chacun leurs propres produits, horaires et coûts d’installation pour les exposants. "@fr . + "2572013f-ee37-4f3b-9342-1ad8f0c2394f" . + "2018-11-13T00:00:00"^^ . + "2018-11-13T00:00:00"^^ . + "Marchés"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d84547D4D31DB4E4BBEAA4707EA2813E2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d84547D4D31DB4E4BBEAA4707EA2813E2 . + . + "Belgique"@fr . + "Harvested"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "air"@fr . + "environnement"@fr . + "qualité de l'air"@fr . + "station de surveillance"@fr . + . + "Het aantal fietsers wordt geteld op verschillende plaatsen in het Brussels Hoofdstedelijk Gewest. De komende jaren zullen er nog verschillende extra tellers geplaatst worden. Om toegang te hebben tot de data kunt u gebruik maken van de API en de geowebservices. Er is zowel Real Time data als historische data beschikbaar.\n\n__API: https://data-mobility.brussels/bike/api/counts/__\n\nLink naar de tellingen op kaart: [Mobigis](http://data-mobility.brussels/mobigis/fr/?x=485352.9798586729&y=6593040.226464508&zoom=12&baselayer=mapquest&layers=rt_counting%3B&opacity=1%3B&filter=#)\n\n[![img mobigis extract](http://data-mobility.brussels/static/bike/bike_counting_pole.jpg)] (http://data-mobility.brussels/mobigis/fr/?x=485352.9798586729&y=6593040.226464508&zoom=12&baselayer=mapquest&layers=rt_counting%3B&opacity=1%3B&filter=#)"@nl . + "Le nombre de cyclistes est compté à différents endroits dans la Région de Bruxelles Capitale. D'autres compteurs seront ajoutés durant les prochaines années. Vous pouvez utiliser l’API ou les services cartographiques pour accéder aux données. Des données en Real Time et des données historiques sont disponibles.\n\n__API: https://data-mobility.brussels/bike/api/counts/__\n\nLien vers les comptages en carte: [Mobigis](http://data-mobility.brussels/mobigis/fr/?x=485352.9798586729&y=6593040.226464508&zoom=12&baselayer=mapquest&layers=rt_counting%3B&opacity=1%3B&filter=#)\n\n[![img mobigis extract](http://data-mobility.brussels/static/bike/bike_counting_pole.jpg)] (http://data-mobility.brussels/mobigis/fr/?x=485352.9798586729&y=6593040.226464508&zoom=12&baselayer=mapquest&layers=rt_counting%3B&opacity=1%3B&filter=#)"@fr . + "The number of bikers are counted on several locations in the Brussels Region. The number of counters will be extended in the next years. To access the data, you can use the API or the geowebservices. Real time and historical data are available.\n\n__API: https://data-mobility.brussels/bike/api/counts/__\n\nDirect link to the countings on map: [Mobigis](http://data-mobility.brussels/mobigis/fr/?x=485352.9798586729&y=6593040.226464508&zoom=12&baselayer=mapquest&layers=rt_counting%3B&opacity=1%3B&filter=#)\n\n[![img mobigis extract](http://data-mobility.brussels/static/bike/bike_counting_pole.jpg)] (http://data-mobility.brussels/mobigis/fr/?x=485352.9798586729&y=6593040.226464508&zoom=12&baselayer=mapquest&layers=rt_counting%3B&opacity=1%3B&filter=#)"@en . + "d4961387-23f4-44f8-b2e7-c7e9a260a83e" . + "2017-09-12T12:37:07.725054"^^ . + "2019-03-05T12:22:53.745624"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0B1692FA5D6921FED89D9C2256FA71F8 . + "Bike counting poles"@en . + "Compteurs Vélo"@fr . + "Telpaal fietsen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d04E7157DE94B76D39C7A8E68066591C4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d04E7157DE94B76D39C7A8E68066591C4 . + "signalisation"@fr . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + "Car Sharing"@fr . + "Reporting Inspire"@fr . + "Région de Bruxelles-Capitale"@fr . + "environnement"@fr . + "installation à haut risque"@fr . + "législation en matière d'environnement"@fr . + "Belgique"@fr . + "site industriel"@fr . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_seveso_area&SRSNAME=EPSG:31370" . + . + . + "De elektriciteitsleveranciers in het Brussels Hoofdstedelijk Gewest"@nl . + "Fournisseurs d'électricité en Région de Bruxelles-Capitale"@fr . + "4c819c9b-5a16-489e-82b2-8a6685366f92" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de elektriciteitsleveranciers in het Brussels Hoofdstedelijk Gewest"@nl . + "Liste des fournisseurs d'électricité en Région de Bruxelles-Capitale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4786B3B2A524A1841D7D277A4B5B787F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4786B3B2A524A1841D7D277A4B5B787F . + . + "You will find an overview of public finance in the Brussels Region: the revenue and expenditure of the Brussels Regional Public Service, the local authorities and para-regional bodies, and the statistics on the outstanding debt of the Region.\n" . + "Public finance " . + . + "5a1d8902-91dd-4add-b1a9-1398f1a00ce7" . + "2018-11-10T00:00:00"^^ . + "2018-11-10T00:00:00"^^ . + "Masterplan"@fr . + "Masterplan"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7CB0731BC5A077DD6F04D041B3EA0953 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7CB0731BC5A077DD6F04D041B3EA0953 . + . + "De lijst van de erkende sociale economieprojecten die werknemers aanvaarden \"artikel 60 §7 - sociale economie\"."@nl . + "La liste des projets d'économie sociale reconnus susceptibles d'accueillir des travailleurs « article 60 §7 – économie sociale »."@fr . + "2e07119c-d061-40db-a535-3b88efe6c1ff" . + "2018-12-13T00:00:00"^^ . + "2018-12-13T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE373CAE178BD99E5056F38D325A666E1 . + "Projecten \"artikel 60 §7 - sociale economie\""@nl . + "Projects « article 60 §7 – social economy »"@en . + "Projets « article 60 §7 – économie sociale »"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d930F0717549000DF4BD2F30F75558791 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d930F0717549000DF4BD2F30F75558791 . + . + "La commune de Saint-Gilles accueille 2 bibliothèques sur son territoire ; une francophone et une néerlandophone présentant chacune divers services et activités aux citoyens.\nCes bibliothèques ainsi que leurs adresses, prix, heures d'ouverture et données statistiques sont disponibles dans ce jeu de données.\n\nHistorique :\nLéguée en 1970 par la famille Hoguet à la Commune de Saint-Gilles à la seule condition de servir à des fins culturelles, la maison Hoguet est occupée dans un premier temps par l’Académie de Musique. En 1997, le bâtiment est consacré à une politique visant notamment à une amélioration de la maîtrise du français. Il accueille donc désormais en ses lieux l’association la « Maison du Livre » et l’actuelle bibliothèque communale de Saint-Gilles.\nDepuis sa construction en 1928, la maison Hoguet a été restaurée à 2 reprises et conserve encore aujourd’hui sa façade d’origine de style art déco classée en 1938.\nDepuis janvier 2009, la commune de Saint-Gilles dispose d’une bibliothèque communale néerlandophone au n°173 de la rue Emile Féron. Rénovée en 2017, celle-ci occupe au total 750 m² et dispose, en plus du grand espace réservé aux livres, d’une salle d’exposition ou polyvalente. \n"@fr . + "f4049757-f13c-4c8f-9675-e599d3218ba7" . + "2018-11-06T00:00:00"^^ . + "2018-11-06T00:00:00"^^ . + "Bibliothèques"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4B578D98233B7CD59C58F982A70EDA06 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4B578D98233B7CD59C58F982A70EDA06 . + . + "(Données limitées à 1000 pour des raisons de performance. Voir &maxFeatures=1000)" . + "WFS" . + "Harvested"@fr . + "Belgique"@fr . + "Belgium"@fr . + "Brussel"@fr . + "Caractéristiques emploi"@fr . + "België"@fr . + "Chômage"@fr . + "Domestic employment"@fr . + "Employment characteristics"@fr . + "Kenmerken van de tewerkstelling"@fr . + "Regionale economische vooruitzichten"@fr . + "Sexe"@fr . + "Unemployment"@fr . + "Working-age population"@fr . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Bureaux d'étude agréés dans la discipline « installations de stockage » (stations‑service) en Région de Bruxelles-Capitale"@fr . + "De erkende studiebureaus op het vlak van “opslaginstallaties” (benzinestations) in het Brussels Hoofdstedelijk Gewest"@nl . + "30a0587a-c2cc-4556-ab3e-2f2f24095f38" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de erkende studiebureaus op het vlak van “opslaginstallaties” (benzinestations) in het Brussels Hoofdstedelijk Gewest"@nl . + "Liste des bureaux d'étude agréés dans la discipline « installations de stockage » (stations‑service) en Région de Bruxelles-Capitale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAE358BB0B4FC69794FB3C2C9BD3069E4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE358BB0B4FC69794FB3C2C9BD3069E4 . + . + . + "Ce rapport reprend les objectifs du plan bruxellois de simplification administrative 2015-2020 et l’état d’avancement de celui-ci."@fr . + "Dit verslag vervat de doelstellingen van het Brusselse plan voor administratieve vereenvoudiging 2015-2020 en de staat van voortgang ter zake."@nl . + "This report contains the objectives of the Brussels administrative simplification plan 2015-2020 and the state of progress in this area."@en . + "0706ba52-1867-4255-926f-3a5f3790f33d" . + "2018-05-22T00:00:00"^^ . + "2018-08-21T00:00:00"^^ . + . + "Annual report Easybrussels"@en . + "Jaarverslag Easybrussels"@nl . + "Rapport annuel Easybrussels"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d2915FE56A67014F452831C4FEA83BAC2 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d2915FE56A67014F452831C4FEA83BAC2 . + . + "Dit thema biedt zeer uitgebreide informatie over de bodembezetting, de kenmerken van de bestaande bebouwde oppervlakte en de verkoop van vastgoed." . + "Ruimtelijke ordening en vastgoed" . + . + . + "Infrastructures sportives (mise à jour annuelle)"@fr . + "Sportinfrastructuur (jaarlijkse bijwerking)"@nl . + "698f80dc-dfa9-40a7-95f4-461bdd6c287f" . + "2018-08-16T06:57:20.848053"^^ . + "2018-08-16T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC9940906CA143F5014C12574324BDC68 . + "Infrastructures sportives"@fr . + "Sportinfrastructuur"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC66892A1896E5A1E39D95582132ECAED . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC66892A1896E5A1E39D95582132ECAED . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_noise_monitoring_stations" . + "Population active occupée au lieu de résidence"@fr . + "Population en âge de travailler"@fr . + "Werkloosheid"@fr . + "Working population at place of residence"@fr . + . + "The enclosed file shows the data from a panel of 20 guided tours organisations. See the full interactive barometer via the following link: https://visit.brussels/en/article/tourism-barometer-of-the-brussels-capital-region\n\nPlease note that some organisations don't provide all of their data. Therefore, there are several panels according to the figures at our disposal."@en . + "27614a38-4700-4cb5-8d0a-a22d5e03ace9" . + "2017-10-10T12:33:13.824455"^^ . + "2018-09-12T13:41:35.547346"^^ . + . + "Guided Tours"@en . + "Rondleidingen"@nl . + "Visites Guidées"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3D63891F27CA83732EC6F76E0C532FA0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3D63891F27CA83732EC6F76E0C532FA0 . + . + "Brussels Hoofdstedelijk Gewest: Informatie over het aantal deactiveringen uitgevoerd door de distributienetbeheerder (“DNB” of “Sibelga”). \n\nBlad “__deactiveringen__” - De deactiveringen worden gekenmerkt door de overgang van de status van een leveringspunt van actief naar inactief. Ze omvatten vijf procedures:\n\n-\tDe “afsluitingen op bevel van de vrederechter” (waarvan het aantal is vermeld op blad 2, in tegenstelling tot de andere soorten deactiveringen);\n-\tDe \"Moza\" die eindigen met een afsluiting: dit is een procedure die wordt opgestart door de leverancier om een leveringspunt af te sluiten wanneer zijn klant het leveringspunt verlaat en hij geen aanvraag voor een leveringscontract heeft ontvangen van een eventuele nieuwe bewoner;\n-\tDe \"End of contract\": dit is een deactivering uitgevoerd op vraag van de leverancier wanneer het energiecontract dat hem aan de klant bindt, afloopt;\n-\tDe \"Move out\" : dit is een deactivering van de elektriciteits-/gasaansluiting op aanvraag van de marktactoren;\n-\tDe “drop” scenario’s: een afsluiting na betalingsachterstand van een professionele klant.\n\nBlad \"__Vrederechter__” - De deactiveringen die op bevel van de vrederechter worden uitgevoerd na een aanvraag tot ontbinding van het energiecontract bij de vrederechter. De aanvraag wordt ingediend door de leverancier wanneer hij vaststel dat de klant de facturen niet betaalt. Bij de afsluitingen wordt vermeld of de klant al of niet van de winterstop heeft genoten. We onderscheiden bijgevolg: \n\n-\tDe \"afsluitingen op bevel van de vrederechter\" die worden uitgevoerd buiten de periode van de winterstop, tussen 1 april en 30 september. \n-\tDe \"afsluitingen op bevel van de vrederechter\" die betrekking hebben op de klanten die van de periode van de winterstop hebben genoten. Aangezien de afsluitingen, hoewel ze werden bevolen, niet kunnen worden uitgevoerd tijdens de winterperiode, worden deze klanten tijdens die periode bevoorraad door Sibelga en worden ze “winterklanten” genoemd. Hun energie wordt dus effectief afgesloten vanaf 1 april;\n\nDe afsluitingen op bevel van de vrederechter zijn vermeld in het vorige blad. \n\nBlad \"__VZC__\" - Het verbruik buiten contract heeft betrekking op de leveringspunten die een verbruik registreren hoewel ze als inactief zijn geregistreerd bij Sibelga. De DNB verzegelt dan het toegangspunt als het verbruik niet is gedekt door een energiecontract. Het leveringspunt werd als inactief beschouwd en wordt, na tussenkomst van de DNB, effectief inactief. \n\nHet meegedeelde aantal is een momentopname van de laatste dag van de maand. \n\nDe gegevens worden geleverd door Sibelga. De meegedeelde gegevens kunnen van rapport tot rapport verschillen. Dat is het gevolg van het rectificatieproces uitgevoerd door de actoren van de energiesector om de kwaliteit van de gegevens te verbeteren.\n"@nl . + "Brussels-Capital Region: Information on the number of deactivations performed by the distribution network operator (\"DNO\" or \"Sibelga\"). \n\n« __désactivations__ » of “__deactiveringen__” sheet - Deactivations are characterised by the transition from an active to an inactive point of supply. They cover five procedures:\n\n-\t\"Disconnections by order of the justice of the peace\" (the number of which is detailed in sheet 2, unlike other types of deactivation);\n-\t\"Moza\" which end with a cut-off: This is a procedure initiated by the supplier in order to close a supply point as soon as its customer leaves the supply point and has not received any request for a supply contract from a potential new occupant;\n-\t“End of contract”: This is a deactivation carried out at the request of the supplier when the energy contract that binds it to the customer comes to an end;\n-\t“Move out “: This is a deactivation of the electrical/gas connection at the request of the market players;\n-\t“Drops” scenarios: This is a cut-off following non-payment by a professional customer.\n\n \"__Juge de paix__ \" of \"__Vrederechter__” sheet - Deactivations by order of the justice of the peace come after a request for termination of the energy contract to the justice of the peace. This request is submitted by the supplier in the wake of the customer's failure to pay the bills. The cut-offs are detailed according to whether the customer has been granted winter truce status or not. This makes it possible to make a distinction between: \n\n-\t\"Cut-offs by order of the justice of the peace” that occur outside the winter truce period between 1 April and 30 September. \n-\t\"Cut-offs by order of the justice of the peace - winter\" for customers who have been granted winter truce status. Since cut-offs, although decided, cannot be executed during the winter period, these customers are supplied by Sibelga during this period and are referred to as \"winter truce customers\". The effective cut-off of their energy therefore takes place from 1 April;\n\nCut-offs by order of the justice of the peace are described in the previous sheet.\n\n\"__CHC__” of “__VZC__\" sheet - Non-contractual consumption refers to points of supply that record consumption even though they are listed as inactive with Sibelga. Once this is found, the DNO seals the access point if the recorded consumption is not covered by an energy contract. The point of supply was deemed to be inactive and, after the intervention of the DNO, becomes inactive in practice. \n\nThe number communicated is an image taken on the last day of the month. \n\nData are provided by Sibelga. The data reported may differ from one report to another. This is the result of the process of rectification used by players in the energy sector in order to improve the quality of the data."@en . + "Région de Bruxelles-Capitale : Information sur le nombre de désactivations exécutées par le gestionnaire du réseau de distribution (« GRD » ou « Sibelga »). \n\nFeuille « __désactivations__» - Les désactivations sont caractérisées par le passage du statut d’un point de fourniture de l’actif vers l’inactif. Elles englobent cinq procédures :\n\n-\tLes « coupures sur ordre du juge de paix» (dont le nombre est détaillé dans la feuille 2, contrairement aux autres types de désactivations) ;\n-\tLes « Moza » qui se concluent par une coupure : Il s’agit d’une procédure lancée par le fournisseur afin de fermer un point de fourniture dès lors que son client quitte le point de fourniture et qu'il n'a reçu aucune demande de contrat de fourniture de la part d'un éventuel nouvel occupant ;\n-\tLes « End of contract » : Il s’agit d’une désactivation réalisée à la demande du fournisseur lorsque le contrat d’énergie qui le lie au client arrive à terme ;\n-\tLes « Move out » : Il s’agit d’une désactivation du raccordement électrique/gaz à la demande des acteurs du marché;\n-\tLes scénarios « drops » : Il s’agit d’une coupure suite à un défaut de paiement qui concerne un client professionnel.\n\nFeuille « __Juge de paix__» - Les désactivations sur ordre du juge de paix interviennent à la suite d’une demande de résiliation du contrat d’énergie auprès du juge de paix. Cette demande est introduite par le fournisseur suite au constat de non-paiement des factures par le client. Les coupures sont détaillées selon que le client ait bénéficié de la trêve hivernale ou pas. Dès lors, on distingue : \n\n-\tLes « coupures sur ordre du juge de paix » qui sont effectuées en dehors de la période de trêve hivernale, entre le 1er avril et le 30 septembre. \n-\tLes « coupures sur ordre du juge de paix – hivernal» qui concernent les clients ayant bénéficié de la période de trêve hivernale. Etant donné que les coupures, bien que décidées, ne peuvent être exécutées pendant la période hivernale, ces clients sont alimentés par Sibelga pendant cette période et ils sont appelés «clients hivernaux». La coupure effective de leur énergie intervient donc à partir du 1er avril ;\n\nLes coupures sur ordre du juge sont reprises dans la feuille précédente.\n\nFeuille « __CHC__ » - Les consommations hors contrat concernent des points de fourniture qui enregistrent une consommation bien qu’ils soient répertoriés comme inactifs auprès de Sibelga. Dès lors, le GRD scelle le point d’accès si la consommation enregistrée n’est pas couverte par un contrat d’énergie. Le point de fourniture était réputé inactif et, après l’intervention du GRD, devient réellement inactif. \n\nLe nombre communiqué est une image prise au dernier jour du mois. \n\nLes données sont fournies par Sibelga. Les données communiquées peuvent différer d'un rapport à l'autre. Ceci résulte du processus de rectification établi par les acteurs du secteur de l'énergie afin d'améliorer la qualité des données.\n"@fr . + "40318c4a-a7c7-481b-b416-5d35636c4cc4" . + "2017-11-29T00:00:00"^^ . + "2018-05-08T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9A7A7A669CEF5DB6A33CF50B16EF4C53 . + "Deactivations"@en . + "Deactiveringen"@nl . + "Désactivations"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d813AF87EE3691B319FF77DD20808DC93 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d813AF87EE3691B319FF77DD20808DC93 . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_monitoring_groundwater_level" . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Preview" . + . + . + "wifi.brussels est un réseau internet wifi gratuit, couvrant certaines zones du territoire de la Région de Bruxelles-Capitale. Info : www.wifi.brussels"@en . + "wifi.brussels est un réseau internet wifi gratuit, couvrant certaines zones du territoire de la Région de Bruxelles-Capitale. Info : www.wifi.brussels"@fr . + "wifi.brussels is een gratis wifinetwerk dat in bepaalde zones van het Brussels Hoofdstedelijk Gewest beschikbaar is. Info : www.wifi.brussels"@nl . + "56345c96-d458-41b2-950f-13f5849cbea9" . + "2016-01-27T09:37:32.906918"^^ . + "2017-10-05T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d362D1DE2134A3B705FD485B05348DBEE . + " Hotspots wifi.brussels"@en . + " Hotspots wifi.brussels"@fr . + " Hotspots wifi.brussels"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCB8C9776E516BCC67F03250592356723 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCB8C9776E516BCC67F03250592356723 . + . + "The BISA compiles data on the results of the different elections held in the Brussels-Capital Region.\nYou will find the results of Regional, local, European and legislative elections held in the Brussels-Capital Region.\n" . + "Elections " . + "Bevolking op beroepsactieve leeftijd"@fr . + "Binnenlandse werkgelegenheid"@fr . + "Brussels"@fr . + "Bruxelles"@fr . + "Emploi intérieur"@fr . + "Geslacht"@fr . + "Regional economic prospects"@fr . + "Sex"@fr . + "Statistics"@fr . + "Statistieken"@fr . + "Statistiques"@fr . + "Werkende beroepsbevolking per woonplaats"@fr . + . + "Ouverte en 1905, la piscine communale Victor Boin présente dispose d’un bassin de 33m/13 entouré de cabines individuelles, d’installations d’hydrothérapie unique en Belgique et d’une cafétéria. Après 1 an de fermeture pour rénovation, la piscine rouvre ses portes au public en début 2018.\n\nAncienne distillerie datant de 1881, l’actuel Centre sportif communal situé au 41 Rue de Russie met plusieurs salles à disposition de ses membres pour la location. Celles-ci incluent un grand hall, un petit hall et une salle d’arts martiaux. Le bâtiment dispose également d’une salle de fitness, une salle d’escalade et une salle de psychomotricité. Des activités pour adultes sont également dispensées par l’asbl Saint-Gilles Sport.\n\nAu n°40 de la Rue du Métal, le gymnase « Métal » est un endroit incontournable de la pratique gymnique. Cette salle dispose de nombreux instruments de gymnastique pour gymnastes débutants et confirmés. \n\nÀ proximité de la station de métro Erasme, la plaine des sports Corneille Barca dispose de 3 terrains de football, 6 terrains de tennis (dont 3 couverts) et 12 vestiaires. Un club house et des jeux pour enfants font également partie de ces installations.\n"@fr . + "515a939c-ceef-4d25-8403-ca9c37d0caa9" . + "2018-10-01T00:00:00"^^ . + "2018-10-01T00:00:00"^^ . + "Infrastructures sportives"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dA0CC1F2B7FDF3770403B89540D78DC94 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dA0CC1F2B7FDF3770403B89540D78DC94 . + . + "Lijst van culturele verenigingen (gesubsidieerd door de gemeente) op 30 september 2017 (jaarlijkse bijwerking)"@nl . + "Liste des associations culturelles (subsidiées par la commune) au 30 septembre 2017 (mise à jour annuelle)"@fr . + "6546248f-1411-4b2c-ac01-9db8fcf3d6f8" . + "2018-08-16T13:07:51.284436"^^ . + "2018-08-16T00:00:00"^^ . + "Associations culturelles (subsidiées)"@fr . + "Culturele verenigingen (gesubsidieerd)"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF844520D4A221D034F83C2BA8DAEC97C . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF844520D4A221D034F83C2BA8DAEC97C . + . + "Bibliographic references on gender equality in Brussels. \nFrom the library catalogue of Amazone, Crossroads for Gender Equality" . + "XML File" . + . + . + . + . + "Het activiteitenverslag is een verslag die de Archiefdienst jaarlijks bezorgt aan het Brussels Hoofdstedelijk Parlement. Overeenkomstig aan artikel 9 van de Ordonnantie betreffende de archieven van het Brussels Hoofdstedelijk Gewest bevat het verslag ten minste een uiteenzetting over de organisatie en het administratief beheer van de archiefbescheiden, de staat van de stukken en van de infrastructuur en de opsomming van de aanwinsten."@nl . + "Le rapport d'activités est un rapport que le Service des Archives transmet annuellement au Parlement de la Région de Bruxelles-Capitale. Conformément à l'article 9 de l'Ordonnance relative aux archives de la Région de Bruxelles-Capitale ce rapport comporte au moins un exposé relatif à l'organisation et à la gestion administrative des archives, à l'état des documents et des infrastructures ainsi qu'à l'énumération des acquisitions."@fr . + "bdc434f2-a21c-4006-aab7-6b1c14fa4da5" . + "2019-02-12T00:00:00"^^ . + "2019-02-12T00:00:00"^^ . + "Activiteitenverslag"@nl . + "Rapport d'activités"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d203888C329EFCDA286B0CCAEEFD862EA . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d203888C329EFCDA286B0CCAEEFD862EA . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Liste des défibrillateurs XLS" . + . + . + "traffic lights - MOBIGIS" . + . + "bikeservice - csv - 2016-01-01" . + . + "Het Studieregister is een administratief document dat alle door de administratieve eenheden van de GOB bestelde en bewaarde studies bevat.\nEen studie wordt uitgevoerd op vraag van een administratieve eenheid door een externe natuurlijke of rechtspersoon (consultant, wetenschapper, onderzoeker, onderzoeksinstelling, studiebureau, universiteit...).\nDe bedoeling van een studie is het verbeteren en updaten van de kennis van een administratieve eenheid en van de GOB, dan wel het voeren van een bepaald beleid of actie."@nl . + "Le Registre des études est un document administratif qui reprend l’ensemble des études commandées et conservées par les unités administratives du SPRB.\nUne étude est réalisée à la demande d’une unité administrative par une personne physique ou morale externe (consultant, scientifique, chercheur, institut de recherche, bureau d’étude, université…).\nUne étude a pour but soit d’améliorer et de mettre à jour les connaissances de l’unité administrative et du SPRB, soit de permettre la mise en place d’une politique ou d’une action."@fr . + "d21bc941-cc8f-43d2-adf6-21c398aeef12" . + "2018-02-20T00:00:00"^^ . + "2018-02-20T00:00:00"^^ . + "Registre des études"@fr . + "Studieregister"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d34D10808BF30DFE1A6234634284FA575 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d34D10808BF30DFE1A6234634284FA575 . + . + . + . + . + . + . + . + . + . + . + . + . + . + "c183f4b0-01ac-45b6-903d-e774dd064ab6" . + "2018-12-14T00:00:00"^^ . + "2018-12-14T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5DC62EEA39912626288A135B3526035A . + "Lijst erkende SINE-werkgevers in het Brussels Hoofdstedelijk Gewest."@nl . + "List \"SINE\" employers recognised in the BCR"@en . + "Liste des employeurs reconnus SINE en Région de Bruxelles-Capitale."@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC6046DEDFB0E49DE1AD8C1707AA3EDCB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC6046DEDFB0E49DE1AD8C1707AA3EDCB . + . + . + . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "Périmètres des Contrats de Quartiers Durables."@fr . + "bb5a01db-e025-4177-8153-3ff6e82948a2" . + "2018-08-23T05:56:01.989686"^^ . + "2018-08-23T11:01:18.055479"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD4D7FFA12F6262B33BCEA8F0D163C1D7 . + "Contrats de Quartiers Durables (Programmes)"@fr . + "Duurzame Wijkcontracten (Programma's)"@nl . + "Sustainable Neighbourhood Contracts"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d27AF0D2E61FE64B61AB9A09D3F235C61 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d27AF0D2E61FE64B61AB9A09D3F235C61 . + . + "De toeristische statistieken betreffen overnachtingen van personen die in het Brussels Hoofdstedelijk Gewest verbleven hebben.\nVoor het culturele aspect vindt u statistieken over de bioscoopzalen en de voorstellingen. " . + "Toerisme en cultuur " . + . + "0ee25be6-274e-4eea-94ae-733d86984ce3" . + "2018-08-24T06:27:00.472377"^^ . + "2018-08-24T06:33:42.955555"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD9B6BD2314CE1A58F051691203DFF411 . + "Lotissements"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8B0CB7C05AB135583138AA768106BEDC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8B0CB7C05AB135583138AA768106BEDC . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_natura_2000_station" . + . + "Gestandaardiseerde en geolokaliseerbare informatie verzameld door de dienst Sociaal Brussel over alle publieke en private organisaties die algemene sociale dienstverlening bieden in het Brussels Hoofdstedelijk Gewest. Zie ook: https://sociaal.brussels/sector/53. Licentie: CC-By"@nl . + "Informations standardisées et géolocalisées rassemblées par le service Bruxelles Social, relatives aux organisations publiques et privées qui offrent de l'aide sociale générale en Région de Bruxelles-Capitale. Voir aussi: https://social.brussels/sector/53. Licence: CC-By"@fr . + "ba53cc1b-789a-44f5-9ff4-becbdd310d10" . + "2018-11-27T00:00:00"^^ . + "2018-11-27T00:00:00"^^ . + "Aide sociale générale"@fr . + "Algemene sociale dienstverlening"@nl . + "General social aid"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCED86C842A76384DDF882B65DFACB591 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCED86C842A76384DDF882B65DFACB591 . + . + . + . + . + . + . + . + . + "Export du registre de population. Sexe des personnes enregistrées à la commune d'Auderghem" . + "10/04/2018 - Population - sexe" . + . + . + . + . + "De aardgas- en elektriciteitsmarkten in het Brussels Hoofdstedelijk gewest in cijfers"@nl . + "Les marchés du gaz et de l'électricité en Région de Bruxelles-Capitale en chiffres"@fr . + "dccf3f7e-1abb-4067-b8f0-0a278f3ca5d9" . + "2017-11-29T00:00:00"^^ . + "2018-03-16T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0F29FFFBEBBFC8606D143D87BCF08DF0 . + "Marktstatistieken"@nl . + "Statistiques de marché"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d20746414F0BBF064417566E1E9889C5A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d20746414F0BBF064417566E1E9889C5A . + . + . + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_european_protected_areas" . + . + "taxi_stops - csv - 2016-01-01" . + . + . + "Because there is something for everyone, ideals and budgets, U Talk Freelance has attempted to list coworking spaces in Brussels.\nThey are differentiated by their atypical design, their location, their audience or by their formulas …" . + "http://www.utalkfreelance.be/" . + . + . + "artistic_heritage - csv - 2016-01-01" . + . + "text/xml" . + . + "Hoewel gezondheid onder de bevoegdheid van de gemeenschappen valt, maakt het BISA hier toch een analyse van (aanbod van gezondheidsdiensten, levensverwachting enz.) door gegevens van de Vlaamse Gemeenschap, van de Franse Gemeenschap en van de Gemeenschappelijke Gemeenschapscommissie te combineren." . + "Gezondheid" . + . + "Liste des exploitants d'un centre de destruction et de recyclage de véhicules hors d'usage habilités à délivrer un certificat de destruction (type C) et enregistrés en Région de Bruxelles-Capitale"@fr . + "Registratie als exploitant van een centrum voor de vernietiging en recycling van afgedankte voertuigen dat ertoe gemachtigd is een vernietigingsattest af te geven (type C) "@nl . + "fc7c62f9-c5f1-4b5d-abab-f704306c0769" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Liste des exploitants d'un centre de destruction et de recyclage de véhicules hors d'usage habilités à délivrer un certificat de destruction (type C) et enregistrés en Région de Bruxelles-Capitale"@fr . + "Registratie als exploitant van een centrum voor de vernietiging en recycling van afgedankte voertuigen dat ertoe gemachtigd is een vernietigingsattest af te geven (type C) "@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB59437485B0B68D2073D428F2D6D2961 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dB59437485B0B68D2073D428F2D6D2961 . + . + . + . + . + . + . + . + . + "De opslagcentra van dierlijk afval"@nl . + "Les établissements d'entreposage de déchets animaux"@fr . + "d1374e9d-d6e2-42ae-bf4a-43905b47b25d" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de opslagcentra van dierlijk afval"@nl . + "Liste des établissements d'entreposage de déchets animaux"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d274B4ADCA66AB8397241B609AEA2A9D7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d274B4ADCA66AB8397241B609AEA2A9D7 . + . + "Preview" . + . + . + . + . + . + "Beveiligd fietsparkeren in Brussels Hoofdstedelijk Gewest"@nl . + "Secure bicycle parking in Brussels-Capital Region"@en . + "Stationnement vélo sécurisé en Région de Bruxelles-Capitale"@fr . + "731baa15-9378-4458-8755-78a4664885dd" . + "2018-10-23T06:51:52.153798"^^ . + "2018-10-23T07:34:05.340672"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d55C6A1C0C99A775344142EFAB20012AA . + "Bicycle box"@en . + "Fiets box"@nl . + "Vélo box"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC82FD5C3B36583D6637C3EE942CDF995 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC82FD5C3B36583D6637C3EE942CDF995 . + . + "The statistics gathered by the BISA relate to road traffic, soft mobility, collective transport and shared mobility, freight transport, road safety and mobility practices." . + "Mobility and Transport " . + . + "Lors de sa fondation en 1216, la paroisse de Saint-Gilles disposa d’une église construite sur le site de l’actuel Parvis, et d’un cimetière entourant celle-ci. D’autres cimetières ont également vu le jour afin de pallier à l’exiguïté de la ville de Bruxelles mais ils furent désaffectés à la fin du 18ème siècle.\nAprès plus de 600 ans, le Cimetière du parvis fut transféré dans l’actuelle rue Gisbert Combaz. Toutefois, l’accroissement de la population amena rapidement les autorités communales à acquérir un terrain de près de 2 hectares sur le territoire de la commune d’Uccle où le cimetière emménagea à nouveau. Celui-ci ne tarda pas à fermer au vu de l’instabilité du terrain.\nLe 28 janvier 1895, l’actuel cimetière de Saint-Gilles fut inauguré dans l’avenue du Silence sur un terrain à flanc de colline de 12 hectares.\n"@fr . + "f6e949f5-dedf-4eee-a362-7b0295385fe7" . + "2018-10-01T00:00:00"^^ . + "2018-10-01T00:00:00"^^ . + "Cimetières"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7B992CA34F29349865C57BCD06B0DED0 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7B992CA34F29349865C57BCD06B0DED0 . + . + . + . + . + "Centres de traitement de déchets animaux"@fr . + "De verwerkingscentra van dierlijk afval"@nl . + "4fc41862-ce9c-4b25-b116-7c21d65f6fee" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + " Liste des centres de traitement de déchets animaux "@fr . + "Lijst van de verwerkingscentra van dierlijk afval"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3E395C4EAF7FFBDFA9916363BF88B3FE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3E395C4EAF7FFBDFA9916363BF88B3FE . + . + . + . + . + "Les statistiques présentées portent notamment sur le nombre de milieux d’accueil et la capacité de ceux-ci." . + "Petite enfance" . + . + . + "De installaties die einde afvalfase verkregen hebben volgens gewestelijke Brusselse criteria."@nl . + "Installations ayant obtenu la fin de statut de déchets selon les critères régionaux bruxellois."@fr . + "b6d26aef-55d5-4226-a8f0-feb9274006b5" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de installaties die einde afvalfase verkregen hebben volgens gewestelijke Brusselse criteria "@nl . + "Liste des installations ayant obtenu la fin de statut de déchets selon les critères régionaux bruxellois "@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF176D3E6AB2E51480AF1EC9583D49A91 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF176D3E6AB2E51480AF1EC9583D49A91 . + . + "Preview" . + . + . + . + . + "Lijst van gemeentelijke kinderdagverblijven (jaarlijkse bijwerking)"@nl . + "Liste des crèches communales (mise à jour annuelle)"@fr . + "361f8229-ad8c-4ff8-bdd3-c22df4155300" . + "2018-08-16T12:49:58.342009"^^ . + "2018-08-16T00:00:00"^^ . + "Crèches communales "@fr . + "Gemeentelijke kinderdagverblijven "@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d1A04E759091E88CD34D17FAF87544EEE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1A04E759091E88CD34D17FAF87544EEE . + . + . + . + . + . + . + . + "Collecteurs des déchets animaux"@fr . + "De ophalers van dierlijk afval "@nl . + "267a93be-980e-4026-99f5-8a0da28b1168" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de ophalers van dierlijk afval"@nl . + "Liste des collecteurs de déchets animaux"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d71C100175350EC6CB2AEFDCC89B4A868 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d71C100175350EC6CB2AEFDCC89B4A868 . + . + . + "Zone de police 5340 (Molenbeek-Saint-Jean, Koekelberg, Jette, Ganshoren et Berchem-Sainte-Agathe)" . + . + "Brussels Hoofdstedelijk Gewest : de entiteit « Rail Type » stelt het tracé voor van de spoor- of tramlijnen. Het onderscheid tussen spoor- en tramlijn wordt weergegeven in de naam zelf van de entiteit (RW = Railway en TW = Tramway).\nOok het niveau wordt gepreciseerd in de naam van de entiteit (0 = grondniveau, M = onderliggend niveau en P = bovenliggend niveau)"@nl . + "Brussels-Capital Region : entity \"rail line\" is the track layout railway or tramway. The distinction between rail railway and tramway track is included in the same name of the entity (RW = TW = Railway and Tramway).\nA concept level is also specified in the name of the entity (ground level = 0, M = P = lower level and upper level)"@en . + "Région de Bruxelles-Capitale : l'entité « Ligne de rail » représente le tracé des voies de chemin de fer ou de tramway. La distinction entre rail de chemin de fer et rail de tramway est reprise dans le nom même de l'entité (RW = Railway et TW = Tramway).\nUne notion de niveau est aussi précisée dans le nom de l'entité (0 = niveau du sol, M = niveau inférieur et P = niveau supérieur)"@fr . + "9b7549b1-b463-4a8d-851b-7c5fb2451c49" . + "2015-03-30T00:00:00"^^ . + "2019-01-29T10:53:33.292521"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0AD7A730011EA64106C15BBE33FFFC33 . + "Ligne de rail"@fr . + "Straatknooppunt"@nl . + "Street node"@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d601B1BB85C8CBDE07F17FD30746642C4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d601B1BB85C8CBDE07F17FD30746642C4 . + . + . + . + . + . + "text/xml" . + . + "Inventaire scientifique des arbres remarquables de la Région de Bruxelles-Capitale."@fr . + "Wetenschappelijke inventaris van de merkwaardige bomen van het Brussels Hoofdstedelijk Gewest."@nl . + "28f48440-ce95-4b67-a54d-dfd9cc7c0182" . + "2018-08-23T12:28:55.671682"^^ . + "2018-08-24T06:06:20.678661"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d16A640AD7A30FA99BE9AB15566B7AD0B . + "Arbres remarquables"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dC4E8C685D3B77B989104CAA288F7BA99 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dC4E8C685D3B77B989104CAA288F7BA99 . + . + "The statistics on tourism relate to the accommodation of visitors to the Brussels-Capital Region.\nAs regards culture, you will find statistics on cinemas and screenings.\n" . + "Tourism and Culture " . + . + "Centres de formation agréés en systèmes de climatisation pour véhicules automobiles"@fr . + "De erkende opleidingscentra op het vlak van klimaatregelingssystemen in motorvoertuigen "@nl . + "735c5b7a-237f-420e-b66d-3b12b035757c" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de erkende opleidingscentra op het vlak van klimaatregelingssystemen in motorvoertuigen"@nl . + "Liste des centres de formation agréés en systèmes de climatisation pour véhicules automobiles"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d780DB9AE7F1EAB3F75CD6FA7B8656C63 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d780DB9AE7F1EAB3F75CD6FA7B8656C63 . + . + . + . + . + "Liste non exhaustive de noms de rue féminins en Région de Bruxelles-Capitale | Niet-exhaustieve lijst van vrouwelijke straatnamen in het Brussels Gewest" . + "Noms de rue féminins en Région bruxelloise - vrouwelijke straatnamen in het Brussels Gewst" . + . + "Le Code bruxellois de l'Aménagement du Territoire (CoBAT) permet de créer un droit de préemption au profit de divers pouvoirs publics. Il est exercé dans l'intérêt général, notamment en vue de lutter contre l'existence d'immeubles abandonnés et insalubres et de réaliser des logements de type social."@fr . + "3fded591-0cda-485f-97e7-3b982c7ff34b" . + "2018-08-23T09:36:13.951080"^^ . + "2018-08-23T11:57:20.332796"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3AB75DE4BF8A787BD56D082D043C9EF7 . + "Zones de préemption"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d85112E4C1AFD42DA2821C3015879CF4A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d85112E4C1AFD42DA2821C3015879CF4A . + . + . + "Brussels Hoofstedelijk Gewest: Een huizenblok staat voor een deel van het gemeentelijk grondgebied waarvan de zijden\nbegrensd worden door straatzijden en/of gemeentegrenzen.\n\nDe omtrekken van de voetpaden (stoepen) zijn niet in de huizenblokken inbegrepen. Rotondes en middenbermen zijn geen huizenblokken. De huizenblokken komen niet alleen overeen met de bebouwde stadszones, maar ook met\nbraakliggende zones, bouwgronden, zones voorbehouden aan spoorweg en metro, groene ruimten en waterpartijen, galerijen."@nl . + "Brussels-Capital Region: The island means a portion of the communal territory whose faces are bounded by the sides of streets and / or boundaries of municipalities.\n\nThe contours of the sidewalks are not included in the islets. Roundabouts and central berms are not islands. Blocks correspond not only to urban areas but also areas of wasteland, the Land, area railway and subway, green spaces and water bodies, galleries."@en . + "Région de Bruxelles-Capitale : L’îlot désigne une portion de territoire communal dont les faces sont délimitées par les côtés de rue et/ou des limites de communes.\n\nLes contours des trottoirs ne sont pas inclus dans les îlots. Les ronds-points et les bermes centrales ne sont pas des îlots. Les îlots correspondent non seulement aux zones urbanisées mais également aux zones de friche, aux terrains à bâtir, aux zones de chemin de fer et de métro, aux espaces verts et aux plans d'eau, aux galeries."@fr . + "68204ea1-819d-433c-81bb-4a1776fd56bc" . + "2015-03-30T00:00:00"^^ . + "2019-01-29T10:58:28.743722"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0386BC6B2D9D1261CE53E69092663780 . + "Block"@en . + "Huizenblok"@nl . + "Ilot"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d56213AB5E3C2ED629E055D687319D1D6 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d56213AB5E3C2ED629E055D687319D1D6 . + . + . + . + . + "Static Data with all information about the parkings.\nFormat Datex II (http://www.datex2.eu)." . + "Parking Occupancy - Static Data" . + . + . + . + . + "Ecoles communales - année scolaire 2017/2018 (mise à jour annuelle)"@fr . + "Gemeentelijke school - Schooljaar 2017/2018 (jaarlijkse bijwerking)"@nl . + "67b8548c-6360-4cba-aa45-31d4c2861256" . + "2018-11-12T14:57:08.974872"^^ . + "2018-11-12T00:00:00"^^ . + "2017/2018 - Ecoles communales"@fr . + "2017/2018 - Gemeentelijke school"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d4409DB53549CE3901C6F13728A2FB331 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d4409DB53549CE3901C6F13728A2FB331 . + . + "text/xml" . + . + . + . + . + . + . + . + . + . + . + "La Commune de Saint-Gilles organise un enseignement fondamental ordinaire, un enseignement primaire individualisé de type 8, un enseignement secondaire, ainsi qu'un enseignement de promotion sociale. Tous ces établissements sont liés par une culture commune construite autour de valeurs humanistes, neutres et respectueuses de toutes les conceptions philosophiques. Elles développent la liberté de conscience.\n\n Il y a aujourd'hui sept écoles fondamentales (maternel et primaire) : l'école 1-2, l'école Ulenspiegel, l'école Les 4 Saisons (immersion en néerlandais à partir de la 3ème maternelle), l'école Peter Pan, l'école J.J. Michel et l'école Nouvelle, ainsi qu'une école primaire spécialisée de type 8, l'école du Parvis.\n\n Depuis septembre 2017, la Commune de Saint-Gilles organise un enseignement secondaire général à pédagogie active, le Lycée Intégral Roger Lallemand.\n\n La Commune organise également un enseignement secondaire artistique à horaire réduit dans les domaines des arts plastiques, de la musique et des arts de la parole au sein des deux académies : l'Académie des Beaux-Arts et l'Académie de Musique Arthur Degreef.\n"@fr . + "e4c4c3c1-6099-41d9-ab3d-74410d308f3a" . + "2018-10-17T13:59:10.364035"^^ . + "2019-02-20T13:27:27.128871"^^ . + "Ecoles"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d852D7DF5713DC210E684C1414DAC6F84 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d852D7DF5713DC210E684C1414DAC6F84 . + . + . + . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_water_watershed" . + . + "Offre de stationnement en voirie en Région de Bruxelles-Capitale"@fr . + "On-street parking supply in the Brussels-Capital Region"@en . + "Parkeeraanbod op de openbare weg in het Brussels Hoodstedelijk Gewest"@nl . + "d8d348b6-2289-417e-8321-92769f7d6705" . + "2017-06-23T11:16:06.189659"^^ . + "2018-10-23T07:35:16.215055"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8ABEB1AB5CA2A24AF103AD140EB3C62E . + "Offre de stationnement en voirie"@fr . + "On-street parking supply"@en . + "Parkeeraanbod op de openbare weg"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6BDE8001DF2942F1AEF6CC60BDF527F4 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6BDE8001DF2942F1AEF6CC60BDF527F4 . + . + "Lijst van speelpleinen (halfjaarlijkse bijwerking)"@nl . + "Liste des plaines de jeux (mise à jour semestrielle)"@fr . + "4ee154a2-e9e0-45a3-8e45-f19f86c543c0" . + "2018-04-10T09:45:56.777731"^^ . + "2018-06-30T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3E17AF425BC148916597B80F2720C364 . + "Plaines de jeux"@fr . + "Speelpleinen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF0312FCB86A5E16D9E03EFB4720EBB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAAF0312FCB86A5E16D9E03EFB4720EBB . + . + . + . + . + "bicyclestore - Mobigis" . + . + . + . + . + . + . + . + . + "59447b03-1f87-4876-9571-91f49bb1afb7" . + "2019-02-20T00:00:00"^^ . + "2019-03-18T00:00:00"^^ . + "Espace public numérique"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5AA78B1425BE930523E3A6B870C4F1AF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5AA78B1425BE930523E3A6B870C4F1AF . + . + "Nombre d'affichage des pages du site internet communal d'Auderghem en 2017." . + "2017 - Affichage des pages du site internet" . + . + "5a8839c7-14b0-488a-871f-24a393640f07" . + "2019-02-20T00:00:00"^^ . + "2019-02-20T00:00:00"^^ . + "Infrastructures communales"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d44B94FAEB48CCB3C88E5D18D2AA11EFD . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d44B94FAEB48CCB3C88E5D18D2AA11EFD . + . + . + . + . + "De lijst van de door een van de drie erkende controleorganen gecertificeerde biologische bedrijven."@nl . + "La liste des entreprises certifiées bio par l'un des trois organismes agréés."@fr . + "838a8812-0b20-451d-a64c-966660479398" . + "2018-12-13T00:00:00"^^ . + "2018-12-13T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d03EFE23A906885D31F5D2FF3F547B61A . + "Lijst van de gecertificeerde biologische bedrijven in het Brussels Hoofdstedelijk Gewest"@nl . + "Liste des opérateurs certifiés bio situés en Région de Bruxelles-Capitale "@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCC8C70A633BD3117C9416C76C17FFF5B . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dCC8C70A633BD3117C9416C76C17FFF5B . + . + "text/xml" . + . + . + . + . + "-De ordonnantie van 22 april 1999 tot vaststelling van de lijst der ingedeelde inrichtingen van klasse 1A (Staatsblad van 05/08/99); \n\n -Het besluit van de Brusselse Hoofdstedelijke Regering van 4 maart 1999 tot vaststelling vand de lijst der ingedeelde inrichtingen van klasse 1B, 2 en 3 (Staatsblad van 07/08/99); \n\n -Het besluit van de Brusselse Hoofdstedelijke Regering van 20 mei 1999 tot verplichting van het inwinnen van het advies van de Brusselses Hoofdstedelijke Dienst voor Brandweer en Dringende Medische Hulp (Staatsblad van 18/08/99); \n\n -Andere besluiten van de Brusselse Hoofdstedelijke Regering die verschillende rubrieke wijzingen of er nieuwe toevegen; \n\n -Het besluit van de Brusselse Hoofdstedelijke Regering tot vaststelling van de lijst van de risicoactiviteiten van 17 december 2009 (B.S. 08/01/2010). "@nl . + "-Ordonnance du 22 avril 1999 fixant la liste des installations de classe 1A (M.B.du 05/08/99); \n\n-Arrêté du Gouvernement de la Région de Bruxelles-Capitale du 4 mars 1999 fixant la liste des installations de classe 1B, 2 et 3 (M.B du 07/08/99); \n\n-Arrêté du Gouvernement de la Région de Bruxelles-Capitale du 20 mai 1999 imposant l'avis du Service Incendie et d'Aide médicale urgente en Région de Bruxelles-Capitale (M.B. du 18/05/99); \n\n-Autres arrêtés du Gouvernement de la Région de Bruxelles-Capital modifiant certaines rubriques ou en incluant des nouvelles; \n\n-Arrêté du Gouvernement de la Région de Bruxelles-Capitale fixant la liste des activités à risque du 17 décembre 2009 (M.B. du 08/01/2010). "@fr . + "b9760bbf-b6d8-4d45-af99-3d968f200e31" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de ingedeelde inrichtingen"@nl . + "Liste coordonnée des installations classées"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d790C843BED6D428E5A6EADE571F8C123 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d790C843BED6D428E5A6EADE571F8C123 . + . + . + "Si la santé relève des compétences communautaires, l’IBSA présente une sélection d’indicateurs (offre de soins de santé, espérance de vie, etc.), en combinant des données provenant de la Communauté flamande, de la Communauté française et de la Commission communautaire commune." . + "Santé" . + . + "Liste des exploitants d'un centre de démontage de véhicules hors d'usage habilités à délivrer un certificat de destruction (type B) et enregistrés en Région de Bruxelles-Capitale"@fr . + "Registratie als exploitant van een demonteercentrum voor afgedankte voertuigen dat ertoe gemachtigd is een vernietigingsattest af te geven (type B) "@nl . + "9c8901d8-5f4a-41cc-80f8-b639e3996ed8" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Liste des exploitants d'un centre de démontage de véhicules hors d'usage habilités à délivrer un certificat de destruction (type B) et enregistrés en Région de Bruxelles-Capitale"@fr . + "Registratie als exploitant van een demonteercentrum voor afgedankte voertuigen dat ertoe gemachtigd is een vernietigingsattest af te geven (type B) "@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d07A839B5CB898E28C4AEF4C9D3A1DB6F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d07A839B5CB898E28C4AEF4C9D3A1DB6F . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : de statistische sectoren worden gegroepeerd om de monitoringwijken te vormen. De wijkmonitoring is een observatiemiddel dat het mogelijk maakt om de evolutie van de Brusselse wijken onder verschillende aspecten (demografisch, sociaal, gezondheid, economie, huisvesting, mobiliteit, leefklimaat, deelname...) te volgen en te begrijpen teneinde de efficiëntie van de stadsbeleidsmaatregelen te verbeteren en de openbare en particuliere investeringen inzake stadsvernieuwing beter te oriënteren. De Monitoring moet beheerd en bijgewerkt worden door het Brusselse Instituut voor de Statistiek en de Analyse (BISA)."@nl . + "Brussels-Capital Region : Brussels-Capital statistical areas are grouped to form the districts of monitoring. Monitoring the neighborhood is an urban observation tool to help track and understand the evolution of Brussels districts in different aspects (demographic, social, health, economy, housing, mobility, quality of life, participation, ... ) to improve the efficiency of urban policy and help guide public and private investment in urban renewal ..."@en . + "Région de Bruxelles-Capitale : les secteurs statistiques sont regroupés pour former les quartiers du monitoring. Le Monitoring des quartiers est un outil d'observation urbain qui doit permettre de suivre et de comprendre l'évolution des quartiers bruxellois sous différentes facettes (démographiques, social, santé, économie, logement, mobilité, cadre de vie, participation,...) afin d'améliorer l'efficience des politiques urbaines et de mieux orienter les investissements publics et privés en matière de rénovation urbaine,..."@fr . + "07e83a81-0c0c-458f-8732-a8f1313ae710" . + "2015-09-28T00:00:00"^^ . + "2019-01-29T10:54:10.432340"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dDB0CB2D98F00E32DFFBE2A2E5CED934B . + "Monitoring District"@en . + "Monitoringwijk"@nl . + "Quartiers du monitoring"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d81ACDBAABB90150F7C85E0DE1D6C74C7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d81ACDBAABB90150F7C85E0DE1D6C74C7 . + . + "Avantages de toute nature et frais de représentation" . + . + . + . + . + . + . + . + . + "Calendrier des séances du conseil communal"@fr . + "Kalender van de zittingen van de Gemeenteraad"@nl . + "a013d97c-42aa-4d92-a714-56a3d19d0325" . + "2017-07-27T08:14:06.387047"^^ . + "2018-08-16T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d0675422B5FF221F19ADF6782B3C43C8A . + "Conseil communal"@fr . + "Gemeenteraad"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7756E19FDDF6ED0DC6AFC24023F6B0E5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d7756E19FDDF6ED0DC6AFC24023F6B0E5 . + . + . + . + . + "artistic_heritage - Mobigis" . + . + "text/xml" . + . + "Brussels Hoofdstedelijk Gewest: Informatie over de marktaandelen van de leveranciers die houder zijn van een leveringsvergunning voor energie die geldig is in het Brussels Gewest, in aantal leveringspunten en in volume. De informatie over de verliesleverancier wordt eveneens meegedeeld, hij dekt de verliezen op het elektriciteitsnet van de DNB. De gegevens worden uitgesplitst per klanttype, \"professioneel\" of \"huishoudelijk\", en per frequentie van de meteropname.\n\nEr zijn 3 meteropnamefrequenties, afhankelijk van het type meter: \n\n-\tDe zogenoemde \"continue\" frequentie van de meteropname voor de \"AMR\"-meters, _automatic meter reading_ of \"meter met automatische opname\". De informatie over de meterstanden wordt elke 15 minuten geregistreerd voor elektriciteit en elk uur voor gas. Daarna wordt ze een keer per dag op afstand doorgestuurd naar de DNB voor de twee energietypes. \n-\tDe zogenoemde \"maandelijkse\" frequentie van de meteropname voor de meters van het \"MMR\"-type, _monthly meter reading_ of \"meter met maandelijkse opname\". De informatie over de meterstanden wordt een keer per maand geregistreerd en op afstand doorgestuurd naar de DNB. \n-\tDe zogenoemde \"jaarlijkse\" frequentie van de meteropname voor de \"YMR\"-meters, _yearly meter reading_ of \"meter met jaarlijkse opname\". De meterstanden worden een keer per jaar fysiek (ter plaatse) opgenomen door het personeel van de DNB.\n\nDe gegevens worden geleverd door Sibelga en Elia. \n\nDe meegedeelde gegevens kunnen van rapport tot rapport verschillen. Dat is het gevolg van het rectificatieproces uitgevoerd door de actoren van de energiesector om de kwaliteit van de gegevens te verbeteren.\n"@nl . + "Brussels-Capital Region: Information on the market shares of suppliers holding an energy supply licence, valid in the Brussels Region, number of points of supply and volume. Information on the loss supplier is also provided, the supplier that covers losses on the DNO's electricity grid. The data are broken down according to \"business\" or \"residential\" customer and by meter reading frequency.\n\nThere are 3 meter reading frequencies depending on the type of meter: \n\n-\t\"Continuous\" reading frequency for “AMR” meters, automatic meter reading meters. Information on indexes is recorded every 15 minutes for electricity and every hour for gas. Then, it is transmitted remotely to the DNO once a day for both energies. \n-\t\"Monthly\" meter reading frequency for “MMR” meters, or monthly meter readings. Index information is recorded and transmitted to the DNO remotely once a month. \n-\t\"Annual\" meter reading frequency for \"YMR\" meters, or yearly meter readings. Index information is physically recorded (on-site) by DNO staff once a year.\n\nData are provided by Sibelga and Elia. \n\nThe data reported may differ from one report to another. This is the result of the rectification process used by players in the energy sector in order to improve the quality of the data.\n"@en . + "Région de Bruxelles-Capitale : Information sur les parts de marché des fournisseurs détenteurs d'une licence de fourniture d'énergie, valable en Région bruxelloise, en nombre de points de fourniture et en volume. L’information sur le fournisseur de perte est également communiquée, celui-ci couvre les pertes sur le réseau électrique du GRD. Les données sont ventilées par type de client « professionnel » ou « résidentiel » et par fréquence de relevé.\n\nIl existe 3 fréquences de relevé selon le type de compteur : \n\n-\tFréquence de relevé dite « continue » pour les compteurs « AMR », _automatic meter reading_ ou \"compteur à relevé automatique\". L’information sur les index est enregistrée toutes les 15 minutes pour l’électricité et toutes les heures pour le gaz. Puis, elle est transmise à distance au GRD une fois par jour pour les deux énergies. \n-\tFréquence de relevé dite « mensuelle » pour les compteurs de type « MMR », _monthly meter reading_ ou \"compteur à relevé mensuel\". L’information sur les index est enregistrée et transmise à distance au GRD une fois par mois. \n-\tFréquence de relevé dite « annuelle » pour les compteurs « YMR », _yearly meter reading_ ou \"compteur à relevé annuel\". L’information sur les index est relevée physiquement (sur place) par le personnel du GRD une fois par an.\n\nLes données sont fournies par Sibelga et Elia. \n\nLes données communiquées peuvent différer d'un rapport à l'autre. Ceci résulte du processus de rectification établis par les acteurs du secteur de l'énergie afin d'améliorer la qualité des données.\n"@fr . + "5454c8ac-5696-4a70-b529-27d43cedacd8" . + "2017-11-29T00:00:00"^^ . + "2018-05-08T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d62A3947A3A310C88D911C8D1D39DAA42 . + "Market shares "@en . + "Marktaandeel "@nl . + "Parts de marché"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d322B10B6203A86CA015B9F44FD12B8DB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d322B10B6203A86CA015B9F44FD12B8DB . + . + . + "Preview" . + . + . + "hierarchy - wfs" . + . + "De stedenbouwkundige zoneverordeningen hebben betrekking op specifieke aspecten van bepaalde gebieden van het grondgebied. Zij dragen bijvoorbeeld bij tot het behoud en de ontwikkeling van een wijk. Zowel het gewest als de gemeenten kunnen ze opstellen."@nl . + "Les règlements d’urbanisme zonés portent sur des aspects spécifiques relatifs à certaines zones du territoire. Ils peuvent être élaborés par la Région ou par les communes. Ils contribuent par exemple à la conservation et au développement d’un quartier."@fr . + "b9023e9e-5986-47ae-bc3f-d68c7540533d" . + "2018-08-24T06:14:30.953423"^^ . + "2018-08-24T06:23:21.915189"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7B0A83B6462DEBA8741B6FA7600E0A36 . + "Gezoneerde verordeningen"@nl . + "Règlements d'urbanisme zonés"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3B419BB42B9323D97DECCF73C9A8E522 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3B419BB42B9323D97DECCF73C9A8E522 . + . + "Brussels Hoofdstedelijk Gewest : een gebouw staat voor elke fysieke constructie bestaande uit vaste buitenmuren, bedekt met een dak, voor altijd bevestigd op een terrein, en bedoeld voor het onderbrengen van een menselijke activiteit en/of dieren, goederen en machines."@nl . + "Brussels-Capital Region : building means any physical construction formed of rigid exterior walls, roofed, permanently attached to land, and designed to accommodate human activity and / or shelter for animals, goods and machinery."@en . + "Région de Bruxelles-Capitale : le bâtiment désigne toute construction physique formée de murs extérieurs rigides, couverte d'un toit, fixée à demeure sur un terrain, et destinée à accueillir une activité humaine et/ou à abriter des animaux, des marchandises et des machines."@fr . + "fdd6a43f-fdbc-4181-9c3d-1efb5abc61f2" . + "2015-09-28T00:00:00"^^ . + "2019-01-29T10:57:58.669533"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dD903FB770CF839C911725C9A81852268 . + "Buildings"@en . + "Bâtiments"@fr . + "Gebouw"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d5890E9EA4B22C403AD5E9E87ACE41839 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d5890E9EA4B22C403AD5E9E87ACE41839 . + . + "taxi_stops - wfs" . + . + . + . + . + "text/xml" . + . + . + . + . + "Information from https://social.brussels"@en . + "L'ensemble des informations hébergées sur Bruxelles Social, la carte sociale des organisations actives dans le domaine social-santé en région de Bruxelles-Capitale.\n\nLes données sont gratuitement mises à disposition sous la licence CC BY.\n\nUn projet pilote de webservices est en cours de création; veuillez adresser toute demande d'information et d'accès aux webservices à la Coördinatrice du service Bruxelles Social : Valérie Wispenninckx (vwispenninckx@cmdc-cdcs.be | 02/639.60.24)"@fr . + "Sociaal Brussel, de tweetalige sociale kaart van organisaties en diensten actief binnen de welzijns- en gezondheidssector in het Brussels Gewest.\nGegevens worden gratis ter beschikking gesteld onder de licentie CC BY.\n\nEr is momenteel een pilootproject gaande dat webservices aanbiedt; gelieve elke informatieaanvraag hier rond voor te leggen aan de coördinator van Sociaal Brussel : Valérie Wispenninckx (vwispenninckx@cmdc-cdcs.be | 02/639.60.24)"@nl . + "ea0deec5-f810-447c-a3b8-759a303d66bf" . + "2018-10-25T00:00:00"^^ . + "2018-10-27T00:00:00"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d839AEB4D9933D25447A9793486A08931 . + "Sociaal.brussels"@nl . + "Social.brussels"@en . + "Social.brussels"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d904C2429CA620CB91CE7C139CDC36DDB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d904C2429CA620CB91CE7C139CDC36DDB . + . + . + "WFS" . + . + "The purpose of the barometer of overnights is to give a monthly view on the bednights spent by tourists in Brussels. It gives a total view of the bednights by month and splits them by purpose of the trip (leisure / professional), by type of accomodation and by country of origin of tourists. The data comes from the National Statistics Office who collects them from hotels in Brussels"@en . + "f03544a1-a01c-4374-b19d-e93697f1ac73" . + "2017-07-28T08:59:58.368114"^^ . + "2018-09-26T13:26:28.206067"^^ . + . + "Aankomsten en Overnachtingen - Gegeven Historiek"@nl . + "Arrivals & Overnights - Data History"@en . + "Arrivées et nuitées - Historique des Données"@fr . + . + "Erkenning van examencentra in koeltechniek"@nl . + "Liste des centres d’examen en technique du froid agréés en Région de Bruxelles-Capitale"@fr . + "57e48d57-2721-4675-99df-9c007dd0835a" . + "2018-10-05T00:00:00"^^ . + "2018-10-06T00:00:00"^^ . + "Erkenning van examencentra in koeltechniek"@nl . + "Liste des centres d’examen en technique du froid agréés en Région de Bruxelles-Capitale"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF4AA0E7103612041E59554867E4A2306 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF4AA0E7103612041E59554867E4A2306 . + . + . + . + . + . + . + . + . + "De personen die erkend zijn als inzamelaar, handelaar en makelaar van gevaarlijk afval mogen alleen de afvalstoffen opgenomen in de onderstaande lijst.\nDe beschrijving van de afvalstoffen die horen bij de codes bevindt zich in het Beschikking 2000/532/EG van de Europese commissie van 3 mei 2000 tot vaststelling van een lijst van afvalstoffen.\n"@nl . + "Les entreprises agréées comme collecteur, négociant et courtier de déchets dangereux ne peuvent reprendre que les déchets indiqués dans la liste ci-dessous.\nLa description des déchets liés aux codes repris se trouve dans la décision de la Commission Européenne 2000/532/CE du 3 mai 2000 établissant une liste de déchets.\n"@fr . + "a5eab045-9c1e-4cc2-bfde-9f40a90bf262" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Aanwijzende lijst van erkende inzamelaars, handelaars en makelaars van gevaarlijk afval"@nl . + "Liste des collecteurs, négociants et courtiers de déchets dangereux "@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dAE3907014DAEF28ABE232BB3B7840BAB . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dAE3907014DAEF28ABE232BB3B7840BAB . + . + "rer_velo - geojson - 2016-01-01" . + . + . + . + . + "25 novembre 1915, en pleine Première guerre mondiale, le Conseil communal de Saint-Gilles prend la décision de créer un espace réservé, au sein du cimetière communal, aux tombes des soldats saint-gillois morts au champ d’honneur. Cette décision aboutit à la création de la crypte militaire une fois la paix rétablie.\nLa pelouse d’honneur fut quant à elle créée par décision du Conseil du 28 avril 1927 afin de rendre hommage aux anciens combattants et aux soldats décédés des suites de maladies contractées au front qui n’étaient dès lors pas admissibles au sein de la crypte militaire. Elle accueille également les dépouilles des soldats alliés, russes et des civils belges. Pour y être enterré, les soldats belges devaient être décédés à Saint-Gilles alors que les combattants alliés et russes devaient y résider, avec obligation notamment pour les soldats russes de posséder une attestation d’invalidité. Les civils belges, hommes ou femmes, quant à eux, devaient être porteurs des médailles commémoratives de la Guerre 14-18 et de la Victoire, ou assimilés aux militaires par ordonnance du Ministère de la Défense Nationale.\nLa Seconde guerre mondiale entraina la création d’une seconde pelouse d’honneur réservée aux dépouilles des soldats saint-gillois morts au front ou des suites de leurs blessures et ce dans un délai de un an à partir du 28 mai 1940, c’est-à-dire un an après la fin de la campagne des 18 jours.\nLa pelouse d’honneur accueille également les restes des résistants saint-gillois morts en service commandé ou des suites de leurs blessures et ce endéans deux ans, à partir du 3 septembre 1945, date de la libération, à l’instar des prisonniers politiques."@fr . + "e5c59623-e1d5-4a97-ae3b-aafb212ad486" . + "2017-10-16T12:58:54.068334"^^ . + "2018-12-05T14:46:14.237247"^^ . + "Sépultures militaires"@fr . + . + "traffic_events - html" . + . + "Données retranscrivant la performance mensuelle du secteur hôtelier en région bruxelloise"@fr . + "e083a504-7ce9-4f76-b0b7-0e64b8469c3b" . + "2017-04-28T13:20:37.969124"^^ . + "2018-10-31T13:59:00.025637"^^ . + . + "Hotels Performance"@en . + "Hotels prestaties"@nl . + "Performance Hôtelière"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d36E341601DFF7A5B5E862A235F79C9CC . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d36E341601DFF7A5B5E862A235F79C9CC . + . + "text/xml" . + . + "0a50e990-2a37-4c25-9156-c9c26b8eed48" . + "2018-12-13T00:00:00"^^ . + "2018-12-13T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d92B17BB0E4314D1C0B539DE5DF349601 . + "Agences d'emploi privées reconnues en RBC"@fr . + "Erkende particuliere bureaus voor arbeidsbemiddeling in het BHG"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d3B6081C4073B928C40D03B078E319E50 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d3B6081C4073B928C40D03B078E319E50 . + . + . + . + . + "tunnelsections - Mobigis" . + . + "Brussels Hoofdstedelijk Gewest: Informatie over het aantal klanten van wie het verbruik wordt gefactureerd door de distributienetbeheerder (“DNB” of “Sibelga”). Het maandelijks aantal klanten wordt uitgesplitst volgens postcode en energietype. \n\nDrie klantencategorieën worden door de DNB bevoorraad in zijn rol van “noodleverancier”:\n\nDe __beschermde klanten__: Het statuut van beschermde klant is een systeem dat werd ingevoerd voor de huishoudelijke klanten die het moeilijk hebben om hun energiefactuur te betalen. Dankzij deze tijdelijke bescherming kunnen gezinnen met betalingsmoeilijkheden de door hun leverancier gevraagde afsluiting vermijden.\nBovendien genieten ze tijdens de duur van de bescherming van het specifiek sociaal tarief, dat voordeliger is dan de prijs die door de commerciële leveranciers wordt aangerekend.\n \nDe __winterklanten__: De afsluiting van een huishoudelijke klant, met toelating van de vrederechter, mag niet worden uitgevoerd tussen 1 oktober en 31 maart. Als een commerciële leverancier of de noodleverancier tijdens deze periode de gerechtelijke ontbinding van het contract verkrijgt, moet Sibelga dus de continuïteit van de levering tegen het specifiek sociaal tarief garanderen tot 31 maart, het einde van de winterstop.\n\nDe __EOC klanten__ : Voor de klanten wier contract afloopt en niet verlengd wordt door hun leverancier en die geen contract afsluiten bij een andere leverancier, zal de energiebevoorrading worden afgesloten. Het gaat hier om zogenaamde EOC-afsluitingen (End of Contract). Tijdens de winterperiode worden deze klanten bevoorraad door Sibelga, totdat het leveringspunt wordt overgenomen door een commerciële leverancier.\n\nHet meegedeelde aantal is een momentopname van de laatste dag van de maand. \n\nDe gegevens worden geleverd door Sibelga. De meegedeelde gegevens kunnen van rapport tot rapport verschillen. Dit is het gevolg van de rectificatieprocessen uitgevoerd door de actoren van de energiesector om de kwaliteit van de gegevens te verbeteren."@nl . + "Brussels-Capital Region: Information on the number of customers whose consumption is billed by the distribution network operator (\"DNO\" or \"Sibelga\"). The monthly number of these customers is broken down by postcode and energy vector. \n\nTwo categories of customers are supplied by the DNO in its role as \"supplier of last resort\":\n\n__Protected customers__: Protected customer status is a protection system set up for residential customers who are struggling to pay their energy bills. This temporary protection allows households experiencing payment difficulties to avoid having their power cut off by their supplier.\nMoreover, during the term of the protection, they benefit from the special social tariff, a lower tariff than the price charged by the commercial suppliers.\n \n__Winter truce customers__: A residential customer cannot have their power cut off between 1 October and 31 March, even if authorised by a justice of the peace . This means that, if a commercial supplier or the supplier of last resort obtains the legal termination of the contract binding it to its customer during this period, Sibelga must ensure the continuity of the supply at the special social rate until 31 March, the end of the winter truce.\n\n__EOC customers__: Customers with an energy contract that is about to expire and that is not being extended by their supplier, and who have not signed a contract with another supplier, have their energy supply cut. These are \"End of Contract (EOC)\" cuts. During the winter break, these customers are supplied by Sibelga until their supply is resumed by a commercial supplier.\n\nThe number provided is an image taken on the last day of the month. \n\nData are provided by Sibelga. The data reported may differ from one report to another. This is the result of the rectification processes used by players in the energy sector in order to improve the quality of the data.\n"@en . + "Région de Bruxelles-Capitale : Information sur le nombre de clients dont la consommation est facturée par le gestionnaire du réseau de distribution (« GRD » ou « Sibelga »). Le nombre mensuel de ces clients est ventilé par code postal et par vecteur d'énergie. \n\nTrois catégories de clients sont fournies par le GRD dans son rôle de \"fournisseur de dernier ressort\":\n\nLes __clients protégés__ : Le statut de client protégé est un système de protection mis en place pour les clients résidentiels qui ont des difficultés à payer leur facture d’énergie. Cette protection temporaire permet aux ménages en difficulté de paiement d’éviter une coupure d’énergie demandée par leur fournisseur.\nDe plus, pendant la durée de la protection, ils bénéficient du tarif social spécifique, tarif plus avantageux que le prix appliqué par les fournisseurs commerciaux.\n \nLes __clients hivernaux__ : La coupure d’un client résidentiel, sur autorisation du juge de paix, ne peut être mise à exécution entre le 1er octobre et le 31 mars. Ainsi, si un fournisseur commercial ou le fournisseur de dernier ressort obtient pendant cette période la résiliation judiciaire du contrat le liant à son client, Sibelga doit assurer la continuité de la fourniture au tarif social spécifique jusqu’au 31 mars, fin de la trêve hivernale.\n\nLes __clients EOC__ : Les clients dont le contrat d’énergie, arrivant à terme, n’est pas prolongé par leur fournisseur et qui n’ont pas signé un contrat auprès d’un autre fournisseur, subissent une coupure de leur fourniture d’énergie. Il s’agit de coupures « End of contract (EOC) ». Pendant la trêve hivernale, ces clients sont alimentés par Sibelga jusqu’à la reprise du point de fourniture par un fournisseur commercial.\n\nLe nombre communiqué est une image prise au dernier jour du mois. \n\nLes données sont fournies par Sibelga. Les données communiquées peuvent différer d'un rapport à l'autre. Ceci résulte des processus de rectification établis par les acteurs du secteur de l'énergie afin d'améliorer la qualité des données.\n"@fr . + "c427b5e7-deaf-4d95-b8fe-74f86c913894" . + "2017-11-29T00:00:00"^^ . + "2018-05-08T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d964B0DAF9CF755F6E5FD8F555D459350 . + "Clientèle sociale régionale"@fr . + "Regional social customers"@en . + "Sociaal, regionaal cliënteel"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d567EEC4D7A008F925B3D177DE80889B5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d567EEC4D7A008F925B3D177DE80889B5 . + . + "Cahier 1 : L'offre de transport à Bruxelles" . + . + . + . + "application/zip" . + . + "Zone de police 5342 (Uccle, Watermael-Boitsfort et Auderghem)" . + . + "Brussels Hoofdstedelijk Gewest: Informatie over de actieve, inactieve en geactiveerde leveringspunten gas en elektriciteit, uitgesplitst volgens het professioneel of huishoudelijk cliënteel en volgens “laag-” of “hoogspanning\" (alleen voor elektriciteit). Voor elektriciteit is een elektrische laagspanning, \"B\", lager dan of gelijk aan 1 kilovolt (1 kV) , terwijl een elektrische hoogspanning, \"H\", hoger is dan een kilovolt (1 kV).\n\nEen leveringspunt is __inactief__ als er voor dit leveringspunt geen enkele leverancier is geregistreerd. Er is geen uitsplitsing per type professioneel of huishoudelijk cliënteel, de informatie is dus niet van toepassing (n.v.t.).\nDe __activeringen__ hebben betrekking op de leveringspunten die naar de status “actief” zijn overgegaan tussen de eerste en de laatste dag van de betrokken maand, terwijl ze de maand voor de genoemde periode een andere status hadden. \n\nDe gegevens worden geleverd door Sibelga en Elia.\n\nDe meegedeelde gegevens kunnen van rapport tot rapport verschillen. Dat is het gevolg van het rectificatieproces uitgevoerd door de actoren van de energiesector om de kwaliteit van de gegevens te verbeteren.\n"@nl . + "Brussels-Capital Region: Information on active, inactive and activated supply points for gas and electricity broken down according to business or residential customers and according to \"low\" or \"high\" voltage (only in electricity). For electricity, a low voltage \"B\" is less than or equal to 1 kilovolt (1 kV), while a high voltage \"H\" is greater than one kilovolt (1 kV).\n\nA point of supply is __inactive__ if no supplier is registered for that access point. There is no breakdown according to business or residential clientele, so the information is not applicable (n/a).\nThe __activations__ relate to points of supply which have switched to \"active\" status between the 1st and last days of the month in question when they were under a different status during the month preceding that period. \n\nData are provided by Sibelga and Elia.\n\nThe data reported may differ from one report to another. This is the result of the process of rectification used by players in the energy sector in order to improve the quality of the data."@en . + "Région de Bruxelles - Capitale : Information sur les points de fourniture actifs, inactifs et activés en gaz et en électricité ventilée selon la clientèle professionnelle ou résidentielle et selon la tension \"basse\" ou \"haute\" (uniquement en électricité). Pour l’électricité, une tension électrique basse, « B », est inférieure ou égale à 1 kilovolt (1 kV) tandis qu’une tension électrique haute, « H », est supérieure à un kilovolt (1 kV).\n\nUn point de fourniture est __inactif__ si aucun fournisseur n'est enregistré, pour ce point d'accès. Il n'y pas de ventilation par type de clientèle professionnel ou résidentiel, l'information est donc non applicable (n/a).\nLes __activations__ concernent les points de fournitures qui sont passés au statut « actif » entre le 1er et le dernier jour du mois considéré alors qu’ils étaient sous un autre statut pendant le mois précédant ladite période. \n\nLes données sont fournies par Sibelga et Elia.\n\nLes données communiquées peuvent différer d'un rapport à l'autre. Ceci résulte du processus de rectification établi par les acteurs du secteur de l'énergie afin d'améliorer la qualité des données.\n"@fr . + "b941643b-8c1f-4a8e-a938-e2b62a8155b1" . + "2017-11-29T00:00:00"^^ . + "2018-05-08T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d23B8BB7B9ADBD9419C195E57E4CFE089 . + "Leveringspunten "@nl . + "Points de fournitures"@fr . + "Points of supply "@en . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d94EDE7F54CE24412D64FFBB70323540A . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d94EDE7F54CE24412D64FFBB70323540A . + . + . + "Katern 2: De verplaatsingsgewoonten in Brussel" . + . + . + "Lijst van de overheidsopdrachten 2017 (jaarelijkse bijwerking)"@nl . + "Liste des marchés publics 2017 (mise à jour annuelle)"@fr . + "e764365b-aed2-4b0c-a627-058b241b0a89" . + "2018-08-16T13:20:01.949783"^^ . + "2018-08-16T00:00:00"^^ . + "Marchés publics"@fr . + "Overheidsopdrachten"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d1145EF867677852CE553D812ACFA63A5 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d1145EF867677852CE553D812ACFA63A5 . + . + "Preview" . + . + . + . + . + . + . + . + . + . + . + . + "On-street parking regulation zones in Brussels-Capital Region"@en . + "Op de openbare weg parkeerreglementering in het Brussels Hoofdstedelijk Gewest"@nl . + "Réglementation du stationnement en voirie en Région de Bruxelles-Capitale"@fr . + "c43d7443-3e5a-4c6a-ae07-743153fa35e6" . + "2017-06-23T11:22:11.560401"^^ . + "2018-10-23T07:33:48.134768"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d14378FFF0C0BD3C80F90963B3FA4F7A6 . + "On-street parking regulation zones"@en . + "Op de openbare weg parkeerreglementering"@nl . + "Réglementation du stationnement en voirie"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dF59082FE2CE120EF836EB6373C323D8D . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dF59082FE2CE120EF836EB6373C323D8D . + . + . + "Le trafic routier est mesuré en plusieurs endroits de la Région Bruxelloise, au moyen de caméras ou de boucles électromagnétiques. Vous pouvez utiliser l’API ou les services cartographiques pour accéder aux données. Pour le moment, les données sont uniquement diffusées en temps réel ; la diffusion de données historiques est envisagée dans le futur.\n\n__API: https://data-mobility.brussels/traffic/api/counts__\n\nLien vers la carte des points de comptages: [Mobigis](https://data-mobility.brussels/mobigis/fr/?x=486153.44345781335&y=6592969.096448178&zoom=13&baselayer=urbis_grey&layers=traffic_live_geom%3BTunnels%3B&opacity=1%3B1%3B&filter=)\n\n[![img mobigis extract] (https://data-mobility.brussels/media/img/traffic_count.jpg)](https://data-mobility.brussels/mobigis/fr/?x=486153.44345781335&y=6592969.096448178&zoom=13&baselayer=urbis_grey&layers=traffic_live_geom%3BTunnels%3B&opacity=1%3B1%3B&filter=)"@fr . + "On several locations in the Brussels Region, traffic is measured using magnetic loops or cameras. To access this data, you can use the API or the geowebservices. For the moment, only real-time data is available, historical data is coming.\n\n__API: https://data-mobility.brussels/traffic/api/counts__\n\nDirect link to the countings on map: [Mobigis](https://data-mobility.brussels/mobigis/fr/?x=486153.44345781335&y=6592969.096448178&zoom=13&baselayer=urbis_grey&layers=traffic_live_geom%3BTunnels%3B&opacity=1%3B1%3B&filter=)\n\n[![img mobigis extract] (https://data-mobility.brussels/media/img/traffic_count.jpg)](https://data-mobility.brussels/mobigis/fr/?x=486153.44345781335&y=6592969.096448178&zoom=13&baselayer=urbis_grey&layers=traffic_live_geom%3BTunnels%3B&opacity=1%3B1%3B&filter=)"@en . + "Op verschillende plaatsen in het Brussels Gewest wordt het verkeer geteld door gebruik te maken van elektromagnetische tellussen of camera’s. De data is toegankelijk via een API en via geowebservices. Op dit ogenblik is er enkel Real Time data beschikbaar, in de toekomst zal eveneens historische data beschikbaar worden gemaakt.\n\n__API: https://data-mobility.brussels/traffic/api/counts__\n\nLink naar de telpunten op kaart: [Mobigis](https://data-mobility.brussels/mobigis/nl/?x=486153.44345781335&y=6592969.096448178&zoom=13&baselayer=urbis_grey&layers=traffic_live_geom%3BTunnels%3B&opacity=1%3B1%3B&filter=)\n\n[![img mobigis extract] (https://data-mobility.brussels/media/img/traffic_count.jpg)](https://data-mobility.brussels/mobigis/fr/?x=486153.44345781335&y=6592969.096448178&zoom=13&baselayer=urbis_grey&layers=traffic_live_geom%3BTunnels%3B&opacity=1%3B1%3B&filter=)"@nl . + "9a047e86-3947-424a-84e8-a192f18a735a" . + "2019-03-05T12:49:45.760963"^^ . + "2019-03-07T14:11:59.901354"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dCDA61420E8AB9F9A1CEDB119FF9A03C2 . + "Comptages traffic"@fr . + "Traffic counting"@en . + "Verkeerstellingen"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d8849B003B708E7DD7746598C1F546BB3 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d8849B003B708E7DD7746598C1F546BB3 . + . + "taxi_stops - geojson - 2016-01-01" . + . + . + . + . + "application/zip" . + . + . + . + . + . + . + . + "De opleidingen erkend door de erkenningscommissie in het kader van het betaald educatief verlof."@nl . + "Les formations reconnues par la Commission d'agrément dans le cadre du congé-éducation payé."@fr . + "7cd74931-c879-467f-adf2-cfbb59a2c424" . + "2018-12-13T00:00:00"^^ . + "2018-12-13T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d9C270F8D208124A178E6DB5D4083AA5D . + "Betaald educatief verlof - Erkenningscommissie"@nl . + "Congé-éducation payé - Commission d'agrément"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE95269EBDCE3E26F69BC9706ED810A56 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE95269EBDCE3E26F69BC9706ED810A56 . + . + "3D modeling of buildings"@en . + "3D-modellen van gebouwen"@nl . + "Modélisation 3D des bâtiments"@fr . + "e35fb9a8-619e-4e68-9063-a808d75aa13e" . + "2016-02-29T00:00:00"^^ . + "2019-03-18T16:12:25.101281"^^ . + "Buildings 3D "@en . + "Bâtiments 3D"@fr . + "Gebouwen 3D"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE7EE0B40716784694B036509823F2A5E . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE7EE0B40716784694B036509823F2A5E . + . + "icr - gpx - 2017-01-01" . + . + "Lijst van sportverenigingen (gesubsidieerd door de gemeente) - (jaarlijkse bijwerking)"@nl . + "Liste de associations sportives (subsidiées par la commune) - (mise à jour annuelle)"@fr . + "ef01e4cb-57ff-4b5e-9def-bdb31f199054" . + "2018-08-16T12:59:53.585128"^^ . + "2018-08-16T00:00:00"^^ . + "Associations sportives (subsidiées)"@fr . + "Sportverenigingen (gesubsidieerd)"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6B3D9739545C9364D3DF204541FDA29F . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6B3D9739545C9364D3DF204541FDA29F . + . + . + . + . + . + "Liste des rues d'Evere"@fr . + "Stratenlijst van Evere"@nl . + "2985d203-a374-4016-a0f5-9bd5e398f724" . + "2019-02-11T00:00:00"^^ . + "2019-02-11T00:00:00"^^ . + "Nom des rues d'Evere"@fr . + "Straatnamen van Evere"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dE767622D4D208738EA4C0C970C431D89 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dE767622D4D208738EA4C0C970C431D89 . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "ef763e97-af1a-4dd7-b1f2-da06d7a30c59" . + "2018-12-14T00:00:00"^^ . + "2018-12-14T00:00:00"^^ . + . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d362E839840D38165D85EAE6FCE2B9D71 . + "Authorised trainings in the context of the training fund service vouchers"@en . + "Formations admises dans le cadre du fonds de formation titres-services"@fr . + "Toegelaten opleidingen in het kader van opleidingsfonds dienstencheques"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d635F101DA107D18104171D3AE85E95BF . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d635F101DA107D18104171D3AE85E95BF . + . + "application/zip" . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : de entiteit « Green Block » (of groenzone) identificeert de delen van het grondgebied ingenomen door groenstroken die dienen tot ontspanning (beplant met gras, bomen, eventueel bloemen, sierbomen en sierstruiken, en vaak versierd met waterpartijen en paden).\n\nDe groenzones werden getekend op basis van diverse plannen (fotogrammetrische opmetingen, NGI-kaarten, kaarten van de gemeenten...).\n \nZij hebben een uiteenlopende nauwkeurigheid omdat ze uit verschillende bronnen afkomstig zijn.\n\nDe entiteit « Green Block » (of groenzone) wordt voorgesteld door drie verschillende types:\n\n- GB-A: grasstroken (bepaalde met gras of bomen beplante middenbermen);\n- GB-B: de parken (de waterpartijen in deze parken zijn niet uitgesloten);\n- GB-F: bossen of wouden (delen van het Zoniënwoud die in het Brussels Gewest gelegen zijn)."@nl . + "Brussels-Capital Region : entity \"Green Block\" (or green area) identifies ROW floor of approval vegetated areas (lawn, trees, possibly planted with flowers and ornamental trees and bushes, and often lined ponds and paths).\n\nGreen areas have been designed on the basis of various plans (photogrammetric surveys, detailed maps, maps of towns, ...). They have a heterogeneous precision because of the diversity of origin.\n\nThe entity \"Green Block\" (or green area) is represented using three different types:\n- GB-A: grassed (some grassy berm plants or trees) bands;\n- GB-B: parks (water parks located in these areas are not excluded);\n- GB-F: forests or wood (islets of the Sonian Forest located in the Brussels Region).\n"@en . + "Région de Bruxelles-Capitale : l'entité « Green Block » (ou zone verte) identifie les emprises au sol des espaces d'agrément végétalisés (engazonné, arboré, éventuellement planté de fleurs et d'arbres et buissons d'ornement, et souvent garni de pièces d'eau et cheminements).\n\nLes zones vertes ont été dessinées sur base de plans divers (levés photogrammétriques, cartes IGN, cartes des communes, …). Elles ont une précision hétérogène du fait de la diversité de provenance.\n\nL'entité « Green Block » (ou zone verte) est représentée via trois types différents :\n- GB-A : les bandes engazonnées (certaines bermes centrales herbeuses ou arborées) ;\n- GB-B : les parcs (les zones d’eau situés dans ces parcs ne sont pas exclues) ;\n- GB-F : les forêts ou bois (îlots de la Forêt de Soignes situés en Région Bruxelloise)."@fr . + "d3911d5d-bac0-4fa0-8d3a-5b8f87a06e84" . + "2015-03-30T00:00:00"^^ . + "2019-01-29T10:52:56.826401"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dB8BECFF6BAF31300DF9D22C2B43500F9 . + "Espaces verts"@fr . + "Green block"@en . + "Groenzone"@nl . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d118C647FBE0DB107B93D2D6D8526D1FE . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d118C647FBE0DB107B93D2D6D8526D1FE . + . + . + . + . + . + . + . + . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : de entiteit BH03 vertegenwoordigt punten van hoogte die aan de bodem door luchtfotogrammetrie worden opgemerkt, grootschalig. Deze punten bevinden zich voornamelijk op openbaar gebied."@nl . + "Brussels-Capital Region : BH03 entity represents the points elevation ground surveys by aerial photogrammetry scale. These points are essentially in the public domain."@en . + "Région de Bruxelles-Capitale : l'entité BH03 représente des points d'altitude relevés au sol par photogrammétrie aérienne à grande échelle. Ces points se trouvent essentiellement dans le domaine public."@fr . + "9cd66984-6da6-47b1-aa09-b43e9ded3353" . + "2015-03-30T00:00:00"^^ . + "2019-01-29T10:59:59.432979"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d208362642605765D41E97A5F8191E91B . + "Altitude points"@en . + "Hoogtepunten"@nl . + "Points d'altitude"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122dBEE33AE874DC0320C0075AFC23F94BF7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122dBEE33AE874DC0320C0075AFC23F94BF7 . + . + . + . + . + . + "Lijst van de erkende EPB-adviseurs (rechtspersonen)"@nl . + "Liste des agréments en matière de réglementation PEB (chauffage, climatisation, travaux et cirtification)."@fr . + "4395c9fb-ffab-403b-9ffc-e982b11ffbfb" . + "2018-08-20T00:00:00"^^ . + "2018-10-10T00:00:00"^^ . + "Lijst van de erkende EPB-adviseurs (rechtspersonen)"@nl . + "Liste des conseillers PEB agréés (personnes morales)"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d6DD8823B931E079AA77D967405159BE7 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d6DD8823B931E079AA77D967405159BE7 . + . + . + . + . + "application/zip" . + . + . + . + . + . + "http://wfs.ibgebim.be/cgi-bin/tinyows.fcgi?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=IBGE:wsl_bruenvi_protection_area_sonian_forest&SRSNAME=EPSG:31370" . + . + . + . + . + "Brussels Hoofdstedelijk Gewest : de PW-entiteit stemt met de officiële database van verwijzing van benaming en codificatie van de openbare wegen gelegen overeen op het grondgebied van de Regio van Brussel-Hoofdstad"@nl . + "Brussels-Capital Region : entity PW is the database reference name and official codification of public roads within the territory of the Brussels-Capital"@en . + "Région de Bruxelles-Capitale : l'entité PW correspond à la base de données de référence de dénomination et de codification officielles des voies publiques situées sur le territoire de la Région de Bruxelles-Capitale"@fr . + "a2dd324e-5bfb-41a8-b17e-09d6ec2cc356" . + "2015-03-30T00:00:00"^^ . + "2019-01-29T10:52:22.938868"^^ . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d7A86E59D559480E5063C00374C120C60 . + "Openbare Weg"@nl . + "Public way"@en . + "Voies publiques"@fr . + _:genid2d1c0fdc56bb274053a43550120a7fab0122d52C0E83B4FADCB042AF748DA915CB072 . +_:genid2d1c0fdc56bb274053a43550120a7fab0122d52C0E83B4FADCB042AF748DA915CB072 . + . + . + . + . + . diff --git a/finding1.md b/finding1.md new file mode 100644 index 00000000000..0bf18da20b0 --- /dev/null +++ b/finding1.md @@ -0,0 +1,66 @@ +# Plan: Fix ID-only join to use ID-binding on RHS + +## Description + +- Problem: In the two-pattern ID join path, `LmdbIdJoinQueryEvaluationStep` materializes the left record back into a `MutableBindingSet` and uses the BindingSet-based iterator for the right-hand side, instead of staying in ID space. +- Evidence in code: + - `core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinQueryEvaluationStep.java:74` creates the left iterator via `dataset.getRecordIterator(leftPattern, bindings)`. + - `core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdJoinQueryEvaluationStep.java:76-84` builds a `MutableBindingSet` from the left record and calls `dataset.getRecordIterator(rightPattern, bs)`. + - Contrast with BGP path: `core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/join/LmdbIdBGPQueryEvaluationStep.java:70` uses the ID-binding overload: `dataset.getRecordIterator(rightPat, varName -> leftInfo.getId(leftRecord, varName))`. +- Impact: Allocates `Value` objects and performs value-store lookups on the right-hand side, negating expected performance gains and increasing GC pressure for ordinary binary joins. + +## Reproduce & Test Plan + +Goal: Prove the RHS iterator is created via the BindingSet overload today and enforce the change to the ID-binding overload with focused tests. + +1) Unit test (overload call verification) +- Add `LmdbIdJoinRightUsesIdBindingTest` under `core/sail/lmdb/src/test/java/.../lmdb/`. +- Approach A (Mockito): + - Spy/mock `LmdbEvaluationDataset` to record invocations of `getRecordIterator(StatementPattern, BindingSet)` vs. `getRecordIterator(StatementPattern, LmdbIdVarBinding)`. + - Build a simple join `?s p1 ?x . ?s p2 ?y` with shared var `?s` and create `LmdbIdJoinQueryEvaluationStep` using a valid `LmdbQueryEvaluationContext`. + - Evaluate with `EmptyBindingSet` and assert the RHS overload used is the ID-binding variant. +- Approach B (test double): + - Implement a small `RecordingDataset` test double implementing `LmdbEvaluationDataset` that throws if `getRecordIterator(…, BindingSet)` is called for RHS and flips a flag if `getRecordIterator(…, LmdbIdVarBinding)` is called. + - Assert the flag after evaluation. + +Suggested targeted command during iteration: +`mvn -o -Dmaven.repo.local=.m2_repo -pl core/sail/lmdb -Dtest=LmdbIdJoinRightUsesIdBindingTest verify | tail -500` + +2) Behavioral sanity (algorithm remains ID join) +- Reuse `LmdbIdJoinEvaluationTest.simpleJoinUsesIdIterator` pattern to assert the join algorithm name remains `LmdbIdJoinIterator` after the change. + +3) Optional micro-observability (no perf assert) +- Wrap `ValueStore` in a counting decorator in a dedicated test to assert that the RHS path does not call `getValue(id)` or materialize `Value` objects during the join (optional and only if trivial to wire without touching production code). + +## Fix Plan + +- Change `LmdbIdJoinQueryEvaluationStep.evaluate` right-factory to use ID binding: + - Replace the construction of a `MutableBindingSet` for RHS with an `LmdbIdVarBinding` lambda that provides IDs from the left record. + - Model the code after the BGP path in `LmdbIdBGPQueryEvaluationStep`. +- Keep materialization to `BindingSet` in the final stage only (e.g., `LmdbIdFinalBindingSetIteration`), ensuring the pipeline stays in ID space end-to-end. +- Update/add tests from the Reproduce plan; ensure they fail before and pass after the change. + +High-level pseudo-diff for clarity (not exact code): +```java +// Before (simplified) +MutableBindingSet bs = context.createBindingSet(); +leftInfo.applyRecord(leftRecord, bs, valueStore); +return dataset.getRecordIterator(rightPattern, bs); + +// After (RHS stays in ID space) +return dataset.getRecordIterator(rightPattern, + varName -> leftInfo.getId(leftRecord, varName)); +``` + +## Why This Needs To Be Fixed + +- Performance: Avoids unnecessary `Value` creation and value-store ID resolution on the RHS, preserving the intended speedup of the ID-only join. +- Consistency: Aligns the 2-pattern join path with the BGP optimization, ensuring uniform behavior across join forms. +- Resource usage: Reduces GC pressure and heap churn for common joins. + +## Validation / Success Criteria + +- New unit test confirms the RHS path uses `getRecordIterator(…, LmdbIdVarBinding)`. +- Existing `LmdbIdJoinEvaluationTest` continues to pass and still reports `LmdbIdJoinIterator` as the chosen algorithm. +- No regressions in `core/sail/lmdb` module tests. + diff --git a/finding2.md b/finding2.md new file mode 100644 index 00000000000..a8a4eccc567 --- /dev/null +++ b/finding2.md @@ -0,0 +1,69 @@ +# Plan: Disable ID-only join when transaction changes are present + +## Description + +- Problem: The safeguard that disables the LMDB ID-only join in the presence of uncommitted writes relies on `LmdbEvaluationDataset.hasTransactionChanges()`. In production, the dataset installed by the store is `LmdbSailStore.LmdbSailDataset`, which does not override this method (default `false`), so the optimization remains enabled. +- Evidence in code: + - Strategy gate: `core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbEvaluationStrategy.java:62-70` checks `ds.hasTransactionChanges()` to decide whether to disable the ID-only join. + - Production dataset: `core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java:1363-1467` implements both `getRecordIterator` overloads but does not override `hasTransactionChanges()`. + - Overlay dataset (tests only): `core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbOverlayEvaluationDataset.java:169-171` returns `true` for `hasTransactionChanges()`, but is only referenced from tests. +- Impact: With the optimization enabled, queries evaluated while a connection has uncommitted writes can read directly from the LMDB snapshot and ignore pending changes, leading to stale/missing results. At minimum, the current safeguard is dead code and gives a false sense of safety. + +## Reproduce & Test Plan + +Goal: Demonstrate the optimization stays enabled with pending changes in a write transaction, then prove it’s disabled and correctness is preserved after the fix. + +1) Integration test (current behavior exposes the gap) +- Add `LmdbIdJoinTxnVisibilityTest` in `core/sail/lmdb/src/test/java/.../lmdb/`. +- Scenario: + 1. Create a store and repository; add baseline triple `:alice :knows :bob`. + 2. Open a connection, begin a transaction (if needed by the harness), and add uncommitted triple `:alice :likes :pizza`. + 3. Execute `SELECT ?person ?item WHERE { ?person :knows ?other . ?person :likes ?item . }` before commit. + 4. Capture the chosen algorithm via `explain` or by precompiling the algebra (as in `LmdbIdJoinEvaluationTest`) and assert it is currently `LmdbIdJoinIterator`. + 5. Assert that results are missing/stale (0 rows) if the store does not expose the overlay to the evaluation path. If the store flushes writes on read in this setup, adapt the test by using a `SailDatasetTripleSource` overlay (see 2). + +2) Deterministic overlay test (mirrors existing test but without manual dataset injection) +- Build an overlay `TripleSource` that unions the baseline with the pending (uncommitted) triple on-the-fly (similar to `LmdbIdJoinDisableOnChangesTest`). +- Construct an evaluation strategy over the baseline triple source, but do NOT install `LmdbOverlayEvaluationDataset` into the thread-local. +- Precompile or evaluate the join and assert that the ID-only join is disabled by the strategy after the fix, purely by inspecting the active triple source (no thread-local overlay dataset needed). + +Targeted command examples: +- `mvn -o -Dmaven.repo.local=.m2_repo -pl core/sail/lmdb -Dtest=LmdbIdJoinTxnVisibilityTest verify | tail -500` +- `mvn -o -Dmaven.repo.local=.m2_repo -pl core/sail/lmdb -Dtest=LmdbIdJoinDisableOnChangesTest verify | tail -500` + +3) Unit-level indicator (dataset signal) +- Add a focused test to assert that the production dataset used during evaluation exposes pending writes appropriately after the fix (e.g., `((LmdbEvaluationDataset) dataset).hasTransactionChanges()` becomes `true` in the same connection during a write). + +## Fix Plan + +Two viable approaches (pick one; Option 2 preferred for correctness): + +1) Quick, conservative fix (global writer visibility) +- Override `hasTransactionChanges()` in `LmdbSailStore.LmdbSailDataset` to return `true` when the store has an active write transaction (e.g., `storeTxnStarted.get()`). +- Pros: Minimal code, immediately makes the safeguard effective. +- Cons: Conservative: disables ID-only join for all queries while any writer is active, even in other connections. + +2) Connection-local detection (preferred) +- Add a connection/thread-local flag in `LmdbSailStore` that is set in `LmdbSailSink.startTransaction(...)` and cleared on commit/rollback. +- Override `hasTransactionChanges()` in `LmdbSailStore.LmdbSailDataset` to consult this connection-local flag, so only queries in the same connection/transaction see `true`. +- Alternatively or additionally, update `LmdbEvaluationStrategy.precompile(...)` to detect that the active `TripleSource` is a `SailDatasetTripleSource` with overlays and install an `LmdbOverlayEvaluationDataset` as the effective dataset in the `QueryEvaluationContext` (which returns `true` for `hasTransactionChanges()`). +- Pros: Accurate semantics, disables ID-only only where necessary. +- Cons: Slightly more wiring; ensure no cross-thread leakage if evaluation occurs on different threads. + +3) Safety net (minimal surface change in strategy) +- If wiring connection-local flags is not feasible, in `LmdbEvaluationStrategy.precompile(...)`, when `tripleSource` is not the LMDB-native source (e.g., it’s a `SailDatasetTripleSource`), wrap it with `new LmdbOverlayEvaluationDataset(tripleSource, valueStore)` as the `effectiveDataset`. This makes `hasTransactionChanges()` return `true` and will disable ID-only joins in `prepare(...)`. +- Pros: No store changes; straightforward. +- Cons: May disable ID-only joins even when no writes are pending; acceptable as a stopgap to ensure correctness. + +## Why This Needs To Be Fixed + +- Correctness: Queries executed within a write transaction must see uncommitted changes from their own transaction. Leaving the optimization enabled can yield stale or missing results. +- Transparency: The current safeguard is effectively dead code unless tests manually install an overlay dataset; production behavior should not depend on test-only wiring. +- Predictability: Aligns optimization behavior with RDF4J’s transactional semantics and user expectations. + +## Validation / Success Criteria + +- The new integration test demonstrates the issue pre-fix and passes post-fix: ID-only join is disabled when pending writes exist in the evaluating connection. +- Existing `LmdbIdJoinDisableOnChangesTest` continues to pass. +- No regressions in `core/sail/lmdb` tests; ID-only join remains enabled when no transaction changes are present. + diff --git a/pom.xml b/pom.xml index 1d2f315909a..a4d354e26b0 100644 --- a/pom.xml +++ b/pom.xml @@ -271,7 +271,7 @@ org.jacoco jacoco-maven-plugin - 0.8.13 + 0.8.14 diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/SparqlOrderByTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/SparqlOrderByTest.java index 43a8e88fd68..86e58850b0e 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/SparqlOrderByTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/SparqlOrderByTest.java @@ -33,7 +33,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +@Timeout(value = 10) public abstract class SparqlOrderByTest { @BeforeAll diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeadLockTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeadLockTest.java index e02fb007d02..c048e13f2f2 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeadLockTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeadLockTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertNull; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.eclipse.rdf4j.common.transaction.IsolationLevel; import org.eclipse.rdf4j.common.transaction.IsolationLevels; @@ -91,7 +92,7 @@ public void test() throws Exception { start.countDown(); a.begin(level); a.add(PICASSO, RDF.TYPE, PAINTER); - commit.await(); + commit.await(10, TimeUnit.SECONDS); a.commit(); } catch (Exception e) { e1.initCause(e); @@ -106,7 +107,7 @@ public void test() throws Exception { start.countDown(); b.begin(level); b.add(REMBRANDT, RDF.TYPE, PAINTER); - commit.await(); + commit.await(10, TimeUnit.SECONDS); b.commit(); } catch (Exception e) { e2.initCause(e); @@ -115,10 +116,10 @@ public void test() throws Exception { end.countDown(); } }).start(); - start.await(); + start.await(10, TimeUnit.SECONDS); commit.countDown(); Thread.sleep(500); - end.await(); + end.await(10, TimeUnit.SECONDS); assertNull(e1.getCause()); assertNull(e2.getCause()); } diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeleteInsertTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeleteInsertTest.java index 01405ca957b..9fe7b8c6217 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeleteInsertTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/DeleteInsertTest.java @@ -66,7 +66,7 @@ public void tearDown() { } } - @Test + @Test(timeout = 10000) public void test() throws Exception { String load = IOUtil.readString(cl.getResource("test/insert-data.ru")); con.prepareUpdate(QueryLanguage.SPARQL, load, NS).execute(); diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/IsolationLevelTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/IsolationLevelTest.java index 04ca3a0a91b..29ef14e0348 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/IsolationLevelTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/IsolationLevelTest.java @@ -107,7 +107,7 @@ public void testReadUncommitted() { readPending(IsolationLevels.READ_UNCOMMITTED); } - @Test + @Test(timeout = 5000) public void testReadCommitted() throws Exception { readCommitted(IsolationLevels.READ_COMMITTED); rollbackTriple(IsolationLevels.READ_COMMITTED); @@ -194,7 +194,7 @@ private void readCommitted(final IsolationLevel level) throws Exception { Thread writer = new Thread(() -> { try (RepositoryConnection write = store.getConnection()) { start.countDown(); - start.await(); + start.await(10, TimeUnit.SECONDS); write.begin(level); write.add(RDF.NIL, RDF.TYPE, RDF.LIST); begin.countDown(); @@ -207,8 +207,8 @@ private void readCommitted(final IsolationLevel level) throws Exception { Thread reader = new Thread(() -> { try (RepositoryConnection read = store.getConnection()) { start.countDown(); - start.await(); - begin.await(); + start.await(10, TimeUnit.SECONDS); + begin.await(10, TimeUnit.SECONDS); read.begin(level); // must not read uncommitted changes long counted = count(read, RDF.NIL, RDF.TYPE, RDF.LIST, false); @@ -245,13 +245,13 @@ private void repeatableRead(final IsolationLevels level) throws Exception { Thread writer = new Thread(() -> { try (RepositoryConnection write = store.getConnection()) { start.countDown(); - start.await(); + start.await(10, TimeUnit.SECONDS); write.begin(level); write.add(RDF.NIL, RDF.TYPE, RDF.LIST); write.commit(); begin.countDown(); - observed.await(); + observed.await(10, TimeUnit.SECONDS); write.begin(level); write.remove(RDF.NIL, RDF.TYPE, RDF.LIST); @@ -264,8 +264,8 @@ private void repeatableRead(final IsolationLevels level) throws Exception { Thread reader = new Thread(() -> { try (RepositoryConnection read = store.getConnection()) { start.countDown(); - start.await(); - begin.await(); + start.await(10, TimeUnit.SECONDS); + begin.await(10, TimeUnit.SECONDS); read.begin(level); long first = count(read, RDF.NIL, RDF.TYPE, RDF.LIST, false); assertEquals(1, first); @@ -343,7 +343,7 @@ private void snapshot(final IsolationLevels level) throws Exception { try { try (RepositoryConnection write = store.getConnection()) { start.countDown(); - start.await(); + start.await(10, TimeUnit.SECONDS); write.begin(level); insertTestStatement(write, 1); write.commit(); @@ -363,8 +363,8 @@ private void snapshot(final IsolationLevels level) throws Exception { Thread reader = new Thread(() -> { try (RepositoryConnection read = store.getConnection()) { start.countDown(); - start.await(); - begin.await(); + start.await(10, TimeUnit.SECONDS); + begin.await(10, TimeUnit.SECONDS); read.begin(level); long first = count(read, null, null, null, false); observed.countDown(); @@ -431,7 +431,7 @@ protected Thread incrementBy(final CountDownLatch start, final CountDownLatch ob return new Thread(() -> { try (RepositoryConnection con = store.getConnection()) { start.countDown(); - start.await(); + start.await(10, TimeUnit.SECONDS); con.begin(level); Literal o1 = readLiteral(con, subj, pred); observed.countDown(); diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SerializableTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SerializableTest.java index 8889c6d39fb..9315662c997 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SerializableTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SerializableTest.java @@ -135,7 +135,7 @@ public void tearDown() { } } - @Test + @Test(timeout = 5000) public void test_independentPattern() { a.begin(level); b.begin(level); @@ -149,7 +149,7 @@ public void test_independentPattern() { assertEquals(2, size(b, null, RDF.TYPE, PAINTER, false)); } - @Test + @Test(timeout = 5000) public void test_safePattern() { a.begin(level); b.begin(level); @@ -171,7 +171,7 @@ public void testPrepare_safePattern() { b.prepare(); } - @Test + @Test(timeout = 5000) public void test_afterPattern() { a.begin(level); b.begin(level); @@ -183,7 +183,7 @@ public void test_afterPattern() { assertEquals(2, size(b, null, RDF.TYPE, PAINTER, false)); } - @Test + @Test(timeout = 5000) public void test_afterInsertDataPattern() { a.begin(level); b.begin(level); @@ -195,7 +195,7 @@ public void test_afterInsertDataPattern() { assertEquals(2, size(b, null, RDF.TYPE, PAINTER, false)); } - @Test + @Test(timeout = 5000) public void test_conflictPattern() { a.begin(level); b.begin(level); @@ -214,7 +214,7 @@ public void test_conflictPattern() { } } - @Test + @Test(timeout = 5000) public void testPrepare_conflictPattern() { a.begin(level); b.begin(level); @@ -233,7 +233,7 @@ public void testPrepare_conflictPattern() { } } - @Test + @Test(timeout = 5000) public void test_safeQuery() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -255,7 +255,7 @@ public void test_safeQuery() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeInsert() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -274,7 +274,7 @@ public void test_safeInsert() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -304,7 +304,7 @@ public void test_conflictQuery() { } } - @Test + @Test(timeout = 5000) public void test_conflictInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -332,7 +332,7 @@ public void test_conflictInsert() { assertEquals(0, size(a, null, RDF.TYPE, PAINTING, false)); } - @Test + @Test(timeout = 5000) public void test_safeOptionalQuery() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -356,7 +356,7 @@ public void test_safeOptionalQuery() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeOptionalInsert() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -375,7 +375,7 @@ public void test_safeOptionalInsert() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictOptionalQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -407,7 +407,7 @@ public void test_conflictOptionalQuery() { assertEquals(7, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictOptionalInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -435,7 +435,7 @@ public void test_conflictOptionalInsert() { assertEquals(7, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeFilterQuery() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -465,7 +465,7 @@ public void test_safeFilterQuery() { } } - @Test + @Test(timeout = 5000) public void test_safeFilterInsert() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -493,7 +493,7 @@ public void test_safeFilterInsert() { } } - @Test + @Test(timeout = 5000) public void test_conflictOptionalFilterQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); a.add(PICASSO, PAINTS, GUERNICA); @@ -526,7 +526,7 @@ public void test_conflictOptionalFilterQuery() { assertEquals(9, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictOptionalFilterInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); a.add(PICASSO, PAINTS, GUERNICA); @@ -557,7 +557,7 @@ public void test_conflictOptionalFilterInsert() { assertEquals(9, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeRangeQuery() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, ARTEMISIA); @@ -593,7 +593,7 @@ public void test_safeRangeQuery() { } } - @Test + @Test(timeout = 5000) public void test_safeRangeInsert() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, ARTEMISIA); @@ -628,7 +628,7 @@ public void test_safeRangeInsert() { } } - @Test + @Test(timeout = 5000) public void test_conflictRangeQuery() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -664,7 +664,7 @@ public void test_conflictRangeQuery() { assertEquals(13, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictRangeInsert() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, NIGHTWATCH); diff --git a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SnapshotTest.java b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SnapshotTest.java index 0beb98f282f..2de76268ebf 100644 --- a/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SnapshotTest.java +++ b/testsuites/repository/src/main/java/org/eclipse/rdf4j/testsuite/repository/optimistic/SnapshotTest.java @@ -128,7 +128,7 @@ public void tearDown() { } } - @Test + @Test(timeout = 5000) public void test_independentPattern() { a.begin(level); b.begin(level); @@ -142,7 +142,7 @@ public void test_independentPattern() { assertEquals(2, size(b, null, RDF.TYPE, PAINTER, false)); } - @Test + @Test(timeout = 5000) public void test_safePattern() { a.begin(level); b.begin(level); @@ -153,7 +153,7 @@ public void test_safePattern() { b.commit(); } - @Test + @Test(timeout = 5000) public void test_afterPattern() { a.begin(level); b.begin(level); @@ -165,7 +165,7 @@ public void test_afterPattern() { assertEquals(2, size(b, null, RDF.TYPE, PAINTER, false)); } - @Test + @Test(timeout = 5000) public void test_afterInsertDataPattern() { a.begin(level); b.begin(level); @@ -177,7 +177,7 @@ public void test_afterInsertDataPattern() { assertEquals(2, size(b, null, RDF.TYPE, PAINTER, false)); } - @Test + @Test(timeout = 5000) public void test_conflictPattern() { a.begin(level); b.begin(level); @@ -195,7 +195,7 @@ public void test_conflictPattern() { } } - @Test + @Test(timeout = 5000) public void test_safeQuery() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -216,7 +216,7 @@ public void test_safeQuery() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeInsert() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -235,7 +235,7 @@ public void test_safeInsert() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -257,7 +257,7 @@ public void test_mergeQuery() { assertEquals(3, size(a, null, RDF.TYPE, PAINTING, false)); } - @Test + @Test(timeout = 5000) public void test_mergeInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -277,7 +277,7 @@ public void test_mergeInsert() { assertEquals(3, size(a, null, RDF.TYPE, PAINTING, false)); } - @Test + @Test(timeout = 5000) public void test_conflictQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -306,7 +306,7 @@ public void test_conflictQuery() { } } - @Test + @Test(timeout = 5000) public void test_conflictInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -333,7 +333,7 @@ public void test_conflictInsert() { } } - @Test + @Test(timeout = 5000) public void test_safeOptionalQuery() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -357,7 +357,7 @@ public void test_safeOptionalQuery() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeOptionalInsert() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -376,7 +376,7 @@ public void test_safeOptionalInsert() { assertEquals(9, size(b, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeOptionalQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -401,7 +401,7 @@ public void test_mergeOptionalQuery() { assertEquals(10, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeOptionalInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -421,7 +421,7 @@ public void test_mergeOptionalInsert() { assertEquals(10, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictOptionalQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -453,7 +453,7 @@ public void test_conflictOptionalQuery() { } } - @Test + @Test(timeout = 5000) public void test_conflictOptionalInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); b.add(REMBRANDT, RDF.TYPE, PAINTER); @@ -480,7 +480,7 @@ public void test_conflictOptionalInsert() { } } - @Test + @Test(timeout = 5000) public void test_safeFilterQuery() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -502,7 +502,7 @@ public void test_safeFilterQuery() { assertEquals(10, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeFilterInsert() { b.add(REMBRANDT, RDF.TYPE, PAINTER); b.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -522,7 +522,7 @@ public void test_safeFilterInsert() { assertEquals(10, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeOptionalFilterQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); a.add(PICASSO, PAINTS, GUERNICA); @@ -548,7 +548,7 @@ public void test_mergeOptionalFilterQuery() { assertEquals(12, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeOptionalFilterInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); a.add(PICASSO, PAINTS, GUERNICA); @@ -571,7 +571,7 @@ public void test_mergeOptionalFilterInsert() { assertEquals(12, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictOptionalFilterQuery() { a.add(PICASSO, RDF.TYPE, PAINTER); a.add(PICASSO, PAINTS, GUERNICA); @@ -605,7 +605,7 @@ public void test_conflictOptionalFilterQuery() { } } - @Test + @Test(timeout = 5000) public void test_conflictOptionalFilterInsert() { a.add(PICASSO, RDF.TYPE, PAINTER); a.add(PICASSO, PAINTS, GUERNICA); @@ -635,7 +635,7 @@ public void test_conflictOptionalFilterInsert() { } } - @Test + @Test(timeout = 5000) public void test_safeRangeQuery() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, ARTEMISIA); @@ -663,7 +663,7 @@ public void test_safeRangeQuery() { assertEquals(17, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_safeRangeInsert() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, ARTEMISIA); @@ -690,7 +690,7 @@ public void test_safeRangeInsert() { assertEquals(17, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeRangeQuery() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -719,7 +719,7 @@ public void test_mergeRangeQuery() { assertEquals(16, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_mergeRangeInsert() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -747,7 +747,7 @@ public void test_mergeRangeInsert() { assertEquals(16, size(a, null, null, null, false)); } - @Test + @Test(timeout = 5000) public void test_conflictRangeQuery() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -783,7 +783,7 @@ public void test_conflictRangeQuery() { } } - @Test + @Test(timeout = 5000) public void test_conflictRangeInsert() { a.add(REMBRANDT, RDF.TYPE, PAINTER); a.add(REMBRANDT, PAINTS, NIGHTWATCH); @@ -828,6 +828,9 @@ private List eval(String var, RepositoryConnection con, String qry) { try { List list = new ArrayList<>(); while (result.hasNext()) { + if (list.size() > 1000000) { + throw new RuntimeException("Too many results: " + list.size()); + } list.add(result.next().getValue(var)); } return list; diff --git a/tmp/LmdbRecordIterator.modified.java b/tmp/LmdbRecordIterator.modified.java new file mode 100644 index 00000000000..219730ebd86 --- /dev/null +++ b/tmp/LmdbRecordIterator.modified.java @@ -0,0 +1,490 @@ +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.eclipse.rdf4j.sail.lmdb.LmdbUtil.E; +import static org.lwjgl.util.lmdb.LMDB.MDB_NEXT; +import static org.lwjgl.util.lmdb.LMDB.MDB_NOTFOUND; +import static org.lwjgl.util.lmdb.LMDB.MDB_SET; +import static org.lwjgl.util.lmdb.LMDB.MDB_SET_RANGE; +import static org.lwjgl.util.lmdb.LMDB.MDB_SUCCESS; +import static org.lwjgl.util.lmdb.LMDB.mdb_cmp; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_close; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_get; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_open; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_renew; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.eclipse.rdf4j.common.concurrent.locks.StampedLongAdderLockManager; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.lmdb.TripleStore.KeyBuilder; +import org.eclipse.rdf4j.sail.lmdb.TripleStore.TripleIndex; +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.lmdb.MDBVal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A record iterator that wraps a native LMDB iterator. + */ +class LmdbRecordIterator implements RecordIterator { + private static final Logger log = LoggerFactory.getLogger(LmdbRecordIterator.class); + private final Pool pool = Pool.get(); + + private TripleIndex index; + + private long subj; + private long pred; + private long obj; + private long context; + + private long cursor; + + private MDBVal maxKey; + + private boolean matchValues; + private GroupMatcher groupMatcher; + + /** + * True when late-bound variables exist beyond the contiguous prefix of the chosen index order, requiring + * value-level filtering. When false, range bounds already guarantee that every visited key matches and the + * GroupMatcher is redundant. + */ + private boolean needMatcher; + + private Txn txnRef; + + private long txnRefVersion; + + private long txn; + + private int dbi; + + private volatile boolean closed = false; + + private MDBVal keyData; + + private MDBVal valueData; + + private ByteBuffer minKeyBuf; + + private ByteBuffer maxKeyBuf; + private boolean externalMinKeyBuf; + private boolean externalMaxKeyBuf; + + private int lastResult; + + private long[] quad; + + private boolean fetchNext = false; + + private StampedLongAdderLockManager txnLockManager; + + private final Thread ownerThread = Thread.currentThread(); + + private boolean initialized = false; + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, null, null, null); + } + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, null, null); + } + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse, ByteBuffer minKeyBufParam, + ByteBuffer maxKeyBufParam) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, minKeyBufParam, + maxKeyBufParam); + } + + LmdbRecordIterator(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef) throws IOException { + this(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, null, null, null); + } + + LmdbRecordIterator(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef, long[] quadReuse, + ByteBuffer minKeyBufParam, ByteBuffer maxKeyBufParam) throws IOException { + initializeInternal(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, + minKeyBufParam, maxKeyBufParam); + initialized = true; + } + + void initialize(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse, ByteBuffer minKeyBufParam, + ByteBuffer maxKeyBufParam) throws IOException { + if (initialized && !closed) { + prepareForReuse(); + } + initializeInternal(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, + minKeyBufParam, maxKeyBufParam); + initialized = true; + } + + private void initializeInternal(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef, long[] quadReuse, + ByteBuffer minKeyBufParam, ByteBuffer maxKeyBufParam) throws IOException { + this.index = index; + this.subj = subj; + this.pred = pred; + this.obj = obj; + this.context = context; + + boolean prevExternalMinKeyBuf = this.externalMinKeyBuf; + boolean prevExternalMaxKeyBuf = this.externalMaxKeyBuf; + + if (quadReuse != null && quadReuse.length >= 4) { + this.quad = quadReuse; + } else if (this.quad == null || this.quad.length < 4) { + this.quad = new long[] { subj, pred, obj, context }; + } + this.quad[0] = subj; + this.quad[1] = pred; + this.quad[2] = obj; + this.quad[3] = context; + + if (this.keyData == null) { + this.keyData = pool.getVal(); + } + if (this.valueData == null) { + this.valueData = pool.getVal(); + } + + if (rangeSearch) { + this.externalMinKeyBuf = minKeyBufParam != null; + if (externalMinKeyBuf) { + this.minKeyBuf = minKeyBufParam; + } else { + if (this.minKeyBuf == null || prevExternalMinKeyBuf) { + this.minKeyBuf = pool.getKeyBuffer(); + } else { + this.minKeyBuf.clear(); + } + } + if (keyBuilder != null) { + keyBuilder.writeMin(minKeyBuf); + } else { + index.getMinKey(minKeyBuf, subj, pred, obj, context); + } + minKeyBuf.flip(); + + if (this.maxKey == null) { + this.maxKey = pool.getVal(); + } + this.externalMaxKeyBuf = maxKeyBufParam != null; + if (externalMaxKeyBuf) { + this.maxKeyBuf = maxKeyBufParam; + } else { + if (this.maxKeyBuf == null || prevExternalMaxKeyBuf) { + this.maxKeyBuf = pool.getKeyBuffer(); + } else { + this.maxKeyBuf.clear(); + } + } + if (keyBuilder != null) { + keyBuilder.writeMax(maxKeyBuf); + } else { + index.getMaxKey(maxKeyBuf, subj, pred, obj, context); + } + maxKeyBuf.flip(); + this.maxKey.mv_data(maxKeyBuf); + } else { + if (this.maxKey != null) { + pool.free(maxKey); + this.maxKey = null; + } + if (this.maxKeyBuf != null && !prevExternalMaxKeyBuf) { + pool.free(maxKeyBuf); + this.maxKeyBuf = null; + } + this.externalMaxKeyBuf = maxKeyBufParam != null; + this.maxKeyBuf = externalMaxKeyBuf ? maxKeyBufParam : null; + + if (subj > 0 || pred > 0 || obj > 0 || context >= 0) { + this.externalMinKeyBuf = minKeyBufParam != null; + if (externalMinKeyBuf) { + this.minKeyBuf = minKeyBufParam; + } else { + if (this.minKeyBuf == null || prevExternalMinKeyBuf) { + this.minKeyBuf = pool.getKeyBuffer(); + } else { + this.minKeyBuf.clear(); + } + } + index.getMinKey(minKeyBuf, subj, pred, obj, context); + minKeyBuf.flip(); + } else { + if (this.minKeyBuf != null && !prevExternalMinKeyBuf) { + pool.free(minKeyBuf); + this.minKeyBuf = null; + } + this.externalMinKeyBuf = minKeyBufParam != null; + if (externalMinKeyBuf) { + this.minKeyBuf = minKeyBufParam; + } else { + this.minKeyBuf = null; + } + } + } + + this.matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0; + int prefixLen = index.getPatternScore(subj, pred, obj, context); + int boundCount = (subj > 0 ? 1 : 0) + (pred > 0 ? 1 : 0) + (obj > 0 ? 1 : 0) + (context >= 0 ? 1 : 0); + this.needMatcher = boundCount > prefixLen; + this.groupMatcher = null; + this.fetchNext = false; + this.lastResult = MDB_SUCCESS; + this.closed = false; + + this.dbi = index.getDB(explicit); + this.txnRef = txnRef; + this.txnLockManager = txnRef.lockManager(); + + long readStamp; + try { + readStamp = txnLockManager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + this.txnRefVersion = txnRef.version(); + this.txn = txnRef.get(); + + // Try to reuse a pooled cursor only for read-only transactions; otherwise open a new one + if (txnRef.isReadOnly()) { + long pooled = pool.getCursor(dbi, index); + if (pooled != 0L) { + long c = pooled; + try { + E(mdb_cursor_renew(txn, c)); + } catch (IOException renewEx) { + // Renewal failed (e.g., incompatible txn). Close pooled cursor and open a fresh one. + mdb_cursor_close(c); + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + c = pp.get(0); + } + } + cursor = c; + } else { + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + cursor = pp.get(0); + } + } + } else { + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + cursor = pp.get(0); + } + } + } finally { + txnLockManager.unlockRead(readStamp); + } + } + + @Override + public long[] next() { + if (closed) { + log.debug("Calling next() on an LmdbRecordIterator that is already closed, returning null"); + return null; + } + StampedLongAdderLockManager manager = txnLockManager; + if (manager == null) { + throw new SailException("Iterator not initialized"); + } + long readStamp; + try { + readStamp = manager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + if (txnRefVersion != txnRef.version()) { + // TODO: None of the tests in the LMDB Store cover this case! + // cursor must be renewed + mdb_cursor_renew(txn, cursor); + if (fetchNext) { + // cursor must be positioned on last item, reuse minKeyBuf if available + if (minKeyBuf == null) { + minKeyBuf = pool.getKeyBuffer(); + } + minKeyBuf.clear(); + index.toKey(minKeyBuf, quad[0], quad[1], quad[2], quad[3]); + minKeyBuf.flip(); + keyData.mv_data(minKeyBuf); + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET); + if (lastResult != MDB_SUCCESS) { + // use MDB_SET_RANGE if key was deleted + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + } + if (lastResult != MDB_SUCCESS) { + closeInternal(false); + return null; + } + } + // update version of txn ref + this.txnRefVersion = txnRef.version(); + } + + if (fetchNext) { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + fetchNext = false; + } else { + if (minKeyBuf != null) { + // set cursor to min key + keyData.mv_data(minKeyBuf); + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + } else { + // set cursor to first item + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + } + } + + while (lastResult == MDB_SUCCESS) { + // if (maxKey != null && TripleStore.COMPARATOR.compare(keyData.mv_data(), maxKey.mv_data()) > 0) { + if (maxKey != null && mdb_cmp(txn, dbi, keyData, maxKey) > 0) { + lastResult = MDB_NOTFOUND; + } else if (matches()) { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + } else { + // Matching value found + index.keyToQuad(keyData.mv_data(), subj, pred, obj, context, quad); + // fetch next value + fetchNext = true; + return quad; + } + } + closeInternal(false); + return null; + } finally { + manager.unlockRead(readStamp); + } + } + + private boolean matches() { + // When there are no late-bound variables beyond the contiguous prefix, range bounds fully determine matches. + if (!needMatcher) { + return false; + } + + if (groupMatcher != null) { + return !this.groupMatcher.matches(keyData.mv_data()); + } else if (matchValues) { + this.groupMatcher = index.createMatcher(subj, pred, obj, context); + return !this.groupMatcher.matches(keyData.mv_data()); + } else { + return false; + } + } + + private void prepareForReuse() { + if (cursor != 0L && txnRef != null) { + if (txnRef.isReadOnly()) { + pool.freeCursor(dbi, index, cursor); + } else { + mdb_cursor_close(cursor); + } + } + cursor = 0L; + groupMatcher = null; + fetchNext = false; + lastResult = MDB_SUCCESS; + matchValues = false; + needMatcher = false; + txnRef = null; + txn = 0L; + txnRefVersion = 0L; + txnLockManager = null; + closed = true; + } + + private void closeInternal(boolean maybeCalledAsync) { + StampedLongAdderLockManager manager = this.txnLockManager; + if (closed) { + return; + } + long writeStamp = 0L; + boolean writeLocked = false; + if (maybeCalledAsync && ownerThread != Thread.currentThread() && manager != null) { + try { + writeStamp = manager.writeLock(); + writeLocked = true; + } catch (InterruptedException e) { + throw new SailException(e); + } + } + try { + if (cursor != 0L && txnRef != null) { + if (txnRef.isReadOnly()) { + pool.freeCursor(dbi, index, cursor); + } else { + mdb_cursor_close(cursor); + } + cursor = 0L; + } + if (keyData != null) { + pool.free(keyData); + keyData = null; + } + if (valueData != null) { + pool.free(valueData); + valueData = null; + } + if (minKeyBuf != null && !externalMinKeyBuf) { + pool.free(minKeyBuf); + } + if (maxKeyBuf != null && !externalMaxKeyBuf) { + pool.free(maxKeyBuf); + } + if (maxKey != null) { + pool.free(maxKey); + maxKey = null; + } + minKeyBuf = null; + maxKeyBuf = null; + externalMinKeyBuf = false; + externalMaxKeyBuf = false; + groupMatcher = null; + fetchNext = false; + lastResult = 0; + matchValues = false; + needMatcher = false; + txnRef = null; + txn = 0L; + dbi = 0; + index = null; + } finally { + closed = true; + if (writeLocked) { + manager.unlockWrite(writeStamp); + } + txnLockManager = null; + } + } + + @Override + public void close() { + closeInternal(true); + } +} diff --git a/tmp/LmdbRecordIterator.original.java b/tmp/LmdbRecordIterator.original.java new file mode 100644 index 00000000000..7f3f9671f0a --- /dev/null +++ b/tmp/LmdbRecordIterator.original.java @@ -0,0 +1,439 @@ +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.eclipse.rdf4j.sail.lmdb.LmdbUtil.E; +import static org.lwjgl.util.lmdb.LMDB.MDB_NEXT; +import static org.lwjgl.util.lmdb.LMDB.MDB_NOTFOUND; +import static org.lwjgl.util.lmdb.LMDB.MDB_SET; +import static org.lwjgl.util.lmdb.LMDB.MDB_SET_RANGE; +import static org.lwjgl.util.lmdb.LMDB.MDB_SUCCESS; +import static org.lwjgl.util.lmdb.LMDB.mdb_cmp; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_close; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_get; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_open; +import static org.lwjgl.util.lmdb.LMDB.mdb_cursor_renew; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.eclipse.rdf4j.common.concurrent.locks.StampedLongAdderLockManager; +import org.eclipse.rdf4j.sail.SailException; +import org.eclipse.rdf4j.sail.lmdb.TripleStore.KeyBuilder; +import org.eclipse.rdf4j.sail.lmdb.TripleStore.TripleIndex; +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.lmdb.MDBVal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A record iterator that wraps a native LMDB iterator. + */ +class LmdbRecordIterator implements RecordIterator { + private static final Logger log = LoggerFactory.getLogger(LmdbRecordIterator.class); + private final Pool pool = Pool.get(); + + private TripleIndex index; + + private long subj; + private long pred; + private long obj; + private long context; + + private long cursor; + + private MDBVal maxKey; + + private boolean matchValues; + private GroupMatcher groupMatcher; + + /** + * True when late-bound variables exist beyond the contiguous prefix of the chosen index order, requiring + * value-level filtering. When false, range bounds already guarantee that every visited key matches and the + * GroupMatcher is redundant. + */ + private boolean needMatcher; + + private Txn txnRef; + + private long txnRefVersion; + + private long txn; + + private int dbi; + + private volatile boolean closed = false; + + private MDBVal keyData; + + private MDBVal valueData; + + private ByteBuffer minKeyBuf; + + private ByteBuffer maxKeyBuf; + private boolean externalMinKeyBuf; + private boolean externalMaxKeyBuf; + + private int lastResult; + + private long[] quad; + + private boolean fetchNext = false; + + private StampedLongAdderLockManager txnLockManager; + + private final Thread ownerThread = Thread.currentThread(); + + private boolean initialized = false; + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, null, null, null); + } + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, null, null); + } + + LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse, ByteBuffer minKeyBufParam, + ByteBuffer maxKeyBufParam) throws IOException { + this(index, null, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, minKeyBufParam, + maxKeyBufParam); + } + + LmdbRecordIterator(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef) throws IOException { + this(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, null, null, null); + } + + LmdbRecordIterator(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef, long[] quadReuse, + ByteBuffer minKeyBufParam, ByteBuffer maxKeyBufParam) throws IOException { + initializeInternal(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, + minKeyBufParam, maxKeyBufParam); + initialized = true; + } + + void initialize(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, long pred, long obj, + long context, boolean explicit, Txn txnRef, long[] quadReuse, ByteBuffer minKeyBufParam, + ByteBuffer maxKeyBufParam) throws IOException { + if (initialized && !closed) { + throw new IllegalStateException("Cannot initialize LMDB record iterator while it is open"); + } + initializeInternal(index, keyBuilder, rangeSearch, subj, pred, obj, context, explicit, txnRef, quadReuse, + minKeyBufParam, maxKeyBufParam); + initialized = true; + } + + private void initializeInternal(TripleIndex index, KeyBuilder keyBuilder, boolean rangeSearch, long subj, + long pred, long obj, long context, boolean explicit, Txn txnRef, long[] quadReuse, + ByteBuffer minKeyBufParam, ByteBuffer maxKeyBufParam) throws IOException { + this.index = index; + this.subj = subj; + this.pred = pred; + this.obj = obj; + this.context = context; + + if (quadReuse != null && quadReuse.length >= 4) { + this.quad = quadReuse; + } else if (this.quad == null || this.quad.length < 4) { + this.quad = new long[] { subj, pred, obj, context }; + } + this.quad[0] = subj; + this.quad[1] = pred; + this.quad[2] = obj; + this.quad[3] = context; + + if (this.keyData == null) { + this.keyData = pool.getVal(); + } + if (this.valueData == null) { + this.valueData = pool.getVal(); + } + + if (rangeSearch) { + this.externalMinKeyBuf = minKeyBufParam != null; + this.minKeyBuf = externalMinKeyBuf ? minKeyBufParam : pool.getKeyBuffer(); + minKeyBuf.clear(); + if (keyBuilder != null) { + keyBuilder.writeMin(minKeyBuf); + } else { + index.getMinKey(minKeyBuf, subj, pred, obj, context); + } + minKeyBuf.flip(); + + if (this.maxKey == null) { + this.maxKey = pool.getVal(); + } + this.externalMaxKeyBuf = maxKeyBufParam != null; + this.maxKeyBuf = externalMaxKeyBuf ? maxKeyBufParam : pool.getKeyBuffer(); + maxKeyBuf.clear(); + if (keyBuilder != null) { + keyBuilder.writeMax(maxKeyBuf); + } else { + index.getMaxKey(maxKeyBuf, subj, pred, obj, context); + } + maxKeyBuf.flip(); + this.maxKey.mv_data(maxKeyBuf); + } else { + if (this.maxKey != null) { + pool.free(maxKey); + this.maxKey = null; + } + if (this.maxKeyBuf != null && !externalMaxKeyBuf) { + pool.free(maxKeyBuf); + } + this.externalMaxKeyBuf = maxKeyBufParam != null; + this.maxKeyBuf = externalMaxKeyBuf ? maxKeyBufParam : null; + + if (subj > 0 || pred > 0 || obj > 0 || context >= 0) { + this.externalMinKeyBuf = minKeyBufParam != null; + this.minKeyBuf = externalMinKeyBuf ? minKeyBufParam : pool.getKeyBuffer(); + minKeyBuf.clear(); + index.getMinKey(minKeyBuf, subj, pred, obj, context); + minKeyBuf.flip(); + } else { + if (this.minKeyBuf != null && !externalMinKeyBuf) { + pool.free(minKeyBuf); + } + this.externalMinKeyBuf = minKeyBufParam != null; + this.minKeyBuf = externalMinKeyBuf ? minKeyBufParam : null; + } + } + + this.matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0; + int prefixLen = index.getPatternScore(subj, pred, obj, context); + int boundCount = (subj > 0 ? 1 : 0) + (pred > 0 ? 1 : 0) + (obj > 0 ? 1 : 0) + (context >= 0 ? 1 : 0); + this.needMatcher = boundCount > prefixLen; + this.groupMatcher = null; + this.fetchNext = false; + this.lastResult = MDB_SUCCESS; + this.closed = false; + + this.dbi = index.getDB(explicit); + this.txnRef = txnRef; + this.txnLockManager = txnRef.lockManager(); + + long readStamp; + try { + readStamp = txnLockManager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + this.txnRefVersion = txnRef.version(); + this.txn = txnRef.get(); + + // Try to reuse a pooled cursor only for read-only transactions; otherwise open a new one + if (txnRef.isReadOnly()) { + long pooled = pool.getCursor(dbi, index); + if (pooled != 0L) { + long c = pooled; + try { + E(mdb_cursor_renew(txn, c)); + } catch (IOException renewEx) { + // Renewal failed (e.g., incompatible txn). Close pooled cursor and open a fresh one. + mdb_cursor_close(c); + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + c = pp.get(0); + } + } + cursor = c; + } else { + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + cursor = pp.get(0); + } + } + } else { + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pp = stack.mallocPointer(1); + E(mdb_cursor_open(txn, dbi, pp)); + cursor = pp.get(0); + } + } + } finally { + txnLockManager.unlockRead(readStamp); + } + } + + @Override + public long[] next() { + if (closed) { + log.debug("Calling next() on an LmdbRecordIterator that is already closed, returning null"); + return null; + } + StampedLongAdderLockManager manager = txnLockManager; + if (manager == null) { + throw new SailException("Iterator not initialized"); + } + long readStamp; + try { + readStamp = manager.readLock(); + } catch (InterruptedException e) { + throw new SailException(e); + } + try { + if (txnRefVersion != txnRef.version()) { + // TODO: None of the tests in the LMDB Store cover this case! + // cursor must be renewed + mdb_cursor_renew(txn, cursor); + if (fetchNext) { + // cursor must be positioned on last item, reuse minKeyBuf if available + if (minKeyBuf == null) { + minKeyBuf = pool.getKeyBuffer(); + } + minKeyBuf.clear(); + index.toKey(minKeyBuf, quad[0], quad[1], quad[2], quad[3]); + minKeyBuf.flip(); + keyData.mv_data(minKeyBuf); + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET); + if (lastResult != MDB_SUCCESS) { + // use MDB_SET_RANGE if key was deleted + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + } + if (lastResult != MDB_SUCCESS) { + closeInternal(false); + return null; + } + } + // update version of txn ref + this.txnRefVersion = txnRef.version(); + } + + if (fetchNext) { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + fetchNext = false; + } else { + if (minKeyBuf != null) { + // set cursor to min key + keyData.mv_data(minKeyBuf); + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_SET_RANGE); + } else { + // set cursor to first item + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + } + } + + while (lastResult == MDB_SUCCESS) { + // if (maxKey != null && TripleStore.COMPARATOR.compare(keyData.mv_data(), maxKey.mv_data()) > 0) { + if (maxKey != null && mdb_cmp(txn, dbi, keyData, maxKey) > 0) { + lastResult = MDB_NOTFOUND; + } else if (matches()) { + lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT); + } else { + // Matching value found + index.keyToQuad(keyData.mv_data(), subj, pred, obj, context, quad); + // fetch next value + fetchNext = true; + return quad; + } + } + closeInternal(false); + return null; + } finally { + manager.unlockRead(readStamp); + } + } + + private boolean matches() { + // When there are no late-bound variables beyond the contiguous prefix, range bounds fully determine matches. + if (!needMatcher) { + return false; + } + + if (groupMatcher != null) { + return !this.groupMatcher.matches(keyData.mv_data()); + } else if (matchValues) { + this.groupMatcher = index.createMatcher(subj, pred, obj, context); + return !this.groupMatcher.matches(keyData.mv_data()); + } else { + return false; + } + } + + private void closeInternal(boolean maybeCalledAsync) { + StampedLongAdderLockManager manager = this.txnLockManager; + if (closed) { + return; + } + long writeStamp = 0L; + boolean writeLocked = false; + if (maybeCalledAsync && ownerThread != Thread.currentThread() && manager != null) { + try { + writeStamp = manager.writeLock(); + writeLocked = true; + } catch (InterruptedException e) { + throw new SailException(e); + } + } + try { + if (cursor != 0L && txnRef != null) { + if (txnRef.isReadOnly()) { + pool.freeCursor(dbi, index, cursor); + } else { + mdb_cursor_close(cursor); + } + cursor = 0L; + } + if (keyData != null) { + pool.free(keyData); + keyData = null; + } + if (valueData != null) { + pool.free(valueData); + valueData = null; + } + if (minKeyBuf != null && !externalMinKeyBuf) { + pool.free(minKeyBuf); + } + if (maxKeyBuf != null && !externalMaxKeyBuf) { + pool.free(maxKeyBuf); + } + if (maxKey != null) { + pool.free(maxKey); + maxKey = null; + } + minKeyBuf = null; + maxKeyBuf = null; + externalMinKeyBuf = false; + externalMaxKeyBuf = false; + groupMatcher = null; + fetchNext = false; + lastResult = 0; + matchValues = false; + needMatcher = false; + txnRef = null; + txn = 0L; + dbi = 0; + index = null; + } finally { + closed = true; + if (writeLocked) { + manager.unlockWrite(writeStamp); + } + txnLockManager = null; + } + } + + @Override + public void close() { + closeInternal(true); + } +} diff --git a/tmp/LmdbRecordIteratorLateBindingTest.original.java b/tmp/LmdbRecordIteratorLateBindingTest.original.java new file mode 100644 index 00000000000..4f0ce6f9f91 --- /dev/null +++ b/tmp/LmdbRecordIteratorLateBindingTest.original.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.sail.lmdb; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn; +import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class LmdbRecordIteratorLateBindingTest { + + @TempDir + File dataDir; + + private TripleStore tripleStore; + + @BeforeEach + void setUp() throws Exception { + tripleStore = new TripleStore(dataDir, new LmdbStoreConfig("spoc,posc"), null); + + tripleStore.startTransaction(); + tripleStore.storeTriple(1, 2, 3, 0, true); + tripleStore.storeTriple(1, 5, 6, 0, true); + tripleStore.storeTriple(1, 6, 9, 0, true); + tripleStore.storeTriple(2, 5, 6, 0, true); + tripleStore.commit(); + } + + @AfterEach + void tearDown() throws Exception { + if (tripleStore != null) { + tripleStore.close(); + } + } + + @Test + void subjectObjectPatternStillFiltersWithMatcher() throws Exception { + try (Txn txn = tripleStore.getTxnManager().createReadTxn(); + LmdbRecordIterator iter = (LmdbRecordIterator) tripleStore.getTriples(txn, 1, -1, 6, -1, true)) { + assertThat(iter).isInstanceOf(LmdbRecordIterator.class); + assertThat(getBooleanField(iter, "needMatcher")).isTrue(); + + List seen = new ArrayList<>(); + long[] next; + while ((next = iter.next()) != null) { + seen.add(next.clone()); + } + + assertThat(seen).containsExactly(new long[] { 1, 5, 6, 0 }); + assertThat(getField(iter, "groupMatcher")).isNotNull(); + } + } + + private static boolean getBooleanField(Object instance, String name) throws Exception { + Field field = LmdbRecordIterator.class.getDeclaredField(name); + field.setAccessible(true); + return field.getBoolean(instance); + } + + private static Object getField(Object instance, String name) throws Exception { + Field field = LmdbRecordIterator.class.getDeclaredField(name); + field.setAccessible(true); + return field.get(instance); + } +}