Repository: BlueBrain/nexus-forge (language: python)

Issue:
Forge elastic does not propertly handle the results 
Currently, `forge.elastic` returns raw Elasticsearch results, e.g.

```
{
    _id: <index_id>,
    _score: 0.00080160325,
    _source: <indexed_resource>,
   ....
}
```

We want to hide by default the ES result fields (`_id`, `_index`, `_score`) as we did for `forge.sparql`. Scores and other ES result fields can be obtained through `result._store_metadata`.


Discussion / hints:

Fix patch:
diff --git a/docs/source/interaction.rst b/docs/source/interaction.rst
index 9f0532c3..0463f877 100644
--- a/docs/source/interaction.rst
+++ b/docs/source/interaction.rst
@@ -226,15 +226,16 @@ SPARQL query if supported by the store (4) or by using an ElasticSearch query if
    forge.elastic(query: str, debug: bool, limit: int, offset: int = None) -> List[Resource] # for elasticsearch query
    forge.download(data: Union[Resource, List[Resource]], follow: str, path: str, overwrite: bool = False, cross_bucket: bool = False) -> None
 
-Currently `forge.search(*filters, **params)` will rewrite the filters as a SPARQL query.
+Currently `forge.search(*filters, **params)` will by default rewrite the filters as a SPARQL query and run it against a configured SPARQL endpoint unless `sparql_endpoint='elastic'` is set and an ElasticSearch search endpoint configured.
 When the `cross_bucket=True` param is set, then it can be complemented with a 'bucket=<str>' param to filter the bucket to search in.
 
-Next are examples of search calls:
+Next are examples of search calls with different query syntax:
 
 .. code-block:: python
 
     from kgforge.core import KnowledgeGraphForge
     from kgforge.specializations.resources import Dataset
+    # see https://github.com/BlueBrain/nexus-forge/blob/master/examples/notebooks/use-cases/prod-forge-nexus.yml for a full forge config example.
     forge = KnowledgeGraphForge(configuration="./config.yml", **kwargs)
 
     # Retrieve by id at a given version
@@ -246,7 +247,10 @@ Next are examples of search calls:
     distinct_results_filters = forge.search(filters, limit=10, offset=0, distinct=True)
     # Filter by type using a paths
     paths = forge.paths("Dataset")
-    result_paths = forge.search(paths.type=="Dataset", limit=10, offset=0, deprecated=False)
+    result_paths = forge.search(paths.type=="Dataset", limit=10, offset=0, deprecated=False) # all python comparison operators are supported
+    # Filter by type using the built-in forge Filter class and hit a configured Elasticsearch search endpoint
+    filters = Filter(operator="__eq__", path=["type"], value="Dataset") # supported operators can be obtained by running [f"{op.value} ({op.name})" for op in FilterOperator]
+    result_paths = forge.search(filters, search_endpoint="elastic")
 
 Versioning
 ----------
diff --git a/examples/notebooks/getting-started/00 - Initialization.ipynb b/examples/notebooks/getting-started/00 - Initialization.ipynb
index f737cea6..aec0e2de 100644
--- a/examples/notebooks/getting-started/00 - Initialization.ipynb
+++ b/examples/notebooks/getting-started/00 - Initialization.ipynb
@@ -36,7 +36,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -221,13 +221,13 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
     "endpoint = \"https://sandbox.bluebrainnexus.io/v1\"\n",
     "org =\"github-users\"\n",
-    "project =\"mfsy\"  # Provide here the automatically created project name corresponding to your login when you logged in the Nexus sandbox instance. "
+    "project =\"mfsy\"  # Provide here the automatically created project name corresponding to your Github login when you logged in the Nexus sandbox instance.\n"
    ]
   },
   {
@@ -260,7 +260,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -269,7 +269,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -278,16 +278,28 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Cloning into '../../models/neuroshapes'...\n",
+      "remote: Enumerating objects: 8415, done.\u001b[K\n",
+      "remote: Total 8415 (delta 0), reused 0 (delta 0), pack-reused 8415\u001b[K\n",
+      "Receiving objects: 100% (8415/8415), 7.57 MiB | 5.72 MiB/s, done.\n",
+      "Resolving deltas: 100% (4188/4188), done.\n"
+     ]
+    }
+   ],
    "source": [
     "! git clone https://github.com/INCF/neuroshapes.git $neuroshapes_path"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -301,7 +313,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -364,7 +376,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -374,6 +386,9 @@
     "    \"searchendpoints\":{\n",
     "        \"sparql\":{\n",
     "            \"endpoint\":\"https://bluebrain.github.io/nexus/vocabulary/defaultSparqlIndex\"\n",
+    "        },\n",
+    "        \"elastic\":{\n",
+    "            \"endpoint\":\"https://bluebrain.github.io/nexus/vocabulary/defaultElasticSearchIndex\"\n",
     "        }\n",
     "    },\n",
     "    \"bucket\": f\"{org}/{project}\",\n",
@@ -474,7 +489,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -533,7 +548,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -551,7 +566,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -560,7 +575,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -571,7 +586,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -585,7 +600,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/01 - Resources.ipynb b/examples/notebooks/getting-started/01 - Resources.ipynb
index 389c45e5..63cd1788 100644
--- a/examples/notebooks/getting-started/01 - Resources.ipynb
+++ b/examples/notebooks/getting-started/01 - Resources.ipynb
@@ -11,12 +11,12 @@
    "source": [
     "# Resources\n",
     "\n",
-    "A [Resource](https://nexus-forge.readthedocs.io/en/latest/interaction.html#resource) is an identifiable data object with a set of properties. This notebook shows how to create a resource from keyword arguments, JSON dictionary, or pandas dataframe."
+    "A [Resource](https://nexus-forge.readthedocs.io/en/latest/interaction.html#resource) is the base data exchange object in Nexus Forge. It is an identifiable data object with potentially a set of properties as metadata. This notebook shows how to create a resource from keyword arguments, JSON dictionary, or pandas dataframe."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 1,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.068658Z",
@@ -37,7 +37,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -53,7 +53,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 3,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.127987Z",
@@ -83,7 +83,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 4,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.143688Z",
@@ -97,7 +97,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 5,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.154752Z",
@@ -134,7 +134,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
     {
@@ -170,7 +170,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
@@ -208,7 +208,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
@@ -299,7 +299,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 9,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.261616Z",
@@ -320,7 +320,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 10,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.187035Z",
@@ -334,7 +334,7 @@
        "'jane.doe@epfl.ch'"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 10,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -345,7 +345,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 11,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.207733Z",
@@ -359,7 +359,7 @@
        "'jane.doe@epfl.ch'"
       ]
      },
-     "execution_count": 15,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -377,7 +377,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [
     {
@@ -398,7 +398,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 13,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.332705Z",
@@ -429,7 +429,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -443,7 +443,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/02 - Datasets.ipynb b/examples/notebooks/getting-started/02 - Datasets.ipynb
index 33b1c5a1..5358a049 100644
--- a/examples/notebooks/getting-started/02 - Datasets.ipynb
+++ b/examples/notebooks/getting-started/02 - Datasets.ipynb
@@ -11,12 +11,12 @@
    "source": [
     "# Datasets\n",
     "\n",
-    "A [Dataset](https://nexus-forge.readthedocs.io/en/latest/interaction.html#dataset) is a specialization of a [Resource](https://nexus-forge.readthedocs.io/en/latest/interaction.html#resource) that aims to register (upload) files with eventually metadata in a configured store."
+    "A [Dataset](https://nexus-forge.readthedocs.io/en/latest/interaction.html#dataset) is a specialization of a [Resource](https://nexus-forge.readthedocs.io/en/latest/interaction.html#resource) that provides users with operations to handle files, record their provenance and describe them with metadata."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.068658Z",
@@ -39,7 +39,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -55,7 +55,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -64,7 +64,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -73,7 +73,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -89,7 +89,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 66,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
     {
@@ -110,7 +110,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 67,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -119,7 +119,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 68,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -128,7 +128,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 69,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [
     {
@@ -146,18 +146,40 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 10,
    "metadata": {
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/16ff246a-50f5-4ce5-b6ca-d874bc1f3760',\n",
+       " 'type': 'Dataset',\n",
+       " 'hasPart': {'distribution': {'type': 'DataDownload',\n",
+       "   'atLocation': {'type': 'Location',\n",
+       "    'store': {'id': 'https://bluebrain.github.io/nexus/vocabulary/diskStorageDefault'}},\n",
+       "   'contentSize': {'unitCode': 'bytes', 'value': 52},\n",
+       "   'contentUrl': 'https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/1ef5cc2a-16ac-4105-80b2-8fd64fcd8f02',\n",
+       "   'digest': {'algorithm': 'SHA-256',\n",
+       "    'value': '1dacd765946963fda4949753659089c5f532714b418d30788bedddfec47a389f'},\n",
+       "   'encodingFormat': 'text/csv',\n",
+       "   'name': 'persons.csv'}},\n",
+       " 'name': 'Interesting Persons'}"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "forge.as_json(persons)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 71,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -166,7 +188,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 72,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -175,7 +197,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 73,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -184,7 +206,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 74,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
@@ -202,23 +224,50 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 15,
    "metadata": {
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/76cbbcf7-deb9-4b84-8a3e-c0a284e03b9e',\n",
+       " 'type': 'Dataset',\n",
+       " 'derivation': {'type': 'Derivation',\n",
+       "  'entity': {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/16ff246a-50f5-4ce5-b6ca-d874bc1f3760?rev=1',\n",
+       "   'type': 'Dataset',\n",
+       "   'name': 'Interesting Persons'}},\n",
+       " 'hasPart': {'distribution': {'type': 'DataDownload',\n",
+       "   'atLocation': {'type': 'Location',\n",
+       "    'store': {'id': 'https://bluebrain.github.io/nexus/vocabulary/diskStorageDefault'}},\n",
+       "   'contentSize': {'unitCode': 'bytes', 'value': 477},\n",
+       "   'contentUrl': 'https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/76ca0b51-5a8d-46b7-8a91-29f211cb12bb',\n",
+       "   'digest': {'algorithm': 'SHA-256',\n",
+       "    'value': '789aa07948683fe036ac29811814a826b703b562f7d168eb70dee1fabde26859'},\n",
+       "   'encodingFormat': 'text/tab-separated-values',\n",
+       "   'name': 'associations.tsv'}},\n",
+       " 'name': 'Associations data'}"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "forge.as_json(associations)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 77,
+   "execution_count": 32,
    "metadata": {},
    "outputs": [],
    "source": [
     "# The argument overwrite: bool can be provided to decide whether to overwrite (True) existing files with the same name or\n",
     "# to create new ones (False) with their names suffixed with a timestamp.\n",
+    "\n",
     "# A cross_bucket argument can be provided to download data from the configured bucket (cross_bucket=False - the default value) \n",
     "# or from a bucket different than the configured one (cross_bucket=True). The configured store should support crossing buckets for this to work.\n",
     "associations.download(path=\"./downloaded/\", source=\"parts\")"
@@ -226,15 +275,16 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 78,
+   "execution_count": 33,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "total 8\n",
-      "-rw-r--r--  1 mfsy  staff  506 Aug 23 11:18 associations.tsv\n"
+      "total 16\n",
+      "-rw-r--r--  1 mfsy  staff  477 Jan  6 16:39 associations.tsv\n",
+      "-rw-r--r--  1 mfsy  staff  477 Jan  6 16:42 associations.tsv.20220106164227\n"
      ]
     }
    ],
@@ -244,7 +294,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -260,7 +310,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 79,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -269,7 +319,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 80,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -278,7 +328,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 81,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -287,7 +337,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 82,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -296,7 +346,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 83,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -305,7 +355,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 84,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [
     {
@@ -324,7 +374,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 85,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -333,7 +383,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 86,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -342,7 +392,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 87,
+   "execution_count": 30,
    "metadata": {},
    "outputs": [
     {
@@ -360,18 +410,39 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 31,
    "metadata": {
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/3a650ade-4cb7-4b53-82e3-b8880818b010',\n",
+       " 'type': 'Dataset',\n",
+       " 'hasPart': [{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/943b282b-c902-4860-a6f4-45e0940f4956?rev=1',\n",
+       "   'type': 'Person',\n",
+       "   'distribution': {'contentUrl': 'https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/4d5d38d4-2b79-4385-b5d1-da18bd4119cc'},\n",
+       "   'name': 'Jane Doe'},\n",
+       "  {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/6c0a27b5-ad35-4627-adee-5b8124215aff?rev=1',\n",
+       "   'type': 'Person',\n",
+       "   'distribution': {'contentUrl': 'https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/c47859af-218e-423c-a97f-076d16902360'},\n",
+       "   'name': 'John Smith'}],\n",
+       " 'name': 'Interesting people'}"
+      ]
+     },
+     "execution_count": 31,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "forge.as_json(dataset)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 88,
+   "execution_count": 34,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -380,17 +451,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 89,
+   "execution_count": 35,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "total 24\n",
-      "-rw-r--r--  1 mfsy  staff  506 Aug 23 11:18 associations.tsv\n",
-      "-rw-r--r--  1 mfsy  staff  506 Aug 23 11:18 associations.tsv.20210823111849\n",
-      "-rw-r--r--  1 mfsy  staff   52 Aug 23 11:18 persons.csv\n"
+      "total 32\n",
+      "-rw-r--r--  1 mfsy  staff  477 Jan  6 16:39 associations.tsv\n",
+      "-rw-r--r--  1 mfsy  staff  477 Jan  6 16:42 associations.tsv.20220106164227\n",
+      "-rw-r--r--  1 mfsy  staff  477 Jan  6 16:42 associations.tsv.20220106164237\n",
+      "-rw-r--r--  1 mfsy  staff   52 Jan  6 16:42 persons.csv\n"
      ]
     }
    ],
@@ -416,9 +488,76 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 36,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "{\n",
+      "    id: https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/943b282b-c902-4860-a6f4-45e0940f4956\n",
+      "    type: Person\n",
+      "    distribution:\n",
+      "    {\n",
+      "        type: DataDownload\n",
+      "        atLocation:\n",
+      "        {\n",
+      "            type: Location\n",
+      "            store:\n",
+      "            {\n",
+      "                id: https://bluebrain.github.io/nexus/vocabulary/diskStorageDefault\n",
+      "            }\n",
+      "        }\n",
+      "        contentSize:\n",
+      "        {\n",
+      "            unitCode: bytes\n",
+      "            value: 477\n",
+      "        }\n",
+      "        contentUrl: https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/4d5d38d4-2b79-4385-b5d1-da18bd4119cc\n",
+      "        digest:\n",
+      "        {\n",
+      "            algorithm: SHA-256\n",
+      "            value: 789aa07948683fe036ac29811814a826b703b562f7d168eb70dee1fabde26859\n",
+      "        }\n",
+      "        encodingFormat: text/tab-separated-values\n",
+      "        name: associations.tsv\n",
+      "    }\n",
+      "    name: Jane Doe\n",
+      "}\n",
+      "{\n",
+      "    id: https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/6c0a27b5-ad35-4627-adee-5b8124215aff\n",
+      "    type: Person\n",
+      "    distribution:\n",
+      "    {\n",
+      "        type: DataDownload\n",
+      "        atLocation:\n",
+      "        {\n",
+      "            type: Location\n",
+      "            store:\n",
+      "            {\n",
+      "                id: https://bluebrain.github.io/nexus/vocabulary/diskStorageDefault\n",
+      "            }\n",
+      "        }\n",
+      "        contentSize:\n",
+      "        {\n",
+      "            unitCode: bytes\n",
+      "            value: 52\n",
+      "        }\n",
+      "        contentUrl: https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/c47859af-218e-423c-a97f-076d16902360\n",
+      "        digest:\n",
+      "        {\n",
+      "            algorithm: SHA-256\n",
+      "            value: 1dacd765946963fda4949753659089c5f532714b418d30788bedddfec47a389f\n",
+      "        }\n",
+      "        encodingFormat: text/csv\n",
+      "        name: persons.csv\n",
+      "    }\n",
+      "    name: John Smith\n",
+      "}\n"
+     ]
+    }
+   ],
    "source": [
     "dataset = Dataset.from_resource(forge, [jane, john], store_metadata=True)\n",
     "print(*dataset, sep=\"\\n\")"
@@ -447,7 +586,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 93,
+   "execution_count": 37,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -456,7 +595,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 94,
+   "execution_count": 38,
    "metadata": {},
    "outputs": [
     {
@@ -505,7 +644,7 @@
        "1  Person  Albert Einstein"
       ]
      },
-     "execution_count": 94,
+     "execution_count": 38,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -516,7 +655,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 95,
+   "execution_count": 39,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -525,7 +664,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 96,
+   "execution_count": 40,
    "metadata": {},
    "outputs": [
     {
@@ -544,7 +683,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 97,
+   "execution_count": 41,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -553,7 +692,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 98,
+   "execution_count": 42,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -562,7 +701,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 99,
+   "execution_count": 43,
    "metadata": {},
    "outputs": [
     {
@@ -580,11 +719,30 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 44,
    "metadata": {
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/03e7a974-e2b4-4d67-8db4-b37448ef3ac2',\n",
+       " 'type': 'Dataset',\n",
+       " 'hasPart': [{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c30555ed-ba4e-4ec6-9ca9-7d99c54173cf?rev=1',\n",
+       "   'type': 'Person',\n",
+       "   'name': 'Marie Curie'},\n",
+       "  {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/81dc5691-db2e-4dea-ad9c-c2f58ed55a47?rev=1',\n",
+       "   'type': 'Person',\n",
+       "   'name': 'Albert Einstein'}],\n",
+       " 'name': 'Interesting people'}"
+      ]
+     },
+     "execution_count": 44,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "forge.as_json(dataset)"
    ]
@@ -598,7 +756,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 108,
+   "execution_count": 45,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -607,7 +765,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 109,
+   "execution_count": 46,
    "metadata": {},
    "outputs": [
     {
@@ -685,7 +843,7 @@
        "1                 male  ../../data/scientists-database/albert_einstein...  "
       ]
      },
-     "execution_count": 109,
+     "execution_count": 46,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -696,7 +854,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 110,
+   "execution_count": 47,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -705,7 +863,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 111,
+   "execution_count": 48,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -714,7 +872,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 112,
+   "execution_count": 49,
    "metadata": {},
    "outputs": [
     {
@@ -762,7 +920,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 113,
+   "execution_count": 50,
    "metadata": {},
    "outputs": [
     {
@@ -781,7 +939,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 114,
+   "execution_count": 51,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -790,7 +948,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 115,
+   "execution_count": 52,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -799,7 +957,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 116,
+   "execution_count": 53,
    "metadata": {},
    "outputs": [
     {
@@ -817,11 +975,32 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 54,
    "metadata": {
     "scrolled": true
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/19bd480f-6036-4197-a710-8b2cd0777600',\n",
+       " 'type': 'Dataset',\n",
+       " 'hasPart': [{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/1d21c77b-8384-48b2-8069-dbbbb82b8332?rev=1',\n",
+       "   'type': 'Association',\n",
+       "   'distribution': {'contentUrl': 'https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/7f84cf2b-a251-404a-b66c-ab80af7b24af'},\n",
+       "   'name': 'Curie Association'},\n",
+       "  {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/70dada05-46dd-4be4-b3a2-9d9bc1da6d07?rev=1',\n",
+       "   'type': 'Association',\n",
+       "   'distribution': {'contentUrl': 'https://sandbox.bluebrainnexus.io/v1/files/github-users/mfsy/95b275f9-0326-4651-95db-368362a61126'},\n",
+       "   'name': 'Einstein Association'}],\n",
+       " 'name': 'Interesting associations'}"
+      ]
+     },
+     "execution_count": 54,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "forge.as_json(dataset)"
    ]
@@ -829,7 +1008,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -843,7 +1022,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/03 - Storing.ipynb b/examples/notebooks/getting-started/03 - Storing.ipynb
index 9c9c9a9b..05d6cd0f 100644
--- a/examples/notebooks/getting-started/03 - Storing.ipynb
+++ b/examples/notebooks/getting-started/03 - Storing.ipynb
@@ -11,7 +11,7 @@
    "source": [
     "# Storing\n",
     "\n",
-    "This notebook demonstrates [Storing](https://nexus-forge.readthedocs.io/en/latest/interaction.html#storing) features."
+    "This notebook demonstrates [Storing](https://nexus-forge.readthedocs.io/en/latest/interaction.html#storing) features. Storing allows users to persist and manage Resources in the configured store."
    ]
   },
   {
@@ -76,7 +76,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -85,7 +85,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 28,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -110,7 +110,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 29,
+   "execution_count": 6,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.048776Z",
@@ -133,7 +133,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 30,
+   "execution_count": 7,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.101282Z",
@@ -147,7 +147,7 @@
        "True"
       ]
      },
-     "execution_count": 30,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -158,7 +158,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 31,
+   "execution_count": 8,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.086424Z",
@@ -173,7 +173,7 @@
        "Action(error=None, message=None, operation='_register_one', succeeded=True)"
       ]
      },
-     "execution_count": 31,
+     "execution_count": 8,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -205,7 +205,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 33,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -214,7 +214,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 34,
+   "execution_count": 11,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.109091Z",
@@ -228,7 +228,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 35,
+   "execution_count": 12,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.121071Z",
@@ -242,7 +242,7 @@
        "False"
       ]
      },
-     "execution_count": 35,
+     "execution_count": 12,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -260,7 +260,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 36,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -269,7 +269,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 37,
+   "execution_count": 14,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.132160Z",
@@ -292,7 +292,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 38,
+   "execution_count": 15,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.141175Z",
@@ -321,7 +321,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 39,
+   "execution_count": 16,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.150719Z",
@@ -335,7 +335,7 @@
        "False"
       ]
      },
-     "execution_count": 39,
+     "execution_count": 16,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -346,7 +346,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 40,
+   "execution_count": 17,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.159904Z",
@@ -360,7 +360,7 @@
        "True"
       ]
      },
-     "execution_count": 40,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -385,7 +385,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 41,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -394,7 +394,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 42,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -403,7 +403,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 43,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -428,7 +428,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 44,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -437,7 +437,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 46,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [
     {
@@ -459,7 +459,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 47,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [
     {
@@ -484,7 +484,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 48,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -493,7 +493,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 49,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -502,7 +502,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 50,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [
     {
@@ -520,7 +520,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 51,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [
     {
@@ -542,7 +542,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 52,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -551,7 +551,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 53,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -560,7 +560,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 54,
+   "execution_count": 31,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.190111Z",
@@ -583,7 +583,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 55,
+   "execution_count": 32,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.199561Z",
@@ -594,21 +594,21 @@
     {
      "data": {
       "text/plain": [
-       "True"
+       "False"
       ]
      },
-     "execution_count": 55,
+     "execution_count": 32,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "association._synchronized"
+    "association.agent._synchronized"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 56,
+   "execution_count": 33,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.217024Z",
@@ -642,7 +642,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 59,
+   "execution_count": 34,
    "metadata": {},
    "outputs": [
     {
@@ -663,7 +663,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 60,
+   "execution_count": 35,
    "metadata": {},
    "outputs": [
     {
@@ -681,7 +681,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 61,
+   "execution_count": 36,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.276769Z",
@@ -708,7 +708,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 62,
+   "execution_count": 37,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.286647Z",
@@ -731,7 +731,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 63,
+   "execution_count": 38,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.297768Z",
@@ -759,7 +759,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -773,7 +773,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/04 - Querying.ipynb b/examples/notebooks/getting-started/04 - Querying.ipynb
index eb5b095b..18cfc078 100644
--- a/examples/notebooks/getting-started/04 - Querying.ipynb
+++ b/examples/notebooks/getting-started/04 - Querying.ipynb
@@ -16,7 +16,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.068658Z",
@@ -37,7 +37,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 162,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -46,19 +46,23 @@
   },
   {
    "cell_type": "markdown",
-   "metadata": {},
+   "metadata": {
+    "toc-hr-collapsed": true,
+    "toc-nb-collapsed": true
+   },
    "source": [
     "## Imports"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 66,
    "metadata": {},
    "outputs": [],
    "source": [
     "from kgforge.core import Resource\n",
-    "from kgforge.specializations.resources import Dataset"
+    "from kgforge.specializations.resources import Dataset\n",
+    "from kgforge.core.wrappings.paths import Filter, FilterOperator"
    ]
   },
   {
@@ -77,7 +81,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -86,7 +90,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -104,7 +108,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -113,16 +117,16 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "True"
+       "False"
       ]
      },
-     "execution_count": 9,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -140,7 +144,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -149,7 +153,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
@@ -167,7 +171,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
@@ -185,7 +189,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -194,7 +198,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
@@ -212,7 +216,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 22,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.317601Z",
@@ -239,7 +243,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 23,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.332678Z",
@@ -253,7 +257,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 24,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.370051Z",
@@ -267,7 +271,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 25,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.379911Z",
@@ -281,7 +285,7 @@
        "True"
       ]
      },
-     "execution_count": 18,
+     "execution_count": 25,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -300,7 +304,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -316,7 +320,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [
     {
@@ -335,7 +339,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
@@ -344,7 +348,7 @@
        "True"
       ]
      },
-     "execution_count": 21,
+     "execution_count": 28,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -369,7 +373,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -379,7 +383,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 30,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -389,7 +393,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 31,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -399,7 +403,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 32,
    "metadata": {},
    "outputs": [
     {
@@ -435,14 +439,14 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The `paths` method load the data structure for the given type.\n",
+    "The `paths` method load the template or property paths for a given type.\n",
     "\n",
-    "Please refer to the [Modeling.ipynb](11%20-%20Modeling.ipynb) notebook to learn about modeling and types."
+    "Please refer to the [Modeling.ipynb](11%20-%20Modeling.ipynb) notebook to learn about templates and types."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 41,
+   "execution_count": 34,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -463,9 +467,16 @@
     "Note: There is a known issue for RdfModel which requires using `p.type.id` instead of `p.type`."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "All [python comparison operators](https://www.w3schools.com/python/gloss_python_comparison_operators.asp) are supported."
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 42,
+   "execution_count": 80,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -474,7 +485,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 43,
+   "execution_count": 81,
    "metadata": {},
    "outputs": [
     {
@@ -483,7 +494,7 @@
        "list"
       ]
      },
-     "execution_count": 43,
+     "execution_count": 81,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -494,7 +505,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 44,
+   "execution_count": 37,
    "metadata": {},
    "outputs": [
     {
@@ -503,7 +514,7 @@
        "3"
       ]
      },
-     "execution_count": 44,
+     "execution_count": 37,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -514,7 +525,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 45,
+   "execution_count": 38,
    "metadata": {},
    "outputs": [
     {
@@ -540,6 +551,8 @@
        "      <th></th>\n",
        "      <th>id</th>\n",
        "      <th>type</th>\n",
+       "      <th>_schemaProject</th>\n",
+       "      <th>name</th>\n",
        "      <th>distribution.type</th>\n",
        "      <th>distribution.atLocation.type</th>\n",
        "      <th>distribution.atLocation.store.id</th>\n",
@@ -550,7 +563,6 @@
        "      <th>distribution.digest.value</th>\n",
        "      <th>distribution.encodingFormat</th>\n",
        "      <th>distribution.name</th>\n",
-       "      <th>name</th>\n",
        "    </tr>\n",
        "  </thead>\n",
        "  <tbody>\n",
@@ -558,38 +570,25 @@
        "      <th>0</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
-       "      <td>DataDownload</td>\n",
-       "      <td>Location</td>\n",
-       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
-       "      <td>bytes</td>\n",
-       "      <td>506.0</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
-       "      <td>SHA-256</td>\n",
-       "      <td>9639abc864e91c645779f510ae5c06a1618941d569eb1a...</td>\n",
-       "      <td>text/tab-separated-values</td>\n",
-       "      <td>associations.tsv</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>Jane Doe</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
-       "      <td>DataDownload</td>\n",
-       "      <td>Location</td>\n",
-       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
-       "      <td>bytes</td>\n",
-       "      <td>126.0</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
-       "      <td>SHA-256</td>\n",
-       "      <td>8eae22fcd5e3b14fd6df3d45b005bda759e78bf8267b63...</td>\n",
-       "      <td>text/csv</td>\n",
-       "      <td>persons.csv</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>John Smith</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>https://www.wikidata.org/wiki/Q937</td>\n",
-       "      <td>Person</td>\n",
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
@@ -600,7 +599,23 @@
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
-       "      <td>Albert Einstein</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>52.0</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>1dacd765946963fda4949753659089c5f532714b418d30...</td>\n",
+       "      <td>text/csv</td>\n",
+       "      <td>persons.csv</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
@@ -610,45 +625,50 @@
        "                                                  id    type  \\\n",
        "0  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "1  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
-       "2                 https://www.wikidata.org/wiki/Q937  Person   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
+       "\n",
+       "                                      _schemaProject        name  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...    Jane Doe   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...  John Smith   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...    Jane Doe   \n",
        "\n",
        "  distribution.type distribution.atLocation.type  \\\n",
-       "0      DataDownload                     Location   \n",
-       "1      DataDownload                     Location   \n",
-       "2               NaN                          NaN   \n",
+       "0               NaN                          NaN   \n",
+       "1               NaN                          NaN   \n",
+       "2      DataDownload                     Location   \n",
        "\n",
        "                    distribution.atLocation.store.id  \\\n",
-       "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
-       "1  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
-       "2                                                NaN   \n",
+       "0                                                NaN   \n",
+       "1                                                NaN   \n",
+       "2  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
        "\n",
        "  distribution.contentSize.unitCode  distribution.contentSize.value  \\\n",
-       "0                             bytes                           506.0   \n",
-       "1                             bytes                           126.0   \n",
-       "2                               NaN                             NaN   \n",
+       "0                               NaN                             NaN   \n",
+       "1                               NaN                             NaN   \n",
+       "2                             bytes                            52.0   \n",
        "\n",
        "                             distribution.contentUrl  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
-       "2                                                NaN   \n",
+       "0                                                NaN   \n",
+       "1                                                NaN   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
        "\n",
        "  distribution.digest.algorithm  \\\n",
-       "0                       SHA-256   \n",
-       "1                       SHA-256   \n",
-       "2                           NaN   \n",
+       "0                           NaN   \n",
+       "1                           NaN   \n",
+       "2                       SHA-256   \n",
        "\n",
        "                           distribution.digest.value  \\\n",
-       "0  9639abc864e91c645779f510ae5c06a1618941d569eb1a...   \n",
-       "1  8eae22fcd5e3b14fd6df3d45b005bda759e78bf8267b63...   \n",
-       "2                                                NaN   \n",
+       "0                                                NaN   \n",
+       "1                                                NaN   \n",
+       "2  1dacd765946963fda4949753659089c5f532714b418d30...   \n",
        "\n",
-       "  distribution.encodingFormat distribution.name             name  \n",
-       "0   text/tab-separated-values  associations.tsv         Jane Doe  \n",
-       "1                    text/csv       persons.csv       John Smith  \n",
-       "2                         NaN               NaN  Albert Einstein  "
+       "  distribution.encodingFormat distribution.name  \n",
+       "0                         NaN               NaN  \n",
+       "1                         NaN               NaN  \n",
+       "2                    text/csv       persons.csv  "
       ]
      },
-     "execution_count": 45,
+     "execution_count": 38,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -659,29 +679,16 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 46,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{'id': 'https://www.wikidata.org/wiki/Q937',\n",
-       " 'type': 'Person',\n",
-       " 'name': 'Albert Einstein'}"
-      ]
-     },
-     "execution_count": 46,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
+   "outputs": [],
    "source": [
     "forge.as_json(resources[2])"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 47,
+   "execution_count": 48,
    "metadata": {},
    "outputs": [
     {
@@ -707,6 +714,15 @@
        "      <th></th>\n",
        "      <th>id</th>\n",
        "      <th>type</th>\n",
+       "      <th>_schemaProject</th>\n",
+       "      <th>name</th>\n",
+       "      <th>_constrainedBy</th>\n",
+       "      <th>_createdAt</th>\n",
+       "      <th>_createdBy</th>\n",
+       "      <th>_deprecated</th>\n",
+       "      <th>_incoming</th>\n",
+       "      <th>_outgoing</th>\n",
+       "      <th>...</th>\n",
        "      <th>distribution.type</th>\n",
        "      <th>distribution.atLocation.type</th>\n",
        "      <th>distribution.atLocation.store.id</th>\n",
@@ -715,17 +731,8 @@
        "      <th>distribution.contentUrl</th>\n",
        "      <th>distribution.digest.algorithm</th>\n",
        "      <th>distribution.digest.value</th>\n",
-       "      <th>...</th>\n",
-       "      <th>_createdBy</th>\n",
-       "      <th>_deprecated</th>\n",
-       "      <th>_incoming</th>\n",
-       "      <th>_outgoing</th>\n",
-       "      <th>_project</th>\n",
-       "      <th>_rev</th>\n",
-       "      <th>_schemaProject</th>\n",
-       "      <th>_self</th>\n",
-       "      <th>_updatedAt</th>\n",
-       "      <th>_updatedBy</th>\n",
+       "      <th>distribution.encodingFormat</th>\n",
+       "      <th>distribution.name</th>\n",
        "    </tr>\n",
        "  </thead>\n",
        "  <tbody>\n",
@@ -733,54 +740,65 @@
        "      <th>0</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
-       "      <td>DataDownload</td>\n",
-       "      <td>Location</td>\n",
-       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
-       "      <td>bytes</td>\n",
-       "      <td>506.0</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
-       "      <td>SHA-256</td>\n",
-       "      <td>9639abc864e91c645779f510ae5c06a1618941d569eb1a...</td>\n",
-       "      <td>...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/schemas/unco...</td>\n",
+       "      <td>2022-01-06T15:46:40.285Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "      <td>False</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
-       "      <td>1</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>2021-08-17T11:00:14.662Z</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>...</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/schemas/unco...</td>\n",
+       "      <td>2022-01-06T15:47:00.719Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
        "      <td>bytes</td>\n",
-       "      <td>126.0</td>\n",
+       "      <td>52.0</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
        "      <td>SHA-256</td>\n",
-       "      <td>8eae22fcd5e3b14fd6df3d45b005bda759e78bf8267b63...</td>\n",
-       "      <td>...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
-       "      <td>False</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
-       "      <td>1</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>2021-08-17T11:00:14.664Z</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>1dacd765946963fda4949753659089c5f532714b418d30...</td>\n",
+       "      <td>text/csv</td>\n",
+       "      <td>persons.csv</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
-       "      <td>https://www.wikidata.org/wiki/Q937</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/schemas/unco...</td>\n",
+       "      <td>2022-01-07T11:26:11.330Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>...</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
@@ -789,17 +807,6 @@
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
        "      <td>NaN</td>\n",
-       "      <td>...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
-       "      <td>False</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
-       "      <td>1</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>2021-08-17T11:00:26.713Z</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
@@ -810,77 +817,77 @@
        "                                                  id    type  \\\n",
        "0  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "1  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
-       "2                 https://www.wikidata.org/wiki/Q937  Person   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "\n",
-       "  distribution.type distribution.atLocation.type  \\\n",
-       "0      DataDownload                     Location   \n",
-       "1      DataDownload                     Location   \n",
-       "2               NaN                          NaN   \n",
+       "                                      _schemaProject      name  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...  Jane Doe   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...  Jane Doe   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...  Jane Doe   \n",
+       "\n",
+       "                                      _constrainedBy  \\\n",
+       "0  https://bluebrain.github.io/nexus/schemas/unco...   \n",
+       "1  https://bluebrain.github.io/nexus/schemas/unco...   \n",
+       "2  https://bluebrain.github.io/nexus/schemas/unco...   \n",
+       "\n",
+       "                 _createdAt  \\\n",
+       "0  2022-01-06T15:46:40.285Z   \n",
+       "1  2022-01-06T15:47:00.719Z   \n",
+       "2  2022-01-07T11:26:11.330Z   \n",
+       "\n",
+       "                                          _createdBy  _deprecated  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/realms/gi...        False   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/realms/gi...        False   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/realms/gi...        False   \n",
+       "\n",
+       "                                           _incoming  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                                           _outgoing  ... distribution.type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...  ...               NaN   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...  ...      DataDownload   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  ...               NaN   \n",
+       "\n",
+       "   distribution.atLocation.type  \\\n",
+       "0                           NaN   \n",
+       "1                      Location   \n",
+       "2                           NaN   \n",
        "\n",
        "                    distribution.atLocation.store.id  \\\n",
-       "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
+       "0                                                NaN   \n",
        "1  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
        "2                                                NaN   \n",
        "\n",
-       "  distribution.contentSize.unitCode  distribution.contentSize.value  \\\n",
-       "0                             bytes                           506.0   \n",
-       "1                             bytes                           126.0   \n",
-       "2                               NaN                             NaN   \n",
+       "  distribution.contentSize.unitCode distribution.contentSize.value  \\\n",
+       "0                               NaN                            NaN   \n",
+       "1                             bytes                           52.0   \n",
+       "2                               NaN                            NaN   \n",
        "\n",
        "                             distribution.contentUrl  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
+       "0                                                NaN   \n",
        "1  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
        "2                                                NaN   \n",
        "\n",
        "  distribution.digest.algorithm  \\\n",
-       "0                       SHA-256   \n",
+       "0                           NaN   \n",
        "1                       SHA-256   \n",
        "2                           NaN   \n",
        "\n",
-       "                           distribution.digest.value  ...  \\\n",
-       "0  9639abc864e91c645779f510ae5c06a1618941d569eb1a...  ...   \n",
-       "1  8eae22fcd5e3b14fd6df3d45b005bda759e78bf8267b63...  ...   \n",
-       "2                                                NaN  ...   \n",
-       "\n",
-       "                                          _createdBy _deprecated  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
-       "\n",
-       "                                           _incoming  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "\n",
-       "                                           _outgoing  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "\n",
-       "                                            _project _rev  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "                           distribution.digest.value  \\\n",
+       "0                                                NaN   \n",
+       "1  1dacd765946963fda4949753659089c5f532714b418d30...   \n",
+       "2                                                NaN   \n",
        "\n",
-       "                                      _schemaProject  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/projects/...   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/projects/...   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/projects/...   \n",
-       "\n",
-       "                                               _self  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
-       "\n",
-       "                 _updatedAt                                         _updatedBy  \n",
-       "0  2021-08-17T11:00:14.662Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
-       "1  2021-08-17T11:00:14.664Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
-       "2  2021-08-17T11:00:26.713Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "  distribution.encodingFormat  distribution.name  \n",
+       "0                         NaN                NaN  \n",
+       "1                    text/csv        persons.csv  \n",
+       "2                         NaN                NaN  \n",
        "\n",
        "[3 rows x 25 columns]"
       ]
      },
-     "execution_count": 47,
+     "execution_count": 48,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -893,7 +900,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Nested property querying"
+    "#### Nested property querying"
    ]
   },
   {
@@ -905,17 +912,17 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 50,
+   "execution_count": 41,
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Search for resources of type Dataset and with a attached files of content type text/tab-separated-values\n",
+    "# Search for resources of type Person and with text/tab-separated-values as distribution.encodingFormat\n",
     "resources = forge.search(p.type.id == \"Person\", p.distribution.encodingFormat == \"text/tab-separated-values\", limit=3)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 51,
+   "execution_count": 42,
    "metadata": {},
    "outputs": [
     {
@@ -924,7 +931,7 @@
        "3"
       ]
      },
-     "execution_count": 51,
+     "execution_count": 42,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -935,7 +942,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 52,
+   "execution_count": 43,
    "metadata": {
     "scrolled": true
    },
@@ -963,6 +970,7 @@
        "      <th></th>\n",
        "      <th>id</th>\n",
        "      <th>type</th>\n",
+       "      <th>_schemaProject</th>\n",
        "      <th>distribution.type</th>\n",
        "      <th>distribution.atLocation.type</th>\n",
        "      <th>distribution.atLocation.store.id</th>\n",
@@ -981,6 +989,7 @@
        "      <th>0</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
@@ -997,6 +1006,7 @@
        "      <th>1</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
@@ -1013,6 +1023,7 @@
        "      <th>2</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
@@ -1035,10 +1046,15 @@
        "1  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "2  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "\n",
-       "  distribution.type distribution.atLocation.type  \\\n",
-       "0      DataDownload                     Location   \n",
-       "1      DataDownload                     Location   \n",
-       "2      DataDownload                     Location   \n",
+       "                                      _schemaProject distribution.type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "\n",
+       "  distribution.atLocation.type  \\\n",
+       "0                     Location   \n",
+       "1                     Location   \n",
+       "2                     Location   \n",
        "\n",
        "                    distribution.atLocation.store.id  \\\n",
        "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
@@ -1071,7 +1087,7 @@
        "2   text/tab-separated-values  associations.tsv  Jane Doe  "
       ]
      },
-     "execution_count": 52,
+     "execution_count": 43,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1096,18 +1112,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 60,
+   "execution_count": 49,
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Search for resources of type Dataset and with conribution from agent named \"Jane Doe\"\n",
-    "filters = {\"type\": \"Person\", \"name\":\"Jane Doe\"}\n",
+    "# Search for resources of type Person and with text/tab-separated-values as distribution.encodingFormat\n",
+    "filters = {\"type\": \"Person\", \"distribution\":{\"encodingFormat\":\"text/tab-separated-values\"}}\n",
     "resources = forge.search(filters, limit=3)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 61,
+   "execution_count": 50,
    "metadata": {},
    "outputs": [
     {
@@ -1116,7 +1132,7 @@
        "list"
       ]
      },
-     "execution_count": 61,
+     "execution_count": 50,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1127,7 +1143,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 62,
+   "execution_count": 51,
    "metadata": {},
    "outputs": [
     {
@@ -1136,7 +1152,7 @@
        "3"
       ]
      },
-     "execution_count": 62,
+     "execution_count": 51,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1147,7 +1163,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 63,
+   "execution_count": 52,
    "metadata": {},
    "outputs": [
     {
@@ -1173,6 +1189,7 @@
        "      <th></th>\n",
        "      <th>id</th>\n",
        "      <th>type</th>\n",
+       "      <th>_schemaProject</th>\n",
        "      <th>distribution.type</th>\n",
        "      <th>distribution.atLocation.type</th>\n",
        "      <th>distribution.atLocation.store.id</th>\n",
@@ -1180,15 +1197,14 @@
        "      <th>distribution.contentSize.value</th>\n",
        "      <th>distribution.contentUrl</th>\n",
        "      <th>distribution.digest.algorithm</th>\n",
-       "      <th>distribution.digest.value</th>\n",
        "      <th>...</th>\n",
+       "      <th>_createdAt</th>\n",
        "      <th>_createdBy</th>\n",
        "      <th>_deprecated</th>\n",
        "      <th>_incoming</th>\n",
        "      <th>_outgoing</th>\n",
        "      <th>_project</th>\n",
        "      <th>_rev</th>\n",
-       "      <th>_schemaProject</th>\n",
        "      <th>_self</th>\n",
        "      <th>_updatedAt</th>\n",
        "      <th>_updatedBy</th>\n",
@@ -1199,22 +1215,22 @@
        "      <th>0</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
        "      <td>bytes</td>\n",
-       "      <td>506.0</td>\n",
+       "      <td>506</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
        "      <td>SHA-256</td>\n",
-       "      <td>9639abc864e91c645779f510ae5c06a1618941d569eb1a...</td>\n",
        "      <td>...</td>\n",
+       "      <td>2021-08-17T11:00:14.662Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "      <td>False</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>1</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>2021-08-17T11:00:14.662Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
@@ -1223,48 +1239,48 @@
        "      <th>1</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>506</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
        "      <td>...</td>\n",
+       "      <td>2021-08-23T09:12:24.049Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "      <td>False</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>1</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>2021-08-17T12:28:14.266Z</td>\n",
+       "      <td>2021-08-23T09:12:24.049Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
        "      <td>bytes</td>\n",
-       "      <td>126.0</td>\n",
+       "      <td>506</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
        "      <td>SHA-256</td>\n",
-       "      <td>8eae22fcd5e3b14fd6df3d45b005bda759e78bf8267b63...</td>\n",
        "      <td>...</td>\n",
+       "      <td>2021-08-23T09:18:43.327Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "      <td>False</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>1</td>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>2021-08-17T12:28:24.873Z</td>\n",
+       "      <td>2021-08-23T09:18:43.327Z</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
@@ -1278,35 +1294,35 @@
        "1  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "2  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "\n",
-       "  distribution.type distribution.atLocation.type  \\\n",
-       "0      DataDownload                     Location   \n",
-       "1               NaN                          NaN   \n",
-       "2      DataDownload                     Location   \n",
+       "                                      _schemaProject distribution.type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "\n",
+       "  distribution.atLocation.type  \\\n",
+       "0                     Location   \n",
+       "1                     Location   \n",
+       "2                     Location   \n",
        "\n",
        "                    distribution.atLocation.store.id  \\\n",
        "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
-       "1                                                NaN   \n",
+       "1  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
        "2  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
        "\n",
        "  distribution.contentSize.unitCode  distribution.contentSize.value  \\\n",
-       "0                             bytes                           506.0   \n",
-       "1                               NaN                             NaN   \n",
-       "2                             bytes                           126.0   \n",
+       "0                             bytes                             506   \n",
+       "1                             bytes                             506   \n",
+       "2                             bytes                             506   \n",
        "\n",
        "                             distribution.contentUrl  \\\n",
        "0  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
-       "1                                                NaN   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
        "2  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
        "\n",
-       "  distribution.digest.algorithm  \\\n",
-       "0                       SHA-256   \n",
-       "1                           NaN   \n",
-       "2                       SHA-256   \n",
-       "\n",
-       "                           distribution.digest.value  ...  \\\n",
-       "0  9639abc864e91c645779f510ae5c06a1618941d569eb1a...  ...   \n",
-       "1                                                NaN  ...   \n",
-       "2  8eae22fcd5e3b14fd6df3d45b005bda759e78bf8267b63...  ...   \n",
+       "  distribution.digest.algorithm  ...                _createdAt  \\\n",
+       "0                       SHA-256  ...  2021-08-17T11:00:14.662Z   \n",
+       "1                       SHA-256  ...  2021-08-23T09:12:24.049Z   \n",
+       "2                       SHA-256  ...  2021-08-23T09:18:43.327Z   \n",
        "\n",
        "                                          _createdBy _deprecated  \\\n",
        "0  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
@@ -1328,11 +1344,6 @@
        "1  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
        "2  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
        "\n",
-       "                                      _schemaProject  \\\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/projects/...   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/projects/...   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/projects/...   \n",
-       "\n",
        "                                               _self  \\\n",
        "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
        "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
@@ -1340,13 +1351,13 @@
        "\n",
        "                 _updatedAt                                         _updatedBy  \n",
        "0  2021-08-17T11:00:14.662Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
-       "1  2021-08-17T12:28:14.266Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
-       "2  2021-08-17T12:28:24.873Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "1  2021-08-23T09:12:24.049Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "2  2021-08-23T09:18:43.327Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
        "\n",
        "[3 rows x 25 columns]"
       ]
      },
-     "execution_count": 63,
+     "execution_count": 52,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1359,22 +1370,57 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Crossbucket search\n",
-    "It is possible to search for resources stored in buckets different than the configured one. The configured store should of course support it."
+    "### Built-in Filter objects"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Supported filter operators"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 83,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['__eq__ (EQUAL)',\n",
+       " '__ne__ (NOT_EQUAL)',\n",
+       " '__lt__ (LOWER_THAN)',\n",
+       " '__le__ (LOWER_OR_Equal_Than)',\n",
+       " '__gt__ (GREATER_Than)',\n",
+       " '__ge__ (GREATER_OR_Equal_Than)']"
+      ]
+     },
+     "execution_count": 83,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "[f\"{op.value} ({op.name})\" for op in FilterOperator] # These are equivalent to the Python comparison operators"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 69,
+   "execution_count": 84,
    "metadata": {},
    "outputs": [],
    "source": [
-    "resources = forge.search(p.type.id == \"Association\", limit=3, cross_bucket=True)  # cross_bucket defaults to False"
+    "# Search for resources of type Person and with text/tab-separated-values as distribution.encodingFormat\n",
+    "\n",
+    "filter_1 = Filter(operator=\"__eq__\", path=[\"type\"], value=\"Person\")\n",
+    "filter_2 = Filter(operator=\"__eq__\", path=[\"distribution\",\"encodingFormat\"], value=\"text/tab-separated-values\")\n",
+    "resources = forge.search(filter_1,filter_2, limit=3)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 70,
+   "execution_count": 85,
    "metadata": {},
    "outputs": [
     {
@@ -1383,7 +1429,7 @@
        "list"
       ]
      },
-     "execution_count": 70,
+     "execution_count": 85,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1394,7 +1440,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 71,
+   "execution_count": 86,
    "metadata": {},
    "outputs": [
     {
@@ -1403,7 +1449,7 @@
        "3"
       ]
      },
-     "execution_count": 71,
+     "execution_count": 86,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1414,7 +1460,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 72,
+   "execution_count": 87,
    "metadata": {},
    "outputs": [
     {
@@ -1440,11 +1486,7 @@
        "      <th></th>\n",
        "      <th>id</th>\n",
        "      <th>type</th>\n",
-       "      <th>agent.type</th>\n",
-       "      <th>agent.gender.id</th>\n",
-       "      <th>agent.gender.type</th>\n",
-       "      <th>agent.gender.label</th>\n",
-       "      <th>agent.name</th>\n",
+       "      <th>_schemaProject</th>\n",
        "      <th>distribution.type</th>\n",
        "      <th>distribution.atLocation.type</th>\n",
        "      <th>distribution.atLocation.store.id</th>\n",
@@ -1452,133 +1494,1226 @@
        "      <th>distribution.contentSize.value</th>\n",
        "      <th>distribution.contentUrl</th>\n",
        "      <th>distribution.digest.algorithm</th>\n",
-       "      <th>distribution.digest.value</th>\n",
-       "      <th>distribution.encodingFormat</th>\n",
-       "      <th>distribution.name</th>\n",
-       "      <th>name</th>\n",
+       "      <th>...</th>\n",
+       "      <th>_createdAt</th>\n",
+       "      <th>_createdBy</th>\n",
+       "      <th>_deprecated</th>\n",
+       "      <th>_incoming</th>\n",
+       "      <th>_outgoing</th>\n",
+       "      <th>_project</th>\n",
+       "      <th>_rev</th>\n",
+       "      <th>_self</th>\n",
+       "      <th>_updatedAt</th>\n",
+       "      <th>_updatedBy</th>\n",
        "    </tr>\n",
        "  </thead>\n",
        "  <tbody>\n",
        "    <tr>\n",
        "      <th>0</th>\n",
-       "      <td>https://kg.example.ch/associations/123</td>\n",
-       "      <td>Association</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
        "      <td>Person</td>\n",
-       "      <td>http://purl.obolibrary.org/obo/PATO_0000383</td>\n",
-       "      <td>LabeledOntologyEntity</td>\n",
-       "      <td>female</td>\n",
-       "      <td>Marie Curie</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
        "      <td>bytes</td>\n",
-       "      <td>46.0</td>\n",
+       "      <td>506</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
        "      <td>SHA-256</td>\n",
-       "      <td>e0fe65f725bf28fe2b88c7bafb51fb5ef1df0ab14c68a3...</td>\n",
-       "      <td>text/plain</td>\n",
-       "      <td>marie_curie.txt</td>\n",
-       "      <td>Curie Association</td>\n",
+       "      <td>...</td>\n",
+       "      <td>2021-08-17T11:00:14.662Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>1</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>2021-08-17T11:00:14.662Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>Association</td>\n",
        "      <td>Person</td>\n",
-       "      <td>http://purl.obolibrary.org/obo/PATO_0000384</td>\n",
-       "      <td>LabeledOntologyEntity</td>\n",
-       "      <td>male</td>\n",
-       "      <td>Albert Einstein</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
        "      <td>DataDownload</td>\n",
        "      <td>Location</td>\n",
        "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
        "      <td>bytes</td>\n",
-       "      <td>50.0</td>\n",
+       "      <td>506</td>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
        "      <td>SHA-256</td>\n",
-       "      <td>91a5ce5c84dc5bead730a4b49d0698b4aaef4bc06ce164...</td>\n",
-       "      <td>text/plain</td>\n",
-       "      <td>albert_einstein.txt</td>\n",
-       "      <td>Einstein Association</td>\n",
+       "      <td>...</td>\n",
+       "      <td>2021-08-23T09:12:24.049Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>1</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>2021-08-23T09:12:24.049Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
        "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>Association</td>\n",
        "      <td>Person</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>Jane Doe</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
-       "      <td>NaN</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>506</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>...</td>\n",
+       "      <td>2021-08-23T09:18:43.327Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>1</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>2021-08-23T09:18:43.327Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
+       "<p>3 rows × 25 columns</p>\n",
        "</div>"
       ],
       "text/plain": [
-       "                                                  id         type agent.type  \\\n",
-       "0             https://kg.example.ch/associations/123  Association     Person   \n",
-       "1  https://sandbox.bluebrainnexus.io/v1/resources...  Association     Person   \n",
-       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Association     Person   \n",
-       "\n",
-       "                               agent.gender.id      agent.gender.type  \\\n",
-       "0  http://purl.obolibrary.org/obo/PATO_0000383  LabeledOntologyEntity   \n",
-       "1  http://purl.obolibrary.org/obo/PATO_0000384  LabeledOntologyEntity   \n",
-       "2                                          NaN                    NaN   \n",
+       "                                                  id    type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
        "\n",
-       "  agent.gender.label       agent.name distribution.type  \\\n",
-       "0             female      Marie Curie      DataDownload   \n",
-       "1               male  Albert Einstein      DataDownload   \n",
-       "2                NaN         Jane Doe               NaN   \n",
+       "                                      _schemaProject distribution.type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
        "\n",
        "  distribution.atLocation.type  \\\n",
        "0                     Location   \n",
        "1                     Location   \n",
-       "2                          NaN   \n",
+       "2                     Location   \n",
        "\n",
        "                    distribution.atLocation.store.id  \\\n",
        "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
        "1  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
-       "2                                                NaN   \n",
+       "2  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
        "\n",
        "  distribution.contentSize.unitCode  distribution.contentSize.value  \\\n",
-       "0                             bytes                            46.0   \n",
-       "1                             bytes                            50.0   \n",
-       "2                               NaN                             NaN   \n",
+       "0                             bytes                             506   \n",
+       "1                             bytes                             506   \n",
+       "2                             bytes                             506   \n",
        "\n",
        "                             distribution.contentUrl  \\\n",
        "0  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
        "1  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
-       "2                                                NaN   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
        "\n",
-       "  distribution.digest.algorithm  \\\n",
-       "0                       SHA-256   \n",
-       "1                       SHA-256   \n",
-       "2                           NaN   \n",
+       "  distribution.digest.algorithm  ...                _createdAt  \\\n",
+       "0                       SHA-256  ...  2021-08-17T11:00:14.662Z   \n",
+       "1                       SHA-256  ...  2021-08-23T09:12:24.049Z   \n",
+       "2                       SHA-256  ...  2021-08-23T09:18:43.327Z   \n",
        "\n",
-       "                           distribution.digest.value  \\\n",
-       "0  e0fe65f725bf28fe2b88c7bafb51fb5ef1df0ab14c68a3...   \n",
-       "1  91a5ce5c84dc5bead730a4b49d0698b4aaef4bc06ce164...   \n",
-       "2                                                NaN   \n",
+       "                                          _createdBy _deprecated  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
        "\n",
-       "  distribution.encodingFormat    distribution.name                  name  \n",
-       "0                  text/plain      marie_curie.txt     Curie Association  \n",
-       "1                  text/plain  albert_einstein.txt  Einstein Association  \n",
-       "2                         NaN                  NaN                   NaN  "
-      ]
-     },
-     "execution_count": 72,
+       "                                           _incoming  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                                           _outgoing  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                                            _project _rev  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "\n",
+       "                                               _self  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                 _updatedAt                                         _updatedBy  \n",
+       "0  2021-08-17T11:00:14.662Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "1  2021-08-23T09:12:24.049Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "2  2021-08-23T09:18:43.327Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "\n",
+       "[3 rows x 25 columns]"
+      ]
+     },
+     "execution_count": 87,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "forge.as_dataframe(resources, store_metadata=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Search Endpoints\n",
+    "\n",
+    "Two types of search endpoints are supported: 'sparql' for graph queries and 'elastic' for document oriented queries. The types of available search endpoint can be configured (see [00-Initialization.ipynb](00%20-%20Initialization.ipynb) for an example of search endpoints config) or set when creating a KnowledgeGraphForge session using the 'searchendpoints' arguments.\n",
+    "\n",
+    "The search endpoint to hit when calling forge.search(...) is 'sparql' by default but can be specified using the 'search_endpoint' argument."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### SPARQL Search Endpoint"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 184,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Search for resources of type Person and with text/tab-separated-values as distribution.encodingFormat\n",
+    "filters = {\"type\": \"Person\", \"distribution\":{\"encodingFormat\":\"text/tab-separated-values\"}}\n",
+    "resources = forge.search(filters, limit=3, search_endpoint='sparql')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 172,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 172,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 173,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 173,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 174,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>id</th>\n",
+       "      <th>type</th>\n",
+       "      <th>_schemaProject</th>\n",
+       "      <th>distribution.type</th>\n",
+       "      <th>distribution.atLocation.type</th>\n",
+       "      <th>distribution.atLocation.store.id</th>\n",
+       "      <th>distribution.contentSize.unitCode</th>\n",
+       "      <th>distribution.contentSize.value</th>\n",
+       "      <th>distribution.contentUrl</th>\n",
+       "      <th>distribution.digest.algorithm</th>\n",
+       "      <th>...</th>\n",
+       "      <th>_createdAt</th>\n",
+       "      <th>_createdBy</th>\n",
+       "      <th>_deprecated</th>\n",
+       "      <th>_incoming</th>\n",
+       "      <th>_outgoing</th>\n",
+       "      <th>_project</th>\n",
+       "      <th>_rev</th>\n",
+       "      <th>_self</th>\n",
+       "      <th>_updatedAt</th>\n",
+       "      <th>_updatedBy</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>506</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>...</td>\n",
+       "      <td>2021-08-17T11:00:14.662Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>1</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>2021-08-17T11:00:14.662Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>506</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>...</td>\n",
+       "      <td>2021-08-23T09:12:24.049Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>1</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>2021-08-23T09:12:24.049Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>506</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>...</td>\n",
+       "      <td>2021-08-23T09:18:43.327Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "      <td>False</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>1</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>2021-08-23T09:18:43.327Z</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/realms/gi...</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "<p>3 rows × 25 columns</p>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                                  id    type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Person   \n",
+       "\n",
+       "                                      _schemaProject distribution.type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...      DataDownload   \n",
+       "\n",
+       "  distribution.atLocation.type  \\\n",
+       "0                     Location   \n",
+       "1                     Location   \n",
+       "2                     Location   \n",
+       "\n",
+       "                    distribution.atLocation.store.id  \\\n",
+       "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
+       "1  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
+       "2  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
+       "\n",
+       "  distribution.contentSize.unitCode  distribution.contentSize.value  \\\n",
+       "0                             bytes                             506   \n",
+       "1                             bytes                             506   \n",
+       "2                             bytes                             506   \n",
+       "\n",
+       "                             distribution.contentUrl  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
+       "\n",
+       "  distribution.digest.algorithm  ...                _createdAt  \\\n",
+       "0                       SHA-256  ...  2021-08-17T11:00:14.662Z   \n",
+       "1                       SHA-256  ...  2021-08-23T09:12:24.049Z   \n",
+       "2                       SHA-256  ...  2021-08-23T09:18:43.327Z   \n",
+       "\n",
+       "                                          _createdBy _deprecated  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/realms/gi...       False   \n",
+       "\n",
+       "                                           _incoming  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                                           _outgoing  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                                            _project _rev  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...    1   \n",
+       "\n",
+       "                                               _self  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...   \n",
+       "\n",
+       "                 _updatedAt                                         _updatedBy  \n",
+       "0  2021-08-17T11:00:14.662Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "1  2021-08-23T09:12:24.049Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "2  2021-08-23T09:18:43.327Z  https://sandbox.bluebrainnexus.io/v1/realms/gi...  \n",
+       "\n",
+       "[3 rows x 25 columns]"
+      ]
+     },
+     "execution_count": 174,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "forge.as_dataframe(resources, store_metadata=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### ElasticSearch Endpoint"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Search for resources of type Person and retrieve their ids and names.\n",
+    "\n",
+    "filters = {\"@type\": \"http://schema.org/Person\"}\n",
+    "resources = forge.search(filters, limit=3, search_endpoint='elastic', debug=True, includes=[\"@id\",\"name\"]) # fields can also be excluded with 'excludes'"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 194,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 194,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 195,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 195,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 196,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>@id</th>\n",
+       "      <th>name</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                                 @id      name\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...  Jane Doe\n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...  Jane Doe\n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Jane Doe"
+      ]
+     },
+     "execution_count": 196,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "forge.as_dataframe(resources, store_metadata=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Crossbucket search\n",
+    "It is possible to search for resources stored in buckets different than the configured one. The configured store should of course support it."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 88,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "resources = forge.search(p.type.id == \"Association\", limit=3, cross_bucket=True)  # cross_bucket defaults to False"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 89,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 89,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 90,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 90,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 91,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>id</th>\n",
+       "      <th>type</th>\n",
+       "      <th>_schemaProject</th>\n",
+       "      <th>agent.type</th>\n",
+       "      <th>agent.gender.id</th>\n",
+       "      <th>agent.gender.type</th>\n",
+       "      <th>agent.gender.label</th>\n",
+       "      <th>agent.name</th>\n",
+       "      <th>distribution.type</th>\n",
+       "      <th>distribution.atLocation.type</th>\n",
+       "      <th>distribution.atLocation.store.id</th>\n",
+       "      <th>distribution.contentSize.unitCode</th>\n",
+       "      <th>distribution.contentSize.value</th>\n",
+       "      <th>distribution.contentUrl</th>\n",
+       "      <th>distribution.digest.algorithm</th>\n",
+       "      <th>distribution.digest.value</th>\n",
+       "      <th>distribution.encodingFormat</th>\n",
+       "      <th>distribution.name</th>\n",
+       "      <th>name</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://kg.example.ch/associations/123</td>\n",
+       "      <td>Association</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>http://purl.obolibrary.org/obo/PATO_0000383</td>\n",
+       "      <td>LabeledOntologyEntity</td>\n",
+       "      <td>female</td>\n",
+       "      <td>Marie Curie</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>46.0</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>e0fe65f725bf28fe2b88c7bafb51fb5ef1df0ab14c68a3...</td>\n",
+       "      <td>text/plain</td>\n",
+       "      <td>marie_curie.txt</td>\n",
+       "      <td>Curie Association</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Association</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>http://purl.obolibrary.org/obo/PATO_0000384</td>\n",
+       "      <td>LabeledOntologyEntity</td>\n",
+       "      <td>male</td>\n",
+       "      <td>Albert Einstein</td>\n",
+       "      <td>DataDownload</td>\n",
+       "      <td>Location</td>\n",
+       "      <td>https://bluebrain.github.io/nexus/vocabulary/d...</td>\n",
+       "      <td>bytes</td>\n",
+       "      <td>50.0</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/files/git...</td>\n",
+       "      <td>SHA-256</td>\n",
+       "      <td>91a5ce5c84dc5bead730a4b49d0698b4aaef4bc06ce164...</td>\n",
+       "      <td>text/plain</td>\n",
+       "      <td>albert_einstein.txt</td>\n",
+       "      <td>Einstein Association</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Association</td>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/projects/...</td>\n",
+       "      <td>Person</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                                  id         type  \\\n",
+       "0             https://kg.example.ch/associations/123  Association   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...  Association   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  Association   \n",
+       "\n",
+       "                                      _schemaProject agent.type  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/projects/...     Person   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/projects/...     Person   \n",
+       "2  https://sandbox.bluebrainnexus.io/v1/projects/...     Person   \n",
+       "\n",
+       "                               agent.gender.id      agent.gender.type  \\\n",
+       "0  http://purl.obolibrary.org/obo/PATO_0000383  LabeledOntologyEntity   \n",
+       "1  http://purl.obolibrary.org/obo/PATO_0000384  LabeledOntologyEntity   \n",
+       "2                                          NaN                    NaN   \n",
+       "\n",
+       "  agent.gender.label       agent.name distribution.type  \\\n",
+       "0             female      Marie Curie      DataDownload   \n",
+       "1               male  Albert Einstein      DataDownload   \n",
+       "2                NaN         Jane Doe               NaN   \n",
+       "\n",
+       "  distribution.atLocation.type  \\\n",
+       "0                     Location   \n",
+       "1                     Location   \n",
+       "2                          NaN   \n",
+       "\n",
+       "                    distribution.atLocation.store.id  \\\n",
+       "0  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
+       "1  https://bluebrain.github.io/nexus/vocabulary/d...   \n",
+       "2                                                NaN   \n",
+       "\n",
+       "  distribution.contentSize.unitCode  distribution.contentSize.value  \\\n",
+       "0                             bytes                            46.0   \n",
+       "1                             bytes                            50.0   \n",
+       "2                               NaN                             NaN   \n",
+       "\n",
+       "                             distribution.contentUrl  \\\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
+       "1  https://sandbox.bluebrainnexus.io/v1/files/git...   \n",
+       "2                                                NaN   \n",
+       "\n",
+       "  distribution.digest.algorithm  \\\n",
+       "0                       SHA-256   \n",
+       "1                       SHA-256   \n",
+       "2                           NaN   \n",
+       "\n",
+       "                           distribution.digest.value  \\\n",
+       "0  e0fe65f725bf28fe2b88c7bafb51fb5ef1df0ab14c68a3...   \n",
+       "1  91a5ce5c84dc5bead730a4b49d0698b4aaef4bc06ce164...   \n",
+       "2                                                NaN   \n",
+       "\n",
+       "  distribution.encodingFormat    distribution.name                  name  \n",
+       "0                  text/plain      marie_curie.txt     Curie Association  \n",
+       "1                  text/plain  albert_einstein.txt  Einstein Association  \n",
+       "2                         NaN                  NaN                   NaN  "
+      ]
+     },
+     "execution_count": 91,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "forge.as_dataframe(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#Furthermore it is possible to filter by bucket when cross_bucket is set to True. Setting a bucket value when cross_bucket is False will trigger a not_supported exception.\n",
+    "resources = forge.search(p.type.id == \"Person\", limit=3, cross_bucket=True, bucket=<str>)  # add a bucket"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "type(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "len(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "forge.as_dataframe(resources)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Graph traversing\n",
+    "\n",
+    "SPARQL is used as a query language to perform graph traversing.\n",
+    "\n",
+    "Nexus Forge implements a SPARQL query rewriting strategy leveraging a configured RDFModel that lets users write SPARQL queries without adding prefix declarations, prefix names or long IRIs. With this strategy, only provides type and property names can be provided.\n",
+    "\n",
+    "Please refer to the [Modeling.ipynb](11%20-%20Modeling.ipynb) notebook to learn about templates."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note: DemoStore doesn't implement SPARQL operations yet. Please use another store for this section."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note: DemoModel and RdfModel schemas have not been synchronized yet. This section is to be run with RdfModel."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 96,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "jane = Resource(type=\"Person\", name=\"Jane Doe\")\n",
+    "contribution_jane = Resource(type=\"Contribution\", agent=jane)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 97,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "john = Resource(type=\"Person\", name=\"John Smith\")\n",
+    "contribution_john = Resource(type=\"Contribution\", agent=john)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 98,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "association = Resource(type=\"Dataset\", contribution=[contribution_jane, contribution_john])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 99,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "<action> _register_one\n",
+      "<succeeded> True\n"
+     ]
+    }
+   ],
+   "source": [
+    "forge.register(association)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 124,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "{\n",
+      "    id: \"\"\n",
+      "    type:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "    }\n",
+      "    annotation:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: Annotation\n",
+      "        hasBody:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            type:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "            }\n",
+      "            label: \"\"\n",
+      "            note: \"\"\n",
+      "        }\n",
+      "        hasTarget:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            type: AnnotationTarget\n",
+      "        }\n",
+      "        note: \"\"\n",
+      "    }\n",
+      "    brainLocation:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: BrainLocation\n",
+      "        atlasSpatialReferenceSystem:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            type: AtlasSpatialReferenceSystem\n",
+      "        }\n",
+      "        brainRegion:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            label: \"\"\n",
+      "        }\n",
+      "        coordinatesInBrainAtlas:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            valueX: 0.0\n",
+      "            valueY: 0.0\n",
+      "            valueZ: 0.0\n",
+      "        }\n",
+      "        coordinatesInSlice:\n",
+      "        {\n",
+      "            spatialReferenceSystem:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "                type: SpatialReferenceSystem\n",
+      "            }\n",
+      "            valueX: 0.0\n",
+      "            valueY: 0.0\n",
+      "            valueZ: 0.0\n",
+      "        }\n",
+      "        distanceToBoundary:\n",
+      "        {\n",
+      "            boundary:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "                label: \"\"\n",
+      "            }\n",
+      "            distance:\n",
+      "            {\n",
+      "                unitCode: \"\"\n",
+      "                value:\n",
+      "                [\n",
+      "                    0.0\n",
+      "                    0\n",
+      "                ]\n",
+      "            }\n",
+      "        }\n",
+      "        layer:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            label: \"\"\n",
+      "        }\n",
+      "        longitudinalAxis:\n",
+      "        [\n",
+      "            Dorsal\n",
+      "            Ventral\n",
+      "        ]\n",
+      "        positionInLayer:\n",
+      "        [\n",
+      "            Deep\n",
+      "            Superficial\n",
+      "        ]\n",
+      "    }\n",
+      "    contribution:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "    }\n",
+      "    distribution:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: DataDownload\n",
+      "        contentSize:\n",
+      "        {\n",
+      "            unitCode: \"\"\n",
+      "            value:\n",
+      "            [\n",
+      "                0.0\n",
+      "                0\n",
+      "            ]\n",
+      "        }\n",
+      "        digest:\n",
+      "        {\n",
+      "            algorithm: \"\"\n",
+      "            value: \"\"\n",
+      "        }\n",
+      "        encodingFormat: \"\"\n",
+      "        license: \"\"\n",
+      "        name: \"\"\n",
+      "    }\n",
+      "    objectOfStudy:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: ObjectOfStudy\n",
+      "    }\n",
+      "    releaseDate: 9999-12-31T00:00:00\n",
+      "    subject:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: Subject\n",
+      "    }\n",
+      "}\n"
+     ]
+    }
+   ],
+   "source": [
+    "forge.template(\"Dataset\") # Templates help know which property to use when writing a query"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Prefix and namespace free SPARQL query\n",
+    "\n",
+    "When a forge RDFModel is configured, then there is no need to provide prefixes and namespaces when writing a SPARQL query. Prefixes and namespaces will be automatically inferred from the provided schemas and/or JSON-LD context and the query rewritten accordingly."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 101,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "query = \"\"\"\n",
+    "    SELECT ?id ?name\n",
+    "    WHERE {\n",
+    "        ?id a Dataset ;\n",
+    "        contribution/agent ?contributor.\n",
+    "        ?contributor name ?name.\n",
+    "    }\n",
+    "\"\"\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 102,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "resources = forge.sparql(query, limit=3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 103,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 103,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 104,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 104,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(resources)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 105,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "kgforge.core.resource.Resource"
+      ]
+     },
+     "execution_count": 105,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "type(resources[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 106,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>id</th>\n",
+       "      <th>name</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>John Smith</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>John Smith</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                                  id        name\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith\n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...    Jane Doe\n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith"
+      ]
+     },
+     "execution_count": 106,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1587,73 +2722,251 @@
     "forge.as_dataframe(resources)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### display rewritten SPARQL query "
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 107,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Submitted query:\n",
+      "   PREFIX dc: <http://purl.org/dc/elements/1.1/>\n",
+      "   PREFIX dcat: <http://www.w3.org/ns/dcat#>\n",
+      "   PREFIX dcterms: <http://purl.org/dc/terms/>\n",
+      "   PREFIX mba: <http://api.brain-map.org/api/v2/data/Structure/>\n",
+      "   PREFIX nsg: <https://neuroshapes.org/>\n",
+      "   PREFIX owl: <http://www.w3.org/2002/07/owl#>\n",
+      "   PREFIX prov: <http://www.w3.org/ns/prov#>\n",
+      "   PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n",
+      "   PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n",
+      "   PREFIX schema: <http://schema.org/>\n",
+      "   PREFIX sh: <http://www.w3.org/ns/shacl#>\n",
+      "   PREFIX shsh: <http://www.w3.org/ns/shacl-shacl#>\n",
+      "   PREFIX skos: <http://www.w3.org/2004/02/skos/core#>\n",
+      "   PREFIX vann: <http://purl.org/vocab/vann/>\n",
+      "   PREFIX void: <http://rdfs.org/ns/void#>\n",
+      "   PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n",
+      "   PREFIX : <https://neuroshapes.org/>\n",
+      "   \n",
+      "       SELECT ?id ?name\n",
+      "       WHERE {\n",
+      "           ?id a schema:Dataset ;\n",
+      "           nsg:contribution/prov:agent ?contributor.\n",
+      "           ?contributor schema:name ?name.\n",
+      "       }\n",
+      "\n"
+     ]
+    }
+   ],
+   "source": [
+    "resources = forge.sparql(query, limit=3, debug=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Full SPARQL query\n",
+    "\n",
+    "regular SPARQL query can also be provided."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 108,
    "metadata": {},
    "outputs": [],
    "source": [
-    "#Furthermore it is possible to filter by bucket when cross_bucket is set to True. Setting a bucket value when cross_bucket is False will trigger a not_supported exception.\n",
-    "resources = forge.search(p.type.id == \"Person\", limit=3, cross_bucket=True, bucket=<str>)  # add a bucket"
+    "query = \"\"\"\n",
+    "PREFIX dc: <http://purl.org/dc/elements/1.1/>\n",
+    "   PREFIX dcat: <http://www.w3.org/ns/dcat#>\n",
+    "   PREFIX dcterms: <http://purl.org/dc/terms/>\n",
+    "   PREFIX mba: <http://api.brain-map.org/api/v2/data/Structure/>\n",
+    "   PREFIX nsg: <https://neuroshapes.org/>\n",
+    "   PREFIX owl: <http://www.w3.org/2002/07/owl#>\n",
+    "   PREFIX prov: <http://www.w3.org/ns/prov#>\n",
+    "   PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n",
+    "   PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n",
+    "   PREFIX schema: <http://schema.org/>\n",
+    "   PREFIX sh: <http://www.w3.org/ns/shacl#>\n",
+    "   PREFIX shsh: <http://www.w3.org/ns/shacl-shacl#>\n",
+    "   PREFIX skos: <http://www.w3.org/2004/02/skos/core#>\n",
+    "   PREFIX vann: <http://purl.org/vocab/vann/>\n",
+    "   PREFIX void: <http://rdfs.org/ns/void#>\n",
+    "   PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n",
+    "   PREFIX : <https://neuroshapes.org/>\n",
+    "   SELECT ?id ?name\n",
+    "   WHERE {\n",
+    "       ?id a schema:Dataset ;\n",
+    "       nsg:contribution/prov:agent ?contributor.\n",
+    "       ?contributor schema:name ?name.\n",
+    "   }\n",
+    "\"\"\""
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 109,
    "metadata": {},
    "outputs": [],
+   "source": [
+    "resources = forge.sparql(query, limit=3)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 110,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "list"
+      ]
+     },
+     "execution_count": 110,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "type(resources)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 111,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 111,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "len(resources)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 112,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "kgforge.core.resource.Resource"
+      ]
+     },
+     "execution_count": 112,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
-    "forge.as_dataframe(resources)"
+    "type(resources[0])"
    ]
   },
   {
-   "cell_type": "markdown",
+   "cell_type": "code",
+   "execution_count": 113,
    "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>id</th>\n",
+       "      <th>name</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>John Smith</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>Jane Doe</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
+       "      <td>John Smith</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "                                                  id        name\n",
+       "0  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith\n",
+       "1  https://sandbox.bluebrainnexus.io/v1/resources...    Jane Doe\n",
+       "2  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith"
+      ]
+     },
+     "execution_count": 113,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
-    "## Graph traversing\n",
-    "\n",
-    "SPARQL is used as a query language.\n",
-    "\n",
-    "A SPARQL query rewriting strategy lets users write simplified queries without using prefix declarations, prefix names or long IRIs. With this strategy, the user could only provides type and property names. For a given entity type, these names could be seen in its template.\n",
-    "\n",
-    "Please refer to the [Modeling.ipynb](11%20-%20Modeling.ipynb) notebook to learn about templates."
+    "forge.as_dataframe(resources)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Note: DemoStore doesn't implement SPARQL operations yet. Please use another store for this section."
+    "## ElasticSearch DSL Query\n",
+    "\n",
+    "ElasticSearch DSL can be used as a query language search for resources provided that the configured store supports it. The 'BlueBrainNexusStore' supports ElasticSearch."
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Note: DemoModel and RdfModel schemas have not been synchronized yet. This section is to be run with RdfModel."
+    "Note: DemoStore doesn't implement ElasaticSearch DSL operations."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 76,
+   "execution_count": 125,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1663,7 +2976,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 77,
+   "execution_count": 126,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1673,7 +2986,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 78,
+   "execution_count": 127,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1682,7 +2995,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 79,
+   "execution_count": 128,
    "metadata": {},
    "outputs": [
     {
@@ -1700,10 +3013,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 80,
-   "metadata": {
-    "scrolled": true
-   },
+   "execution_count": 129,
+   "metadata": {},
    "outputs": [
     {
      "name": "stdout",
@@ -1722,7 +3033,10 @@
       "        hasBody:\n",
       "        {\n",
       "            id: \"\"\n",
-      "            type: AnnotationBody\n",
+      "            type:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "            }\n",
       "            label: \"\"\n",
       "            note: \"\"\n",
       "        }\n",
@@ -1840,273 +3154,51 @@
     }
    ],
    "source": [
-    "forge.template(\"Dataset\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Prefix and namespace free SPARQL query\n",
-    "\n",
-    "When a forge RDFModel is configured, then there is no need to provide prefixes and namespaces when writing a SPARQL query. Prefixes and namespaces will be automatically inferred from the provided schemas and/or JSON-LD context and the query rewritten accordingly."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 81,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "query = \"\"\"\n",
-    "    SELECT ?id ?name\n",
-    "    WHERE {\n",
-    "        ?id a Dataset ;\n",
-    "        contribution/agent ?contributor.\n",
-    "        ?contributor name ?name.\n",
-    "    }\n",
-    "\"\"\""
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 82,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "resources = forge.sparql(query, limit=3)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 83,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "list"
-      ]
-     },
-     "execution_count": 83,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "type(resources)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 84,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "3"
-      ]
-     },
-     "execution_count": 84,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "len(resources)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 85,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "kgforge.core.resource.Resource"
-      ]
-     },
-     "execution_count": 85,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "type(resources[0])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 86,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<div>\n",
-       "<style scoped>\n",
-       "    .dataframe tbody tr th:only-of-type {\n",
-       "        vertical-align: middle;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe tbody tr th {\n",
-       "        vertical-align: top;\n",
-       "    }\n",
-       "\n",
-       "    .dataframe thead th {\n",
-       "        text-align: right;\n",
-       "    }\n",
-       "</style>\n",
-       "<table border=\"1\" class=\"dataframe\">\n",
-       "  <thead>\n",
-       "    <tr style=\"text-align: right;\">\n",
-       "      <th></th>\n",
-       "      <th>id</th>\n",
-       "      <th>name</th>\n",
-       "    </tr>\n",
-       "  </thead>\n",
-       "  <tbody>\n",
-       "    <tr>\n",
-       "      <th>0</th>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>John Smith</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>1</th>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>Jane Doe</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>2</th>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>John Smith</td>\n",
-       "    </tr>\n",
-       "  </tbody>\n",
-       "</table>\n",
-       "</div>"
-      ],
-      "text/plain": [
-       "                                                  id        name\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith\n",
-       "1  https://sandbox.bluebrainnexus.io/v1/resources...    Jane Doe\n",
-       "2  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith"
-      ]
-     },
-     "execution_count": 86,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "forge.as_dataframe(resources)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### rewritten query display\n",
-    "The prefix free SPARQL query provided above is rewritten as the ouput of cell when a forge Model is configured. "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 87,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Submitted query:\n",
-      "   PREFIX dc: <http://purl.org/dc/elements/1.1/>\n",
-      "   PREFIX dcat: <http://www.w3.org/ns/dcat#>\n",
-      "   PREFIX dcterms: <http://purl.org/dc/terms/>\n",
-      "   PREFIX mba: <http://api.brain-map.org/api/v2/data/Structure/>\n",
-      "   PREFIX nsg: <https://neuroshapes.org/>\n",
-      "   PREFIX owl: <http://www.w3.org/2002/07/owl#>\n",
-      "   PREFIX prov: <http://www.w3.org/ns/prov#>\n",
-      "   PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n",
-      "   PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n",
-      "   PREFIX schema: <http://schema.org/>\n",
-      "   PREFIX sh: <http://www.w3.org/ns/shacl#>\n",
-      "   PREFIX shsh: <http://www.w3.org/ns/shacl-shacl#>\n",
-      "   PREFIX skos: <http://www.w3.org/2004/02/skos/core#>\n",
-      "   PREFIX vann: <http://purl.org/vocab/vann/>\n",
-      "   PREFIX void: <http://rdfs.org/ns/void#>\n",
-      "   PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n",
-      "   PREFIX : <https://neuroshapes.org/>\n",
-      "   \n",
-      "       SELECT ?id ?name\n",
-      "       WHERE {\n",
-      "           ?id a schema:Dataset ;\n",
-      "           nsg:contribution/prov:agent ?contributor.\n",
-      "           ?contributor schema:name ?name.\n",
-      "       }\n",
-      "\n"
-     ]
-    }
-   ],
-   "source": [
-    "resources = forge.sparql(query, limit=3, debug=True)"
+    "forge.template(\"Dataset\") # Templates help know which property to use when writing a query"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### SPARQL query\n",
-    "\n",
-    "regular SPARQL query can also be provided."
+    "### Plain ElasticSearch DSL"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 88,
+   "execution_count": 155,
    "metadata": {},
    "outputs": [],
    "source": [
     "query = \"\"\"\n",
-    "PREFIX dc: <http://purl.org/dc/elements/1.1/>\n",
-    "   PREFIX dcat: <http://www.w3.org/ns/dcat#>\n",
-    "   PREFIX dcterms: <http://purl.org/dc/terms/>\n",
-    "   PREFIX mba: <http://api.brain-map.org/api/v2/data/Structure/>\n",
-    "   PREFIX nsg: <https://neuroshapes.org/>\n",
-    "   PREFIX owl: <http://www.w3.org/2002/07/owl#>\n",
-    "   PREFIX prov: <http://www.w3.org/ns/prov#>\n",
-    "   PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n",
-    "   PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n",
-    "   PREFIX schema: <http://schema.org/>\n",
-    "   PREFIX sh: <http://www.w3.org/ns/shacl#>\n",
-    "   PREFIX shsh: <http://www.w3.org/ns/shacl-shacl#>\n",
-    "   PREFIX skos: <http://www.w3.org/2004/02/skos/core#>\n",
-    "   PREFIX vann: <http://purl.org/vocab/vann/>\n",
-    "   PREFIX void: <http://rdfs.org/ns/void#>\n",
-    "   PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n",
-    "   PREFIX : <https://neuroshapes.org/>\n",
-    "   SELECT ?id ?name\n",
-    "   WHERE {\n",
-    "       ?id a schema:Dataset ;\n",
-    "       nsg:contribution/prov:agent ?contributor.\n",
-    "       ?contributor schema:name ?name.\n",
-    "   }\n",
+    "        {\n",
+    "          \"_source\": {\n",
+    "            \"includes\": [\n",
+    "              \"@id\",\n",
+    "              \"name\"\n",
+    "            ]\n",
+    "          },\n",
+    "          \"query\": {\n",
+    "            \"term\": {\n",
+    "              \"@type\": \"http://schema.org/Dataset\"\n",
+    "            }\n",
+    "          }\n",
+    "        }\n",
     "\"\"\""
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 89,
+   "execution_count": 156,
    "metadata": {},
    "outputs": [],
    "source": [
-    "resources = forge.sparql(query, limit=3)"
+    "resources = forge.elastic(query, limit=3) # limit and offset (when provided in this method call) superseed 'size' and 'from' values provided in the query"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 90,
+   "execution_count": 157,
    "metadata": {},
    "outputs": [
     {
@@ -2115,7 +3207,7 @@
        "list"
       ]
      },
-     "execution_count": 90,
+     "execution_count": 157,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -2126,7 +3218,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 91,
+   "execution_count": 158,
    "metadata": {},
    "outputs": [
     {
@@ -2135,7 +3227,7 @@
        "3"
       ]
      },
-     "execution_count": 91,
+     "execution_count": 158,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -2146,7 +3238,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 92,
+   "execution_count": 159,
    "metadata": {},
    "outputs": [
     {
@@ -2155,7 +3247,7 @@
        "kgforge.core.resource.Resource"
       ]
      },
-     "execution_count": 92,
+     "execution_count": 159,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -2166,7 +3258,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 93,
+   "execution_count": 160,
    "metadata": {},
    "outputs": [
     {
@@ -2190,38 +3282,43 @@
        "  <thead>\n",
        "    <tr style=\"text-align: right;\">\n",
        "      <th></th>\n",
-       "      <th>id</th>\n",
+       "      <th>@id</th>\n",
        "      <th>name</th>\n",
        "    </tr>\n",
        "  </thead>\n",
        "  <tbody>\n",
        "    <tr>\n",
        "      <th>0</th>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>John Smith</td>\n",
+       "      <td>https://bbp.epfl.ch/neurosciencegraph/data/neu...</td>\n",
+       "      <td>Scnn1a-Tg3-Cre;Ai14-187849.06.01.01</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>Jane Doe</td>\n",
+       "      <td>https://bbp.epfl.ch/neurosciencegraph/data/neu...</td>\n",
+       "      <td>H17.06.004.11.05.04</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
-       "      <td>https://sandbox.bluebrainnexus.io/v1/resources...</td>\n",
-       "      <td>John Smith</td>\n",
+       "      <td>https://bbp.epfl.ch/neurosciencegraph/data/neu...</td>\n",
+       "      <td>H16.06.009.01.01.15.01</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
        "</div>"
       ],
       "text/plain": [
-       "                                                  id        name\n",
-       "0  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith\n",
-       "1  https://sandbox.bluebrainnexus.io/v1/resources...    Jane Doe\n",
-       "2  https://sandbox.bluebrainnexus.io/v1/resources...  John Smith"
+       "                                                 @id  \\\n",
+       "0  https://bbp.epfl.ch/neurosciencegraph/data/neu...   \n",
+       "1  https://bbp.epfl.ch/neurosciencegraph/data/neu...   \n",
+       "2  https://bbp.epfl.ch/neurosciencegraph/data/neu...   \n",
+       "\n",
+       "                                  name  \n",
+       "0  Scnn1a-Tg3-Cre;Ai14-187849.06.01.01  \n",
+       "1                  H17.06.004.11.05.04  \n",
+       "2               H16.06.009.01.01.15.01  "
       ]
      },
-     "execution_count": 93,
+     "execution_count": 160,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -2246,7 +3343,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 94,
+   "execution_count": 114,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2255,7 +3352,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 95,
+   "execution_count": 115,
    "metadata": {},
    "outputs": [
     {
@@ -2276,7 +3373,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 96,
+   "execution_count": 116,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2285,7 +3382,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 97,
+   "execution_count": 117,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2294,7 +3391,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 98,
+   "execution_count": 118,
    "metadata": {},
    "outputs": [
     {
@@ -2312,7 +3409,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 99,
+   "execution_count": 122,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2325,22 +3422,19 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 100,
+   "execution_count": 123,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "total 464\n",
-      "-rw-r--r--  1 mfsy  staff     506 Aug 23 11:18 associations.tsv\n",
-      "-rw-r--r--  1 mfsy  staff     506 Aug 23 11:18 associations.tsv.20210823111849\n",
-      "-rw-r--r--  1 mfsy  staff     477 Aug 23 11:35 associations.tsv.20210823113551\n",
-      "-rw-r--r--  1 mfsy  staff      16 Aug 23 11:35 my_data.xwz\n",
-      "-rw-r--r--  1 mfsy  staff      24 Aug 23 11:35 my_data_derived.txt\n",
-      "-rw-r--r--  1 mfsy  staff      52 Aug 23 11:18 persons.csv\n",
-      "-rw-r--r--  1 mfsy  staff      52 Aug 23 11:35 persons.csv.20210823113551\n",
-      "-rw-r--r--  1 mfsy  staff  204848 Aug 23 11:35 tfidfvectorizer_model_schemaorg_linking\n"
+      "total 440\n",
+      "-rw-r--r--  1 mfsy  staff     477 Jan  7 13:51 associations.tsv\n",
+      "-rw-r--r--  1 mfsy  staff      16 Jan  7 13:51 my_data.xwz\n",
+      "-rw-r--r--  1 mfsy  staff      24 Jan  7 13:51 my_data_derived.txt\n",
+      "-rw-r--r--  1 mfsy  staff      52 Jan  7 13:51 persons.csv\n",
+      "-rw-r--r--  1 mfsy  staff  204848 Jan  7 13:51 tfidfvectorizer_model_schemaorg_linking\n"
      ]
     }
    ],
@@ -2350,17 +3444,17 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 121,
    "metadata": {},
    "outputs": [],
    "source": [
-    "# ! rm -R ./downloaded/"
+    "#! rm -R ./downloaded/"
    ]
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -2374,7 +3468,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/05 - Versioning.ipynb b/examples/notebooks/getting-started/05 - Versioning.ipynb
index 761c6b6b..57e5299b 100644
--- a/examples/notebooks/getting-started/05 - Versioning.ipynb
+++ b/examples/notebooks/getting-started/05 - Versioning.ipynb
@@ -197,7 +197,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 10,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.262122Z",
@@ -221,7 +221,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
@@ -230,7 +230,7 @@
        "Action(error='TaggingError', message='resource should have an id', operation='_tag_one', succeeded=False)"
       ]
      },
-     "execution_count": 13,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -250,7 +250,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -259,7 +259,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -277,7 +277,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -286,7 +286,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
@@ -304,7 +304,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 16,
    "metadata": {
     "scrolled": true
    },
@@ -312,14 +312,14 @@
     {
      "data": {
       "text/plain": [
-       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/project_name/_/ae2845fd-3e47-4034-9ebd-f297ed44c04a',\n",
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/95198bba-fc1b-488b-99cb-1d2cde22393c',\n",
        " 'type': 'Association',\n",
-       " 'agent': {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/project_name/_/8de1df33-8c74-47fb-b90a-7932e452108e',\n",
+       " 'agent': {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/848d46c5-20ea-42b8-bb5e-9638585f5c9f',\n",
        "  'type': 'Person',\n",
        "  'name': 'Jane Doe'}}"
       ]
      },
-     "execution_count": 18,
+     "execution_count": 16,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -330,7 +330,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
@@ -352,7 +352,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
@@ -374,7 +374,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
@@ -392,7 +392,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 20,
    "metadata": {
     "scrolled": true
    },
@@ -400,14 +400,14 @@
     {
      "data": {
       "text/plain": [
-       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/project_name/_/ae2845fd-3e47-4034-9ebd-f297ed44c04a?rev=1',\n",
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/95198bba-fc1b-488b-99cb-1d2cde22393c?rev=1',\n",
        " 'type': 'Association',\n",
-       " 'agent': {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/project_name/_/8de1df33-8c74-47fb-b90a-7932e452108e?rev=1',\n",
+       " 'agent': {'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/848d46c5-20ea-42b8-bb5e-9638585f5c9f?rev=1',\n",
        "  'type': 'Person',\n",
        "  'name': 'Jane Doe'}}"
       ]
      },
-     "execution_count": 22,
+     "execution_count": 20,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -419,7 +419,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -433,7 +433,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/09 - Resolving.ipynb b/examples/notebooks/getting-started/09 - Resolving.ipynb
index 08f9e6c1..49ee3981 100644
--- a/examples/notebooks/getting-started/09 - Resolving.ipynb
+++ b/examples/notebooks/getting-started/09 - Resolving.ipynb
@@ -74,7 +74,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
@@ -95,7 +95,7 @@
     }
    ],
    "source": [
-    "forge.resolvers()"
+    "forge.resolvers() # The values are taken from \"../../configurations/forge.yml\""
    ]
   },
   {
@@ -276,7 +276,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
@@ -297,7 +297,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
@@ -326,7 +326,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 19,
    "metadata": {
     "scrolled": true
    },
@@ -349,7 +349,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -387,7 +387,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -410,7 +410,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [
     {
@@ -438,7 +438,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [
     {
@@ -469,7 +469,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -478,7 +478,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [
     {
@@ -487,7 +487,7 @@
        "list"
       ]
      },
-     "execution_count": 19,
+     "execution_count": 25,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -498,7 +498,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [
     {
@@ -507,7 +507,7 @@
        "2"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 26,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -518,7 +518,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [
     {
@@ -527,7 +527,7 @@
        "kgforge.core.resource.Resource"
       ]
      },
-     "execution_count": 21,
+     "execution_count": 27,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -538,7 +538,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
@@ -572,7 +572,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [
     {
@@ -593,11 +593,11 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 37,
    "metadata": {},
    "outputs": [],
    "source": [
-    "resource_resolved = forge.resolve(resource, scope=\"terms\", target=\"sexontology\",\n",
+    "resource_resolved_merged = forge.resolve(resource, scope=\"terms\", target=\"sexontology\",\n",
     "                                  strategy=ResolvingStrategy.ALL_MATCHES,\n",
     "                                  property_to_resolve=\"gender\",\n",
     "                                  merge_inplace_as=\"gender_resolved\",\n",
@@ -606,7 +606,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 38,
    "metadata": {},
    "outputs": [
     {
@@ -615,18 +615,18 @@
        "kgforge.core.resource.Resource"
       ]
      },
-     "execution_count": 25,
+     "execution_count": 38,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "type(resource_resolved)"
+    "type(resource_resolved_merged)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 26,
+   "execution_count": 39,
    "metadata": {},
    "outputs": [
     {
@@ -654,16 +654,16 @@
     }
    ],
    "source": [
-    "print(resource_resolved)"
+    "print(resource_resolved_merged)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 40,
    "metadata": {},
    "outputs": [],
    "source": [
-    "resource_resolved = forge.resolve(resource, scope=\"terms\", target=\"sexontology\",\n",
+    "resource_resolved_separated = forge.resolve(resource, scope=\"terms\", target=\"sexontology\",\n",
     "                                  strategy=ResolvingStrategy.ALL_MATCHES,\n",
     "                                  property_to_resolve=\"gender\",\n",
     "                                  threshold=0.8)"
@@ -671,7 +671,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 28,
+   "execution_count": 41,
    "metadata": {},
    "outputs": [
     {
@@ -680,18 +680,18 @@
        "list"
       ]
      },
-     "execution_count": 28,
+     "execution_count": 41,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "type(resource_resolved)"
+    "type(resource_resolved_separated)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 29,
+   "execution_count": 42,
    "metadata": {},
    "outputs": [
     {
@@ -700,18 +700,18 @@
        "2"
       ]
      },
-     "execution_count": 29,
+     "execution_count": 42,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "len(resource_resolved)"
+    "len(resource_resolved_separated)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 30,
+   "execution_count": 43,
    "metadata": {},
    "outputs": [
     {
@@ -732,7 +732,7 @@
     }
    ],
    "source": [
-    "print(*resource_resolved, sep=\"\\n\")"
+    "print(*resource_resolved_separated, sep=\"\\n\")"
    ]
   },
   {
@@ -751,7 +751,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 32,
+   "execution_count": 44,
    "metadata": {},
    "outputs": [
     {
@@ -775,7 +775,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -789,7 +789,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/10 - Reshaping.ipynb b/examples/notebooks/getting-started/10 - Reshaping.ipynb
index 693f9083..5ca753d9 100644
--- a/examples/notebooks/getting-started/10 - Reshaping.ipynb
+++ b/examples/notebooks/getting-started/10 - Reshaping.ipynb
@@ -198,7 +198,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -207,7 +207,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -216,7 +216,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 27,
    "metadata": {
     "scrolled": true
    },
@@ -227,7 +227,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
@@ -251,7 +251,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [
     {
@@ -276,12 +276,13 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### reshape versioned"
+    "### reshape versioned\n",
+    "This is useful to switch resource references to versioned identifiers."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 30,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -290,7 +291,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 31,
    "metadata": {},
    "outputs": [
     {
@@ -308,7 +309,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 32,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -317,7 +318,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 33,
    "metadata": {
     "scrolled": true
    },
@@ -325,12 +326,12 @@
     {
      "data": {
       "text/plain": [
-       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/project_name/_/f32194a9-ea3f-4b8a-9943-35ecb9ed7847',\n",
+       "{'id': 'https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/81d0c7ca-1338-44bf-ba37-6e3adc6117c1',\n",
        " 'type': 'Person',\n",
        " 'name': 'Jane Doe'}"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 33,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -351,7 +352,7 @@
      "output_type": "stream",
      "text": [
       "{\n",
-      "    id: https://sandbox.bluebrainnexus.io/v1/resources/github-users/project_name/_/f32194a9-ea3f-4b8a-9943-35ecb9ed7847?rev=1\n",
+      "    id: https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/26ea4e1a-8172-4879-b69a-1362a8d3fad6?rev=1\n",
       "    type: Person\n",
       "}\n"
      ]
@@ -370,7 +371,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 34,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -379,7 +380,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 35,
    "metadata": {
     "scrolled": true
    },
@@ -390,7 +391,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 36,
    "metadata": {},
    "outputs": [
     {
@@ -399,7 +400,7 @@
        "True"
       ]
      },
-     "execution_count": 24,
+     "execution_count": 36,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -411,7 +412,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -425,7 +426,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/11 - Modeling.ipynb b/examples/notebooks/getting-started/11 - Modeling.ipynb
index cfa81c3a..d23b8794 100644
--- a/examples/notebooks/getting-started/11 - Modeling.ipynb
+++ b/examples/notebooks/getting-started/11 - Modeling.ipynb
@@ -84,22 +84,22 @@
      "output_type": "stream",
      "text": [
       "Used prefixes:\n",
-      " dc       http://purl.org/dc/elements/1.1/               \n",
-      " dcat     http://www.w3.org/ns/dcat#                     \n",
-      " dcterms  http://purl.org/dc/terms/                      \n",
-      " mba      http://api.brain-map.org/api/v2/data/Structure/\n",
-      " nsg      https://neuroshapes.org/                       \n",
-      " owl      http://www.w3.org/2002/07/owl#                 \n",
-      " prov     http://www.w3.org/ns/prov#                     \n",
-      " rdf      http://www.w3.org/1999/02/22-rdf-syntax-ns#    \n",
-      " rdfs     http://www.w3.org/2000/01/rdf-schema#          \n",
-      " schema   http://schema.org/                             \n",
-      " sh       http://www.w3.org/ns/shacl#                    \n",
-      " shsh     http://www.w3.org/ns/shacl-shacl#              \n",
-      " skos     http://www.w3.org/2004/02/skos/core#           \n",
-      " vann     http://purl.org/vocab/vann/                    \n",
-      " void     http://rdfs.org/ns/void#                       \n",
-      " xsd      http://www.w3.org/2001/XMLSchema#              \n"
+      "dc      http://purl.org/dc/elements/1.1/               \n",
+      "dcat    http://www.w3.org/ns/dcat#                     \n",
+      "dcterms http://purl.org/dc/terms/                      \n",
+      "mba     http://api.brain-map.org/api/v2/data/Structure/\n",
+      "nsg     https://neuroshapes.org/                       \n",
+      "owl     http://www.w3.org/2002/07/owl#                 \n",
+      "prov    http://www.w3.org/ns/prov#                     \n",
+      "rdf     http://www.w3.org/1999/02/22-rdf-syntax-ns#    \n",
+      "rdfs    http://www.w3.org/2000/01/rdf-schema#          \n",
+      "schema  http://schema.org/                             \n",
+      "sh      http://www.w3.org/ns/shacl#                    \n",
+      "shsh    http://www.w3.org/ns/shacl-shacl#              \n",
+      "skos    http://www.w3.org/2004/02/skos/core#           \n",
+      "vann    http://purl.org/vocab/vann/                    \n",
+      "void    http://rdfs.org/ns/void#                       \n",
+      "xsd     http://www.w3.org/2001/XMLSchema#              \n"
      ]
     }
    ],
@@ -148,19 +148,19 @@
    "metadata": {},
    "source": [
     "## Templates\n",
-    "The template will provide a set of properties for the givent type that is recomended to be used when creating Resources."
+    "A resource type template provides a set of properties for the given type as defined in a corresponding schema. A resource template can inform resource creation and Mappiinig definition."
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### showing the properties of a type + getting the template of a Mapping for a type"
+    "### Printing the expected properties of a type"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 9,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.482501Z",
@@ -174,17 +174,140 @@
      "text": [
       "{\n",
       "    id: \"\"\n",
+      "    type:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "    }\n",
+      "    annotation:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: Annotation\n",
+      "        hasBody:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            type:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "            }\n",
+      "            label: \"\"\n",
+      "            note: \"\"\n",
+      "        }\n",
+      "        hasTarget:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            type: AnnotationTarget\n",
+      "        }\n",
+      "        note: \"\"\n",
+      "    }\n",
+      "    brainLocation:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: BrainLocation\n",
+      "        atlasSpatialReferenceSystem:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            type: AtlasSpatialReferenceSystem\n",
+      "        }\n",
+      "        brainRegion:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            label: \"\"\n",
+      "        }\n",
+      "        coordinatesInBrainAtlas:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            valueX: 0.0\n",
+      "            valueY: 0.0\n",
+      "            valueZ: 0.0\n",
+      "        }\n",
+      "        coordinatesInSlice:\n",
+      "        {\n",
+      "            spatialReferenceSystem:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "                type: SpatialReferenceSystem\n",
+      "            }\n",
+      "            valueX: 0.0\n",
+      "            valueY: 0.0\n",
+      "            valueZ: 0.0\n",
+      "        }\n",
+      "        distanceToBoundary:\n",
+      "        {\n",
+      "            boundary:\n",
+      "            {\n",
+      "                id: \"\"\n",
+      "                label: \"\"\n",
+      "            }\n",
+      "            distance:\n",
+      "            {\n",
+      "                unitCode: \"\"\n",
+      "                value:\n",
+      "                [\n",
+      "                    0.0\n",
+      "                    0\n",
+      "                ]\n",
+      "            }\n",
+      "        }\n",
+      "        layer:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            label: \"\"\n",
+      "        }\n",
+      "        longitudinalAxis:\n",
+      "        [\n",
+      "            Dorsal\n",
+      "            Ventral\n",
+      "        ]\n",
+      "        positionInLayer:\n",
+      "        [\n",
+      "            Deep\n",
+      "            Superficial\n",
+      "        ]\n",
+      "    }\n",
+      "    contribution:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "    }\n",
+      "    distribution:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: DataDownload\n",
+      "        contentSize:\n",
+      "        {\n",
+      "            unitCode: \"\"\n",
+      "            value: \"\"\n",
+      "        }\n",
+      "        digest:\n",
+      "        {\n",
+      "            algorithm: \"\"\n",
+      "            value: \"\"\n",
+      "        }\n",
+      "        encodingFormat: \"\"\n",
+      "        license: \"\"\n",
+      "        name: \"\"\n",
+      "    }\n",
+      "    objectOfStudy:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: ObjectOfStudy\n",
+      "    }\n",
+      "    releaseDate: 9999-12-31T00:00:00\n",
+      "    subject:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: Subject\n",
+      "    }\n",
       "}\n"
      ]
     }
    ],
    "source": [
-    "forge.template(\"Person\")"
+    "forge.template(\"Dataset\")"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 10,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.704970Z",
@@ -198,12 +321,40 @@
      "text": [
       "{\n",
       "    id: \"\"\n",
+      "    type:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "    }\n",
+      "    brainLocation:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: BrainLocation\n",
+      "        brainRegion:\n",
+      "        {\n",
+      "            id: \"\"\n",
+      "            label: \"\"\n",
+      "        }\n",
+      "    }\n",
+      "    contribution:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "    }\n",
+      "    distribution:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: DataDownload\n",
+      "    }\n",
+      "    subject:\n",
+      "    {\n",
+      "        id: \"\"\n",
+      "        type: Subject\n",
+      "    }\n",
       "}\n"
      ]
     }
    ],
    "source": [
-    "forge.template(\"Person\", only_required=True)"
+    "forge.template(\"Dataset\", only_required=True)"
    ]
   },
   {
@@ -222,7 +373,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -241,7 +392,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -253,7 +404,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -262,7 +413,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
@@ -284,12 +435,12 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "#### programmatically (Dict)"
+    "#### programmatically (Dict) from a template"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -298,7 +449,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -307,7 +458,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -316,7 +467,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
@@ -349,7 +500,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -358,7 +509,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -367,7 +518,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -376,7 +527,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 25,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.716542Z",
@@ -400,7 +551,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 26,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.734693Z",
@@ -414,7 +565,7 @@
        "Action(error=None, message=None, operation='_validate_many', succeeded=True)"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 26,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -425,7 +576,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 27,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.749126Z",
@@ -439,7 +590,7 @@
        "True"
       ]
      },
-     "execution_count": 21,
+     "execution_count": 27,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -452,12 +603,13 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### automatic status update"
+    "### automatic status update\n",
+    "Not this status update only work for first level (e.g not nested properties) update."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 28,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.787796Z",
@@ -471,7 +623,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 29,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.832020Z",
@@ -485,7 +637,7 @@
        "False"
       ]
      },
-     "execution_count": 23,
+     "execution_count": 29,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -503,7 +655,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 30,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -512,7 +664,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 31,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -521,7 +673,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 26,
+   "execution_count": 32,
    "metadata": {},
    "outputs": [
     {
@@ -547,7 +699,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 33,
    "metadata": {},
    "outputs": [
     {
@@ -579,7 +731,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 28,
+   "execution_count": 34,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.917767Z",
@@ -593,7 +745,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 29,
+   "execution_count": 35,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -603,7 +755,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 30,
+   "execution_count": 36,
    "metadata": {},
    "outputs": [
     {
@@ -621,6 +773,7 @@
       "\tSource Shape: this11:DatasetShape\n",
       "\tFocus Node: [ <https://neuroshapes.org/contribution> [ rdf:type <http://schema.org/Person> ] ; rdf:type <http://schema.org/Dataset> ]\n",
       "\tValue Node: [ <https://neuroshapes.org/contribution> [ rdf:type <http://schema.org/Person> ] ; rdf:type <http://schema.org/Dataset> ]\n",
+      "\tMessage: Value does not conform to Shape this2:MINDSShape\n",
       "\n"
      ]
     }
@@ -631,7 +784,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 31,
+   "execution_count": 37,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:20.986359Z",
@@ -642,10 +795,10 @@
     {
      "data": {
       "text/plain": [
-       "Action(error='ValidationError', message='\\nValidation Report\\nConforms: False\\nResults (1):\\nConstraint Violation in NodeConstraintComponent (http://www.w3.org/ns/shacl#NodeConstraintComponent):\\n\\tSeverity: sh:Violation\\n\\tSource Shape: this11:DatasetShape\\n\\tFocus Node: [ <https://neuroshapes.org/contribution> [ rdf:type <http://schema.org/Person> ] ; rdf:type <http://schema.org/Dataset> ]\\n\\tValue Node: [ <https://neuroshapes.org/contribution> [ rdf:type <http://schema.org/Person> ] ; rdf:type <http://schema.org/Dataset> ]\\n', operation='_validate_one', succeeded=False)"
+       "Action(error='ValidationError', message='\\nValidation Report\\nConforms: False\\nResults (1):\\nConstraint Violation in NodeConstraintComponent (http://www.w3.org/ns/shacl#NodeConstraintComponent):\\n\\tSeverity: sh:Violation\\n\\tSource Shape: this11:DatasetShape\\n\\tFocus Node: [ <https://neuroshapes.org/contribution> [ rdf:type <http://schema.org/Person> ] ; rdf:type <http://schema.org/Dataset> ]\\n\\tValue Node: [ <https://neuroshapes.org/contribution> [ rdf:type <http://schema.org/Person> ] ; rdf:type <http://schema.org/Dataset> ]\\n\\tMessage: Value does not conform to Shape this2:MINDSShape\\n', operation='_validate_one', succeeded=False)"
       ]
      },
-     "execution_count": 31,
+     "execution_count": 37,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -656,7 +809,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 32,
+   "execution_count": 38,
    "metadata": {
     "ExecuteTime": {
      "end_time": "2019-09-23T18:50:21.016505Z",
@@ -670,7 +823,7 @@
        "False"
       ]
      },
-     "execution_count": 32,
+     "execution_count": 38,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -682,7 +835,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -696,7 +849,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/13 - JSON-LD IO.ipynb b/examples/notebooks/getting-started/13 - JSON-LD IO.ipynb
index 361ee2f0..9b510f70 100644
--- a/examples/notebooks/getting-started/13 - JSON-LD IO.ipynb
+++ b/examples/notebooks/getting-started/13 - JSON-LD IO.ipynb
@@ -17,21 +17,23 @@
     "1. directly set in the resource using the property `context`: e.g.`jane_resource.context=\"https://schema.org/docs/jsonldcontext.json\"`\n",
     "2. set in the `Model` section of the forge configuration file: e.g.\n",
     "\n",
+    "\n",
     "'''\n",
     "\n",
     "Model:\n",
     "\n",
-    "  name: RdfModel\n",
+    "     name: RdfModel\n",
     "  \n",
-    "  origin: ...\n",
+    "     origin: ...\n",
     "  \n",
-    "  source: ...\n",
+    "     source: ...\n",
     "  \n",
-    "  context:\n",
+    "     context:\n",
     "  \n",
-    "    iri: \"https://schema.org/docs/jsonldcontext.json\"\n",
+    "         iri: \"https://schema.org/docs/jsonldcontext.json\"\n",
     "    \n",
-    "'''    \n",
+    "''' \n",
+    "\n",
     "\n",
     "3. set by the configured store. A store (such as BlueBrainNexus) can support JSON-LD and add a default JSON-LD context whenever one is not provided."
    ]
@@ -202,7 +204,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
@@ -229,7 +231,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -238,7 +240,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -259,7 +261,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
@@ -289,7 +291,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -298,7 +300,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
@@ -318,7 +320,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 17,
    "metadata": {
     "scrolled": true
    },
@@ -1851,7 +1853,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
@@ -1880,7 +1882,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1889,7 +1891,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -1917,7 +1919,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
@@ -1937,7 +1939,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [
     {
@@ -1957,7 +1959,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [
     {
@@ -1976,7 +1978,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [
     {
@@ -2003,7 +2005,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2012,7 +2014,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2025,7 +2027,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 26,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2034,7 +2036,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
@@ -2043,7 +2045,7 @@
        "True"
       ]
      },
-     "execution_count": 27,
+     "execution_count": 28,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -2055,7 +2057,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -2069,7 +2071,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/14 - RDF IO.ipynb b/examples/notebooks/getting-started/14 - RDF IO.ipynb
index 3b846f2f..380823f0 100644
--- a/examples/notebooks/getting-started/14 - RDF IO.ipynb
+++ b/examples/notebooks/getting-started/14 - RDF IO.ipynb
@@ -69,7 +69,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -78,7 +78,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -96,7 +96,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -105,7 +105,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
@@ -114,7 +114,7 @@
        "True"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -125,15 +125,15 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), rdflib.term.URIRef('http://schema.org/Person'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('http://schema.org/name'), rdflib.term.Literal('Jane Doe'))\n"
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('http://schema.org/name'), rdflib.term.Literal('Jane Doe'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), rdflib.term.URIRef('http://schema.org/Person'))\n"
      ]
     }
    ],
@@ -144,16 +144,16 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "14"
+       "13"
       ]
      },
-     "execution_count": 22,
+     "execution_count": 9,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -165,27 +165,26 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/outgoing'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f/outgoing'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/constrainedBy'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/schemas/unconstrained.json'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/schemaProject'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/projects/github-users/mfsy'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/createdBy'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/realms/github/users/mfsy'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/deprecated'), rdflib.term.Literal('false', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#boolean')))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/rev'), rdflib.term.Literal('1', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), rdflib.term.URIRef('http://schema.org/Person'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('http://schema.org/name'), rdflib.term.Literal('Jane Doe'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/incoming'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f/incoming'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/project'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/projects/github-users/mfsy'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/updatedAt'), rdflib.term.Literal('2021-08-23T09:50:21.828000+00:00', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#dateTime')))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/createdAt'), rdflib.term.Literal('2021-08-23T09:50:21.828000+00:00', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#dateTime')))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/updatedBy'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/realms/github/users/mfsy'))\n",
-      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/self'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/e75061b0-8054-47e1-965b-260b3af3204f'))\n"
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/deprecated'), rdflib.term.Literal('false', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#boolean')))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/createdBy'), rdflib.term.Literal('https://sandbox.bluebrainnexus.io/v1/realms/github/users/mfsy'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/self'), rdflib.term.Literal('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/updatedAt'), rdflib.term.Literal('2022-01-07T13:28:45.479Z'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/createdAt'), rdflib.term.Literal('2022-01-07T13:28:45.479Z'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/rev'), rdflib.term.Literal('1', datatype=rdflib.term.URIRef('http://www.w3.org/2001/XMLSchema#integer')))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), rdflib.term.URIRef('http://schema.org/Person'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/outgoing'), rdflib.term.Literal('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731/outgoing'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/constrainedBy'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/schemas/unconstrained.json'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('http://schema.org/name'), rdflib.term.Literal('Jane Doe'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/project'), rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/projects/github-users/mfsy'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/incoming'), rdflib.term.Literal('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731/incoming'))\n",
+      "(rdflib.term.URIRef('https://sandbox.bluebrainnexus.io/v1/resources/github-users/mfsy/_/c0ec0826-b2ff-4531-ba7d-24f90fc6d731'), rdflib.term.URIRef('https://bluebrain.github.io/nexus/vocabulary/updatedBy'), rdflib.term.Literal('https://sandbox.bluebrainnexus.io/v1/realms/github/users/mfsy'))\n"
      ]
     }
    ],
@@ -203,11 +202,11 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
-    "#data in n3 format\n",
+    "#data in n3 format. Other RDF formats can be used\n",
     "data = '''\n",
     "@prefix schema: <http://schema.org/> .\n",
     "[] a schema:Person;\n",
@@ -225,7 +224,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -234,7 +233,7 @@
        "8"
       ]
      },
-     "execution_count": 25,
+     "execution_count": 13,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -248,7 +247,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 26,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -257,7 +256,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
@@ -266,7 +265,7 @@
        "True"
       ]
      },
-     "execution_count": 27,
+     "execution_count": 15,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -284,7 +283,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 28,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -297,7 +296,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 29,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
@@ -306,7 +305,7 @@
        "True"
       ]
      },
-     "execution_count": 29,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -317,7 +316,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 30,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
@@ -344,7 +343,7 @@
        "  'schema:name': 'John Doe'}]"
       ]
      },
-     "execution_count": 30,
+     "execution_count": 18,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -362,7 +361,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 31,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -372,7 +371,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 32,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -381,7 +380,7 @@
        "True"
       ]
      },
-     "execution_count": 32,
+     "execution_count": 20,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -392,7 +391,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 33,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
@@ -420,7 +419,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 34,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [
     {
@@ -429,7 +428,7 @@
        "True"
       ]
      },
-     "execution_count": 34,
+     "execution_count": 22,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -440,7 +439,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 35,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -451,7 +450,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 36,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [
     {
@@ -460,7 +459,7 @@
        "True"
       ]
      },
-     "execution_count": 36,
+     "execution_count": 24,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -478,7 +477,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 37,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -488,7 +487,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 38,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [
     {
@@ -497,7 +496,7 @@
        "True"
       ]
      },
-     "execution_count": 38,
+     "execution_count": 26,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -509,7 +508,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -523,7 +522,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/15 - SQL IO.ipynb b/examples/notebooks/getting-started/15 - SQL IO.ipynb
index abd54ee9..8104b57e 100644
--- a/examples/notebooks/getting-started/15 - SQL IO.ipynb
+++ b/examples/notebooks/getting-started/15 - SQL IO.ipynb
@@ -16,17 +16,9 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "/bin/bash: pip: command not found\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "! pip install flask_sqlalchemy"
    ]
@@ -392,7 +384,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -406,7 +398,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/getting-started/16 - Debugging.ipynb b/examples/notebooks/getting-started/16 - Debugging.ipynb
index 442952fe..1c990d3a 100644
--- a/examples/notebooks/getting-started/16 - Debugging.ipynb
+++ b/examples/notebooks/getting-started/16 - Debugging.ipynb
@@ -80,7 +80,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -100,7 +100,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -109,7 +109,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
@@ -117,7 +117,7 @@
      "output_type": "stream",
      "text": [
       "<action> resolve\n",
-      "<error> ResolvingError: resolving scope or resolver name should be specified\n",
+      "<error> ResolvingError: resolving scope or resolver name should be specified in case of multiple available resolvers\n",
       "\n"
      ]
     }
@@ -128,7 +128,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
@@ -137,7 +137,7 @@
        "True"
       ]
      },
-     "execution_count": 22,
+     "execution_count": 8,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -155,7 +155,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -172,7 +172,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -181,7 +181,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 11,
    "metadata": {
     "scrolled": true
    },
@@ -190,7 +190,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "ResolvingError: resolving scope or resolver name should be specified\n"
+      "ResolvingError: resolving scope or resolver name should be specified in case of multiple available resolvers\n"
      ]
     }
    ],
@@ -210,7 +210,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 26,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -219,7 +219,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -228,14 +228,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 28,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "ResolvingError: resolving scope or resolver name should be specified\n"
+      "ResolvingError: resolving scope or resolver name should be specified in case of multiple available resolvers\n"
      ]
     }
    ],
@@ -248,7 +248,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 29,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -257,7 +257,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 30,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
@@ -265,7 +265,7 @@
      "output_type": "stream",
      "text": [
       "<action> resolve\n",
-      "<error> ResolvingError: resolving scope or resolver name should be specified\n",
+      "<error> ResolvingError: resolving scope or resolver name should be specified in case of multiple available resolvers\n",
       "\n"
      ]
     }
@@ -276,7 +276,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 31,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
@@ -285,7 +285,7 @@
        "True"
       ]
      },
-     "execution_count": 31,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -297,7 +297,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python (nexusforgelatest)",
+   "display_name": "Python 3.7 (nexusforgelatest)",
    "language": "python",
    "name": "nexusforgelatest"
   },
@@ -311,7 +311,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.10"
+   "version": "3.7.10"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/use-cases/prod-forge-nexus.yml b/examples/notebooks/use-cases/prod-forge-nexus.yml
index febee52f..05d1bf5e 100644
--- a/examples/notebooks/use-cases/prod-forge-nexus.yml
+++ b/examples/notebooks/use-cases/prod-forge-nexus.yml
@@ -12,6 +12,10 @@ Store:
   searchendpoints:
     sparql:
       endpoint: "https://bluebrain.github.io/nexus/vocabulary/defaultSparqlIndex"
+    elastic:
+      endpoint: "https://bbp.epfl.ch/neurosciencegraph/data/views/es/dataset"
+      mapping: "https://bbp.epfl.ch/neurosciencegraph/data/views/es/dataset"
+      default_str_keyword_field: "keyword"
   vocabulary:
     metadata:
       iri: "https://bluebrain.github.io/nexus/contexts/metadata.json"
diff --git a/kgforge/core/archetypes/store.py b/kgforge/core/archetypes/store.py
index 45134452..c564b2de 100644
--- a/kgforge/core/archetypes/store.py
+++ b/kgforge/core/archetypes/store.py
@@ -1,16 +1,17 @@
-# 
+#
 # Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # Blue Brain Nexus Forge is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 # General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+import json
 
 import re
 import time
@@ -21,9 +22,16 @@
 from kgforge.core import Resource
 from kgforge.core.commons.attributes import repr_class
 from kgforge.core.commons.context import Context
-from kgforge.core.commons.exceptions import (DeprecationError, DownloadingError, FreezingError,
-                                             RegistrationError, TaggingError, UpdatingError,
-                                             UploadingError, QueryingError)
+from kgforge.core.commons.exceptions import (
+    DeprecationError,
+    DownloadingError,
+    FreezingError,
+    RegistrationError,
+    TaggingError,
+    UpdatingError,
+    UploadingError,
+    QueryingError,
+)
 from kgforge.core.commons.execution import not_supported, run
 from kgforge.core.reshaping import collect_values
 
@@ -33,8 +41,22 @@
 # FIXME: need to find a comprehensive way (different than list) to get all SPARQL reserved clauses
 from kgforge.core.wrappings.dict import DictWrapper
 
-SPARQL_CLAUSES = ["where", "filter", "select", "union","limit","construct",
-                  "optional", "bind","values", "offset", "order by", "prefix", "graph", "distinct"]
+SPARQL_CLAUSES = [
+    "where",
+    "filter",
+    "select",
+    "union",
+    "limit",
+    "construct",
+    "optional",
+    "bind",
+    "values",
+    "offset",
+    "order by",
+    "prefix",
+    "graph",
+    "distinct",
+]
 
 
 class Store(ABC):
@@ -48,22 +70,39 @@ class Store(ABC):
     # TODO Move from BDD to classical testing to have a more parameterizable test suite. DKE-135.
     # POLICY Implementations should pass tests/specializations/stores/demo_store.feature tests.
 
-    def __init__(self, endpoint: Optional[str] = None, bucket: Optional[str] = None,
-                 token: Optional[str] = None, versioned_id_template: Optional[str] = None,
-                 file_resource_mapping: Optional[str] = None,
-                 model_context: Optional[Context] = None, searchendpoints:Optional[Dict] = None, **store_config) -> None:
+    def __init__(
+        self,
+        endpoint: Optional[str] = None,
+        bucket: Optional[str] = None,
+        token: Optional[str] = None,
+        versioned_id_template: Optional[str] = None,
+        file_resource_mapping: Optional[str] = None,
+        model_context: Optional[Context] = None,
+        searchendpoints: Optional[Dict] = None,
+        **store_config,
+    ) -> None:
         # file_resource_mapping: Optional[Union[Hjson, FilePath, URL]].
         # POLICY There could be data caching but it should be aware of changes made in the source.
         self.endpoint: Optional[str] = endpoint
         self.bucket: Optional[str] = bucket
         self.token: Optional[str] = token
         self.versioned_id_template: Optional[str] = versioned_id_template
-        loaded = self.mapping.load(file_resource_mapping) if file_resource_mapping else None
+        loaded = (
+            self.mapping.load(file_resource_mapping) if file_resource_mapping else None
+        )
         self.file_mapping: Optional[Any] = loaded
         self.model_context: Optional[Context] = model_context
-        self.service: Any = self._initialize_service(self.endpoint, self.bucket, self.token, searchendpoints, **store_config)
-        self.context: Context = self.service.context if hasattr(self.service, "context") else None
-        self.metadata_context: Context = self.service.metadata_context if hasattr(self.service, "metadata_context") else None
+        self.service: Any = self._initialize_service(
+            self.endpoint, self.bucket, self.token, searchendpoints, **store_config
+        )
+        self.context: Context = (
+            self.service.context if hasattr(self.service, "context") else None
+        )
+        self.metadata_context: Context = (
+            self.service.metadata_context
+            if hasattr(self.service, "metadata_context")
+            else None
+        )
 
     def __repr__(self) -> str:
         return repr_class(self)
@@ -80,10 +119,20 @@ def mapper(self) -> Optional[Callable]:
 
     # [C]RUD.
 
-    def register(self, data: Union[Resource, List[Resource]], schema_id: str = None) -> None:
+    def register(
+        self, data: Union[Resource, List[Resource]], schema_id: str = None
+    ) -> None:
         # Replace None by self._register_many to switch to optimized bulk registration.
-        run(self._register_one, None, data, required_synchronized=False, execute_actions=True,
-            exception=RegistrationError, monitored_status="_synchronized", schema_id=schema_id)
+        run(
+            self._register_one,
+            None,
+            data,
+            required_synchronized=False,
+            execute_actions=True,
+            exception=RegistrationError,
+            monitored_status="_synchronized",
+            schema_id=schema_id,
+        )
 
     def _register_many(self, resources: List[Resource], schema_id: str) -> None:
         # Bulk registration could be optimized by overriding this method in the specialization.
@@ -110,7 +159,9 @@ def upload(self, path: str, content_type: str) -> Union[Resource, List[Resource]
     def _upload(self, path: Path, content_type: str) -> Union[Any, List[Any]]:
         # path: Union[FilePath, DirPath].
         if path.is_dir():
-            filepaths = [x for x in path.iterdir() if x.is_file() and not x.name.startswith(".")]
+            filepaths = [
+                x for x in path.iterdir() if x.is_file() and not x.name.startswith(".")
+            ]
             return self._upload_many(filepaths, content_type)
         else:
             return self._upload_one(path, content_type)
@@ -128,8 +179,9 @@ def _upload_one(self, path: Path, content_type: str) -> Any:
     # C[R]UD.
 
     @abstractmethod
-    def retrieve(self, id: str, version: Optional[Union[int, str]], cross_bucket: bool
-                 ) -> Resource:
+    def retrieve(
+        self, id: str, version: Optional[Union[int, str]], cross_bucket: bool
+    ) -> Resource:
         # POLICY Should notify of failures with exception RetrievalError including a message.
         # POLICY Resource _store_metadata should be set using wrappers.dict.wrap_dict().
         # POLICY Resource _synchronized should be set to True.
@@ -140,8 +192,14 @@ def _retrieve_filename(self, id: str) -> str:
         # TODO This operation might be adapted if other file metadata are needed.
         not_supported()
 
-    def download(self, data: Union[Resource, List[Resource]], follow: str, path: str,
-                 overwrite: bool, cross_bucket: bool) -> None:
+    def download(
+        self,
+        data: Union[Resource, List[Resource]],
+        follow: str,
+        path: str,
+        overwrite: bool,
+        cross_bucket: bool,
+    ) -> None:
         # path: DirPath.
         urls = []
         store_metadata = []
@@ -149,10 +207,14 @@ def download(self, data: Union[Resource, List[Resource]], follow: str, path: str
         for d in to_download:
             collected_values = collect_values(d, follow, DownloadingError)
             urls.extend(collected_values)
-            store_metadata.extend([d._store_metadata for _ in range(len(collected_values))])
+            store_metadata.extend(
+                [d._store_metadata for _ in range(len(collected_values))]
+            )
         count = len(urls)
         if count == 0:
-            raise DownloadingError(f"path to follow '{follow}' was not found in any provided resource.")
+            raise DownloadingError(
+                f"path to follow '{follow}' was not found in any provided resource."
+            )
         dirpath = Path(path)
         dirpath.mkdir(parents=True, exist_ok=True)
         timestamp = time.strftime("%Y%m%d%H%M%S")
@@ -169,24 +231,47 @@ def download(self, data: Union[Resource, List[Resource]], follow: str, path: str
         else:
             self._download_one(urls[0], filepaths[0], store_metadata, cross_bucket)
 
-    def _download_many(self, urls: List[str], paths: List[str], store_metadata: Optional[List[DictWrapper]], cross_bucket: bool) -> None:
+    def _download_many(
+        self,
+        urls: List[str],
+        paths: List[str],
+        store_metadata: Optional[List[DictWrapper]],
+        cross_bucket: bool,
+    ) -> None:
         # paths: List[FilePath].
         # Bulk downloading could be optimized by overriding this method in the specialization.
         # POLICY Should follow self._download_one() policies.
         for url, path in zip(urls, paths):
             self._download_one(url, path)
 
-    def _download_one(self, url: str, path: str, store_metadata: Optional[DictWrapper], cross_bucket: bool) -> None:
+    def _download_one(
+        self,
+        url: str,
+        path: str,
+        store_metadata: Optional[DictWrapper],
+        cross_bucket: bool,
+    ) -> None:
         # path: FilePath.
         # POLICY Should notify of failures with exception DownloadingError including a message.
         not_supported()
 
     # CR[U]D.
 
-    def update(self, data: Union[Resource, List[Resource]], schema_id: Optional[str]) -> None:
+    def update(
+        self, data: Union[Resource, List[Resource]], schema_id: Optional[str]
+    ) -> None:
         # Replace None by self._update_many to switch to optimized bulk update.
-        run(self._update_one, None, data, id_required=True, required_synchronized=False,
-            execute_actions=True, exception=UpdatingError, monitored_status="_synchronized", schema_id=schema_id)
+        run(
+            self._update_one,
+            None,
+            data,
+            id_required=True,
+            required_synchronized=False,
+            execute_actions=True,
+            exception=UpdatingError,
+            monitored_status="_synchronized",
+            schema_id=schema_id,
+        )
 
     def _update_many(self, resources: List[Resource], schema_id: Optional[str]) -> None:
         # Bulk update could be optimized by overriding this method in the specialization.
@@ -203,8 +288,15 @@ def _update_one(self, resource: Resource, schema_id: Optional[str]) -> None:
     def tag(self, data: Union[Resource, List[Resource]], value: str) -> None:
         # Replace None by self._tag_many to switch to optimized bulk tagging.
         # POLICY If tagging modify the resource, run() should have status='_synchronized'.
-        run(self._tag_one, None, data, id_required=True, required_synchronized=True,
-            exception=TaggingError, value=value)
+        run(
+            self._tag_one,
+            None,
+            data,
+            id_required=True,
+            required_synchronized=True,
+            exception=TaggingError,
+            value=value,
+        )
 
     def _tag_many(self, resources: List[Resource], value: str) -> None:
         # Bulk tagging could be optimized by overriding this method in the specialization.
@@ -221,8 +313,15 @@ def _tag_one(self, resource: Resource, value: str) -> None:
 
     def deprecate(self, data: Union[Resource, List[Resource]]) -> None:
         # Replace None by self._deprecate_many to switch to optimized bulk deprecation.
-        run(self._deprecate_one, None, data, id_required=True, required_synchronized=True,
-            exception=DeprecationError, monitored_status="_synchronized")
+        run(
+            self._deprecate_one,
+            None,
+            data,
+            id_required=True,
+            required_synchronized=True,
+            exception=DeprecationError,
+            monitored_status="_synchronized",
+        )
 
     def _deprecate_many(self, resources: List[Resource]) -> None:
         # Bulk deprecation could be optimized by overriding this method in the specialization.
@@ -237,7 +336,9 @@ def _deprecate_one(self, resource: Resource) -> None:
 
     # Querying.
 
-    def search(self, resolvers: Optional[List["Resolver"]], *filters, **params) -> List[Resource]:
+    def search(
+        self, resolvers: Optional[List["Resolver"]], *filters, **params
+    ) -> List[Resource]:
 
         # Positional arguments in 'filters' are instances of type Filter from wrappings/paths.py
         # A dictionary can be provided for filters:
@@ -250,15 +351,22 @@ def search(self, resolvers: Optional[List["Resolver"]], *filters, **params) -> L
         #   - deprecated: bool,
         #   - resolving: str, with values in ('exact', 'fuzzy'),
         #   - lookup: str, with values in ('current', 'children').
-        # POLICY Should use sparql() when SPARQL is chosen here has the querying language.
+        # POLICY Should use sparql() when 'sparql' is chosen as value  for the param 'search_endpoint'.
+        # POLICY Should use elastic() when 'elastic' is chosen as value  for the param 'search_endpoint'.
         # POLICY Should notify of failures with exception QueryingError including a message.
         # POLICY Resource _store_metadata should be set using wrappers.dict.wrap_dict().
         # POLICY Resource _synchronized should be set to True.
         # TODO These two operations might be abstracted here when other stores will be implemented.
         not_supported()
 
-    def sparql(self, query: str, debug: bool, limit: int, offset: int = None) -> List[Resource]:
-        qr = rewrite_sparql(query, self.model_context) if self.model_context is not None else query
+    def sparql(
+        self, query: str, debug: bool, limit: int, offset: int = None
+    ) -> List[Resource]:
+        qr = (
+            rewrite_sparql(query, self.model_context)
+            if self.model_context is not None
+            else query
+        )
         if debug:
             self._debug_query(qr)
         return self._sparql(qr, limit, offset)
@@ -269,10 +377,15 @@ def _sparql(self, query: str, limit: int, offset: int) -> List[Resource]:
         # POLICY Resource _synchronized should not be set (default is False).
         not_supported()
 
-    def elastic(self, query: str, debug: bool, limit: int, offset: int = None) -> List[Resource]:
+    def elastic(
+        self, query: str, debug: bool, limit: int, offset: int
+    ) -> List[Resource]:
+        query_dict = json.loads(query)
+        query_dict["size"] = limit if limit else query_dict.get("size", 100)
+        query_dict["from"] = offset if offset else query_dict.get("from", 0)
         if debug:
-            self._debug_query(query)
-        return self._elastic(query, limit, offset)
+            self._debug_query(query_dict)
+        return self._elastic(json.dumps(query_dict), limit, offset)
 
     def _elastic(self, query: str, limit: int, offset: int) -> List[Resource]:
         # POLICY Should notify of failures with exception QueryingError including a message.
@@ -284,8 +397,14 @@ def _elastic(self, query: str, limit: int, offset: int) -> List[Resource]:
 
     def freeze(self, data: Union[Resource, List[Resource]]) -> None:
         # Replace None by self._freeze_many to switch to optimized bulk freezing.
-        run(self._freeze_one, None, data, id_required=True, required_synchronized=True,
-            exception=FreezingError)
+        run(
+            self._freeze_one,
+            None,
+            data,
+            id_required=True,
+            required_synchronized=True,
+            exception=FreezingError,
+        )
 
     def _freeze_many(self, resources: List[Resource]) -> None:
         # Bulk freezing could be optimized by overriding this method in the specialization.
@@ -308,8 +427,14 @@ def _freeze_one(self, resource: Resource) -> None:
     # Utils.
 
     @abstractmethod
-    def _initialize_service(self, endpoint: Optional[str], bucket: Optional[str],
-                            token: Optional[str], searchendpoints:Optional[Dict] = None, **store_config) -> Any:
+    def _initialize_service(
+        self,
+        endpoint: Optional[str],
+        bucket: Optional[str],
+        token: Optional[str],
+        searchendpoints: Optional[Dict] = None,
+        **store_config,
+    ) -> Any:
         # POLICY Should initialize the access to the store according to its configuration.
         pass
 
@@ -329,7 +454,10 @@ def rewrite_sparql(query: str, context: Context) -> str:
     In the case of contexts using prefixed names, prefixes are added to the SPARQL query prologue.
     In the case of non available contexts and vocab then the query is returned unchanged.
     """
-    ctx = {k: v["@id"] if isinstance(v, Dict) else v for k, v in context.document["@context"].items()}
+    ctx = {
+        k: v["@id"] if isinstance(v, Dict) else v
+        for k, v in context.document["@context"].items()
+    }
     prefixes = context.prefixes
     has_prefixes = prefixes is not None and len(prefixes.keys()) > 0
     if ctx.get("type") == "@type":
@@ -343,11 +471,17 @@ def replace(match: Match) -> str:
         if m4 is None:
             return match.group(0)
         else:
-            v = ctx.get(m4, ":" + m4 if context.has_vocab() else None) if str(m4).lower() not in SPARQL_CLAUSES and \
-                not str(m4).startswith("https") else m4
+            v = (
+                ctx.get(m4, ":" + m4 if context.has_vocab() else None)
+                if str(m4).lower() not in SPARQL_CLAUSES
+                and not str(m4).startswith("https")
+                else m4
+            )
             if v is None:
-                raise QueryingError(f"Failed to construct a valid SPARQL query: add '{m4}'"
-                                    f" or define an @vocab in the configured JSON-LD context.")
+                raise QueryingError(
+                    f"Failed to construct a valid SPARQL query: add '{m4}'"
+                    f" or define an @vocab in the configured JSON-LD context."
+                )
             m5 = match.group(5)
             if "//" in v:
                 return f"<{v}>{m5}"
diff --git a/kgforge/core/commons/__init__.py b/kgforge/core/commons/__init__.py
index 2fd3623a..dcdedb1c 100644
--- a/kgforge/core/commons/__init__.py
+++ b/kgforge/core/commons/__init__.py
@@ -1,15 +1,15 @@
-# 
+#
 # Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # Blue Brain Nexus Forge is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 # General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
 
-# Expected to be empty.
+from .context import Context
diff --git a/kgforge/core/commons/es_query_builder.py b/kgforge/core/commons/es_query_builder.py
new file mode 100644
index 00000000..33c1a7e1
--- /dev/null
+++ b/kgforge/core/commons/es_query_builder.py
@@ -0,0 +1,418 @@
+﻿#
+# Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Blue Brain Nexus Forge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+import copy
+
+import datetime
+import dateutil
+import elasticsearch_dsl
+from dateutil.parser import ParserError
+from elasticsearch_dsl import Field
+from typing import Tuple, List, Dict, Optional, Any
+
+from kgforge.core.commons.context import Context
+from kgforge.core.commons.query_builder import QueryBuilder
+from kgforge.core.wrappings import Filter, FilterOperator
+
+elasticsearch_operator_range_map = {
+    FilterOperator.LOWER_THAN.value: "lt",
+    FilterOperator.LOWER_OR_Equal_Than.value: "lte",
+    FilterOperator.GREATER_Than.value: "gt",
+    FilterOperator.GREATER_OR_Equal_Than.value: "gte",
+}
+
+
+class ESQueryBuilder(QueryBuilder):
+    @staticmethod
+    def build(
+        schema: Dict,
+        resolvers: Optional[List["Resolver"]],
+        context: Context,
+        *filters,
+        **params,
+    ) -> Tuple[List, List, List]:
+
+        es_filters = list()
+        musts = list()
+        must_nots = list()
+        script_scores = list()
+
+        default_str_keyword_field = params.get("default_str_keyword_field", "keyword")
+        includes = params.get("includes", None)
+        excludes = params.get("excludes", None)
+
+        m = elasticsearch_dsl.Mapping()
+        dynamic = True
+        if schema is not None:
+            m._update_from_dict(schema)
+            dynamic = m._meta["dynamic"] if "dynamic" in m._meta else dynamic
+
+        for index, f in enumerate(*filters):
+            _filter = None
+            must = None
+            must_not = None
+            script_score = None
+            last_path = f.path[-1]
+            property_path = ".".join(f.path)
+            found_path_parts = None
+            nested_path, mapping_type = m.resolve_nested(field_path=property_path)
+            if isinstance(mapping_type, elasticsearch_dsl.Nested) or isinstance(
+                mapping_type, elasticsearch_dsl.Object
+            ):
+                raise ValueError(
+                    f"The provided path {f.path} can't be used for filter/search because it is not a leaf property."
+                )
+            if (
+                isinstance(mapping_type, elasticsearch_dsl.DenseVector)
+                and f.operator != FilterOperator.EQUAL.value
+            ):
+                raise ValueError(
+                    f"The provided DenseVector path '{f.path}' can't be used for filter/search but only with the "
+                    f"'__eq__' operator for similarity search."
+                )
+
+            if mapping_type is None and nested_path == ():
+                if not dynamic:
+                    raise ValueError(
+                        f"The provided path {f.path} is not present in the provided elasticsearch mapping (dynamic == {dynamic}). "
+                        f"Please search with a path present in the elasticsearch mapping or provide a dynamic mapping."
+                    )
+                else:
+                    (
+                        nested_path,
+                        found_path_parts,
+                        mapping_type,
+                    ), property_path = _look_up_known_parent_paths(
+                        f, last_path, property_path, m
+                    )
+            # else (i.e mapping_type == Text, Keyword, ...) (i.e nested_path with value => in a nested field
+            if len(nested_path) >= 1:
+                keyword_path = _build_keyword_path(mapping_type, property_path)
+                if keyword_path:
+                    filter_or_must_or_must_not = (
+                        "filter"
+                        if f.operator == FilterOperator.EQUAL.value
+                        else "must_not"
+                    )
+                    k_path = keyword_path
+                    term_or_match = "term"
+                else:
+                    filter_or_must_or_must_not = (
+                        "must"
+                        if f.operator == FilterOperator.EQUAL.value
+                        else "must_not"
+                    )
+                    k_path = property_path
+                    term_or_match = "match"
+                n_path = nested_path
+
+            # nested_path = [] => (i.e mapping_type == Text, Date, Keyword, ...) - nested_path empty
+            else:
+                found_path = ".".join(found_path_parts) if found_path_parts else None
+                if mapping_type is None or (found_path and found_path != property_path):
+                    mapping_type = _detect_mapping_type(f.value)
+                    keyword_path = _build_keyword_path(
+                        None,
+                        property_path,
+                        dynamic_mapping_type=mapping_type,
+                        default_str_keyword_field=default_str_keyword_field
+                    )
+                else:
+                    keyword_path = _build_keyword_path(mapping_type, property_path)
+                if keyword_path:
+                    filter_or_must_or_must_not = (
+                        "filter"
+                        if f.operator == FilterOperator.EQUAL.value
+                        else "must_not"
+                    )
+                    k_path = keyword_path
+                    term_or_match = "term"
+                else:
+                    filter_or_must_or_must_not = (
+                        "must"
+                        if f.operator == FilterOperator.EQUAL.value
+                        else "must_not"
+                    )
+                    k_path = property_path
+                    term_or_match = "match"
+                n_path = None
+
+            _filter, must, must_not, script_score = _build_bool_query(
+                f,
+                mapping_type,
+                k_path,
+                property_path,
+                filter_or_must_or_must_not,
+                term_or_match,
+                n_path,
+            )
+
+            if _filter:
+                es_filters.append(_filter)
+            if must:
+                musts.append(must)
+            if must_not:
+                must_nots.append(must_not)
+            if script_score:
+                script_scores.append(script_score)
+
+        if len(script_scores) > 1:
+            raise ValueError(
+                f"Multiple dense vector similarity query are not supported: {len(script_scores)} filters involving dense vectors were provided."
+            )
+        elif len(script_scores) == 1:
+            bool_query = _wrap_in_bool_query(es_filters, musts, must_nots)
+            return _wrap_in_script_score_query(
+                bool_query, script_scores[0], includes, excludes
+            )
+        else:
+            return _wrap_in_bool_query(es_filters, musts, must_nots, includes, excludes)
+
+
+def _look_up_known_parent_paths(f, last_path, property_path, m):
+    if (
+        len(f.path) >= 2
+        and last_path in ["id", "@id"]
+        and f.path[-2] in ["type", "@type"]
+    ):  # to cope with paths.type.id
+        property_path = _join_property_path(f.path[:-2], "@type")
+
+    if len(f.path) >= 1 and last_path in ["type", "@type"]:
+        property_path = _join_property_path(f.path[:-1], "@type")
+
+    if len(f.path) >= 1 and last_path in ["id", "@id"]:
+        property_path = _join_property_path(f.path[:-1], "@id")
+
+    parts = property_path.split(".")
+    return _recursive_resolve_nested(m, parts), property_path
+
+
+def _join_property_path(f_path, to_join_with):
+    property_path = ".".join(f_path)
+    property_path = (
+        property_path + "." + to_join_with if property_path != "" else to_join_with
+    )
+    return property_path
+
+
+def _wrap_in_bool_query(filters, musts, must_nots, includes=None, excludes=None):
+    bool_query = {
+        "query": {"bool": {"filter": filters, "must": musts, "must_not": must_nots}}
+    }
+    return _add_source(bool_query, includes, excludes)
+
+
+def _wrap_in_script_score_query(query, script, includes=None, excludes=None):
+    # Todo: Catch non supported combination of nested query and non nested ones
+    if isinstance(script, elasticsearch_dsl.query.Nested):
+        script.query = elasticsearch_dsl.query.ScriptScore(
+            query=query["query"], script=script.query.to_dict()["script"]
+        )
+        script_score_query = script
+    else:
+        script_score_query = elasticsearch_dsl.query.ScriptScore(
+            query=query["query"], script=script.to_dict()["script"]
+        )
+    query = elasticsearch_dsl.Search().query(script_score_query.to_dict()).to_dict()
+    return _add_source(query, includes, excludes)
+
+
+def _add_source(query, includes, excludes):
+    _source = {}
+    if includes:
+        _source["includes"] = includes
+    if excludes:
+        _source["excludes"] = excludes
+    if _source:
+        query_copy = copy.deepcopy(query)
+        query_copy["_source"] = _source
+        return query_copy
+    else:
+        return query
+
+
+def _recursive_resolve_nested(m, field_path):
+    nested_path, mapping_type = m.resolve_nested(field_path=".".join(field_path))
+    if mapping_type is None and nested_path == ():
+        if len(field_path) > 1:
+            return _recursive_resolve_nested(
+                m, field_path=field_path[0 : len(field_path) - 1]
+            )
+        else:
+            return nested_path, field_path, mapping_type
+
+    else:
+        return nested_path, field_path, mapping_type
+
+
+def _build_keyword_path(
+    mapping_type,
+    property_path,
+    dynamic_mapping_type=None,
+    default_str_keyword_field=None,
+):
+    if isinstance(mapping_type, elasticsearch_dsl.Keyword):
+        keyword_path = property_path
+    elif (
+        mapping_type is None
+        and default_str_keyword_field is not None
+        and isinstance(dynamic_mapping_type, elasticsearch_dsl.Text)
+    ):
+        keyword_path = ".".join([property_path, default_str_keyword_field])
+    elif mapping_type is not None:
+        keyword_fields = [
+            keyword_field
+            for keyword_field, val in mapping_type.fields.to_dict().items()
+            if isinstance(val, elasticsearch_dsl.Keyword)
+        ]
+        keyword_path = (
+            ".".join([property_path, keyword_fields[0]])
+            if len(keyword_fields) >= 1
+            else None
+        )
+    else:
+        keyword_path = None
+    return keyword_path
+
+
+def _build_bool_query(
+    filter: Filter,
+    mapping_type: Field,
+    k_path: str,
+    property_path: str,
+    filter_or_must_or_must_not: str,
+    term_or_match: str,
+    nested_path: List[str] = None,
+):
+    _filter = None
+    must = None
+    must_not = None
+    script_score = None
+    if (
+        filter.operator in elasticsearch_operator_range_map.keys()
+        and not isinstance(mapping_type, elasticsearch_dsl.Text)
+        and not isinstance(mapping_type, elasticsearch_dsl.Boolean)
+    ):  # range filter query
+        if nested_path:
+            _filter = _wrap_in_nested_bool_query(
+                nested_path[-1],
+                "filter",
+                "range",
+                {
+                    property_path: {
+                        elasticsearch_operator_range_map[filter.operator]: filter.value
+                    }
+                },
+            )
+        else:
+            _filter = _wrap_in_non_nested_query_query(
+                property_path,
+                "range",
+                {elasticsearch_operator_range_map[filter.operator]: filter.value},
+            )
+
+    elif filter.operator in elasticsearch_operator_range_map.keys() and (
+        isinstance(mapping_type, elasticsearch_dsl.Text)
+        or isinstance(mapping_type, elasticsearch_dsl.Boolean)
+    ):
+        raise ValueError(
+            f"Using the range operator {filter.operator} on the Text field/path {filter.path} is not supported."
+        )
+    elif filter.operator == FilterOperator.EQUAL.value and isinstance(
+        mapping_type, elasticsearch_dsl.DenseVector
+    ):
+        if nested_path:
+            sim_query = _wrap_in_script_query(k_path, filter.value)
+            script_score = _wrap_in_nested_query(
+                path=nested_path[-1], query=sim_query.to_dict()
+            )
+        else:
+            script_score = _wrap_in_script_query(k_path, filter.value)
+
+        must = _wrap_in_non_nested_query_query(
+            "field", "exists", k_path
+        )  # otherwise script_score will fail. This will be added in a nested script_score query
+    else:
+        if nested_path:
+            query = _wrap_in_nested_bool_query(
+                nested_path[-1],
+                filter_or_must_or_must_not,
+                term_or_match,
+                {k_path: filter.value},
+            )
+        else:
+            query = _wrap_in_non_nested_query_query(k_path, term_or_match, filter.value)
+
+        if filter_or_must_or_must_not == "filter":
+            _filter = query
+        elif filter_or_must_or_must_not == "must":
+            must = query
+        else:
+            must_not = query
+    return _filter, must, must_not, script_score
+
+
+def _wrap_in_nested_bool_query(
+    path, filter_or_must_or_must_not, filter_type, filter_value
+):
+    return _wrap_in_nested_query(
+        path=path,
+        query=elasticsearch_dsl.query.Q(
+            "bool",
+            **{
+                filter_or_must_or_must_not: elasticsearch_dsl.query.Q(
+                    filter_type, **filter_value
+                ).to_dict()
+            },
+        ),
+    ).to_dict()
+
+
+def _wrap_in_nested_query(path: str, query: Dict):
+    return elasticsearch_dsl.query.Nested(path=path, query=query)
+
+
+def _detect_mapping_type(value: Any):
+
+    try:
+        # integer
+        if str(value).isnumeric():
+            return elasticsearch_dsl.Long()
+        # bool
+        if value is True or value is False:
+            return elasticsearch_dsl.Boolean()
+        # double
+        if float(value):
+            return elasticsearch_dsl.Float()
+    except ValueError as ve:
+        pass
+    except TypeError as te:
+        pass
+    try:
+        # Date
+        if isinstance(dateutil.parser.parse(str(value)), datetime.datetime):
+            return elasticsearch_dsl.Date()
+    except ParserError as pe:
+        return elasticsearch_dsl.Text()
+
+
+def _wrap_in_non_nested_query_query(path, filter_type, filter_value):
+    query = {filter_type: {path: filter_value}}
+    return query
+
+
+def _wrap_in_script_query(field: str, queryVector: List):
+    return elasticsearch_dsl.query.Script(
+        source=f"doc['{field}'].size() == 0 ? 0 : (cosineSimilarity(params.queryVector, doc['{field}'])+1.0) / 2",
+        params={"queryVector": queryVector},
+    )
diff --git a/kgforge/core/commons/query_builder.py b/kgforge/core/commons/query_builder.py
new file mode 100644
index 00000000..1a288958
--- /dev/null
+++ b/kgforge/core/commons/query_builder.py
@@ -0,0 +1,35 @@
+#
+# Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Blue Brain Nexus Forge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+from typing import Any, Optional, List
+
+from abc import abstractmethod, ABC
+
+from kgforge.core.commons.attributes import repr_class
+from kgforge.core.commons.context import Context
+
+
+class QueryBuilder(ABC):
+    def __repr__(self) -> str:
+        return repr_class(self)
+
+    @staticmethod
+    @abstractmethod
+    def build(
+        schema: Any,
+        resolvers: Optional[List["Resolver"]],
+        context: Context,
+        *filters,
+        **params
+    ) -> Any:
+        pass
diff --git a/kgforge/core/conversions/rdf.py b/kgforge/core/conversions/rdf.py
index 2d4c2e54..8ce996bb 100644
--- a/kgforge/core/conversions/rdf.py
+++ b/kgforge/core/conversions/rdf.py
@@ -40,16 +40,33 @@ class Form(Enum):
 LD_KEYS = {"id": "@id", "type": "@type", "list": "@list", "set": "@set"}
 
 
-def as_graph(data: Union[Resource, List[Resource]], store_metadata: bool,
-             model_context: Optional[Context], metadata_context: Optional[Context],
-             context_resolver: Optional[Callable]) -> Graph:
-    return dispatch(data, _as_graph_many, _as_graph_one, store_metadata, model_context,
-                    metadata_context, context_resolver)
-
-
-def as_jsonld(data: Union[Resource, List[Resource]], form: str, store_metadata: bool,
-              model_context: Optional[Context], metadata_context: Optional[Context],
-              context_resolver: Optional[Callable], na: Union[Any, List[Any]]) -> Union[Dict, List[Dict]]:
+def as_graph(
+    data: Union[Resource, List[Resource]],
+    store_metadata: bool,
+    model_context: Optional[Context],
+    metadata_context: Optional[Context],
+    context_resolver: Optional[Callable],
+) -> Graph:
+    return dispatch(
+        data,
+        _as_graph_many,
+        _as_graph_one,
+        store_metadata,
+        model_context,
+        metadata_context,
+        context_resolver,
+    )
+
+
+def as_jsonld(
+    data: Union[Resource, List[Resource]],
+    form: str,
+    store_metadata: bool,
+    model_context: Optional[Context],
+    metadata_context: Optional[Context],
+    context_resolver: Optional[Callable],
+    na: Union[Any, List[Any]],
+) -> Union[Dict, List[Dict]]:
     try:
         valid_form = Form(form.lower())
     except ValueError:
@@ -57,8 +74,16 @@ def as_jsonld(data: Union[Resource, List[Resource]], form: str, store_metadata:
         raise NotSupportedError(f"supported serialization forms are {supported_forms}")
 
     return dispatch(
-        data, _as_jsonld_many, _as_jsonld_one, valid_form, store_metadata, model_context,
-        metadata_context, context_resolver, na)
+        data,
+        _as_jsonld_many,
+        _as_jsonld_one,
+        valid_form,
+        store_metadata,
+        model_context,
+        metadata_context,
+        context_resolver,
+        na,
+    )
 
 
 def from_jsonld(data: Union[Dict, List[Dict]]) -> Union[Resource, List[Resource]]:
@@ -70,12 +95,18 @@ def from_jsonld(data: Union[Dict, List[Dict]]) -> Union[Resource, List[Resource]
         raise TypeError("not a dictionary nor a list of dictionaries")
 
 
-def from_graph(data: Graph, type: Optional[Union[str, List]] = None, frame: Dict = None,
-               model_context: Optional[Context] = None) -> Union[Resource, List[Resource]]:
+def from_graph(
+    data: Graph,
+    type: Optional[Union[str, List]] = None,
+    frame: Dict = None,
+    model_context: Optional[Context] = None,
+) -> Union[Resource, List[Resource]]:
     from collections import OrderedDict
 
     if not type:
-        _types = data.triples((None, RDF.type, None))  # type of data to transform to JSONLD
+        _types = data.triples(
+            (None, RDF.type, None)
+        )  # type of data to transform to JSONLD
         _types = [str(_type[2]) for _type in _types]
     else:
         _types = type
@@ -92,20 +123,19 @@ def from_graph(data: Graph, type: Optional[Union[str, List]] = None, frame: Dict
         context = graph_json[CONTEXT]
 
     if not frame:
-        frame = {
-            "@context": context,
-            "@type": _types,
-            "@embed": True
-        }
+        frame = {"@context": context, "@type": _types, "@embed": True}
 
     framed = jsonld.frame(graph_json, frame)
     framed = _graph_free_jsonld(framed)
     if isinstance(framed, list):
-        framed = [jsonld.compact(item, ctx=context,
-                                 options={'processingMode': 'json-ld-1.1'}) for item in framed]
+        framed = [
+            jsonld.compact(item, ctx=context, options={"processingMode": "json-ld-1.1"})
+            for item in framed
+        ]
     else:
-        framed = jsonld.compact(framed, ctx=context,
-                                options={'processingMode': 'json-ld-1.1'})
+        framed = jsonld.compact(
+            framed, ctx=context, options={"processingMode": "json-ld-1.1"}
+        )
     return from_jsonld(framed)
 
 
@@ -142,32 +172,66 @@ def _from_jsonld_one(data: Dict) -> Resource:
         raise NotImplementedError("not implemented yet (expanded json-ld)")
 
 
-def _as_jsonld_many(resources: List[Resource], form: Form, store_metadata: bool,
-                    model_context: Optional[Context], metadata_context: Optional[Context],
-                    context_resolver: Optional[Callable], na: Optional[List[Any]]) -> List[Dict]:
+def _as_jsonld_many(
+    resources: List[Resource],
+    form: Form,
+    store_metadata: bool,
+    model_context: Optional[Context],
+    metadata_context: Optional[Context],
+    context_resolver: Optional[Callable],
+    na: Optional[List[Any]],
+) -> List[Dict]:
     if na is not None and len(na) != len(resources):
-        raise ValueError(f"len(na) should be equal to len(resources): {len(na)} != {len(resources)}")
-
-    return [_as_jsonld_one(resource, form, store_metadata, model_context, metadata_context,
-                           context_resolver, na[i]) if na is not None else
-            _as_jsonld_one(resource, form, store_metadata, model_context, metadata_context,
-                           context_resolver, na)
-            for i, resource in enumerate(resources)]
-
-
-def _as_jsonld_one(resource: Resource, form: Form, store_metadata: bool,
-                   model_context: Optional[Context], metadata_context: Optional[Context],
-                   context_resolver: Optional[Callable], na: Any) -> Dict:
+        raise ValueError(
+            f"len(na) should be equal to len(resources): {len(na)} != {len(resources)}"
+        )
+
+    return [
+        _as_jsonld_one(
+            resource,
+            form,
+            store_metadata,
+            model_context,
+            metadata_context,
+            context_resolver,
+            na[i],
+        )
+        if na is not None
+        else _as_jsonld_one(
+            resource,
+            form,
+            store_metadata,
+            model_context,
+            metadata_context,
+            context_resolver,
+            na,
+        )
+        for i, resource in enumerate(resources)
+    ]
+
+
+def _as_jsonld_one(
+    resource: Resource,
+    form: Form,
+    store_metadata: bool,
+    model_context: Optional[Context],
+    metadata_context: Optional[Context],
+    context_resolver: Optional[Callable],
+    na: Any,
+) -> Dict:
     context = _resource_context(resource, model_context, context_resolver)
     resolved_context = context.document
-    output_context = context.iri if context.is_http_iri() else context.document["@context"]
+    output_context = (
+        context.iri if context.is_http_iri() else context.document["@context"]
+    )
     resource_types = getattr(resource, "type", getattr(resource, "@type", None))
     # this is to ensure the framing is centered on the provided resource in case nested resources share the same type
     resource.type = "https://bluebrain.github.io/nexus/vocabulary/FramedType"
     if store_metadata and resource._store_metadata:
         if metadata_context:
-            resolved_context = _merge_jsonld(resolved_context["@context"],
-                                             metadata_context.document["@context"])
+            resolved_context = _merge_jsonld(
+                resolved_context["@context"], metadata_context.document["@context"]
+            )
             if metadata_context.is_http_iri():
                 metadata_context_output = metadata_context.iri
             else:
@@ -176,46 +240,72 @@ def _as_jsonld_one(resource: Resource, form: Form, store_metadata: bool,
         else:
             raise NotSupportedError("no available context in the metadata")
     try:
-        data_graph, metadata_graph, encoded_resource = _as_graphs(resource, store_metadata, context, metadata_context)
+        data_graph, metadata_graph, encoded_resource = _as_graphs(
+            resource, store_metadata, context, metadata_context
+        )
         data_graph.remove((None, None, Literal(na)))
     except Exception as e:
         raise ValueError(e)
-    jsonld_data_expanded = jsonld.expand(encoded_resource, options={'expandContext': resolved_context})
-
+    jsonld_data_expanded = jsonld.expand(
+        encoded_resource, options={"expandContext": resolved_context}
+    )
+    resource_id = None
     if hasattr(resource, "id") or hasattr(resource, "@id"):
-        resource_id = resource.id if hasattr(resource, "id") else getattr(resource, "@id")
+        resource_id = (
+            resource.id if hasattr(resource, "id") else getattr(resource, "@id")
+        )
+    if resource_id:
         if context.base:
             uri = context.resolve(resource_id)
         else:
-            uri = str(data_graph.namespace_manager.absolutize(resource_id, defrag=0)) if not str(
-                resource_id).startswith("_:") else resource_id
-        frame = {"@id": uri}
+            uri = (
+                str(data_graph.namespace_manager.absolutize(resource_id, defrag=0))
+                if not str(resource_id).startswith("_:")
+                else resource_id
+            )
+        frame_resource = {"@id": uri}
+        frame_metadata = {"@id": uri}
     else:
-        frame = dict()
+        frame_resource = dict()
+        frame_metadata = dict()
         for k, v in resource.__dict__.items():
-            if k not in Resource._RESERVED and not isinstance(v, (Resource, dict, list)):
+            if k not in Resource._RESERVED and not isinstance(
+                v, (Resource, dict, list)
+            ):
                 if k == "context":
                     continue
                 elif k == "type" or k == TYPE:
                     t = context.expand(v)
                     if t:
-                        frame["@type"] = context.expand(v)
+                        frame_resource["@type"] = context.expand(v)
                 else:
                     key = context.expand(k)
                     if key and isinstance(v, str):
-                        frame[key] = v
-    frame["@embed"] = "@link"
-    data_framed = jsonld.frame(jsonld_data_expanded, frame, options={'processingMode': 'json-ld-1.1'})
+                        frame_resource[key] = v
+        if resource._store_metadata:
+            for k, v in resource._store_metadata.__dict__.items():
+                key_metadata = metadata_context.expand(k)
+                if key_metadata and isinstance(v, str):
+                    frame_metadata[key_metadata] = v
+
+    frame_resource["@embed"] = "@link"
+    frame_metadata["@embed"] = "@link"
+    data_framed = jsonld.frame(
+        jsonld_data_expanded, frame_resource, options={"processingMode": "json-ld-1.1"}
+    )
     resource_graph_node = _graph_free_jsonld(data_framed)
     if resource_types is None:
         resource_graph_node.pop(TYPE)
     else:
-        resource_graph_node[TYPE] = context.expand(resource_types) if isinstance(resource_types, str) else [
-            context.expand(resource_type) for resource_type in resource_types]
+        resource_graph_node[TYPE] = (
+            context.expand(resource_types)
+            if isinstance(resource_types, str)
+            else [context.expand(resource_type) for resource_type in resource_types]
+        )
     resource.type = resource_types
     if store_metadata is True and len(metadata_graph) > 0:
         metadata_expanded = json.loads(metadata_graph.serialize(format="json-ld"))
-        metadata_framed = jsonld.frame(metadata_expanded, {"@id": resource.id})
+        metadata_framed = jsonld.frame(metadata_expanded, frame_metadata)
         if form is Form.COMPACTED:
             data_compacted = jsonld.compact(data_framed, resolved_context)
             metadata_compacted = jsonld.compact(metadata_framed, resolved_context)
@@ -237,45 +327,78 @@ def _as_jsonld_one(resource: Resource, form: Form, store_metadata: bool,
             compacted = jsonld.compact(data_framed, resolved_context)
             compacted["@context"] = output_context
             # this is to be able to register contexts documents
-            if len(compacted) == 1 and hasattr(resource, "id"):
-                compacted["@id"] = resource.id
+            if len(compacted) == 1 and resource_id:
+                compacted["@id"] = resource_id
             return compacted
         elif form is Form.EXPANDED:
             return _unpack_from_list(data_framed)
 
 
-def _as_graph_many(resources: List[Resource], store_metadata: bool, model_context: Optional[Context],
-                   metadata_context: Optional[Context], context_resolver: Optional[Callable]) -> Graph:
+def _as_graph_many(
+    resources: List[Resource],
+    store_metadata: bool,
+    model_context: Optional[Context],
+    metadata_context: Optional[Context],
+    context_resolver: Optional[Callable],
+) -> Graph:
     graph = Graph()
     for resource in resources:
         # Do not use _as_graph_one as it will lead to using graph1 + graph2 operation which can lead to blank node collisions
-        json_ld = _as_jsonld_one(resource, Form.EXPANDED, store_metadata, model_context,
-                                 metadata_context, context_resolver, None)
+        json_ld = _as_jsonld_one(
+            resource,
+            Form.EXPANDED,
+            store_metadata,
+            model_context,
+            metadata_context,
+            context_resolver,
+            None,
+        )
         graph.parse(data=json.dumps(json_ld), format="json-ld")
     return graph
 
 
-def _as_graph_one(resource: Resource, store_metadata: bool, model_context: Optional[Context],
-                  metadata_context: Optional[Context], context_resolver: Optional[Callable]) -> Graph:
-    json_ld = _as_jsonld_one(resource, Form.EXPANDED, store_metadata, model_context,
-                             metadata_context, context_resolver, None)
+def _as_graph_one(
+    resource: Resource,
+    store_metadata: bool,
+    model_context: Optional[Context],
+    metadata_context: Optional[Context],
+    context_resolver: Optional[Callable],
+) -> Graph:
+    json_ld = _as_jsonld_one(
+        resource,
+        Form.EXPANDED,
+        store_metadata,
+        model_context,
+        metadata_context,
+        context_resolver,
+        None,
+    )
     return Graph().parse(data=json.dumps(json_ld), format="json-ld")
 
 
-def _as_graphs(resource: Resource, store_metadata: bool, context: Context,
-               metadata_context: Context) -> Tuple[Graph, Graph, Dict]:
+def _as_graphs(
+    resource: Resource,
+    store_metadata: bool,
+    context: Context,
+    metadata_context: Context,
+) -> Tuple[Graph, Graph, Dict]:
     """Returns a data and a metadata graph"""
     if hasattr(resource, "context"):
         output_context = resource.context
     else:
-        output_context = context.iri if context.is_http_iri() else context.document["@context"]
+        output_context = (
+            context.iri if context.is_http_iri() else context.document["@context"]
+        )
     converted = _add_ld_keys(resource, output_context, context.base)
     converted["@context"] = context.document["@context"]
-    return _dicts_to_graph(converted, resource._store_metadata, store_metadata, metadata_context) + (converted,)
+    return _dicts_to_graph(
+        converted, resource._store_metadata, store_metadata, metadata_context
+    ) + (converted,)
 
 
-def _dicts_to_graph(data: Dict, metadata: Dict, store_meta: bool,
-                    metadata_context: Context) -> Tuple[Graph, Graph]:
+def _dicts_to_graph(
+    data: Dict, metadata: Dict, store_meta: bool, metadata_context: Context
+) -> Tuple[Graph, Graph]:
     json_str = json.dumps(data)
     graph = Graph().parse(data=json_str, format="json-ld")
     meta_data_graph = Graph()
@@ -291,27 +414,42 @@ def _dicts_to_graph(data: Dict, metadata: Dict, store_meta: bool,
     return graph, meta_data_graph
 
 
-def recursive_resolve(context: Union[Dict, List, str], resolver: Optional[Callable]) -> Dict:
+def recursive_resolve(
+    context: Union[Dict, List, str],
+    resolver: Optional[Callable],
+    already_loaded: List = [],
+) -> Dict:
     document = dict()
     if isinstance(context, list):
         for x in context:
-            document.update(recursive_resolve(x, resolver))
-    elif isinstance(context, str):
+            if x not in already_loaded:
+                document.update(recursive_resolve(x, resolver, already_loaded))
+    elif isinstance(context, str) and context not in already_loaded:
         doc = resolver(context)
-        document.update(recursive_resolve(doc, resolver))
+        document.update(recursive_resolve(doc, resolver, already_loaded))
+        already_loaded.append(context)
     elif isinstance(context, dict):
         document.update(context)
     return document
 
 
-def _resource_context(resource: Resource, model_context: Context, context_resolver: Callable) -> Context:
+def _resource_context(
+    resource: Resource, model_context: Context, context_resolver: Callable
+) -> Context:
     if hasattr(resource, "context"):
         if model_context and resource.context == model_context.iri:
             context = model_context
         else:
             iri = resource.context if isinstance(resource.context, str) else None
             try:
-                document = recursive_resolve(resource.context, context_resolver)
+                document = recursive_resolve(
+                    resource.context,
+                    context_resolver,
+                    [
+                        "https://bluebrainnexus.io/contexts/metadata.json",
+                        "https://bluebrain.github.io/nexus/contexts/metadata.json",
+                    ],
+                )
                 context = Context(document, iri)
             except (HTTPError, URLError, NotSupportedError):
                 try:
@@ -343,8 +481,9 @@ def _unpack_from_list(data):
         return node
 
 
-def _dicts_to_graph(data: Dict, metadata: Dict, store_meta: bool,
-                    metadata_context: Context) -> Tuple[Graph, Graph]:
+def _dicts_to_graph(
+    data: Dict, metadata: Dict, store_meta: bool, metadata_context: Context
+) -> Tuple[Graph, Graph]:
     json_str = json.dumps(data)
     graph = Graph().parse(data=json_str, format="json-ld")
     meta_data_graph = Graph()
@@ -360,7 +499,11 @@ def _dicts_to_graph(data: Dict, metadata: Dict, store_meta: bool,
     return graph, meta_data_graph
 
 
-def _add_ld_keys(rsc: [Resource, Dict], context: Optional[Union[Dict, List, str]], base: Optional[str]) -> Dict:
+def _add_ld_keys(
+    rsc: [Resource, Dict],
+    context: Optional[Union[Dict, List, str]],
+    base: Optional[str],
+) -> Dict:
     local_attrs = dict()
     local_context = None
     items = rsc.__dict__.items() if isinstance(rsc, Resource) else rsc.items()
@@ -378,18 +521,26 @@ def _add_ld_keys(rsc: [Resource, Dict], context: Optional[Union[Dict, List, str]
                     if isinstance(v, Resource) or isinstance(v, Dict):
                         local_attrs[key] = _add_ld_keys(v, context, base)
                     elif isinstance(v, list):
-                        local_attrs[key] = [_add_ld_keys(item, context, base)
-                                            if isinstance(item, Resource) or isinstance(item, Dict)
-                                            else item for item in v]
+                        local_attrs[key] = [
+                            _add_ld_keys(item, context, base)
+                            if isinstance(item, Resource) or isinstance(item, Dict)
+                            else item
+                            for item in v
+                        ]
                     else:
                         if isinstance(v, LazyAction):
-                            raise ValueError("can't convert, resource contains LazyActions")
-                        local_attrs[key] = v.replace(base, "") if base and isinstance(v, str) else v
+                            raise ValueError(
+                                "can't convert, resource contains LazyActions"
+                            )
+                        local_attrs[key] = (
+                            v.replace(base, "") if base and isinstance(v, str) else v
+                        )
     return local_attrs
 
 
-def _remove_ld_keys(dictionary: dict, context: Context,
-                    to_resource: Optional[bool] = True) -> Union[Dict, Resource]:
+def _remove_ld_keys(
+    dictionary: dict, context: Context, to_resource: Optional[bool] = True
+) -> Union[Dict, Resource]:
     local_attrs = dict()
     for k, v in dictionary.items():
         if k == "@context":
@@ -404,8 +555,12 @@ def _remove_ld_keys(dictionary: dict, context: Context,
                 if isinstance(v, dict):
                     local_attrs[k] = _remove_ld_keys(v, context, to_resource)
                 elif isinstance(v, list):
-                    local_attrs[k] = [_remove_ld_keys(item, context, to_resource)
-                                      if isinstance(item, dict) else item for item in v]
+                    local_attrs[k] = [
+                        _remove_ld_keys(item, context, to_resource)
+                        if isinstance(item, dict)
+                        else item
+                        for item in v
+                    ]
                 else:
                     if k in context.terms:
                         if context.terms[k].type == "@id":
@@ -444,7 +599,9 @@ def _merge_second_list_into_first(first: List, second: List) -> List:
             _merge_dict_into_list(first, element)
 
 
-def _merge_jsonld(first: Union[Dict, List, str], second: Union[Dict, List, str]) -> Union[Dict, List, str]:
+def _merge_jsonld(
+    first: Union[Dict, List, str], second: Union[Dict, List, str]
+) -> Union[Dict, List, str]:
     result = None
     if isinstance(first, str):
         if isinstance(second, str):
diff --git a/kgforge/core/forge.py b/kgforge/core/forge.py
index d26994b6..a394ad2e 100644
--- a/kgforge/core/forge.py
+++ b/kgforge/core/forge.py
@@ -588,7 +588,7 @@ def elastic(
         self,
         query: str,
         debug: bool = False,
-        limit: int = 100,
+        limit: Optional[int] = None,
         offset: Optional[int] = None,
     ) -> List[Resource]:
         """
diff --git a/kgforge/core/wrappings/__init__.py b/kgforge/core/wrappings/__init__.py
index 51003cbf..008e33eb 100644
--- a/kgforge/core/wrappings/__init__.py
+++ b/kgforge/core/wrappings/__init__.py
@@ -12,4 +12,4 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
 
-# Expected to be empty.
+from .paths import Filter, FilterOperator
diff --git a/kgforge/core/wrappings/paths.py b/kgforge/core/wrappings/paths.py
index d0f0810a..b20e7871 100644
--- a/kgforge/core/wrappings/paths.py
+++ b/kgforge/core/wrappings/paths.py
@@ -1,31 +1,50 @@
-# 
+#
 # Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # Blue Brain Nexus Forge is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 # General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+from enum import Enum, auto
 
 from typing import Any, Dict, List, Union
 
 from kgforge.core.commons.attributes import check_collisions, repr_class
 
 
-class Filter:
+class FilterOperator(Enum):
+    EQUAL = "__eq__"
+    NOT_EQUAL = "__ne__"
+    LOWER_THAN = "__lt__"
+    LOWER_OR_Equal_Than = "__le__"
+    GREATER_Than = "__gt__"
+    GREATER_OR_Equal_Than = "__ge__"
+
 
+class Filter:
     def __init__(self, path: List[str], operator: str, value: Any) -> None:
         self.path: List[str] = path
-        self.operator: str = operator
+        try:
+            self.operator: str = FilterOperator(operator).value
+        except Exception as e:
+            raise ValueError(
+                f"Invalid operator value '{operator}'. Allowed operators are {[member.value for name, member in FilterOperator.__members__.items()]}"
+            )
         self.value: Any = value
 
     def __eq__(self, other):
-        return isinstance(other, self.__class__) and self.path == other.path and self.operator == other.operator and self.value == other.value
+        return (
+            isinstance(other, self.__class__)
+            and self.path == other.path
+            and self.operator == other.operator
+            and self.value == other.value
+        )
 
     def __repr__(self) -> str:
         return repr_class(self)
@@ -42,35 +61,33 @@ def __repr__(self) -> str:
         return repr_class(self)
 
     def __lt__(self, other: Any) -> Filter:
-        return self._for("__lt__", other)
+        return self._for(FilterOperator.LOWER_THAN.value, other)
 
     def __le__(self, other: Any) -> Filter:
-        return self._for("__le__", other)
+        return self._for(FilterOperator.LOWER_OR_Equal_Than.value, other)
 
     def __eq__(self, other: Any) -> Filter:
-        return self._for("__eq__", other)
+        return self._for(FilterOperator.EQUAL.value, other)
 
     def __ne__(self, other: Any) -> Filter:
-        return self._for("__ne__", other)
+        return self._for(FilterOperator.NOT_EQUAL.value, other)
 
     def __gt__(self, other: Any) -> Filter:
-        return self._for("__gt__", other)
+        return self._for(FilterOperator.GREATER_Than.value, other)
 
     def __ge__(self, other: Any) -> Filter:
-        return self._for("__ge__", other)
+        return self._for(FilterOperator.GREATER_OR_Equal_Than.value, other)
 
     def _for(self, operator: str, other: Any) -> Filter:
         return Filter(self._path, operator, other)
 
 
 class PathWrapper(FilterMixin):
-
     def __init__(self, path: List[str]) -> None:
         super().__init__(path)
 
 
 class PathsWrapper(FilterMixin):
-
     def __init__(self, path: List[str], paths: Dict) -> None:
         check_collisions(self._RESERVED, paths.keys())
         super().__init__(path)
@@ -98,6 +115,6 @@ def create_filters_from_dict(filter_dict: Dict, path_prefix=None) -> List[Filter
         if isinstance(v, dict):
             filters.extend(create_filters_from_dict(v, path))
         else:
-            filters.append(Filter(path, "__eq__", v))
+            filters.append(Filter(path, FilterOperator.EQUAL.value, v))
         path_prefix.remove(k)
     return filters
diff --git a/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py b/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py
index 5960c9d6..a06ef35f 100644
--- a/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py
+++ b/kgforge/specializations/resolvers/entity_linking/service/entity_linking_elastic_service.py
@@ -17,16 +17,30 @@
 
 from typing import Callable, Dict, List, Optional, Union, Any
 
+from kgforge.core import Resource
 from kgforge.core.archetypes import Store
+from kgforge.core.conversions.json import as_json
+from kgforge.core.resource import encode
+from kgforge.core.wrappings import Filter, FilterOperator
 from kgforge.specializations.mappers import DictionaryMapper
 from kgforge.specializations.mappings import DictionaryMapping
-from kgforge.specializations.resolvers.entity_linking.service.entity_linking_service import EntityLinkerService
-from kgforge.specializations.resources.entity_linking_candidate import EntityLinkingCandidate
+from kgforge.specializations.resolvers.entity_linking.service.entity_linking_service import (
+    EntityLinkerService,
+)
+from kgforge.specializations.resources.entity_linking_candidate import (
+    EntityLinkingCandidate,
+)
 
 
 class EntityLinkerElasticService(EntityLinkerService):
-
-    def __init__(self, store: Callable, targets: Dict[str, str], encoder, result_resource_mapping, **store_config):
+    def __init__(
+        self,
+        store: Callable,
+        targets: Dict[str, str],
+        encoder,
+        result_resource_mapping,
+        **store_config
+    ):
         super().__init__(is_distance=False)
         self.sources: Dict[str, Store] = dict()
         for target, bucket in targets.items():
@@ -43,8 +57,9 @@ def mapping(self) -> Callable:
     def mapper(self) -> Callable:
         return DictionaryMapper
 
-    def generate_candidates(self, mentions, target, mention_context, limit, bulk) -> Optional[Union[EntityLinkingCandidate, List[EntityLinkingCandidate]]]:
-
+    def generate_candidates(
+        self, mentions, target, mention_context, limit, bulk
+    ) -> Optional[Union[EntityLinkingCandidate, List[EntityLinkingCandidate]]]:
         def _(d, resource):
             return EntityLinkingCandidate(d, **resource)
 
@@ -55,44 +70,61 @@ def _(d, resource):
         for mention in mentions_labels:
             call_url = self.encoder.format(x=mention)
             embedding_object = requests.get(url=call_url)
-            embedding = self.mapper().map(embedding_object.json(), self.result_mapping, None)
-            if embedding is not None and hasattr(embedding, "embedding"):
-                mention_resources, mention_resources_scores = self._similar(embedding.embedding, target, limit)
+            embedding = self.mapper().map(
+                embedding_object.json(), self.result_mapping, None
+            )
+            if embedding is not None:
+                embedding_json = encode(embedding)
+                vector_field = list(embedding_json.keys())[0]
+                mention_resources, mention_resources_scores = self._similar(
+                    vector_field,embedding_json[vector_field], target, limit
+                )
                 resources.append(mention_resources)
                 scores.append(mention_resources_scores)
-        i_res = {m: [_(scores[j][i], resource) for i, resource in enumerate(rs)] for j, (m, rs) in
-                 enumerate(zip(itertools.cycle(mentions_labels), resources))}
+        i_res = {
+            m: [_(scores[j][i], resource) for i, resource in enumerate(rs)]
+            for j, (m, rs) in enumerate(
+                zip(itertools.cycle(mentions_labels), resources)
+            )
+        }
         return [(m, i_res[m]) for i, m in mentions_index if m in i_res]
 
-    def _similar(self, item_embedding, target, limit, offset=0):
+    def _similar(self, vector_field, item_embedding, target, limit, offset=0):
         """
         Given a vector, find similar top [limit] resources, ranked by cosine similarity
         """
+        embedding_filter = Filter(
+            operator=FilterOperator.EQUAL.value,
+            path=[vector_field],
+            value=item_embedding,
+        )
+
+        resources = self.sources[target].search(
+            None,
+            embedding_filter,
+            limit=limit,
+            offset=offset,
+            excludes=[vector_field],
+            search_endpoint="elastic",
+        )
 
-        query = """{
-            "size": %s,
-            "_source": {
-                "exclude": ["embedding"]
-            },
-            "query": {
-                "script_score": {
-                    "query": {
-                        "exists": {
-                            "field": "embedding"
-                        }
-                    },
-                    "script": {
-                        "source": "doc['embedding'].size() == 0 ? 0 : (cosineSimilarity(params.queryVector, doc['embedding'])+1.0) / 2",
-                        "params": {
-                            "queryVector": %s
-                        }
-                    }
-                }
-            }
-        }""" % (limit, item_embedding)
-        resources = self.sources[target].elastic(query, False, limit=limit, offset=offset)
         if len(resources) > 0:
-            scores = [r._score for r in resources if hasattr(r, "_score")]
-            return [dict(r._source) for r in resources if hasattr(r, "_source")], scores
+            scores = [
+                r._store_metadata._score
+                for r in resources
+                if hasattr(r._store_metadata, "_score")
+            ]
+
+            return (
+                as_json(
+                    resources,
+                    expanded=False,
+                    store_metadata=True,
+                    model_context=None,
+                    metadata_context=None,
+                    context_resolver=None,
+                ),
+                scores,
+            )
         else:
             return None
diff --git a/kgforge/specializations/stores/bluebrain_nexus.py b/kgforge/specializations/stores/bluebrain_nexus.py
index 2a514d41..ea3b2847 100644
--- a/kgforge/specializations/stores/bluebrain_nexus.py
+++ b/kgforge/specializations/stores/bluebrain_nexus.py
@@ -11,15 +11,22 @@
 #
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+import datetime
 
 import asyncio
 import copy
+
+import dateutil
 import json
 import mimetypes
+import rdflib
 import re
 from asyncio import Semaphore, Task
 from enum import Enum
 from json import JSONDecodeError
+
+from dateutil.parser import ParserError
+from kgforge.core.commons.es_query_builder import ESQueryBuilder
 from pathlib import Path
 from typing import Any, Callable, Dict, List, Optional, Tuple, Union
 from urllib.parse import quote_plus, unquote, urlparse, parse_qs
@@ -32,15 +39,34 @@
 from pyld import jsonld
 from rdflib import Graph
 from rdflib.plugins.sparql.parser import Query
+from elasticsearch_dsl import (
+    Mapping,
+    Nested,
+    Object,
+    Keyword,
+    AttrDict,
+    Text,
+    Date,
+    Float,
+    Boolean,
+)
 
 from requests import HTTPError
 
 from kgforge.core import Resource
 from kgforge.core.archetypes import Store
+from kgforge.core.commons.actions import LazyAction
 from kgforge.core.commons.context import Context
-from kgforge.core.commons.exceptions import (DeprecationError, DownloadingError, QueryingError,
-                                             RegistrationError, RetrievalError, TaggingError,
-                                             UpdatingError, UploadingError)
+from kgforge.core.commons.exceptions import (
+    DeprecationError,
+    DownloadingError,
+    QueryingError,
+    RegistrationError,
+    RetrievalError,
+    TaggingError,
+    UpdatingError,
+    UploadingError,
+)
 from kgforge.core.commons.execution import run, not_supported
 from kgforge.core.commons.files import is_valid_url
 from kgforge.core.conversions.rdf import as_jsonld, from_jsonld
@@ -62,16 +88,16 @@ class CategoryDataType(Enum):
     bool: CategoryDataType.BOOLEAN,
     int: CategoryDataType.NUMBER,
     float: CategoryDataType.NUMBER,
-    complex: CategoryDataType.NUMBER
+    complex: CategoryDataType.NUMBER,
 }
 
 format_type = {
     CategoryDataType.NUMBER: lambda x: x,
-    CategoryDataType.LITERAL: lambda x: f"\"{x}\"",
-    CategoryDataType.BOOLEAN: lambda x: "true" if x is True else "false"
+    CategoryDataType.LITERAL: lambda x: f'"{x}"',
+    CategoryDataType.BOOLEAN: lambda x: "true" if x is True else "false",
 }
 
-operator_map = {
+sparql_operator_map = {
     "__lt__": "<",
     "__le__": "<=",
     "__eq__": "=",
@@ -80,16 +106,36 @@ class CategoryDataType(Enum):
     "__ge__": ">=",
 }
 
+elasticsearch_operator_map = {
+    "__lt__": "lt",
+    "__le__": "lte",
+    "__gt__": "gt",
+    "__ge__": "gte",
+}
 
-class BlueBrainNexus(Store):
 
-    def __init__(self, endpoint: Optional[str] = None, bucket: Optional[str] = None,
-                 token: Optional[str] = None, versioned_id_template: Optional[str] = None,
-                 file_resource_mapping: Optional[str] = None,
-                 model_context: Optional[Context] = None, searchendpoints: Optional[Dict] = None,
-                 **store_config) -> None:
-        super().__init__(endpoint, bucket, token, versioned_id_template, file_resource_mapping,
-                         model_context, searchendpoints, **store_config)
+class BlueBrainNexus(Store):
+    def __init__(
+        self,
+        endpoint: Optional[str] = None,
+        bucket: Optional[str] = None,
+        token: Optional[str] = None,
+        versioned_id_template: Optional[str] = None,
+        file_resource_mapping: Optional[str] = None,
+        model_context: Optional[Context] = None,
+        searchendpoints: Optional[Dict] = None,
+        **store_config,
+    ) -> None:
+        super().__init__(
+            endpoint,
+            bucket,
+            token,
+            versioned_id_template,
+            file_resource_mapping,
+            model_context,
+            searchendpoints,
+            **store_config,
+        )
 
     @property
     def mapping(self) -> Optional[Callable]:
@@ -99,60 +145,106 @@ def mapping(self) -> Optional[Callable]:
     def mapper(self) -> Optional[Callable]:
         return DictionaryMapper
 
-    def register(self, data: Union[Resource, List[Resource]], schema_id: str = None) -> None:
-        run(self._register_one, self._register_many, data, required_synchronized=False,
-            execute_actions=True, exception=RegistrationError, monitored_status="_synchronized",
-            schema_id=schema_id)
+    def register(
+        self, data: Union[Resource, List[Resource]], schema_id: str = None
+    ) -> None:
+        run(
+            self._register_one,
+            self._register_many,
+            data,
+            required_synchronized=False,
+            execute_actions=True,
+            exception=RegistrationError,
+            monitored_status="_synchronized",
+            schema_id=schema_id,
+        )
 
     def _register_many(self, resources: List[Resource], schema_id: str) -> None:
-
         def register_callback(task: Task):
             result = task.result()
             if isinstance(result.response, Exception):
                 self.service.synchronize_resource(
-                    result.resource, result.response, self._register_many.__name__, False, False)
+                    result.resource,
+                    result.response,
+                    self._register_many.__name__,
+                    False,
+                    False,
+                )
             else:
                 result.resource.id = result.response["@id"]
                 if not hasattr(result.resource, "context"):
                     context = self.model_context or self.context
-                    result.resource.context = context.iri if context.is_http_iri() else context.document["@context"]
+                    result.resource.context = (
+                        context.iri
+                        if context.is_http_iri()
+                        else context.document["@context"]
+                    )
                 self.service.synchronize_resource(
-                    result.resource, result.response, self._register_many.__name__, True, True)
+                    result.resource,
+                    result.response,
+                    self._register_many.__name__,
+                    True,
+                    True,
+                )
 
         verified = self.service.verify(
-            resources, self._register_many.__name__, RegistrationError, id_required=False,
-            required_synchronized=False, execute_actions=True)
+            resources,
+            self._register_many.__name__,
+            RegistrationError,
+            id_required=False,
+            required_synchronized=False,
+            execute_actions=True,
+        )
         params_register = copy.deepcopy(self.service.params.get("register", {}))
         self.service.batch_request(
-            verified, BatchAction.CREATE, register_callback, RegistrationError, schema_id=schema_id,
-            params=params_register)
+            verified,
+            BatchAction.CREATE,
+            register_callback,
+            RegistrationError,
+            schema_id=schema_id,
+            params=params_register,
+        )
 
     def _register_one(self, resource: Resource, schema_id: str) -> None:
         context = self.model_context or self.context
-        data = as_jsonld(resource, "compacted", False, model_context=context,
-                         metadata_context=None, context_resolver=self.service.resolve_context, na=nan)
+        data = as_jsonld(
+            resource,
+            "compacted",
+            False,
+            model_context=context,
+            metadata_context=None,
+            context_resolver=self.service.resolve_context,
+            na=nan,
+        )
 
         try:
             schema = quote_plus(schema_id) if schema_id else "_"
             url_base = f"{self.service.url_resources}/{schema}"
-            url = f"{url_base}/{quote_plus(data['@id'])}" if hasattr(data, "@id") else url_base
+            url = (
+                f"{url_base}/{quote_plus(data['@id'])}"
+                if hasattr(data, "@id")
+                else url_base
+            )
             params_register = copy.deepcopy(self.service.params.get("register", None))
-            response = requests.post(url, headers=self.service.headers, data=json.dumps(data, ensure_ascii=True),
-                                     params=params_register)
+            response = requests.post(
+                url,
+                headers=self.service.headers,
+                data=json.dumps(data, ensure_ascii=True),
+                params=params_register,
+            )
             response.raise_for_status()
 
         except nexus.HTTPError as e:
             raise RegistrationError(_error_message(e))
         else:
             response_json = response.json()
-            resource.id = response_json['@id']
+            resource.id = response_json["@id"]
             # If resource had no context, update it with the one provided by the store.
             if not hasattr(resource, "context"):
                 resource.context = data["@context"]
             self.service.sync_metadata(resource, response_json)
 
     def _upload_many(self, paths: List[Path], content_type: str) -> List[Dict]:
-
         async def _bulk():
             loop = asyncio.get_event_loop()
             semaphore = Semaphore(self.service.max_connection)
@@ -162,7 +254,7 @@ async def _bulk():
 
         def _create_task(path, loop, semaphore, session):
             default = "application/octet-stream"
-            mime_type = (content_type or mimetypes.guess_type(str(path))[0] or default)
+            mime_type = content_type or mimetypes.guess_type(str(path))[0] or default
             # FIXME Nexus seems to not parse the Content-Disposition 'filename*' field  properly.
             # data = FormData()
             # data.add_field("file", path.open("rb"), content_type=mime_type, filename=path.name)
@@ -170,7 +262,9 @@ def _create_task(path, loop, semaphore, session):
             data = MultipartWriter("form-data")
             part = data.append(path.open("rb"))
             part.headers[CONTENT_TYPE] = mime_type
-            part.headers[CONTENT_DISPOSITION] = f'form-data; name="file"; filename="{path.name}"'
+            part.headers[
+                CONTENT_DISPOSITION
+            ] = f'form-data; name="file"; filename="{path.name}"'
             return loop.create_task(_upload(data, semaphore, session))
 
         async def _upload(data, semaphore, session):
@@ -180,7 +274,9 @@ async def _upload(data, semaphore, session):
                     if response.status < 400:
                         return body
                     else:
-                        msg = " ".join(re.findall('[A-Z][^A-Z]*', body["@type"])).lower()
+                        msg = " ".join(
+                            re.findall("[A-Z][^A-Z]*", body["@type"])
+                        ).lower()
                         raise UploadingError(msg)
 
         return asyncio.run(_bulk())
@@ -191,8 +287,9 @@ def _upload_one(self, path: Path, content_type: str) -> Dict:
         if mime_type is None:
             mime_type = "application/octet-stream"
         try:
-            response = nexus.files.create(self.organisation, self.project, file,
-                                          content_type=mime_type)
+            response = nexus.files.create(
+                self.organisation, self.project, file, content_type=mime_type
+            )
         except HTTPError as e:
             raise UploadingError(_error_message(e))
         else:
@@ -200,8 +297,9 @@ def _upload_one(self, path: Path, content_type: str) -> Dict:
 
     # C[R]UD.
 
-    def retrieve(self, id: str, version: Optional[Union[int, str]],
-                 cross_bucket: bool) -> Resource:
+    def retrieve(
+        self, id: str, version: Optional[Union[int, str]], cross_bucket: bool
+    ) -> Resource:
 
         parsed_id = urlparse(id)
         params = None
@@ -216,23 +314,27 @@ def retrieve(self, id: str, version: Optional[Union[int, str]],
         fragment = None
         query_params = None
         # urlparse is not separating fragment and query params when the latter are put after a fragment
-        if parsed_id.fragment is not None and '?' in str(parsed_id.fragment):
+        if parsed_id.fragment is not None and "?" in str(parsed_id.fragment):
             fragment_parts = urlparse(parsed_id.fragment)
             query_params = parse_qs(fragment_parts.query)
             fragment = fragment_parts.path
-        elif parsed_id.fragment is not None and parsed_id.fragment != '':
+        elif parsed_id.fragment is not None and parsed_id.fragment != "":
             fragment = parsed_id.fragment
-        elif parsed_id.query is not None and parsed_id.query != '':
+        elif parsed_id.query is not None and parsed_id.query != "":
             query_params = parse_qs(parsed_id.query)
 
         if params is not None and isinstance(query_params, dict):
             query_params.update(params)
 
         id_without_query = f"{parsed_id.scheme}://{parsed_id.netloc}{parsed_id.path}{'#' + fragment if fragment is not None else ''}"
-        url_base = self.service.url_resolver if cross_bucket else self.service.url_resources
+        url_base = (
+            self.service.url_resolver if cross_bucket else self.service.url_resources
+        )
         url = "/".join((url_base, "_", quote_plus(id_without_query)))
         try:
-            response = requests.get(url, params=query_params, headers=self.service.headers)
+            response = requests.get(
+                url, params=query_params, headers=self.service.headers
+            )
             response.raise_for_status()
         except HTTPError as e:
             raise RetrievalError(_error_message(e))
@@ -252,29 +354,41 @@ def _retrieve_filename(self, id: str) -> str:
         except HTTPError as e:
             raise DownloadingError(_error_message(e))
 
-    def _download_many(self, urls: List[str], paths: List[str], store_metadata: Optional[DictWrapper],
-                       cross_bucket: bool) -> None:
-
+    def _download_many(
+        self,
+        urls: List[str],
+        paths: List[str],
+        store_metadata: Optional[DictWrapper],
+        cross_bucket: bool,
+    ) -> None:
         async def _bulk():
             loop = asyncio.get_event_loop()
             semaphore = Semaphore(self.service.max_connection)
             async with ClientSession(headers=self.service.headers_download) as session:
-                tasks = (_create_task(x, y, z, loop, semaphore, session) for x, y, z in
-                         zip(urls, paths, store_metadata))
+                tasks = (
+                    _create_task(x, y, z, loop, semaphore, session)
+                    for x, y, z in zip(urls, paths, store_metadata)
+                )
                 return await asyncio.gather(*tasks)
 
         def _create_task(url, path, store_metadata, loop, semaphore, session):
-            return loop.create_task(_download(url, path, store_metadata, semaphore, session))
+            return loop.create_task(
+                _download(url, path, store_metadata, semaphore, session)
+            )
 
         async def _download(url, path, store_metadata, semaphore, session):
             async with semaphore:
                 params_download = copy.deepcopy(self.service.params.get("download", {}))
-                url_base, org, project = self._prepare_download_one(url, path, store_metadata, cross_bucket)
+                url_base, org, project = self._prepare_download_one(
+                    url, path, store_metadata, cross_bucket
+                )
                 async with session.get(url_base, params=params_download) as response:
                     try:
                         response.raise_for_status()
                     except Exception as e:
-                        raise DownloadingError(f"Downloading from {org}/{project}:{_error_message(e)}")
+                        raise DownloadingError(
+                            f"Downloading from {org}/{project}:{_error_message(e)}"
+                        )
                     else:
                         with open(path, "wb") as f:
                             data = await response.read()
@@ -282,21 +396,40 @@ async def _download(url, path, store_metadata, semaphore, session):
 
         return asyncio.run(_bulk())
 
-    def _download_one(self, url: str, path: str, store_metadata: Optional[DictWrapper], cross_bucket: bool) -> None:
+    def _download_one(
+        self,
+        url: str,
+        path: str,
+        store_metadata: Optional[DictWrapper],
+        cross_bucket: bool,
+    ) -> None:
         try:
-            url_base, org, project = self._prepare_download_one(url, path, store_metadata, cross_bucket)
+            url_base, org, project = self._prepare_download_one(
+                url, path, store_metadata, cross_bucket
+            )
             params_download = copy.deepcopy(self.service.params.get("download", {}))
-            response = requests.get(url=url_base, headers=self.service.headers_download, params=params_download)
+            response = requests.get(
+                url=url_base,
+                headers=self.service.headers_download,
+                params=params_download,
+            )
             response.raise_for_status()
         except Exception as e:
-            raise DownloadingError(f"Downloading from {org}/{project}:{_error_message(e)}")
+            raise DownloadingError(
+                f"Downloading from {org}/{project}:{_error_message(e)}"
+            )
         else:
             with open(path, "wb") as f:
                 for chunk in response.iter_content(chunk_size=4096):
                     f.write(chunk)
 
-    def _prepare_download_one(self, url: str, path: str, store_metadata: Optional[DictWrapper], cross_bucket: bool) -> \
-    Tuple[str, str, str]:
+    def _prepare_download_one(
+        self,
+        url: str,
+        path: str,
+        store_metadata: Optional[DictWrapper],
+        cross_bucket: bool,
+    ) -> Tuple[str, str, str]:
         # this is a hack since _self and _id have the same uuid
         file_id = url.split("/")[-1]
         file_id = unquote(file_id)
@@ -306,43 +439,82 @@ def _prepare_download_one(self, url: str, path: str, store_metadata: Optional[Di
             raise DownloadingError("Invalid file name")
         if cross_bucket:
             if store_metadata is not None:
-                project = store_metadata._project.split('/')[-1]
-                org = store_metadata._project.split('/')[-2]
+                project = store_metadata._project.split("/")[-1]
+                org = store_metadata._project.split("/")[-2]
             else:
                 raise ValueError(
-                    f"Downloading non registered file is not allowed when cross_bucket is set to {cross_bucket}")
+                    f"Downloading non registered file is not allowed when cross_bucket is set to {cross_bucket}"
+                )
         else:
             org = self.service.organisation
             project = self.service.project
 
-        url_base = "/".join((self.service.url_base_files, quote_plus(org), quote_plus(project), quote_plus(file_id)))
+        url_base = "/".join(
+            (
+                self.service.url_base_files,
+                quote_plus(org),
+                quote_plus(project),
+                quote_plus(file_id),
+            )
+        )
         return url_base, org, project
 
     # CR[U]D.
 
     def update(self, data: Union[Resource, List[Resource]], schema_id: str) -> None:
-        run(self._update_one, self._update_many, data, id_required=True, required_synchronized=False,
-            execute_actions=True, exception=UpdatingError, monitored_status="_synchronized", schema_id=schema_id)
+        run(
+            self._update_one,
+            self._update_many,
+            data,
+            id_required=True,
+            required_synchronized=False,
+            execute_actions=True,
+            exception=UpdatingError,
+            monitored_status="_synchronized",
+            schema_id=schema_id,
+        )
 
     def _update_many(self, resources: List[Resource], schema_id: str) -> None:
         update_callback = self.service.default_callback(self._update_many.__name__)
         verified = self.service.verify(
-            resources, self._update_many.__name__, UpdatingError, id_required=True,
-            required_synchronized=False, execute_actions=True)
+            resources,
+            self._update_many.__name__,
+            UpdatingError,
+            id_required=True,
+            required_synchronized=False,
+            execute_actions=True,
+        )
         params_update = copy.deepcopy(self.service.params.get("update", {}))
-        self.service.batch_request(verified, BatchAction.UPDATE, update_callback, UpdatingError, params=params_update)
+        self.service.batch_request(
+            verified,
+            BatchAction.UPDATE,
+            update_callback,
+            UpdatingError,
+            params=params_update,
+        )
 
     def _update_one(self, resource: Resource, schema_id: str) -> None:
         context = self.model_context or self.context
-        data = as_jsonld(resource, "compacted", False, model_context=context, metadata_context=None,
-                         context_resolver=self.service.resolve_context, na=nan)
+        data = as_jsonld(
+            resource,
+            "compacted",
+            False,
+            model_context=context,
+            metadata_context=None,
+            context_resolver=self.service.resolve_context,
+            na=nan,
+        )
         params_update = copy.deepcopy(self.service.params.get("update", {}))
         params_update["rev"] = resource._store_metadata._rev
         schema = quote_plus(schema_id) if schema_id else "_"
         url = f"{self.service.url_resources}/{schema}/{quote_plus(resource.id)}"
         try:
-            response = requests.put(url, headers=self.service.headers,
-                                    data=json.dumps(data, ensure_ascii=True), params=params_update)
+            response = requests.put(
+                url,
+                headers=self.service.headers,
+                data=json.dumps(data, ensure_ascii=True),
+                params=params_update,
+            )
             response.raise_for_status()
         except HTTPError as e:
             raise UpdatingError(_error_message(e))
@@ -350,28 +522,47 @@ def _update_one(self, resource: Resource, schema_id: str) -> None:
             self.service.sync_metadata(resource, response.json())
 
     def tag(self, data: Union[Resource, List[Resource]], value: str) -> None:
-        run(self._tag_one, self._tag_many, data, id_required=True, required_synchronized=True,
-            exception=TaggingError, value=value)
+        run(
+            self._tag_one,
+            self._tag_many,
+            data,
+            id_required=True,
+            required_synchronized=True,
+            exception=TaggingError,
+            value=value,
+        )
 
     def _tag_many(self, resources: List[Resource], value: str) -> None:
         tag_callback = self.service.default_callback(self._tag_many.__name__)
         verified = self.service.verify(
-            resources, self._tag_many.__name__, TaggingError, id_required=True,
-            required_synchronized=True, execute_actions=False)
+            resources,
+            self._tag_many.__name__,
+            TaggingError,
+            id_required=True,
+            required_synchronized=True,
+            execute_actions=False,
+        )
         params_tag = copy.deepcopy(self.service.params.get("tag", {}))
         self.service.batch_request(
-            verified, BatchAction.TAG, tag_callback, TaggingError, tag=value, params=params_tag)
+            verified,
+            BatchAction.TAG,
+            tag_callback,
+            TaggingError,
+            tag=value,
+            params=params_tag,
+        )
 
     def _tag_one(self, resource: Resource, value: str) -> None:
-        data = {
-            "tag": value,
-            "rev": resource._store_metadata._rev
-        }
+        data = {"tag": value, "rev": resource._store_metadata._rev}
         url = f"{self.service.url_resources}/_/{quote_plus(resource.id)}/tags?rev={resource._store_metadata._rev}"
         try:
             params_tag = copy.deepcopy(self.service.params.get("tag", None))
-            response = requests.post(url, headers=self.service.headers,
-                                     data=json.dumps(data, ensure_ascii=True), params=params_tag)
+            response = requests.post(
+                url,
+                headers=self.service.headers,
+                data=json.dumps(data, ensure_ascii=True),
+                params=params_tag,
+            )
             response.raise_for_status()
         except HTTPError as e:
             raise TaggingError(_error_message(e))
@@ -381,23 +572,44 @@ def _tag_one(self, resource: Resource, value: str) -> None:
     # CRU[D].
 
     def deprecate(self, data: Union[Resource, List[Resource]]) -> None:
-        run(self._deprecate_one, self._deprecate_many, data, id_required=True,
-            required_synchronized=True, exception=DeprecationError, monitored_status="_synchronized")
+        run(
+            self._deprecate_one,
+            self._deprecate_many,
+            data,
+            id_required=True,
+            required_synchronized=True,
+            exception=DeprecationError,
+            monitored_status="_synchronized",
+        )
 
     def _deprecate_many(self, resources: List[Resource]) -> None:
-        deprecate_callback = self.service.default_callback(self._deprecate_many.__name__)
+        deprecate_callback = self.service.default_callback(
+            self._deprecate_many.__name__
+        )
         verified = self.service.verify(
-            resources, self._deprecate_many.__name__, DeprecationError, id_required=True,
-            required_synchronized=True, execute_actions=False)
+            resources,
+            self._deprecate_many.__name__,
+            DeprecationError,
+            id_required=True,
+            required_synchronized=True,
+            execute_actions=False,
+        )
         params_deprecate = copy.deepcopy(self.service.params.get("deprecate", {}))
         self.service.batch_request(
-            verified, BatchAction.DEPRECATE, deprecate_callback, DeprecationError, params=params_deprecate)
+            verified,
+            BatchAction.DEPRECATE,
+            deprecate_callback,
+            DeprecationError,
+            params=params_deprecate,
+        )
 
     def _deprecate_one(self, resource: Resource) -> None:
         url = f"{self.service.url_resources}/_/{quote_plus(resource.id)}?rev={resource._store_metadata._rev}"
         try:
             params_deprecate = copy.deepcopy(self.service.params.get("deprecate", None))
-            response = requests.delete(url, headers=self.service.headers, params=params_deprecate)
+            response = requests.delete(
+                url, headers=self.service.headers, params=params_deprecate
+            )
             response.raise_for_status()
         except HTTPError as e:
             raise DeprecationError(_error_message(e))
@@ -406,7 +618,9 @@ def _deprecate_one(self, resource: Resource) -> None:
 
         # Querying.
 
-    def search(self, resolvers: Optional[List["Resolver"]], *filters, **params) -> List[Resource]:
+    def search(
+        self, resolvers: Optional[List["Resolver"]], *filters, **params
+    ) -> List[Resource]:
 
         if self.model_context is None:
             raise ValueError("context model missing")
@@ -419,39 +633,138 @@ def search(self, resolvers: Optional[List["Resolver"]], *filters, **params) -> L
         bucket = params.get("bucket", None)
         search_in_graph = params.get("search_in_graph", True)
         distinct = params.get("distinct", False)
-        project_statements = ''
+        includes = params.get("includes", None)
+        excludes = params.get("excludes", None)
+        search_endpoint = params.get(
+            "search_endpoint", self.service.sparql_endpoint["type"]
+        )
+        if search_endpoint not in [
+            self.service.sparql_endpoint["type"],
+            self.service.elastic_endpoint["type"],
+        ]:
+            raise ValueError(
+                f"The provided search_endpoint value '{search_endpoint}' is not supported. Supported "
+                f"search_endpoint values are: '{self.service.sparql_endpoint['type'], self.service.elastic_endpoint['type']}'"
+            )
         if bucket and not cross_bucket:
             not_supported(("bucket", True))
-        elif bucket:
-            project_statements = f"Filter (?project = <{'/'.join([self.endpoint, 'projects', bucket])}>)"
-        elif not cross_bucket:
-            project_statements = f"Filter (?project = <{'/'.join([self.endpoint, 'projects', self.organisation, self.project])}>)"
 
         if filters and isinstance(filters[0], dict):
             filters = create_filters_from_dict(filters[0])
-        query_statements, query_filters = build_query_statements(self.model_context, filters)
-        query_statements.insert(0, f"<{self.service.project_property}> ?project")
-        query_statements.insert(1,
-                                f"<{self.service.deprecated_property}> {format_type[CategoryDataType.BOOLEAN](deprecated)}")
-        statements = "\n".join((";\n ".join(query_statements), ".\n ".join(query_filters)))
-        query = _create_select_query(f"?id {statements} {project_statements}", distinct, search_in_graph)
-
-        resources = self.sparql(query, debug=debug, limit=limit, offset=offset)
-        results = self.service.batch_request(resources, BatchAction.FETCH, None, QueryingError)
-        resources = list()
-        for result in results:
-            resource = result.resource
-            try:
-                resource = self.service.to_resource(result.response)
-            except Exception as e:
-                self.service.synchronize_resource(
-                    resource, result.response, self.search.__name__, False, False)
-                raise ValueError(e)
-            finally:
-                self.service.synchronize_resource(
-                    resource, result.response, self.search.__name__, True, True)
-            resources.append(resource)
-        return resources
+        if search_endpoint == self.service.sparql_endpoint["type"]:
+            if includes or excludes:
+                raise ValueError(
+                    "Field inclusion and exclusion are not supported when using SPARQL"
+                )
+            project_statements = ""
+            if bucket:
+                project_statements = f"Filter (?project = <{'/'.join([self.endpoint, 'projects', bucket])}>)"
+            elif not cross_bucket:
+                project_statements = f"Filter (?project = <{'/'.join([self.endpoint, 'projects', self.organisation, self.project])}>)"
+
+            query_statements, query_filters = build_sparql_query_statements(
+                self.model_context, filters
+            )
+            query_statements.insert(0, f"<{self.service.project_property}> ?project")
+            query_statements.insert(
+                1,
+                f"<{self.service.deprecated_property}> {format_type[CategoryDataType.BOOLEAN](deprecated)}",
+            )
+            statements = "\n".join(
+                (";\n ".join(query_statements), ".\n ".join(query_filters))
+            )
+            query = _create_select_query(
+                f"?id {statements} {project_statements}", distinct, search_in_graph
+            )
+
+            # consider retrieving resources at their indexed revision
+            # support @id and @type
+            resources = self.sparql(query, debug=debug, limit=limit, offset=offset)
+            # TODO: Move away from sync and stick to what is retrieved from the view
+            results = self.service.batch_request(
+                resources, BatchAction.FETCH, None, QueryingError
+            )
+            resources = list()
+            for result in results:
+                resource = result.resource
+                try:
+                    resource = self.service.to_resource(result.response)
+                except Exception as e:
+                    self.service.synchronize_resource(
+                        resource, result.response, self.search.__name__, False, False
+                    )
+                    raise ValueError(e)
+                finally:
+                    self.service.synchronize_resource(
+                        resource, result.response, self.search.__name__, True, True
+                    )
+                resources.append(resource)
+            return resources
+        else:
+            if isinstance(self.service.elastic_endpoint["view"], LazyAction):
+                self.service.elastic_endpoint["view"] = self.service.elastic_endpoint[
+                    "view"
+                ].execute()
+
+            elastic_mapping = (
+                self.service.elastic_endpoint["view"]["mapping"]
+                if "mapping" in self.service.elastic_endpoint["view"]
+                else None
+            )
+            default_str_keyword_field = self.service.elastic_endpoint[
+                "default_str_keyword_field"
+            ]
+            deprecated_property_context_term = self.service.metadata_context.find_term(
+                self.service.deprecated_property
+            )
+            project_property_context_term = self.service.metadata_context.find_term(
+                self.service.project_property
+            )
+            filters.append(
+                Filter(
+                    operator="__eq__",
+                    path=[
+                        deprecated_property_context_term.name
+                        if deprecated_property_context_term is not None
+                        else "_deprecated"
+                    ],
+                    value=deprecated,
+                )
+
+            )
+            _project = None
+            if bucket:
+                _project = '/'.join([self.endpoint, 'projects', bucket])
+
+            elif not cross_bucket:
+                _project = '/'.join([self.endpoint, 'projects', self.organisation, self.project])
+
+            if _project:
+                filters.append(
+                    Filter(
+                        operator="__eq__",
+                        path=[
+                            project_property_context_term.name
+                            if project_property_context_term is not None
+                            else "_project"
+                        ],
+                        value=_project
+                    )
+                )
+
+            query = ESQueryBuilder.build(
+                elastic_mapping,
+                resolvers,
+                self.model_context,
+                filters,
+                default_str_keyword_field=default_str_keyword_field,
+                includes=includes,
+                excludes=excludes,
+            )
+
+            return self.elastic(
+                json.dumps(query), debug=debug, limit=limit, offset=offset
+            )
 
     def _sparql(self, query: str, limit: int, offset: int = None) -> List[Resource]:
 
@@ -460,7 +773,10 @@ def _sparql(self, query: str, limit: int, offset: int = None) -> List[Resource]:
         query = f"{query} {s_limit} {s_offset}"
         try:
             response = requests.post(
-                self.service.sparql_endpoint["endpoint"], data=query, headers=self.service.headers_sparql)
+                self.service.sparql_endpoint["endpoint"],
+                data=query,
+                headers=self.service.headers_sparql,
+            )
             response.raise_for_status()
         except Exception as e:
             raise QueryingError(e)
@@ -472,7 +788,7 @@ def _sparql(self, query: str, limit: int, offset: int = None) -> List[Resource]:
             if q_comp.name == "ConstructQuery":
                 subject_triples = {}
                 for r in data["results"]["bindings"]:
-                    subject = r['subject']['value']
+                    subject = r["subject"]["value"]
                     s = f"<{r['subject']['value']}>"
                     p = f"<{r['predicate']['value']}>"
                     if r["object"]["type"] == "uri":
@@ -490,12 +806,17 @@ def _sparql(self, query: str, limit: int, offset: int = None) -> List[Resource]:
                 def triples_to_resource(iri, triples):
                     graph = Graph().parse(data=triples, format="nt")
                     data_expanded = json.loads(graph.serialize(format="json-ld"))
+                    data_expanded = json.loads(graph.serialize(format="json-ld"))
                     frame = {"@id": iri}
                     data_framed = jsonld.frame(data_expanded, frame)
                     context = self.model_context or self.context
                     compacted = jsonld.compact(data_framed, context.document)
                     resource = from_jsonld(compacted)
-                    resource.context = context.iri if context.is_http_iri() else context.document["@context"]
+                    resource.context = (
+                        context.iri
+                        if context.is_http_iri()
+                        else context.document["@context"]
+                    )
                     return resource
 
                 return [triples_to_resource(s, t) for s, t in subject_triples.items()]
@@ -503,52 +824,101 @@ def triples_to_resource(iri, triples):
             else:
                 # SELECT QUERY
                 results = data["results"]["bindings"]
-                return [Resource(**{k: v["value"] for k, v in x.items()}) for x in results]
+                return [
+                    Resource(**{k: v["value"] for k, v in x.items()}) for x in results
+                ]
 
     def _elastic(self, query: str, limit: int, offset: int = None) -> List[Resource]:
         try:
             response = requests.post(
-                self.service.elastic_endpoint["endpoint"], data=query, headers=self.service.headers_elastic)
+                self.service.elastic_endpoint["endpoint"],
+                data=query,
+                headers=self.service.headers_elastic,
+            )
             response.raise_for_status()
         except Exception as e:
             raise QueryingError(e)
         else:
             results = response.json()
-            return [Resource(**{k: v for k, v in hit.items()}) for hit in results["hits"]['hits']]
+            return [
+                self.service.to_resource(
+                    hit["_source"],
+                    True,
+                    **{
+                        "_index": hit.get("_index", None),
+                        "_score": hit.get("_score", None),
+                    },
+                )
+                for hit in results["hits"]["hits"]
+            ]
 
     # Utils.
 
-    def _initialize_service(self, endpoint: Optional[str], bucket: Optional[str],
-                            token: Optional[str], searchendpoints: Optional[Dict], **store_config) -> Any:
+    def _initialize_service(
+        self,
+        endpoint: Optional[str],
+        bucket: Optional[str],
+        token: Optional[str],
+        searchendpoints: Optional[Dict],
+        **store_config,
+    ) -> Any:
         try:
-            self.organisation, self.project = self.bucket.split('/')
+            self.organisation, self.project = self.bucket.split("/")
             max_connection = store_config.pop("max_connection", 50)
             if max_connection <= 0:
-                raise ValueError(f"max_connection value should be great than 0 but {max_connection} is provided")
+                raise ValueError(
+                    f"max_connection value should be great than 0 but {max_connection} is provided"
+                )
             store_context_config = store_config.pop("vocabulary", {})
-            nexus_metadata_context = store_context_config.get("metadata", {"iri": Service.NEXUS_CONTEXT_FALLBACK,
-                                                                           "local_iri": Service.NEXUS_CONTEXT_FALLBACK})
+            nexus_metadata_context = store_context_config.get(
+                "metadata",
+                {
+                    "iri": Service.NEXUS_CONTEXT_FALLBACK,
+                    "local_iri": Service.NEXUS_CONTEXT_FALLBACK,
+                },
+            )
             nexus_context_iri = nexus_metadata_context.get("iri")
             nexus_context_local_iri = nexus_metadata_context.get("local_iri")
-            namespace = store_context_config.get("namespace", Service.NEXUS_NAMESPACE_FALLBACK)
-            project_property = store_context_config.get("project_property", Service.PROJECT_PROPERTY_FALLBACK)
-            deprecated_property = store_context_config.get("deprecated_property", Service.DEPRECATED_PROPERTY_FALLBACK)
+            namespace = store_context_config.get(
+                "namespace", Service.NEXUS_NAMESPACE_FALLBACK
+            )
+            project_property = store_context_config.get(
+                "project_property", Service.PROJECT_PROPERTY_FALLBACK
+            )
+            deprecated_property = store_context_config.get(
+                "deprecated_property", Service.DEPRECATED_PROPERTY_FALLBACK
+            )
             content_type = store_config.pop("Content-Type", "application/ld+json")
             accept = store_config.pop("Accept", "application/ld+json")
-            files_upload_config = store_config.pop("files_upload", {"Accept": "application/ld+json"})
-            files_download_config = store_config.pop("files_download", {"Accept": "*/*"})
+            files_upload_config = store_config.pop(
+                "files_upload", {"Accept": "application/ld+json"}
+            )
+            files_download_config = store_config.pop(
+                "files_download", {"Accept": "*/*"}
+            )
             params = store_config.pop("params", {})
         except Exception as ve:
             raise ValueError(f"Store configuration error: {ve}")
         else:
-            return Service(endpoint=endpoint, org=self.organisation, prj=self.project, token=token,
-                           model_context=self.model_context, max_connection=max_connection,
-                           searchendpoints=searchendpoints,
-                           store_context=nexus_context_iri, store_local_context= nexus_context_local_iri,
-                           namespace=namespace, project_property=project_property,
-                           deprecated_property=deprecated_property, content_type=content_type, accept=accept,
-                           files_upload_config=files_upload_config, files_download_config=files_download_config,
-                           **params)
+            return Service(
+                endpoint=endpoint,
+                org=self.organisation,
+                prj=self.project,
+                token=token,
+                model_context=self.model_context,
+                max_connection=max_connection,
+                searchendpoints=searchendpoints,
+                store_context=nexus_context_iri,
+                store_local_context=nexus_context_local_iri,
+                namespace=namespace,
+                project_property=project_property,
+                deprecated_property=deprecated_property,
+                content_type=content_type,
+                accept=accept,
+                files_upload_config=files_upload_config,
+                files_download_config=files_download_config,
+                **params,
+            )
 
 
 def _error_message(error: HTTPError) -> str:
@@ -569,26 +939,37 @@ def format_message(msg):
         return format_message(str(error))
 
 
-def build_query_statements(context: Context, *conditions) -> Tuple[List, List]:
+def build_sparql_query_statements(context: Context, *conditions) -> Tuple[List, List]:
     statements = list()
     filters = list()
     for index, f in enumerate(*conditions):
+        last_path = f.path[-1]
         try:
-            last_term = context.terms[f.path[-1]]
+            last_term = context.terms[last_path]
         except KeyError:
             last_term = None
-        if f.path[-1] == "id":
+        if last_path in ["id", "@id"]:
             property_path = "/".join(f.path[:-1])
+        elif last_path == "@type":
+            minus_last_path = f.path[:-1]
+            minus_last_path.append("type")
+            property_path = "/".join(minus_last_path)
         else:
             property_path = "/".join(f.path)
-        if f.path[-1] == "type" or f.path[-1] == "id" or (last_term is not None and last_term.type == "@id"):
+        if (
+            last_path in ["type", "@type"]
+            or last_path in ["id", "@id"]
+            or (last_term is not None and last_term.type == "@id")
+        ):
             if f.operator == "__eq__":
                 statements.append(f"{property_path} {_box_value_as_full_iri(f.value)}")
             elif f.operator == "__ne__":
                 statements.append(f"{property_path} ?v{index}")
                 filters.append(f"FILTER(?v{index} != {f.value})")
             else:
-                raise NotImplementedError(f"operator '{f.operator}' is not supported in this query")
+                raise NotImplementedError(
+                    f"operator '{f.operator}' is not supported in this query"
+                )
         else:
             value_type = type_map[type(f.value)]
             value = format_type[value_type](f.value)
@@ -597,7 +978,9 @@ def build_query_statements(context: Context, *conditions) -> Tuple[List, List]:
                 filters.append(f"FILTER(?v{index} = {_box_value_as_full_iri(value)})")
             else:
                 statements.append(f"{property_path} ?v{index}")
-                filters.append(f"FILTER(?v{index} {operator_map[f.operator]} {_box_value_as_full_iri(value)})")
+                filters.append(
+                    f"FILTER(?v{index} {sparql_operator_map[f.operator]} {_box_value_as_full_iri(value)})"
+                )
 
     return statements, filters
 
@@ -607,7 +990,8 @@ def _box_value_as_full_iri(value):
 
 
 def _create_select_query(statements, distinct, search_in_graph):
-    where_clauses = f"{{ Graph ?g {{{statements}}}}}" if search_in_graph else \
-        f"{{{statements}}}"
+    where_clauses = (
+        f"{{ Graph ?g {{{statements}}}}}" if search_in_graph else f"{{{statements}}}"
+    )
     select_vars = "DISTINCT ?id ?project" if distinct else "?id ?project"
     return f"SELECT {select_vars} WHERE {where_clauses}"
diff --git a/kgforge/specializations/stores/nexus/service.py b/kgforge/specializations/stores/nexus/service.py
index 01760264..93a267d4 100644
--- a/kgforge/specializations/stores/nexus/service.py
+++ b/kgforge/specializations/stores/nexus/service.py
@@ -32,10 +32,19 @@
 from requests import HTTPError
 
 from kgforge.core import Resource
-from kgforge.core.commons.actions import Action, collect_lazy_actions, execute_lazy_actions
+from kgforge.core.commons.actions import (
+    Action,
+    collect_lazy_actions,
+    execute_lazy_actions,
+    LazyAction,
+)
 from kgforge.core.commons.context import Context
-from kgforge.core.conversions.rdf import (_from_jsonld_one, _remove_ld_keys, as_jsonld,
-                                          recursive_resolve)
+from kgforge.core.conversions.rdf import (
+    _from_jsonld_one,
+    _remove_ld_keys,
+    as_jsonld,
+    recursive_resolve,
+)
 from kgforge.core.wrappings.dict import wrap_dict
 
 
@@ -57,16 +66,34 @@ class Service:
 
     NEXUS_NAMESPACE_FALLBACK = "https://bluebrain.github.io/nexus/vocabulary/"
     NEXUS_CONTEXT_FALLBACK = "https://bluebrain.github.io/nexus/contexts/resource.json"
-    NEXUS_CONTEXT_SOURCE_FALLBACK = "https%3A%2F%2Fbluebrain.github.io%2Fnexus%2Fcontexts%2Fresource.json/source"
+    NEXUS_CONTEXT_SOURCE_FALLBACK = (
+        "https%3A%2F%2Fbluebrain.github.io%2Fnexus%2Fcontexts%2Fresource.json/source"
+    )
     DEFAULT_SPARQL_INDEX_FALLBACK = f"{NEXUS_NAMESPACE_FALLBACK}defaultSparqlIndex"
     DEFAULT_ES_INDEX_FALLBACK = f"{NEXUS_NAMESPACE_FALLBACK}defaultElasticSearchIndex"
     DEPRECATED_PROPERTY_FALLBACK = f"{NEXUS_NAMESPACE_FALLBACK}deprecated"
     PROJECT_PROPERTY_FALLBACK = f"{NEXUS_NAMESPACE_FALLBACK}project"
 
-    def __init__(self, endpoint: str, org: str, prj: str, token: str, model_context: Context,
-                 max_connection: int, searchendpoints: Dict, store_context:  str, store_local_context: str, namespace: str, project_property: str,
-                 deprecated_property: bool, content_type: str, accept: str, files_upload_config: Dict,
-                 files_download_config: Dict, **params):
+    def __init__(
+        self,
+        endpoint: str,
+        org: str,
+        prj: str,
+        token: str,
+        model_context: Context,
+        max_connection: int,
+        searchendpoints: Dict,
+        store_context: str,
+        store_local_context: str,
+        namespace: str,
+        project_property: str,
+        deprecated_property: bool,
+        content_type: str,
+        accept: str,
+        files_upload_config: Dict,
+        files_download_config: Dict,
+        **params,
+    ):
 
         nexus.config.set_environment(endpoint)
         self.endpoint = endpoint
@@ -84,32 +111,39 @@ def __init__(self, endpoint: str, org: str, prj: str, token: str, model_context:
         self.default_sparql_index = f"{self.namespace}defaultSparqlIndex"
         self.default_es_index = f"{self.namespace}defaultElasticSearchIndex"
 
-        self.headers = {
-            "Content-Type": content_type,
-            "Accept": accept
-        }
+        self.headers = {"Content-Type": content_type, "Accept": accept}
 
-        sparql_config = searchendpoints['sparql'] if searchendpoints and "sparql" in searchendpoints else None
-        elastic_config = searchendpoints['elastic'] if searchendpoints and "elastic" in searchendpoints else None
+        sparql_config = (
+            searchendpoints["sparql"]
+            if searchendpoints and "sparql" in searchendpoints
+            else None
+        )
+        elastic_config = (
+            searchendpoints["elastic"]
+            if searchendpoints and "elastic" in searchendpoints
+            else None
+        )
 
         self.headers_sparql = {
-            "Content-Type": sparql_config['Content-Type'] if sparql_config and 'Content-Type' in sparql_config
+            "Content-Type": sparql_config["Content-Type"]
+            if sparql_config and "Content-Type" in sparql_config
             else "text/plain",
-            "Accept": sparql_config['Accept'] if sparql_config and 'Accept' in sparql_config
-            else "application/sparql-results+json"
+            "Accept": sparql_config["Accept"]
+            if sparql_config and "Accept" in sparql_config
+            else "application/sparql-results+json",
         }
         self.headers_elastic = {
-            "Content-Type": elastic_config['Content-Type'] if elastic_config and 'Content-Type' in elastic_config
+            "Content-Type": elastic_config["Content-Type"]
+            if elastic_config and "Content-Type" in elastic_config
+            else "application/json",
+            "Accept": elastic_config["Accept"]
+            if elastic_config and "Accept" in elastic_config
             else "application/json",
-            "Accept": elastic_config['Accept'] if elastic_config and 'Accept' in elastic_config
-            else "application/json"
         }
         self.headers_upload = {
             "Accept": files_upload_config.pop("Accept"),
         }
-        self.headers_download = {
-            "Accept": files_download_config.pop("Accept")
-        }
+        self.headers_download = {"Accept": files_download_config.pop("Accept")}
 
         if token is not None:
             nexus.config.set_token(token)
@@ -121,21 +155,77 @@ def __init__(self, endpoint: str, org: str, prj: str, token: str, model_context:
         self.context = Context(self.get_project_context())
 
         self.url_base_files = "/".join((self.endpoint, "files"))
-        self.url_files = "/".join((self.url_base_files, quote_plus(org), quote_plus(prj)))
-        self.url_resources = "/".join((self.endpoint, "resources", quote_plus(org), quote_plus(prj)))
-        self.url_resolver = "/".join((self.endpoint,"resolvers", quote_plus(org), quote_plus(prj)))
-        self.metadata_context = Context(recursive_resolve(self.store_context, self.resolve_context), store_context)
-
-        sparql_view = sparql_config['endpoint'] if sparql_config and "endpoint" in sparql_config else self.default_sparql_index
-        elastic_view = elastic_config['endpoint'] if elastic_config and "endpoint" in elastic_config else self.default_es_index
+        self.url_files = "/".join(
+            (self.url_base_files, quote_plus(org), quote_plus(prj))
+        )
+        self.url_resources = "/".join(
+            (self.endpoint, "resources", quote_plus(org), quote_plus(prj))
+        )
+        self.url_resolver = "/".join(
+            (self.endpoint, "resolvers", quote_plus(org), quote_plus(prj))
+        )
+        self.metadata_context = Context(
+            recursive_resolve(
+                self.store_context, self.resolve_context, already_loaded=[]
+            ),
+            store_context,
+        )
+        sparql_view = (
+            sparql_config["endpoint"]
+            if sparql_config and "endpoint" in sparql_config
+            else self.default_sparql_index
+        )
+        elastic_view = (
+            elastic_config["endpoint"]
+            if elastic_config and "endpoint" in elastic_config
+            else self.default_es_index
+        )
+        default_str_keyword_field = (
+            elastic_config["default_str_keyword_field"]
+            if elastic_config and "default_str_keyword_field" in elastic_config
+            else None
+        )
+
+        es_mapping = (
+            elastic_config["mapping"]
+            if elastic_config and "mapping" in elastic_config
+            else None
+        )
 
         self.sparql_endpoint = dict()
         self.elastic_endpoint = dict()
-        self.sparql_endpoint["endpoint"] = "/".join((self.endpoint, "views", quote_plus(org), quote_plus(prj), quote_plus(sparql_view), "sparql"))
+        self.sparql_endpoint["endpoint"] = "/".join(
+            (
+                self.endpoint,
+                "views",
+                quote_plus(org),
+                quote_plus(prj),
+                quote_plus(sparql_view),
+                "sparql",
+            )
+        )
         self.sparql_endpoint["type"] = "sparql"
 
-        self.elastic_endpoint["endpoint"] = "/".join((self.endpoint, "views", quote_plus(org), quote_plus(prj), quote_plus(elastic_view), "_search"))
+        self.elastic_endpoint["endpoint"] = "/".join(
+            (
+                self.endpoint,
+                "views",
+                quote_plus(org),
+                quote_plus(prj),
+                quote_plus(elastic_view),
+                "_search",
+            )
+        )
         self.elastic_endpoint["type"] = "elastic"
+        self.elastic_endpoint["view"] = LazyAction(
+            nexus.views.fetch,
+            quote_plus(org),
+            quote_plus(prj),
+            es_mapping
+            if es_mapping
+            else elastic_view,  # Todo consider using Dict for es_mapping
+        )
+        self.elastic_endpoint["default_str_keyword_field"] = default_str_keyword_field
 
         # The following code is for async to work on jupyter notebooks
         try:
@@ -146,17 +236,16 @@ def __init__(self, endpoint: str, org: str, prj: str, token: str, model_context:
 
     def get_project_context(self) -> Dict:
         project_data = nexus.projects.fetch(self.organisation, self.project)
-        context = {
-            "@base": project_data["base"],
-            "@vocab": project_data["vocab"]
-        }
+        context = {"@base": project_data["base"], "@vocab": project_data["vocab"]}
         return context
 
     def resolve_context(self, iri: str, local_only: Optional[bool] = False) -> Dict:
         if iri in self.context_cache:
             return self.context_cache[iri]
         try:
-            context_to_resolve = self.store_local_context if iri == self.store_context else iri
+            context_to_resolve = (
+                self.store_local_context if iri == self.store_context else iri
+            )
             url = "/".join((self.url_resolver, "_", quote_plus(context_to_resolve)))
             response = requests.get(url, headers=self.headers)
             response.raise_for_status()
@@ -181,10 +270,17 @@ def resolve_context(self, iri: str, local_only: Optional[bool] = False) -> Dict:
         self.context_cache.update({context_to_resolve: document})
         return document
 
-    def batch_request(self, resources: List[Resource], action: BatchAction, callback: Callable,
-                      error_type: Callable, **kwargs) -> (BatchResults, BatchResults):
-
-        def create_tasks(semaphore, session, loop, data, batch_action, f_callback, error):
+    def batch_request(
+        self,
+        resources: List[Resource],
+        action: BatchAction,
+        callback: Callable,
+        error_type: Callable,
+        **kwargs,
+    ) -> (BatchResults, BatchResults):
+        def create_tasks(
+            semaphore, session, loop, data, batch_action, f_callback, error
+        ):
             futures = []
             schema_id = kwargs.get("schema_id")
             schema_id = "_" if schema_id is None else quote_plus(schema_id)
@@ -192,66 +288,140 @@ def create_tasks(semaphore, session, loop, data, batch_action, f_callback, error
                 params = deepcopy(kwargs.get("params", {}))
                 if batch_action == batch_action.CREATE:
                     context = self.model_context or self.context
-                    payload = as_jsonld(resource, "compacted", False,
-                                        model_context=context, metadata_context=None,
-                                        context_resolver=self.resolve_context, na=nan)
+                    payload = as_jsonld(
+                        resource,
+                        "compacted",
+                        False,
+                        model_context=context,
+                        metadata_context=None,
+                        context_resolver=self.resolve_context,
+                        na=nan,
+                    )
                     url = f"{self.url_resources}/{schema_id}"
                     prepared_request = loop.create_task(
-                        queue(hdrs.METH_POST, semaphore, session, url, resource, error, payload, params))
+                        queue(
+                            hdrs.METH_POST,
+                            semaphore,
+                            session,
+                            url,
+                            resource,
+                            error,
+                            payload,
+                            params,
+                        )
+                    )
 
                 if batch_action == batch_action.UPDATE:
-                    url = "/".join((self.url_resources, schema_id, quote_plus(resource.id)))
+                    url = "/".join(
+                        (self.url_resources, schema_id, quote_plus(resource.id))
+                    )
                     params["rev"] = resource._store_metadata._rev
 
-                    payload = as_jsonld(resource, "compacted", False,
-                                        model_context=self.model_context,
-                                        metadata_context=None,
-                                        context_resolver=self.resolve_context, na=nan)
+                    payload = as_jsonld(
+                        resource,
+                        "compacted",
+                        False,
+                        model_context=self.model_context,
+                        metadata_context=None,
+                        context_resolver=self.resolve_context,
+                        na=nan,
+                    )
                     prepared_request = loop.create_task(
-                        queue(hdrs.METH_PUT, semaphore, session, url, resource, error, payload,
-                              params))
+                        queue(
+                            hdrs.METH_PUT,
+                            semaphore,
+                            session,
+                            url,
+                            resource,
+                            error,
+                            payload,
+                            params,
+                        )
+                    )
 
                 if batch_action == batch_action.TAG:
-                    url = "/".join((self.url_resources, "_", quote_plus(resource.id), "tags"))
+                    url = "/".join(
+                        (self.url_resources, "_", quote_plus(resource.id), "tags")
+                    )
                     rev = resource._store_metadata._rev
                     params["rev"] = rev
                     payload = {"tag": kwargs.get("tag"), "rev": rev}
                     prepared_request = loop.create_task(
-                        queue(hdrs.METH_POST, semaphore, session, url, resource, error, payload,
-                              params))
+                        queue(
+                            hdrs.METH_POST,
+                            semaphore,
+                            session,
+                            url,
+                            resource,
+                            error,
+                            payload,
+                            params,
+                        )
+                    )
 
                 if batch_action == batch_action.DEPRECATE:
                     url = "/".join((self.url_resources, "_", quote_plus(resource.id)))
                     params["rev"] = resource._store_metadata._rev
                     prepared_request = loop.create_task(
-                        queue(hdrs.METH_DELETE, semaphore, session, url, resource, error,
-                              params=params))
+                        queue(
+                            hdrs.METH_DELETE,
+                            semaphore,
+                            session,
+                            url,
+                            resource,
+                            error,
+                            params=params,
+                        )
+                    )
 
                 if batch_action == BatchAction.FETCH:
                     resource_org, resource_prj = resource.project.split("/")[-2:]
-                    resource_url = "/".join((self.endpoint, "resources", quote_plus(resource_org), quote_plus(resource_prj)))
+                    resource_url = "/".join(
+                        (
+                            self.endpoint,
+                            "resources",
+                            quote_plus(resource_org),
+                            quote_plus(resource_prj),
+                        )
+                    )
                     url = "/".join((resource_url, "_", quote_plus(resource.id)))
                     prepared_request = loop.create_task(
-                        queue(hdrs.METH_GET, semaphore, session, url, resource, error))
+                        queue(hdrs.METH_GET, semaphore, session, url, resource, error)
+                    )
 
                 if f_callback:
                     prepared_request.add_done_callback(f_callback)
                 futures.append(prepared_request)
             return futures
 
-        async def queue(method, semaphore, session, url, resource, exception, payload=None,
-                        params=None):
+        async def queue(
+            method,
+            semaphore,
+            session,
+            url,
+            resource,
+            exception,
+            payload=None,
+            params=None,
+        ):
             async with semaphore:
-                return await request(method, session, url, resource, payload, params, exception)
+                return await request(
+                    method, session, url, resource, payload, params, exception
+                )
 
         async def request(method, session, url, resource, payload, params, exception):
-            async with session.request(method, url, headers=self.headers,
-                                       data=json.dumps(payload), params=params) as response:
+            async with session.request(
+                method,
+                url,
+                headers=self.headers,
+                data=json.dumps(payload),
+                params=params,
+            ) as response:
                 content = await response.json()
                 if response.status < 400:
                     return BatchResult(resource, content)
                 else:
-                    msg = " ".join(re.findall('[A-Z][^A-Z]*', content["@type"])).lower()
+                    msg = " ".join(re.findall("[A-Z][^A-Z]*", content["@type"])).lower()
                     error = exception(msg)
                     return BatchResult(resource, error)
 
@@ -259,20 +429,37 @@ async def dispatch_action():
             semaphore = asyncio.Semaphore(self.max_connection)
             loop = asyncio.get_event_loop()
             async with ClientSession() as session:
-                tasks = create_tasks(semaphore, session, loop, resources, action, callback, error_type)
+                tasks = create_tasks(
+                    semaphore, session, loop, resources, action, callback, error_type
+                )
                 return await asyncio.gather(*tasks)
 
         return asyncio.run(dispatch_action())
 
     def sync_metadata(self, resource: Resource, result: Dict) -> None:
-        metadata = {"id": resource.id}
+        metadata = (
+            {"id": resource.id}
+            if hasattr(resource, "id")
+            else (
+                {"id": resource.__getattribute__("@id")}
+                if hasattr(resource, "@id")
+                else dict()
+            )
+        )
         keys = sorted(self.metadata_context.terms.keys())
+        keys.extend(["_index", "_score"])
         only_meta = {k: v for k, v in result.items() if k in keys}
         metadata.update(_remove_ld_keys(only_meta, self.metadata_context, False))
         resource._store_metadata = wrap_dict(metadata)
 
-    def synchronize_resource(self, resource: Resource, response: Union[Exception, Dict],
-                             action_name: str, succeeded: bool, synchronized: bool) -> None:
+    def synchronize_resource(
+        self,
+        resource: Resource,
+        response: Union[Exception, Dict],
+        action_name: str,
+        succeeded: bool,
+        synchronized: bool,
+    ) -> None:
         if succeeded:
             action = Action(action_name, succeeded, None)
             self.sync_metadata(resource, response)
@@ -286,14 +473,24 @@ def callback(task: Task):
             result = task.result()
             if isinstance(result.response, Exception):
                 self.synchronize_resource(
-                    result.resource, result.response, fun_name, False, False)
+                    result.resource, result.response, fun_name, False, False
+                )
             else:
                 self.synchronize_resource(
-                    result.resource, result.response, fun_name, True, True)
+                    result.resource, result.response, fun_name, True, True
+                )
+
         return callback
 
-    def verify(self, resources: List[Resource], function_name, exception: Callable,
-               id_required: bool, required_synchronized: bool, execute_actions: bool) -> List[Resource]:
+    def verify(
+        self,
+        resources: List[Resource],
+        function_name,
+        exception: Callable,
+        id_required: bool,
+        required_synchronized: bool,
+        execute_actions: bool,
+    ) -> List[Resource]:
         valid = list()
         for resource in resources:
             if id_required and not hasattr(resource, "id"):
@@ -305,7 +502,9 @@ def verify(self, resources: List[Resource], function_name, exception: Callable,
                 if synchronized is not required_synchronized:
                     be_or_not_be = "be" if required_synchronized is True else "not be"
                     error = exception(f"resource should {be_or_not_be} synchronized")
-                    self.synchronize_resource(resource, error, function_name, False, False)
+                    self.synchronize_resource(
+                        resource, error, function_name, False, False
+                    )
                     continue
             if execute_actions:
                 lazy_actions = collect_lazy_actions(resource)
@@ -314,13 +513,16 @@ def verify(self, resources: List[Resource], function_name, exception: Callable,
                         execute_lazy_actions(resource, lazy_actions)
                     except Exception as e:
                         self.synchronize_resource(
-                            resource, exception(e), function_name, False, False)
+                            resource, exception(e), function_name, False, False
+                        )
                         continue
             valid.append(resource)
         return valid
 
-    def to_resource(self, payload: Dict) -> Resource:
-        data_context = deepcopy(payload["@context"])
+    def to_resource(
+        self, payload: Dict, sync_metadata: bool = True, **kwargs
+    ) -> Resource:
+        data_context = deepcopy(payload.get("@context", None))
         if not isinstance(data_context, list):
             data_context = [data_context]
         if self.store_context in data_context:
@@ -336,13 +538,28 @@ def to_resource(self, payload: Dict) -> Resource:
             else:
                 data[k] = v
 
-        if self.model_context and data_context == self.model_context.iri:
+        if (
+            self.model_context
+            and data_context is not None
+            and data_context == self.model_context.iri
+        ):
             resolved_ctx = self.model_context.document["@context"]
+        elif data_context is not None:
+            resolved_ctx = recursive_resolve(
+                data_context,
+                self.resolve_context,
+                already_loaded=[self.store_local_context, self.store_context],
+            )
         else:
-            resolved_ctx = recursive_resolve(data_context, self.resolve_context)
-        data["@context"] = resolved_ctx
-        resource = _from_jsonld_one(data)
-        resource.context = data_context
-        if len(metadata) > 0:
+            resolved_ctx = None
+        if resolved_ctx:
+            data["@context"] = resolved_ctx
+            resource = _from_jsonld_one(data)
+            resource.context = data_context
+        else:
+            resource = Resource.from_json(data)
+
+        if len(metadata) > 0 and sync_metadata:
+            metadata.update(kwargs)
             self.sync_metadata(resource, metadata)
         return resource
diff --git a/setup.py b/setup.py
index 4ad2d47d..e643c742 100644
--- a/setup.py
+++ b/setup.py
@@ -1,14 +1,14 @@
-# 
+#
 # Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # Blue Brain Nexus Forge is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 # General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
 
@@ -51,12 +51,20 @@
         "pyshacl==v0.17.2",
         "nest-asyncio>=1.5.1",
         "pyparsing>=2.0.2",
-        "owlrl>=5.2.3"
+        "owlrl>=5.2.3",
+        "elasticsearch_dsl==7.4.0"
     ],
     extras_require={
-        "dev": ["tox", "pytest", "pytest-bdd==3.4.0", "pytest-cov", "pytest-mock", "codecov"],
+        "dev": [
+            "tox",
+            "pytest",
+            "pytest-bdd==3.4.0",
+            "pytest-cov",
+            "pytest-mock",
+            "codecov",
+        ],
         "docs": ["sphinx", "sphinx-bluebrain-theme"],
-        "linking_sklearn": ["sklearn"]
+        "linking_sklearn": ["sklearn"],
     },
     classifiers=[
         "Development Status :: 2 - Pre-Alpha",
@@ -65,6 +73,6 @@
         "Topic :: Scientific/Engineering",
         "Programming Language :: Python :: 3 :: Only",
         "Natural Language :: English",
-        'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)'
-    ]
+        "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
+    ],
 )


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index a08dd4ff..a0c56cf4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,14 +1,14 @@
-# 
+#
 # Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # Blue Brain Nexus Forge is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 # General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
 
@@ -42,14 +42,11 @@ def do(fun: Callable, data: Union[Resource, List[Resource]], *args) -> None:
 
 # Resource(s) creation.
 
+
 @pytest.fixture
 def json_one():
-    return {
-        "id": "123",
-        "type": "Type",
-        "p1": "v1a",
-        "p2": "v2a"
-    }
+    return {"id": "123", "type": "Type", "p1": "v1a", "p2": "v2a"}
+
 
 @pytest.fixture
 def json_list_one():
@@ -58,21 +55,12 @@ def json_list_one():
         "type": "Other",
         "p3": "v3c",
         "p4": [
-            {
-                "id": "123",
-                "type": "Type",
-                "p1": "v1a",
-                "p2": "v2a"
-            },
-            {
-                "id": "345",
-                "type": "Type",
-                "p1": "v1b",
-                "p2": "v2b"
-            }
-        ]
+            {"id": "123", "type": "Type", "p1": "v1a", "p2": "v2a"},
+            {"id": "345", "type": "Type", "p1": "v1b", "p2": "v2b"},
+        ],
     }
 
+
 def resource(valid: bool, index: int = 0) -> Resource:
     rid = str(uuid4())
     r = Resource(type="Person", id=rid)
@@ -109,6 +97,7 @@ def invalid_resources():
 
 # Resource(s) modifications.
 
+
 @when("I modify the resource.")
 def modify_resource(data):
     data.name = "other"
@@ -116,14 +105,18 @@ def modify_resource(data):
 
 # Resource(s) verifications.
 
+
 @then(parsers.parse("The '{attr}' status of {} resource should be '{value}'."))
 def check_resource_status(data, attr, value):
-    def fun(x): assert str(getattr(x, attr)) == value
+    def fun(x):
+        assert str(getattr(x, attr)) == value
+
     do(fun, data)
 
 
 # Action(s) verifications.
 
+
 def check_report(capsys, rc, err, msg, op):
     out = capsys.readouterr().out[:-1]
     heads = {
@@ -141,30 +134,39 @@ def check_report(capsys, rc, err, msg, op):
 
 @then("I should be able to access the report of the action on a resource.")
 def check_action(data):
-    def fun(x): assert isinstance(x._last_action, Action)
+    def fun(x):
+        assert isinstance(x._last_action, Action)
+
     do(fun, data)
 
 
 @then(parsers.parse("The report should say that the operation was '{value}'."))
 def check_action_operation(data, value):
-    def fun(x): assert x._last_action.operation == value
+    def fun(x):
+        assert x._last_action.operation == value
+
     do(fun, data)
 
 
 @then(parsers.parse("The report should say that the operation success is '{value}'."))
 def check_action_success(data, value):
-    def fun(x): assert str(x._last_action.succeeded) == value
+    def fun(x):
+        assert str(x._last_action.succeeded) == value
+
     do(fun, data)
 
 
 @then(parsers.parse("The report should say that the error was '{value}'."))
 def check_action_error(data, value):
-    def fun(x): assert str(x._last_action.error) == value
+    def fun(x):
+        assert str(x._last_action.error) == value
+
     do(fun, data)
 
 
 # Fixtures for Resource to JSON-LD conversion and vice versa
 
+
 @pytest.fixture
 def forge():
     config = {
@@ -179,14 +181,15 @@ def forge():
     }
     return KnowledgeGraphForge(config)
 
+
 @pytest.fixture
 def custom_context():
     return {
         "@context": {
             "@base": "http://example.org/",
-            "foaf":"http://xmlns.com/foaf/0.1/",
+            "foaf": "http://xmlns.com/foaf/0.1/",
             "Person": "foaf:Person",
-            "name": "foaf:name"
+            "name": "foaf:name",
         }
     }
 
@@ -195,32 +198,30 @@ def custom_context():
 def metadata_context() -> Context:
     document = {
         "deprecated": "https://store.net/vocabulary/deprecated",
-        "version": "https://store.net/vocabulary/version"
+        "version": "https://store.net/vocabulary/version",
     }
     return Context(document, "http://store.org/metadata.json")
 
 
 @pytest.fixture
 def metadata_data_compacted():
-    return {
-        "deprecated": False,
-        "version": 1
-    }
+    return {"deprecated": False, "version": 1}
 
 
 @pytest.fixture
 def metadata_data_expanded():
     return {
         "https://store.net/vocabulary/deprecated": False,
-        "https://store.net/vocabulary/version": 1
+        "https://store.net/vocabulary/version": 1,
     }
 
+
 @pytest.fixture
 def store_metadata_context():
     return {
         "meta": "https://store.net/vocabulary/",
         "deprecated": "meta:deprecated",
-        "version": "meta:version"
+        "version": "meta:version",
     }
 
 
@@ -233,7 +234,8 @@ def store_metadata_value(store_metadata_context, metadata_data_compacted):
 
 @pytest.fixture
 def person(custom_context):
-    return Resource(context=custom_context, type=["Person","Agent"], name="Jami Booth")
+    return Resource(context=custom_context, type=["Person", "Agent"], name="Jami Booth")
+
 
 @pytest.fixture
 def registered_person_custom_context(person, custom_context, store_metadata_value):
@@ -245,8 +247,11 @@ def registered_person_custom_context(person, custom_context, store_metadata_valu
 
 @pytest.fixture
 def organization(registered_person_custom_context, store_metadata_value):
-    contribution = Resource(type=["Organization", "Agent"], name="Reichel Inc",
-                            founder=registered_person_custom_context)
+    contribution = Resource(
+        type=["Organization", "Agent"],
+        name="Reichel Inc",
+        founder=registered_person_custom_context,
+    )
     return contribution
 
 
@@ -257,7 +262,9 @@ def building():
     description = "The Empire State Building is a 102-story landmark in New York City."
     image = "http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg"
     geo = {"latitude": "40.75"}
-    return Resource(type=type_, name=name, description=description, image=image, geo=geo)
+    return Resource(
+        type=type_, name=name, description=description, image=image, geo=geo
+    )
 
 
 @pytest.fixture(scope="session")
@@ -291,34 +298,39 @@ def model_prefixes():
         "nsg": "https://neuroshapes.org/",
         "owl": "http://www.w3.org/2002/07/owl#",
         "xsd": "http://www.w3.org/2001/XMLSchema#",
-        'foaf': 'http://xmlns.com/foaf/0.1/'
+        "foaf": "http://xmlns.com/foaf/0.1/",
     }
 
+
 @pytest.fixture
 def building_jsonld(metadata_context, metadata_data_compacted, metadata_data_expanded):
     def _make_jsonld_expanded(resource, store_metadata, context):
         data = dict()
         if hasattr(resource, "id"):
             data["@id"] = resource.id
-        ctx = Context(resource.context) if hasattr(resource, "context") else Context(context)
+        ctx = (
+            Context(resource.context)
+            if hasattr(resource, "context")
+            else Context(context)
+        )
         latitude_term = ctx.terms.get("latitude")
         if latitude_term.type:
             latitude_node = {
                 "@type": latitude_term.type,
-                "@value": resource.geo["latitude"]
+                "@value": resource.geo["latitude"],
             }
         else:
             latitude_node = resource.geo["latitude"]
-        geo_expanded = {
-            latitude_term.id: latitude_node
-        }
-        data.update({
-            "@type": ctx.expand(resource.type),
-            ctx.expand("description"): resource.description,
-            ctx.expand("geo"): geo_expanded,
-            ctx.expand("image"): {"@id": resource.image},
-            ctx.expand("name"): resource.name
-        })
+        geo_expanded = {latitude_term.id: latitude_node}
+        data.update(
+            {
+                "@type": ctx.expand(resource.type),
+                ctx.expand("description"): resource.description,
+                ctx.expand("geo"): geo_expanded,
+                ctx.expand("image"): {"@id": resource.image},
+                ctx.expand("name"): resource.name,
+            }
+        )
         if store_metadata and resource._store_metadata is not None:
             data.update(metadata_data_expanded)
         return data
@@ -327,31 +339,41 @@ def _make_jsonld_compacted(resource, store_metadata, context):
         data = dict()
         data_context = resource.context if hasattr(resource, "context") else context
         if store_metadata and resource._store_metadata is not None:
-            metadata_context_output = metadata_context.iri if metadata_context.is_http_iri() else metadata_context.document["@context"]
+            metadata_context_output = (
+                metadata_context.iri
+                if metadata_context.is_http_iri()
+                else metadata_context.document["@context"]
+            )
             data["@context"] = _merge_jsonld(data_context, metadata_context_output)
         else:
             data["@context"] = data_context
         if hasattr(resource, "id"):
             data["@id"] = resource.id
-        data.update({
-            "@type": resource.type,
-            "description": resource.description,
-            "geo": resource.geo,
-            "image": resource.image,
-            "name": resource.name
-        })
+        data.update(
+            {
+                "@type": resource.type,
+                "description": resource.description,
+                "geo": resource.geo,
+                "image": resource.image,
+                "name": resource.name,
+            }
+        )
         if store_metadata and resource._store_metadata is not None:
             data.update(metadata_data_compacted)
         return data
 
-    def _make_jsonld(rsrc: Resource, form: str, store_metadata: bool,
-                     context: Optional[Union[Dict, List, str]]):
+    def _make_jsonld(
+        rsrc: Resource,
+        form: str,
+        store_metadata: bool,
+        context: Optional[Union[Dict, List, str]],
+    ):
         if form is Form.COMPACTED.value:
             return _make_jsonld_compacted(rsrc, store_metadata, context)
         elif form is Form.EXPANDED.value:
             return _make_jsonld_expanded(rsrc, store_metadata, context)
-    return _make_jsonld
 
+    return _make_jsonld
 
 
 SCOPE = "terms"
@@ -360,26 +382,35 @@ def _make_jsonld(rsrc: Resource, form: str, store_metadata: bool,
 RESOLVER = "DemoResolver"
 
 
-@pytest.fixture(params=[
-    MODEL,
-    f"{MODEL} from kgforge.specializations.models",
-    f"{MODEL} from kgforge.specializations.models.demo_model"])
+@pytest.fixture(
+    params=[
+        MODEL,
+        f"{MODEL} from kgforge.specializations.models",
+        f"{MODEL} from kgforge.specializations.models.demo_model",
+    ]
+)
 def model(request):
     return request.param
 
 
-@pytest.fixture(params=[
-    STORE,
-    f"{STORE} from kgforge.specializations.stores",
-    f"{STORE} from kgforge.specializations.stores.demo_store"])
+@pytest.fixture(
+    params=[
+        STORE,
+        f"{STORE} from kgforge.specializations.stores",
+        f"{STORE} from kgforge.specializations.stores.demo_store",
+    ]
+)
 def store(request):
     return request.param
 
 
-@pytest.fixture(params=[
-    RESOLVER,
-    f"{RESOLVER} from kgforge.specializations.resolvers",
-    f"{RESOLVER} from kgforge.specializations.resolvers.demo_resolver"])
+@pytest.fixture(
+    params=[
+        RESOLVER,
+        f"{RESOLVER} from kgforge.specializations.resolvers",
+        f"{RESOLVER} from kgforge.specializations.resolvers.demo_resolver",
+    ]
+)
 def resolver(request):
     return request.param
 
@@ -394,7 +425,7 @@ def config(model, store, resolver):
         },
         "Store": {
             "name": store,
-            "versioned_id_template": "{x.id}?_version={x._store_metadata.version}"
+            "versioned_id_template": "{x.id}?_version={x._store_metadata.version}",
         },
         "Resolvers": {
             SCOPE: [
@@ -412,4 +443,233 @@ def config(model, store, resolver):
                 },
             ],
         },
-    }
\ No newline at end of file
+    }
+
+
+@pytest.fixture
+def es_mapping_dict():
+    return {
+        "dynamic": True,
+        "properties": {
+            "@id": {"type": "keyword"},
+            "@type": {"type": "keyword"},
+            "annotation": {
+                "properties": {
+                    "hasBody": {
+                        "properties": {
+                            "label": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "text",
+                            },
+                            "prefLabel": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "text",
+                            },
+                        },
+                        "type": "nested",
+                    }
+                },
+                "type": "object",
+            },
+            "atlasRelease": {
+                "properties": {
+                    "@id": {
+                        "fields": {"keyword": {"type": "keyword"}},
+                        "type": "keyword",
+                    }
+                },
+                "type": "object",
+            },
+            "brainLocation": {
+                "properties": {
+                    "atlasSpatialReferenceSystem": {
+                        "properties": {
+                            "@id": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "keyword",
+                            }
+                        },
+                        "type": "object",
+                    },
+                    "brainRegion": {
+                        "properties": {
+                            "@id": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "keyword",
+                            },
+                            "label": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "text",
+                            },
+                        },
+                        "type": "object",
+                    },
+                    "coordinatesInBrainAtlas": {
+                        "properties": {
+                            "valueX": {
+                                "properties": {
+                                    "@type": {"type": "keyword"},
+                                    "@value": {
+                                        "fields": {"keyword": {"type": "keyword"}},
+                                        "type": "float",
+                                    },
+                                },
+                                "type": "object",
+                            },
+                            "valueY": {
+                                "properties": {
+                                    "@type": {"type": "keyword"},
+                                    "@value": {
+                                        "fields": {"keyword": {"type": "keyword"}},
+                                        "type": "float",
+                                    },
+                                },
+                                "type": "object",
+                            },
+                            "valueZ": {
+                                "properties": {
+                                    "@type": {"type": "keyword"},
+                                    "@value": {
+                                        "fields": {"keyword": {"type": "keyword"}},
+                                        "type": "float",
+                                    },
+                                },
+                                "type": "object",
+                            },
+                        },
+                        "type": "object",
+                    },
+                    "layer": {
+                        "properties": {
+                            "label": {
+                                "type": "text",
+                            }
+                        },
+                        "type": "object",
+                    },
+                },
+                "type": "object",
+            },
+            "contribution": {
+                "properties": {
+                    "agent": {
+                        "properties": {
+                            "@id": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "keyword",
+                            },
+                            "@type": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "text",
+                            },
+                        },
+                        "type": "nested",
+                    }
+                },
+                "type": "nested",
+            },
+            "derivation": {
+                "properties": {
+                    "entity": {
+                        "properties": {
+                            "@type": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "text",
+                            },
+                            "name": {
+                                "fields": {"keyword": {"type": "keyword"}},
+                                "type": "text",
+                            },
+                        },
+                        "type": "nested",
+                    },
+                    "a_dense_vector": {
+                        "dims": 3,
+                        "type": "dense_vector",
+                    },
+                },
+                "type": "nested",
+            },
+            "description": {"fields": {"keyword": {"type": "keyword"}}, "type": "text"},
+            "dimension": {"type": "nested"},
+            "distribution": {
+                "properties": {
+                    "contentSize": {"type": "nested"},
+                    "contentUrl": {"type": "keyword"},
+                    "digest": {
+                        "properties": {"value": {"type": "keyword"}},
+                        "type": "nested",
+                    },
+                    "encodingFormat": {"type": "keyword"},
+                },
+                "type": "nested",
+            },
+            "generation": {"type": "nested"},
+            "isRegisteredIn": {
+                "properties": {"@id": {"type": "keyword"}},
+                "type": "object",
+            },
+            "license": {
+                "properties": {
+                    "label": {
+                        "fields": {"keyword": {"type": "keyword"}},
+                        "type": "text",
+                    }
+                },
+                "type": "object",
+            },
+            "name": {"fields": {"keyword": {"type": "keyword"}}, "type": "text"},
+            "an_integer": {
+                "fields": {"keyword": {"type": "keyword"}},
+                "type": "integer",
+            },
+            "a_dense_vector": {
+                "dims": 3,
+                "type": "dense_vector",
+            },
+            "a_boolean": {
+                "type": "boolean",
+            },
+            "objectOfStudy": {
+                "properties": {"label": {"type": "text"}},
+                "type": "object",
+            },
+            "parcellationOntology": {
+                "properties": {"@id": {"type": "keyword"}},
+                "type": "object",
+            },
+            "parcellationVolume": {
+                "properties": {"@id": {"type": "keyword"}},
+                "type": "object",
+            },
+            "recordMeasure": {"type": "nested"},
+            "series": {
+                "properties": {
+                    "statistic": {
+                        "fields": {"keyword": {"type": "keyword"}},
+                        "type": "text",
+                    },
+                    "unitCode": {
+                        "fields": {"keyword": {"type": "keyword"}},
+                        "type": "text",
+                    },
+                    "a_float": {
+                        "type": "float",
+                    },
+                },
+                "type": "nested",
+            },
+            "spatialReferenceSystem": {
+                "properties": {
+                    "@id": {
+                        "fields": {"keyword": {"type": "keyword"}},
+                        "type": "keyword",
+                    }
+                },
+                "type": "object",
+            },
+            "subject": {"type": "object"},
+            "_createdBy": {"type": "keyword"},
+            "_updatedBy": {"type": "keyword"},
+        },
+    }
diff --git a/tests/core/__init__.py b/tests/core/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/core/commons/__init__.py b/tests/core/commons/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/core/commons/test_es_query_builder.py b/tests/core/commons/test_es_query_builder.py
new file mode 100644
index 00000000..031f53fe
--- /dev/null
+++ b/tests/core/commons/test_es_query_builder.py
@@ -0,0 +1,762 @@
+# Blue Brain Nexus Forge is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Blue Brain Nexus Forge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+from typing import List
+
+import elasticsearch_dsl
+import pytest
+from elasticsearch_dsl import Text, Keyword, Nested, Object, Integer, Q
+
+
+from kgforge.core.commons.es_query_builder import (
+    _recursive_resolve_nested,
+    _build_keyword_path,
+    _detect_mapping_type,
+    ESQueryBuilder,
+)
+from kgforge.core.wrappings import Filter
+
+
+class TestESQueryBuilder:
+    @pytest.mark.parametrize(
+        "property_path, mapping_type, dynamic_mapping_type, default_str_keyword_field, keyword_path",
+        [
+            pytest.param(
+                ("annotation.hasBody.label"),
+                (Text(fields={"keyword": Keyword()})),
+                (None),
+                (None),
+                ("annotation.hasBody.label.keyword"),
+                id="text_keyword_field",
+            ),
+            pytest.param(
+                ("@type"),
+                (Keyword()),
+                (None),
+                (None),
+                ("@type"),
+                id="keyword_field_path",
+            ),
+            pytest.param(
+                ("objectOfStudy.label"),
+                (Text()),
+                (None),
+                (None),
+                (None),
+                id="non_keyword_field",
+            ),
+            pytest.param(
+                ("brainLocation.brainRegion"),
+                (Object()),
+                (None),
+                (None),
+                (None),
+                id="object_field_path",
+            ),
+            pytest.param(
+                ("contribution.agent.type"),
+                (None),
+                (Text()),
+                ("keyword"),
+                ("contribution.agent.type.keyword"),
+                id="dynamic_mapping_type",
+            ),
+            pytest.param(
+                ("contribution.agent.type"),
+                (None),
+                (None),
+                ("keyword"),
+                (None),
+                id="noat_mapping_type",
+            ),
+        ],
+    )
+    def test__build_keyword_path(
+        self,
+        property_path,
+        mapping_type,
+        dynamic_mapping_type,
+        default_str_keyword_field,
+        keyword_path,
+    ):
+        k_p = _build_keyword_path(
+            mapping_type, property_path, dynamic_mapping_type, default_str_keyword_field
+        )
+        assert k_p == keyword_path
+
+    @pytest.mark.parametrize(
+        "field_path, nested_path, found_path, mapping_type",
+        [
+            pytest.param(
+                (["annotation", "hasBody", "label"]),
+                (["annotation.hasBody"]),
+                (["annotation","hasBody", "label"]),
+                (Text(fields={"keyword": Keyword()})),
+                id="text_keyword_field_in_nested",
+            ),
+            pytest.param(
+                (["annotation", "hasBody"]),
+                (["annotation.hasBody"]),
+                (["annotation","hasBody"]),
+                (Nested()),
+                id="nested_field",
+            ),
+            pytest.param(
+                (["annotation", "hasBody", "isMeasurementOf", "label"]),
+                (["annotation.hasBody"]),
+                (["annotation","hasBody"]),
+                (Nested()),
+                id="non_existing_field_in_nested",
+            ),
+            pytest.param(
+                (["brainLocation", "brainRegion"]),
+                ([]),
+                (["brainLocation","brainRegion"]),
+                (Object()),
+                id="object_field"
+            ),
+            pytest.param(
+                (["brainLocation", "brainRegion", "label"]),
+                ([]),
+                (["brainLocation", "brainRegion", "label"]),
+                (Text(fields={"keyword": Keyword()})),
+                id="text_keyword_field_in_object",
+            ),
+            pytest.param(
+                (["an_integer"]),
+                ([]),
+                (["an_integer"]),
+                (Integer(fields={"keyword": Keyword()})),
+                id="integer_field",
+            ),
+            pytest.param((["@type"]), ([]), (["@type"]), (Keyword()), id="keyword_field"),
+        ],
+    )
+    def test__recursive_resolve_nested(
+        self, field_path, nested_path, found_path, mapping_type, es_mapping_dict
+    ):
+        es_mapping = elasticsearch_dsl.Mapping()
+        es_mapping._update_from_dict(es_mapping_dict)
+        n_p, f_p, m_t = _recursive_resolve_nested(es_mapping, field_path)
+        assert n_p == nested_path
+        assert f_p == found_path
+        assert str(m_t) == str(mapping_type)
+
+    @pytest.mark.parametrize(
+        "value, mapping_type",
+        [
+            pytest.param(
+                ("A str value"), (elasticsearch_dsl.Text()), id="text_mapping_type"
+            ),
+            pytest.param(
+                ("2020-10-20T13:53:22.880Z"),
+                (elasticsearch_dsl.Date()),
+                id="date_mapping_type",
+            ),
+            pytest.param((3), (elasticsearch_dsl.Long()), id="integer_mapping_type"),
+            pytest.param(
+                ("8.2"), (elasticsearch_dsl.Float()), id="str_double_mapping_type"
+            ),
+            pytest.param(
+                (True), (elasticsearch_dsl.Boolean()), id="true_bool_mapping_type"
+            ),
+            pytest.param(
+                (False), (elasticsearch_dsl.Boolean()), id="false_bool_mapping_type"
+            ),
+        ],
+    )
+    def test__detect_mapping_type(self, value, mapping_type):
+        m_t = _detect_mapping_type(value)
+        assert str(m_t) == str(mapping_type)
+
+    @pytest.mark.parametrize(
+        "filters, params, es_query",
+        [
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["annotation", "hasBody", "label"],
+                            value="An annotation",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().filter(
+                        "nested",
+                        path="annotation.hasBody",
+                        query=elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Q(
+                                    "term",
+                                    **{
+                                        "annotation.hasBody.label.keyword": "An annotation"
+                                    },
+                                )
+                            ]
+                        ),
+                    )
+                ),
+                id="build_nested_filter_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["annotation", "hasBody", "isMeasurementOf"],
+                            value="An annotation",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        "bool",
+                        must=[
+                            elasticsearch_dsl.query.Nested(
+                                path="annotation.hasBody",
+                                query=elasticsearch_dsl.query.Bool(
+                                    must=[
+                                        elasticsearch_dsl.query.Q(
+                                            "match",
+                                            **{
+                                                "annotation.hasBody.isMeasurementOf": "An annotation"
+                                            },
+                                        )
+                                    ]
+                                ),
+                            ).to_dict()
+                        ],
+                    )
+                    # )
+                ),
+                id="build_unknown_nested_must_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["brainLocation", "brainRegion", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Term(
+                                    **{
+                                        "brainLocation.brainRegion.label.keyword": "A label"
+                                    }
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_filter_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["brainLocation", "layer", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            must=[
+                                elasticsearch_dsl.query.Match(
+                                    **{"brainLocation.layer.label": "A label"}
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_match_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["brainLocation", "brainRegion", "isDefinedBy"],
+                            value="A definition",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Term(
+                                    **{
+                                        "brainLocation.brainRegion.isDefinedBy.keyword": "A definition"
+                                    }
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_unknown_filter_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__ne__",
+                            path=["brainLocation", "brainRegion", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            must_not=[
+                                elasticsearch_dsl.query.Term(
+                                    **{
+                                        "brainLocation.brainRegion.label.keyword": "A label"
+                                    }
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_must_not_filter_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__ne__",
+                            path=["brainLocation", "layer", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            must_not=[
+                                elasticsearch_dsl.query.Match(
+                                    **{"brainLocation.layer.label": "A label"}
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_must_not_match_query",
+            ),
+            pytest.param(
+                ([Filter(operator="__gt__", path=["an_integer"], value="3")]),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Range(
+                                    **{"an_integer": {"gt": "3"}}
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_range_gt_query",
+            ),
+            pytest.param(
+                ([Filter(operator="__le__", path=["series", "a_float"], value="2.0")]),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().filter(
+                        "nested",
+                        path="series",
+                        query=elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Range(
+                                    **{"series.a_float": {"lte": "2.0"}}
+                                )
+                            ]
+                        ),
+                    )
+                ),
+                id="build_range_le_nested_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["unknown", "path"],
+                            value="Unknown value",
+                        )
+                    ]
+                ),
+                ({"default_str_keyword_field": "keyword"}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Term(
+                                    **{"unknown.path.keyword": "Unknown value"}
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_complete_unknown_path_filter_text",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["unknown", "path"],
+                            value="Unknown value",
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Term(
+                                    **{"unknown.path.keyword": "Unknown value"}
+                                )
+                            ]
+                        )
+                    )
+                ),
+                id="build_complete_unknown_path_filter_text_no_default_str_keyword_field",
+            ),
+            pytest.param(
+                ([Filter(operator="__eq__", path=["unknown", "path"], value="3.0")]),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Bool(
+                            must=[
+                                elasticsearch_dsl.query.Match(**{"unknown.path": "3.0"})
+                            ]
+                        )
+                    )
+                ),
+                id="build_complete_unknown_path_must_match_float",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["a_dense_vector"],
+                            value=[0.1, 0.3, 0.8],
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.ScriptScore(
+                            query=elasticsearch_dsl.query.Bool(
+                                must=[
+                                    elasticsearch_dsl.query.Exists(
+                                        field="a_dense_vector"
+                                    )
+                                ]
+                            ),
+                            script={
+                                "source": "doc['a_dense_vector'].size() == 0 ? 0 : (cosineSimilarity(params.queryVector, doc['a_dense_vector'])+1.0) / 2",
+                                "params": {"queryVector": [0.1, 0.3, 0.8]},
+                            },
+                        )
+                    )
+                ),
+                id="build_dense_vector_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["derivation", "a_dense_vector"],
+                            value=[0.1, -0.3, 0.8],
+                        )
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        elasticsearch_dsl.query.Nested(
+                            path="derivation",
+                            query=elasticsearch_dsl.query.ScriptScore(
+                                query=elasticsearch_dsl.query.Bool(
+                                    must=[
+                                        elasticsearch_dsl.query.Exists(
+                                            field="derivation.a_dense_vector"
+                                        )
+                                    ]
+                                ),
+                                script={
+                                    "source": "doc['derivation.a_dense_vector'].size() == 0 ? 0 : (cosineSimilarity(params.queryVector, doc['derivation.a_dense_vector'])+1.0) / 2",
+                                    "params": {"queryVector": [0.1, -0.3, 0.8]},
+                                },
+                            ),
+                        )
+                    )
+                ),
+                id="build_nested_dense_vector_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["brainLocation", "brainRegion", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                ({"includes": ["to_include"], "excludes": ["to_exclude"]}),
+                (
+                    elasticsearch_dsl.Search()
+                    .query(
+                        elasticsearch_dsl.query.Bool(
+                            filter=[
+                                elasticsearch_dsl.query.Term(
+                                    **{
+                                        "brainLocation.brainRegion.label.keyword": "A label"
+                                    }
+                                )
+                            ]
+                        )
+                    )
+                    .source(includes=["to_include"], excludes=["to_exclude"])
+                ),
+                id="build_query_with_source",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["contribution", "agent", "type"],
+                            value="Person",
+                        ),
+                        Filter(
+                            operator="__eq__",
+                            path=["contribution", "agent", "id"],
+                            value="https://orcid",
+                        ),
+                        Filter(
+                            operator="__ne__",
+                            path=["derivation", "entity", "@type"],
+                            value="Entity",
+                        ),
+                    ]
+                ),
+                ({}),
+                (
+                    elasticsearch_dsl.Search().query(
+                        "bool",
+                        filter=[
+                            elasticsearch_dsl.query.Nested(
+                                path="contribution.agent",
+                                query=elasticsearch_dsl.query.Bool(
+                                    filter=[
+                                        elasticsearch_dsl.query.Term(
+                                            **{
+                                                "contribution.agent.@type.keyword": "Person"
+                                            }
+                                        )
+                                    ]
+                                ),
+                            ).to_dict(),
+                            elasticsearch_dsl.query.Nested(
+                                path="contribution.agent",
+                                query=elasticsearch_dsl.query.Bool(
+                                    filter=[
+                                        elasticsearch_dsl.query.Term(
+                                            **{
+                                                "contribution.agent.@id": "https://orcid"
+                                            }
+                                        )
+                                    ]
+                                ),
+                            ).to_dict(),
+                        ],
+                        must_not=[
+                            elasticsearch_dsl.query.Nested(
+                                path="derivation.entity",
+                                query=elasticsearch_dsl.query.Bool(
+                                    must_not=[
+                                        elasticsearch_dsl.query.Term(
+                                            **{
+                                                "derivation.entity.@type.keyword": "Entity"
+                                            }
+                                        )
+                                    ]
+                                ),
+                            ).to_dict()
+                        ],
+                    )
+                ),
+                id="rewrite_type_id",
+            ),
+        ],
+    )
+    def test_build(self, filters, params, es_query, es_mapping_dict):
+        es_mapping = elasticsearch_dsl.Mapping()
+        es_mapping._update_from_dict(es_mapping_dict)
+        query = ESQueryBuilder.build(es_mapping_dict, None, None, filters, **params)
+        assert isinstance(query, dict)
+        assert elasticsearch_dsl.Search.from_dict(query) == es_query
+
+    @pytest.mark.parametrize(
+        "filters, default_str_keyword_field",
+        [
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["brainLocation", "brainRegion"],
+                            value="A brainRegion",
+                        )
+                    ]
+                ),
+                (None),
+                id="build_failing_object_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__gt__",
+                            path=["brainLocation", "brainRegion", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                (None),
+                id="build_failing_gt_Text_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__ge__",
+                            path=["annotation", "hasBody", "label"],
+                            value="A label",
+                        )
+                    ]
+                ),
+                (None),
+                id="build_failing_ge_nested_Text_query",
+            ),
+            pytest.param(
+                ([Filter(operator="__lt__", path=["a_boolean"], value="A boolean")]),
+                (None),
+                id="build_failing_lt_Boolean_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__lt__",
+                            path=["a_dense_vector"],
+                            value=[0.1, 0.3, 0.8],
+                        )
+                    ]
+                ),
+                (None),
+                id="build_failing_dense_vector_query",
+            ),
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["a_dense_vector"],
+                            value=[0.1, 0.3, 0.8],
+                        ),
+                        Filter(
+                            operator="__eq__",
+                            path=["a_dense_vector"],
+                            value=[0.5, 0.5, 0.9],
+                        ),
+                    ]
+                ),
+                (None),
+                id="build_failing_multiple_dense_vector_query",
+            ),
+        ],
+    )
+    def test_build_exception(self, filters, default_str_keyword_field, es_mapping_dict):
+        es_mapping = elasticsearch_dsl.Mapping()
+        es_mapping._update_from_dict(es_mapping_dict)
+
+        with pytest.raises(ValueError):
+            ESQueryBuilder.build(
+                es_mapping_dict,
+                None,
+                None,
+                filters,
+                default_str_keyword_field=default_str_keyword_field,
+            )
+
+    @pytest.mark.parametrize(
+        "filters, default_str_keyword_field",
+        [
+            pytest.param(
+                (
+                    [
+                        Filter(
+                            operator="__eq__",
+                            path=["annotation", "hasBody", "isMeasurementOf"],
+                            value="An annotation",
+                        )
+                    ]
+                ),
+                (None),
+                id="build_nested_filter_query",
+            )
+        ],
+    )
+    def test_build_dynamic_false(
+        self, filters, default_str_keyword_field, es_mapping_dict
+    ):
+        es_mapping = elasticsearch_dsl.Mapping()
+        es_mapping._update_from_dict(es_mapping_dict)
+
+        with pytest.raises(ValueError):
+            es_mapping_dict["dynamic"] = False
+            ESQueryBuilder.build(
+                es_mapping_dict,
+                None,
+                None,
+                filters,
+                default_str_keyword_field=default_str_keyword_field,
+            )
diff --git a/tests/core/wrappings/test_paths.py b/tests/core/wrappings/test_paths.py
index b4e501d8..d29dde40 100644
--- a/tests/core/wrappings/test_paths.py
+++ b/tests/core/wrappings/test_paths.py
@@ -18,27 +18,71 @@
 
 from kgforge.core.wrappings.paths import Filter, create_filters_from_dict
 
-@pytest.mark.parametrize("dict_filters, expected", [
-        pytest.param(({"agent":{"name":"EPFL"}}),
-                     ([Filter(["agent", "name"], "__eq__", "EPFL")]),
-                     id="literal"),
-        pytest.param(({"agent": {"name": "EPFL", "address":{"postalCode":"1015"}}}),
-                     ([Filter(["agent", "name"], "__eq__", "EPFL"),
-                       Filter(["agent", "address","postalCode"], "__eq__", "1015")]),
-                     id="one_nested"),
-        pytest.param(({"type":"Contribution","agent": {"type":"Agent","name": "EPFL", "address": {
-                        "type":"PostalAddress","postalCode": "1015","addressCountry":{
-                        "type":"Country","name":"Switzerland"}
-                        }}}),
-                     ([Filter(["type"], "__eq__", "Contribution"),
-                       Filter(["agent", "type"], "__eq__", "Agent"),
-                       Filter(["agent", "name"], "__eq__", "EPFL"),
-                       Filter(["agent", "address", "type"], "__eq__", "PostalAddress"),
-                       Filter(["agent", "address", "postalCode"], "__eq__", "1015"),
-                       Filter(["agent", "address", "addressCountry","type"], "__eq__", "Country"),
-                       Filter(["agent", "address", "addressCountry", "name"], "__eq__", "Switzerland")]),
-                     id="many_nested")
-    ])
+
+@pytest.mark.parametrize(
+    "dict_filters, expected",
+    [
+        pytest.param(
+            ({"agent": {"name": "EPFL"}}),
+            ([Filter(["agent", "name"], "__eq__", "EPFL")]),
+            id="literal",
+        ),
+        pytest.param(
+            ({"agent": {"name": "EPFL", "address": {"postalCode": "1015"}}}),
+            (
+                [
+                    Filter(["agent", "name"], "__eq__", "EPFL"),
+                    Filter(["agent", "address", "postalCode"], "__eq__", "1015"),
+                ]
+            ),
+            id="one_nested",
+        ),
+        pytest.param(
+            (
+                {
+                    "type": "Contribution",
+                    "agent": {
+                        "type": "Agent",
+                        "name": "EPFL",
+                        "address": {
+                            "type": "PostalAddress",
+                            "postalCode": "1015",
+                            "addressCountry": {
+                                "type": "Country",
+                                "name": "Switzerland",
+                            },
+                        },
+                    },
+                }
+            ),
+            (
+                [
+                    Filter(["type"], "__eq__", "Contribution"),
+                    Filter(["agent", "type"], "__eq__", "Agent"),
+                    Filter(["agent", "name"], "__eq__", "EPFL"),
+                    Filter(["agent", "address", "type"], "__eq__", "PostalAddress"),
+                    Filter(["agent", "address", "postalCode"], "__eq__", "1015"),
+                    Filter(
+                        ["agent", "address", "addressCountry", "type"],
+                        "__eq__",
+                        "Country",
+                    ),
+                    Filter(
+                        ["agent", "address", "addressCountry", "name"],
+                        "__eq__",
+                        "Switzerland",
+                    ),
+                ]
+            ),
+            id="many_nested",
+        ),
+    ],
+)
 def test_dict_to_filter(dict_filters, expected):
     filters = create_filters_from_dict(dict_filters)
     assert filters == expected
+
+
+def test_bad_filter_operator():
+    with pytest.raises(ValueError):
+        Filter(["agent", "type"], "__equal__", "Agent")
diff --git a/tests/specializations/stores/test_bluebrain_nexus.py b/tests/specializations/stores/test_bluebrain_nexus.py
index beb2bd72..e9627387 100644
--- a/tests/specializations/stores/test_bluebrain_nexus.py
+++ b/tests/specializations/stores/test_bluebrain_nexus.py
@@ -11,6 +11,7 @@
 #
 # You should have received a copy of the GNU Lesser General Public License
 # along with Blue Brain Nexus Forge. If not, see <https://choosealicense.com/licenses/lgpl-3.0/>.
+import json
 
 import os
 from unittest import mock
@@ -28,7 +29,11 @@
 from kgforge.core.conversions.rdf import _merge_jsonld
 from kgforge.core.wrappings.dict import wrap_dict
 from kgforge.core.wrappings.paths import Filter, create_filters_from_dict
-from kgforge.specializations.stores.bluebrain_nexus import BlueBrainNexus, build_query_statements, _create_select_query
+from kgforge.specializations.stores.bluebrain_nexus import (
+    BlueBrainNexus,
+    build_sparql_query_statements,
+    _create_select_query,
+)
 
 # FIXME mock Nexus for unittests
 # TODO To be port to the generic parameterizable test suite for stores in test_stores.py. DKE-135.
@@ -40,8 +45,9 @@
 NEXUS_PROJECT_CONTEXT = {"base": "http://data.net/", "vocab": "http://vocab.net/"}
 
 VERSIONED_TEMPLATE = "{x.id}?rev={x._store_metadata._rev}"
-FILE_RESOURCE_MAPPING = os.sep.join((os.path.curdir, "tests", "data", "nexus-store",
-                                     "file-to-resource-mapping.hjson"))
+FILE_RESOURCE_MAPPING = os.sep.join(
+    (os.path.curdir, "tests", "data", "nexus-store", "file-to-resource-mapping.hjson")
+)
 
 
 @pytest.fixture
@@ -52,9 +58,13 @@ def nested_resource():
 
 @pytest.fixture
 def nested_registered_resource(nested_resource):
-    ingredients = [Resource(id=i, type='Ingredient') for i in range(3)]
-    resource = Resource(id="a_recipe", type="Recipe", ingridients=ingredients,
-                        author=Resource(id="a_person", type="Person"))
+    ingredients = [Resource(id=i, type="Ingredient") for i in range(3)]
+    resource = Resource(
+        id="a_recipe",
+        type="Recipe",
+        ingridients=ingredients,
+        author=Resource(id="a_person", type="Person"),
+    )
     do_recursive(add_metadata, resource)
     return resource
 
@@ -64,7 +74,7 @@ def metadata_data_compacted():
     return {
         "_deprecated": False,
         "_updatedBy": "http://integration.kfgorge.test",
-        "_rev": 1
+        "_rev": 1,
     }
 
 
@@ -77,8 +87,11 @@ def store_metadata_value(metadata_data_compacted):
 
 @pytest.fixture
 def registered_building(building, model_context, store_metadata_value):
-    building.context = model_context.iri if model_context.is_http_iri() else model_context.document[
-        "@context"]
+    building.context = (
+        model_context.iri
+        if model_context.is_http_iri()
+        else model_context.document["@context"]
+    )
     if model_context.base:
         building.id = f"{model_context.base}{str(uuid4())}"
     else:
@@ -101,11 +114,15 @@ def registered_person(person, store_metadata_value):
 
 
 @pytest.fixture
-@mock.patch('nexussdk.projects.fetch', return_value=NEXUS_PROJECT_CONTEXT)
-@mock.patch('nexussdk.resources.fetch', side_effect=nexussdk.HTTPError('404'))
+@mock.patch("nexussdk.projects.fetch", return_value=NEXUS_PROJECT_CONTEXT)
+@mock.patch("nexussdk.resources.fetch", side_effect=nexussdk.HTTPError("404"))
 def nexus_store(context_project_patch, metadata_context_patch):
-    return BlueBrainNexus(endpoint=NEXUS, bucket=BUCKET, token=TOKEN,
-                          file_resource_mapping=FILE_RESOURCE_MAPPING)
+    return BlueBrainNexus(
+        endpoint=NEXUS,
+        bucket=BUCKET,
+        token=TOKEN,
+        file_resource_mapping=FILE_RESOURCE_MAPPING,
+    )
 
 
 @pytest.fixture
@@ -149,123 +166,232 @@ def test_to_resource(nexus_store, registered_building, building_jsonld):
 
 
 class TestQuerying:
-
     @pytest.fixture
     def context(self):
         document = {
             "@context": {
-                "@vocab":"http://example.org/vocab/",
+                "@vocab": "http://example.org/vocab/",
                 "contribution": {
                     "@id": "https://neuroshapes.org/contribution",
-                    "@type": "@id"
+                    "@type": "@id",
                 },
-                "agent": {
-                    "@id": "http://www.w3.org/ns/prov#agent",
-                    "@type": "@id"
-                },
-                "type":"rdf:type",
+                "agent": {"@id": "http://www.w3.org/ns/prov#agent", "@type": "@id"},
+                "type": "rdf:type",
                 "Person": "http://schema.org/Person",
                 "address": "http://schema.org/address",
                 "name": "http://schema.org/name",
                 "postalCode": "http://schema.org/postalCode",
                 "streetAddress": "http://schema.org/streetAddress",
                 "deprecated": "https://bluebrain.github.io/nexus/vocabulary/deprecated",
-                "identifier": {
-                   "@type":"@id",
-                   "@id":"http://schema.org/identifier"
-                }
+                "identifier": {"@type": "@id", "@id": "http://schema.org/identifier"},
             }
         }
         return Context(document)
 
-    @pytest.mark.parametrize("filters,expected", [
-        pytest.param((Filter(["agent", "name"], "__eq__", "Allen Institute"),),
-                     (["agent/name ?v0"], ["FILTER(?v0 = \"Allen Institute\")"]),
-                     id="literal"),
-        pytest.param((Filter(["address", "postalCode"], "__lt__", 50070),),
-                     (["address/postalCode ?v0"], ["FILTER(?v0 < 50070)"]),
-                     id="number-lt"),
-        pytest.param((Filter(["address", "postalCode"], "__gt__", 50070),),
-                     (["address/postalCode ?v0"], ["FILTER(?v0 > 50070)"]),
-                     id="number-gt"),
-        pytest.param((Filter(["address", "postalCode"], "__ge__", 50070),),
-                     (["address/postalCode ?v0"], ["FILTER(?v0 >= 50070)"]),
-                     id="number-ge"),
-        pytest.param((Filter(["address", "postalCode"], "__le__", 50070),),
-                     (["address/postalCode ?v0"], ["FILTER(?v0 <= 50070)"]),
-                     id="number-le"),
-        pytest.param((Filter(["deprecated"], "__eq__", False),),
-                     (["deprecated ?v0"], ["FILTER(?v0 = false)"]),
-                     id="boolean-false"),
-        pytest.param((Filter(["deprecated"], "__eq__", True),),
-                     (["deprecated ?v0"], ["FILTER(?v0 = true)"]),
-                     id="boolean-true"),
-        pytest.param((Filter(["type"], "__eq__", "Person"),),
-                     (["type Person"], []),
-                     id="iri-eq"),
-        pytest.param((Filter(["type"], "__ne__", "Person"),),
-                     (["type ?v0"], ["FILTER(?v0 != Person)"]),
-                     id="iri-ne"),
-        pytest.param((Filter(["type"], "__eq__", "https://github.com/BlueBrain/nexus-forge"),),
-                     (["type <https://github.com/BlueBrain/nexus-forge>"], []),
-                     id="filter-by-url"),
-        pytest.param((Filter(["type"], "__ne__", "Person"), Filter(["name"], "__eq__", "toto")),
-                     (["type ?v0", "name ?v1"],
-                      ["FILTER(?v0 != Person)", "FILTER(?v1 = \"toto\")"]),
-                     id="iri-ne-name-eq"),
-        pytest.param((Filter(["type"], "__ne__", "Person"),
-                      Filter(operator='__eq__', path=['affiliation', 'id'],
-                                                                   value='https://www.grid.ac/institutes/grid.5333.6'),
-                      Filter(["identifier"], "__eq__", "http://orcid.org/id")),
-                     (["type ?v0", "affiliation <https://www.grid.ac/institutes/grid.5333.6>",
-                       "identifier <http://orcid.org/id>"],
-                      ["FILTER(?v0 != Person)"]),
-                     id="filter-by-id")
-    ])
+    @pytest.mark.parametrize(
+        "filters,expected",
+        [
+            pytest.param(
+                (Filter(["agent", "name"], "__eq__", "Allen Institute"),),
+                (["agent/name ?v0"], ['FILTER(?v0 = "Allen Institute")']),
+                id="literal",
+            ),
+            pytest.param(
+                (Filter(["address", "postalCode"], "__lt__", 50070),),
+                (["address/postalCode ?v0"], ["FILTER(?v0 < 50070)"]),
+                id="number-lt",
+            ),
+            pytest.param(
+                (Filter(["address", "postalCode"], "__gt__", 50070),),
+                (["address/postalCode ?v0"], ["FILTER(?v0 > 50070)"]),
+                id="number-gt",
+            ),
+            pytest.param(
+                (Filter(["address", "postalCode"], "__ge__", 50070),),
+                (["address/postalCode ?v0"], ["FILTER(?v0 >= 50070)"]),
+                id="number-ge",
+            ),
+            pytest.param(
+                (Filter(["address", "postalCode"], "__le__", 50070),),
+                (["address/postalCode ?v0"], ["FILTER(?v0 <= 50070)"]),
+                id="number-le",
+            ),
+            pytest.param(
+                (Filter(["deprecated"], "__eq__", False),),
+                (["deprecated ?v0"], ["FILTER(?v0 = false)"]),
+                id="boolean-false",
+            ),
+            pytest.param(
+                (Filter(["deprecated"], "__eq__", True),),
+                (["deprecated ?v0"], ["FILTER(?v0 = true)"]),
+                id="boolean-true",
+            ),
+            pytest.param(
+                (Filter(["type"], "__eq__", "Person"),),
+                (["type Person"], []),
+                id="iri-eq",
+            ),
+            pytest.param(
+                (Filter(["type"], "__ne__", "Person"),),
+                (["type ?v0"], ["FILTER(?v0 != Person)"]),
+                id="iri-ne",
+            ),
+            pytest.param(
+                (
+                    Filter(
+                        ["type"], "__eq__", "https://github.com/BlueBrain/nexus-forge"
+                    ),
+                ),
+                (["type <https://github.com/BlueBrain/nexus-forge>"], []),
+                id="filter-by-url",
+            ),
+            pytest.param(
+                (
+                    Filter(["type"], "__ne__", "Person"),
+                    Filter(["name"], "__eq__", "toto"),
+                ),
+                (
+                    ["type ?v0", "name ?v1"],
+                    ["FILTER(?v0 != Person)", 'FILTER(?v1 = "toto")'],
+                ),
+                id="iri-ne-name-eq",
+            ),
+            pytest.param(
+                (
+                    Filter(["type"], "__ne__", "Person"),
+                    Filter(
+                        operator="__eq__",
+                        path=["affiliation", "id"],
+                        value="https://www.grid.ac/institutes/grid.5333.6",
+                    ),
+                    Filter(["identifier"], "__eq__", "http://orcid.org/id"),
+                ),
+                (
+                    [
+                        "type ?v0",
+                        "affiliation <https://www.grid.ac/institutes/grid.5333.6>",
+                        "identifier <http://orcid.org/id>",
+                    ],
+                    ["FILTER(?v0 != Person)"],
+                ),
+                id="filter-by-id",
+            ),
+        ],
+    )
     def test_filter_to_query_statements(self, context, filters, expected):
-        statements = build_query_statements(context, filters)
+        statements = build_sparql_query_statements(context, filters)
         assert statements == expected
 
     def test_create_select_query(self):
         statements = f"?id type <https://github.com/BlueBrain/nexus-forge>"
         query = _create_select_query(statements, distinct=False, search_in_graph=True)
-        assert query == "SELECT ?id ?project WHERE { Graph ?g {?id type <https://github.com/BlueBrain/nexus-forge>}}"
+        assert (
+            query
+            == "SELECT ?id ?project WHERE { Graph ?g {?id type <https://github.com/BlueBrain/nexus-forge>}}"
+        )
         query = _create_select_query(statements, distinct=True, search_in_graph=True)
-        assert query == "SELECT DISTINCT ?id ?project WHERE { Graph ?g {?id type <https://github.com/BlueBrain/nexus-forge>}}"
+        assert (
+            query
+            == "SELECT DISTINCT ?id ?project WHERE { Graph ?g {?id type <https://github.com/BlueBrain/nexus-forge>}}"
+        )
         query = _create_select_query(statements, distinct=False, search_in_graph=False)
-        assert query == "SELECT ?id ?project WHERE {?id type <https://github.com/BlueBrain/nexus-forge>}"
+        assert (
+            query
+            == "SELECT ?id ?project WHERE {?id type <https://github.com/BlueBrain/nexus-forge>}"
+        )
         query = _create_select_query(statements, distinct=True, search_in_graph=False)
-        assert query == "SELECT DISTINCT ?id ?project WHERE {?id type <https://github.com/BlueBrain/nexus-forge>}"
-
-    @pytest.mark.parametrize("filters,expected", [
-        pytest.param(({"type":"Person"}),
-                     ([Filter(operator='__eq__', path=['type'], value='Person')]),
-                     id="json_filter_type"),
-        pytest.param(({"type": "Contribution", "agent":{"name":"John Doe", "affiliation":{"type":"Organization", "name":"EPFL"}},
-                       "hadRole":{"label":"PI"}, "description":"A description"}),
-                     ([Filter(operator='__eq__', path=['type'], value='Contribution'),
-                       Filter(operator='__eq__', path=['agent','name'], value='John Doe'),
-                       Filter(operator='__eq__', path=['agent', 'affiliation','type'], value='Organization'),
-                       Filter(operator='__eq__', path=['agent', 'affiliation','name'], value='EPFL'),
-                       Filter(operator='__eq__', path=['hadRole', 'label'], value='PI'),
-                       Filter(operator='__eq__', path=['description'], value='A description')]),
-                     id="nested_json_filter_type"),
-        pytest.param(({"type": "Person", "affiliation":{"type":"Organization", "id":"https://www.grid.ac/institutes/grid.5333.6"}}),
-                     ([Filter(operator='__eq__', path=['type'], value='Person'),
-                       Filter(operator='__eq__', path=['affiliation', 'type'], value='Organization'),
-                       Filter(operator='__eq__', path=['affiliation', 'id'], value='https://www.grid.ac/institutes/grid.5333.6')]),
-                     id="json_filter_id")
-
-    ])
+        assert (
+            query
+            == "SELECT DISTINCT ?id ?project WHERE {?id type <https://github.com/BlueBrain/nexus-forge>}"
+        )
+
+    @pytest.mark.parametrize(
+        "filters,expected",
+        [
+            pytest.param(
+                ({"type": "Person"}),
+                ([Filter(operator="__eq__", path=["type"], value="Person")]),
+                id="json_filter_type",
+            ),
+            pytest.param(
+                (
+                    {
+                        "type": "Contribution",
+                        "agent": {
+                            "name": "John Doe",
+                            "affiliation": {"type": "Organization", "name": "EPFL"},
+                        },
+                        "hadRole": {"label": "PI"},
+                        "description": "A description",
+                    }
+                ),
+                (
+                    [
+                        Filter(operator="__eq__", path=["type"], value="Contribution"),
+                        Filter(
+                            operator="__eq__", path=["agent", "name"], value="John Doe"
+                        ),
+                        Filter(
+                            operator="__eq__",
+                            path=["agent", "affiliation", "type"],
+                            value="Organization",
+                        ),
+                        Filter(
+                            operator="__eq__",
+                            path=["agent", "affiliation", "name"],
+                            value="EPFL",
+                        ),
+                        Filter(
+                            operator="__eq__", path=["hadRole", "label"], value="PI"
+                        ),
+                        Filter(
+                            operator="__eq__",
+                            path=["description"],
+                            value="A description",
+                        ),
+                    ]
+                ),
+                id="nested_json_filter_type",
+            ),
+            pytest.param(
+                (
+                    {
+                        "type": "Person",
+                        "affiliation": {
+                            "type": "Organization",
+                            "id": "https://www.grid.ac/institutes/grid.5333.6",
+                        },
+                    }
+                ),
+                (
+                    [
+                        Filter(operator="__eq__", path=["type"], value="Person"),
+                        Filter(
+                            operator="__eq__",
+                            path=["affiliation", "type"],
+                            value="Organization",
+                        ),
+                        Filter(
+                            operator="__eq__",
+                            path=["affiliation", "id"],
+                            value="https://www.grid.ac/institutes/grid.5333.6",
+                        ),
+                    ]
+                ),
+                id="json_filter_id",
+            ),
+        ],
+    )
     def test_dict_to_filters(self, filters, expected):
         filters_from_dict = create_filters_from_dict(filters)
         assert filters_from_dict == expected
 
+
 # Helpers
 
 
 def assert_frozen_id(resource: Resource):
-    assert resource.id.endswith('?rev=' + str(resource._store_metadata['_rev']))
+    assert resource.id.endswith("?rev=" + str(resource._store_metadata["_rev"]))
 
 
 def add_metadata(resource: Resource):
@@ -280,7 +406,7 @@ def add_metadata(resource: Resource):
         "_updatedAt": "2019-03-28T13:40:38.934Z",
         "_updatedBy": "https://nexus/u1",
         "_incoming": "https:/nexus/incoming",
-        "_outgoing": "https://nexux/outgoing"
+        "_outgoing": "https://nexux/outgoing",
     }
     resource._synchronized = True
     resource._validated = True