diff --git a/CHANGELOG.md b/CHANGELOG.md
index afa13ec..37a28f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
 
 ## Master
 
+* [#49](https://github.com/contentful-labs/contentful.ex/pull/49) Adds extended query syntax for building more complex queries, as suggested by @ryansch in [#38](https://github.com/contentful-labs/contentful.ex/issues/38)
+* adds testgin against Elixir 1.10.4
+
 ## 0.3.2
 
 * [#47](https://github.com/contentful-labs/contentful.ex/pull/47) Handle bitstring case when resolving assets (thanks @aspala)
diff --git a/fixture/vcr_cassettes/multiple_assets,_filtered_by_name,_negated.json b/fixture/vcr_cassettes/multiple_assets,_filtered_by_name,_negated.json
new file mode 100644
index 0000000..5619788
--- /dev/null
+++ b/fixture/vcr_cassettes/multiple_assets,_filtered_by_name,_negated.json
@@ -0,0 +1,50 @@
+[
+  {
+    "request": {
+      "body": "",
+      "headers": {
+        "authorization": "***",
+        "User-Agent": "Contentful Elixir SDK",
+        "accept": "application/json"
+      },
+      "method": "get",
+      "options": [],
+      "request_body": "",
+      "url": "https://cdn.contentful.com/spaces/bmehzfuz4raf/environments/master/assets?fields.title%5Bne%5D=bafoo"
+    },
+    "response": {
+      "binary": false,
+      "body": "{\n  \"sys\": {\n    \"type\": \"Array\"\n  },\n  \"total\": 1,\n  \"skip\": 0,\n  \"limit\": 100,\n  \"items\": [\n    {\n      \"sys\": {\n        \"space\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Space\",\n            \"id\": \"bmehzfuz4raf\"\n          }\n        },\n        \"id\": \"5ECf6ltDUOnX441PtBR8Wk\",\n        \"type\": \"Asset\",\n        \"createdAt\": \"2020-03-16T10:06:11.604Z\",\n        \"updatedAt\": \"2020-06-10T12:59:00.879Z\",\n        \"environment\": {\n          \"sys\": {\n            \"id\": \"master\",\n            \"type\": \"Link\",\n            \"linkType\": \"Environment\"\n          }\n        },\n        \"revision\": 3,\n        \"locale\": \"en-US\"\n      },\n      \"fields\": {\n        \"file\": {\n          \"url\": \"//images.ctfassets.net/bmehzfuz4raf/5ECf6ltDUOnX441PtBR8Wk/fa3dc5cde2b4d4c5736566c956f20163/Screenshot_from_2020-03-11_22-56-44.png\",\n          \"details\": {\n            \"size\": 247990,\n            \"image\": {\n              \"width\": 1920,\n              \"height\": 1053\n            }\n          },\n          \"fileName\": \"Screenshot from 2020-03-11 22-56-44.png\",\n          \"contentType\": \"image/png\"\n        }\n      }\n    }\n  ]\n}\n",
+      "headers": {
+        "Connection": "keep-alive",
+        "Content-Length": "1131",
+        "Access-Control-Allow-Headers": "Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature",
+        "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS",
+        "Access-Control-Allow-Origin": "*",
+        "Access-Control-Expose-Headers": "Etag",
+        "Access-Control-Max-Age": "86400",
+        "CF-Environment-Id": "master",
+        "CF-Environment-Uuid": "69b0fe47-9d71-4e0c-a7e0-4e12cb573930",
+        "CF-Organization-Id": "0bEbsBel9WL3OIaiqx0MnO",
+        "CF-Space-Id": "bmehzfuz4raf",
+        "Content-Type": "application/vnd.contentful.delivery.v1+json",
+        "Contentful-Api": "cda_cached",
+        "ETag": "W/\"6071154412135978374\"",
+        "Server": "Contentful",
+        "X-Content-Type-Options": "nosniff",
+        "X-Contentful-Region": "us-east-1",
+        "Accept-Ranges": "bytes",
+        "Date": "Sun, 05 Jul 2020 14:18:56 GMT",
+        "Via": "1.1 varnish",
+        "Age": "0",
+        "X-Served-By": "cache-hhn4034-HHN",
+        "X-Cache": "MISS",
+        "X-Cache-Hits": "0",
+        "Vary": "Accept-Encoding",
+        "x-contentful-request-id": "591a465a-dc43-4014-a5bb-dca6d905fcdb"
+      },
+      "status_code": 200,
+      "type": "ok"
+    }
+  }
+]
\ No newline at end of file
diff --git a/fixture/vcr_cassettes/multiple_assets,_filtered_by_name.json b/fixture/vcr_cassettes/multiple_assets,_filtered_by_name.json
new file mode 100644
index 0000000..39bb9c7
--- /dev/null
+++ b/fixture/vcr_cassettes/multiple_assets,_filtered_by_name.json
@@ -0,0 +1,50 @@
+[
+  {
+    "request": {
+      "body": "",
+      "headers": {
+        "authorization": "***",
+        "User-Agent": "Contentful Elixir SDK",
+        "accept": "application/json"
+      },
+      "method": "get",
+      "options": [],
+      "request_body": "",
+      "url": "https://cdn.contentful.com/spaces/bmehzfuz4raf/environments/master/assets?fields.title=bafoo"
+    },
+    "response": {
+      "binary": false,
+      "body": "{\n  \"sys\": {\n    \"type\": \"Array\"\n  },\n  \"total\": 1,\n  \"skip\": 0,\n  \"limit\": 100,\n  \"items\": [\n    {\n      \"sys\": {\n        \"space\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Space\",\n            \"id\": \"bmehzfuz4raf\"\n          }\n        },\n        \"id\": \"577fpmbIfYD71VCjCpYA84\",\n        \"type\": \"Asset\",\n        \"createdAt\": \"2020-03-16T10:30:45.706Z\",\n        \"updatedAt\": \"2020-03-16T10:30:45.706Z\",\n        \"environment\": {\n          \"sys\": {\n            \"id\": \"master\",\n            \"type\": \"Link\",\n            \"linkType\": \"Environment\"\n          }\n        },\n        \"revision\": 1,\n        \"locale\": \"en-US\"\n      },\n      \"fields\": {\n        \"title\": \"bafoo\",\n        \"description\": \"a pdf\",\n        \"file\": {\n          \"url\": \"//assets.ctfassets.net/bmehzfuz4raf/577fpmbIfYD71VCjCpYA84/6456d4157900af3b35537f5f15dd97c2/Travis_Picking_-_The_LEGENDARY_picking_pattern.pdf\",\n          \"details\": {\n            \"size\": 90783\n          },\n          \"fileName\": \"Travis Picking - The LEGENDARY picking pattern.pdf\",\n          \"contentType\": \"application/pdf\"\n        }\n      }\n    }\n  ]\n}\n",
+      "headers": {
+        "Connection": "keep-alive",
+        "Content-Length": "1120",
+        "Access-Control-Allow-Headers": "Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature",
+        "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS",
+        "Access-Control-Allow-Origin": "*",
+        "Access-Control-Expose-Headers": "Etag",
+        "Access-Control-Max-Age": "86400",
+        "CF-Environment-Id": "master",
+        "CF-Environment-Uuid": "69b0fe47-9d71-4e0c-a7e0-4e12cb573930",
+        "CF-Organization-Id": "0bEbsBel9WL3OIaiqx0MnO",
+        "CF-Space-Id": "bmehzfuz4raf",
+        "Content-Type": "application/vnd.contentful.delivery.v1+json",
+        "Contentful-Api": "cda_cached",
+        "ETag": "W/\"14856425138171294666\"",
+        "Server": "Contentful",
+        "X-Content-Type-Options": "nosniff",
+        "X-Contentful-Region": "us-east-1",
+        "Accept-Ranges": "bytes",
+        "Date": "Sun, 05 Jul 2020 14:17:51 GMT",
+        "Via": "1.1 varnish",
+        "Age": "0",
+        "X-Served-By": "cache-fra19140-FRA",
+        "X-Cache": "MISS",
+        "X-Cache-Hits": "0",
+        "Vary": "Accept-Encoding",
+        "x-contentful-request-id": "f2ae57c3-a489-4c9a-a31a-6dbaa55c3d06"
+      },
+      "status_code": 200,
+      "type": "ok"
+    }
+  }
+]
\ No newline at end of file
diff --git a/fixture/vcr_cassettes/multiple_entries,_filtered_by_content_type.json b/fixture/vcr_cassettes/multiple_entries,_filtered_by_content_type.json
new file mode 100644
index 0000000..f241bce
--- /dev/null
+++ b/fixture/vcr_cassettes/multiple_entries,_filtered_by_content_type.json
@@ -0,0 +1,50 @@
+[
+  {
+    "request": {
+      "body": "",
+      "headers": {
+        "authorization": "***",
+        "User-Agent": "Contentful Elixir SDK",
+        "accept": "application/json"
+      },
+      "method": "get",
+      "options": [],
+      "request_body": "",
+      "url": "https://cdn.contentful.com/spaces/bmehzfuz4raf/environments/master/entries?content_type=category"
+    },
+    "response": {
+      "binary": false,
+      "body": "{\n  \"sys\": {\n    \"type\": \"Array\"\n  },\n  \"total\": 2,\n  \"skip\": 0,\n  \"limit\": 100,\n  \"items\": [\n    {\n      \"sys\": {\n        \"space\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Space\",\n            \"id\": \"bmehzfuz4raf\"\n          }\n        },\n        \"id\": \"7qCGg4LadgJUcx5cr35Ou9\",\n        \"type\": \"Entry\",\n        \"createdAt\": \"2020-07-05T09:44:36.704Z\",\n        \"updatedAt\": \"2020-07-05T09:44:36.704Z\",\n        \"environment\": {\n          \"sys\": {\n            \"id\": \"master\",\n            \"type\": \"Link\",\n            \"linkType\": \"Environment\"\n          }\n        },\n        \"revision\": 1,\n        \"contentType\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"ContentType\",\n            \"id\": \"category\"\n          }\n        },\n        \"locale\": \"en-US\"\n      },\n      \"fields\": {}\n    },\n    {\n      \"sys\": {\n        \"space\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Space\",\n            \"id\": \"bmehzfuz4raf\"\n          }\n        },\n        \"id\": \"4RPjazUzQMqemyNlcD3b9i\",\n        \"type\": \"Entry\",\n        \"createdAt\": \"2020-03-17T15:22:17.537Z\",\n        \"updatedAt\": \"2020-04-30T08:18:45.388Z\",\n        \"environment\": {\n          \"sys\": {\n            \"id\": \"master\",\n            \"type\": \"Link\",\n            \"linkType\": \"Environment\"\n          }\n        },\n        \"revision\": 2,\n        \"contentType\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"ContentType\",\n            \"id\": \"category\"\n          }\n        },\n        \"locale\": \"en-US\"\n      },\n      \"fields\": {\n        \"name\": \"A standard category\",\n        \"image\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Asset\",\n            \"id\": \"5ECf6ltDUOnX441PtBR8Wk\"\n          }\n        }\n      }\n    }\n  ],\n  \"includes\": {\n    \"Asset\": [\n      {\n        \"sys\": {\n          \"space\": {\n            \"sys\": {\n              \"type\": \"Link\",\n              \"linkType\": \"Space\",\n              \"id\": \"bmehzfuz4raf\"\n            }\n          },\n          \"id\": \"5ECf6ltDUOnX441PtBR8Wk\",\n          \"type\": \"Asset\",\n          \"createdAt\": \"2020-03-16T10:06:11.604Z\",\n          \"updatedAt\": \"2020-06-10T12:59:00.879Z\",\n          \"environment\": {\n            \"sys\": {\n              \"id\": \"master\",\n              \"type\": \"Link\",\n              \"linkType\": \"Environment\"\n            }\n          },\n          \"revision\": 3,\n          \"locale\": \"en-US\"\n        },\n        \"fields\": {\n          \"file\": {\n            \"url\": \"//images.ctfassets.net/bmehzfuz4raf/5ECf6ltDUOnX441PtBR8Wk/fa3dc5cde2b4d4c5736566c956f20163/Screenshot_from_2020-03-11_22-56-44.png\",\n            \"details\": {\n              \"size\": 247990,\n              \"image\": {\n                \"width\": 1920,\n                \"height\": 1053\n              }\n            },\n            \"fileName\": \"Screenshot from 2020-03-11 22-56-44.png\",\n            \"contentType\": \"image/png\"\n          }\n        }\n      }\n    ]\n  }\n}\n",
+      "headers": {
+        "Connection": "keep-alive",
+        "Content-Length": "2952",
+        "Access-Control-Allow-Headers": "Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature",
+        "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS",
+        "Access-Control-Allow-Origin": "*",
+        "Access-Control-Expose-Headers": "Etag",
+        "Access-Control-Max-Age": "86400",
+        "CF-Environment-Id": "master",
+        "CF-Environment-Uuid": "69b0fe47-9d71-4e0c-a7e0-4e12cb573930",
+        "CF-Organization-Id": "0bEbsBel9WL3OIaiqx0MnO",
+        "CF-Space-Id": "bmehzfuz4raf",
+        "Content-Type": "application/vnd.contentful.delivery.v1+json",
+        "Contentful-Api": "cda_cached",
+        "ETag": "W/\"17446375927234312903\"",
+        "Server": "Contentful",
+        "X-Content-Type-Options": "nosniff",
+        "X-Contentful-Region": "us-east-1",
+        "Accept-Ranges": "bytes",
+        "Date": "Sun, 05 Jul 2020 10:15:36 GMT",
+        "Via": "1.1 varnish",
+        "Age": "1137",
+        "X-Served-By": "cache-hhn4067-HHN",
+        "X-Cache": "HIT",
+        "X-Cache-Hits": "2",
+        "Vary": "Accept-Encoding",
+        "x-contentful-request-id": "f23efb99-72c1-4342-8dcb-991550eee851"
+      },
+      "status_code": 200,
+      "type": "ok"
+    }
+  }
+]
\ No newline at end of file
diff --git a/fixture/vcr_cassettes/single_entry_with_select_filters.json b/fixture/vcr_cassettes/single_entry_with_select_filters.json
new file mode 100644
index 0000000..ebc97a4
--- /dev/null
+++ b/fixture/vcr_cassettes/single_entry_with_select_filters.json
@@ -0,0 +1,50 @@
+[
+  {
+    "request": {
+      "body": "",
+      "headers": {
+        "authorization": "***",
+        "User-Agent": "Contentful Elixir SDK",
+        "accept": "application/json"
+      },
+      "method": "get",
+      "options": [],
+      "request_body": "",
+      "url": "https://cdn.contentful.com/spaces/bmehzfuz4raf/environments/master/entries?content_type=category&sys.id=7qCGg4LadgJUcx5cr35Ou9"
+    },
+    "response": {
+      "binary": false,
+      "body": "{\n  \"sys\": {\n    \"type\": \"Array\"\n  },\n  \"total\": 1,\n  \"skip\": 0,\n  \"limit\": 100,\n  \"items\": [\n    {\n      \"sys\": {\n        \"space\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Space\",\n            \"id\": \"bmehzfuz4raf\"\n          }\n        },\n        \"id\": \"7qCGg4LadgJUcx5cr35Ou9\",\n        \"type\": \"Entry\",\n        \"createdAt\": \"2020-07-05T09:44:36.704Z\",\n        \"updatedAt\": \"2020-07-05T09:44:36.704Z\",\n        \"environment\": {\n          \"sys\": {\n            \"id\": \"master\",\n            \"type\": \"Link\",\n            \"linkType\": \"Environment\"\n          }\n        },\n        \"revision\": 1,\n        \"contentType\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"ContentType\",\n            \"id\": \"category\"\n          }\n        },\n        \"locale\": \"en-US\"\n      },\n      \"fields\": {}\n    }\n  ]\n}\n",
+      "headers": {
+        "Connection": "keep-alive",
+        "Content-Length": "846",
+        "Access-Control-Allow-Headers": "Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature",
+        "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS",
+        "Access-Control-Allow-Origin": "*",
+        "Access-Control-Expose-Headers": "Etag",
+        "Access-Control-Max-Age": "86400",
+        "CF-Environment-Id": "master",
+        "CF-Environment-Uuid": "69b0fe47-9d71-4e0c-a7e0-4e12cb573930",
+        "CF-Organization-Id": "0bEbsBel9WL3OIaiqx0MnO",
+        "CF-Space-Id": "bmehzfuz4raf",
+        "Content-Type": "application/vnd.contentful.delivery.v1+json",
+        "Contentful-Api": "cda_cached",
+        "ETag": "\"5660330800706425331\"",
+        "Server": "Contentful",
+        "X-Content-Type-Options": "nosniff",
+        "X-Contentful-Region": "us-east-1",
+        "Accept-Ranges": "bytes",
+        "Date": "Sun, 05 Jul 2020 10:49:58 GMT",
+        "Via": "1.1 varnish",
+        "Age": "0",
+        "X-Served-By": "cache-fra19145-FRA",
+        "X-Cache": "MISS",
+        "X-Cache-Hits": "0",
+        "Vary": "Accept-Encoding",
+        "x-contentful-request-id": "3d69ef80-daf0-4be8-bbf5-9bf0b0f7929e"
+      },
+      "status_code": 200,
+      "type": "ok"
+    }
+  }
+]
\ No newline at end of file
diff --git a/fixture/vcr_cassettes/some_entries_not_having_an_id.json b/fixture/vcr_cassettes/some_entries_not_having_an_id.json
new file mode 100644
index 0000000..3cc995c
--- /dev/null
+++ b/fixture/vcr_cassettes/some_entries_not_having_an_id.json
@@ -0,0 +1,50 @@
+[
+  {
+    "request": {
+      "body": "",
+      "headers": {
+        "authorization": "***",
+        "User-Agent": "Contentful Elixir SDK",
+        "accept": "application/json"
+      },
+      "method": "get",
+      "options": [],
+      "request_body": "",
+      "url": "https://cdn.contentful.com/spaces/bmehzfuz4raf/environments/master/entries?content_type=category&sys.id%5Bne%5D=7qCGg4LadgJUcx5cr35Ou9"
+    },
+    "response": {
+      "binary": false,
+      "body": "{\n  \"sys\": {\n    \"type\": \"Array\"\n  },\n  \"total\": 1,\n  \"skip\": 0,\n  \"limit\": 100,\n  \"items\": [\n    {\n      \"sys\": {\n        \"space\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Space\",\n            \"id\": \"bmehzfuz4raf\"\n          }\n        },\n        \"id\": \"4RPjazUzQMqemyNlcD3b9i\",\n        \"type\": \"Entry\",\n        \"createdAt\": \"2020-03-17T15:22:17.537Z\",\n        \"updatedAt\": \"2020-04-30T08:18:45.388Z\",\n        \"environment\": {\n          \"sys\": {\n            \"id\": \"master\",\n            \"type\": \"Link\",\n            \"linkType\": \"Environment\"\n          }\n        },\n        \"revision\": 2,\n        \"contentType\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"ContentType\",\n            \"id\": \"category\"\n          }\n        },\n        \"locale\": \"en-US\"\n      },\n      \"fields\": {\n        \"name\": \"A standard category\",\n        \"image\": {\n          \"sys\": {\n            \"type\": \"Link\",\n            \"linkType\": \"Asset\",\n            \"id\": \"5ECf6ltDUOnX441PtBR8Wk\"\n          }\n        }\n      }\n    }\n  ],\n  \"includes\": {\n    \"Asset\": [\n      {\n        \"sys\": {\n          \"space\": {\n            \"sys\": {\n              \"type\": \"Link\",\n              \"linkType\": \"Space\",\n              \"id\": \"bmehzfuz4raf\"\n            }\n          },\n          \"id\": \"5ECf6ltDUOnX441PtBR8Wk\",\n          \"type\": \"Asset\",\n          \"createdAt\": \"2020-03-16T10:06:11.604Z\",\n          \"updatedAt\": \"2020-06-10T12:59:00.879Z\",\n          \"environment\": {\n            \"sys\": {\n              \"id\": \"master\",\n              \"type\": \"Link\",\n              \"linkType\": \"Environment\"\n            }\n          },\n          \"revision\": 3,\n          \"locale\": \"en-US\"\n        },\n        \"fields\": {\n          \"file\": {\n            \"url\": \"//images.ctfassets.net/bmehzfuz4raf/5ECf6ltDUOnX441PtBR8Wk/fa3dc5cde2b4d4c5736566c956f20163/Screenshot_from_2020-03-11_22-56-44.png\",\n            \"details\": {\n              \"size\": 247990,\n              \"image\": {\n                \"width\": 1920,\n                \"height\": 1053\n              }\n            },\n            \"fileName\": \"Screenshot from 2020-03-11 22-56-44.png\",\n            \"contentType\": \"image/png\"\n          }\n        }\n      }\n    ]\n  }\n}\n",
+      "headers": {
+        "Connection": "keep-alive",
+        "Content-Length": "2205",
+        "Access-Control-Allow-Headers": "Accept,Accept-Language,Authorization,Cache-Control,Content-Length,Content-Range,Content-Type,DNT,Destination,Expires,If-Match,If-Modified-Since,If-None-Match,Keep-Alive,Last-Modified,Origin,Pragma,Range,User-Agent,X-Http-Method-Override,X-Mx-ReqToken,X-Requested-With,X-Contentful-Version,X-Contentful-Content-Type,X-Contentful-Organization,X-Contentful-Skip-Transformation,X-Contentful-User-Agent,X-Contentful-Enable-Alpha-Feature",
+        "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS",
+        "Access-Control-Allow-Origin": "*",
+        "Access-Control-Expose-Headers": "Etag",
+        "Access-Control-Max-Age": "86400",
+        "CF-Environment-Id": "master",
+        "CF-Environment-Uuid": "69b0fe47-9d71-4e0c-a7e0-4e12cb573930",
+        "CF-Organization-Id": "0bEbsBel9WL3OIaiqx0MnO",
+        "CF-Space-Id": "bmehzfuz4raf",
+        "Content-Type": "application/vnd.contentful.delivery.v1+json",
+        "Contentful-Api": "cda_cached",
+        "ETag": "W/\"144981087548954756\"",
+        "Server": "Contentful",
+        "X-Content-Type-Options": "nosniff",
+        "X-Contentful-Region": "us-east-1",
+        "Accept-Ranges": "bytes",
+        "Date": "Sun, 05 Jul 2020 11:44:23 GMT",
+        "Via": "1.1 varnish",
+        "Age": "0",
+        "X-Served-By": "cache-fra19143-FRA",
+        "X-Cache": "MISS",
+        "X-Cache-Hits": "0",
+        "Vary": "Accept-Encoding",
+        "x-contentful-request-id": "4872994b-841a-4870-b38e-f97140ec1281"
+      },
+      "status_code": 200,
+      "type": "ok"
+    }
+  }
+]
\ No newline at end of file
diff --git a/lib/contentful/configuration.ex b/lib/contentful/configuration.ex
new file mode 100644
index 0000000..7ab4ed0
--- /dev/null
+++ b/lib/contentful/configuration.ex
@@ -0,0 +1,14 @@
+defmodule Contentful.Configuration do
+  @moduledoc """
+  Provides Configuration for the different Contentful modules, providing easier access to the values
+  in config/*.exs
+  """
+
+  def get(key, context \\ :delivery) do
+    context |> config() |> Keyword.get(key)
+  end
+
+  defp config(context) do
+    Application.get_env(:contentful, context, [])
+  end
+end
diff --git a/lib/contentful/misc.ex b/lib/contentful/misc.ex
new file mode 100644
index 0000000..774f152
--- /dev/null
+++ b/lib/contentful/misc.ex
@@ -0,0 +1,26 @@
+defmodule Contentful.Misc do
+  @moduledoc """
+  Provides some functions that fit nowhere else and can be shared freely.
+  """
+
+  @doc """
+  provides a generic fallback method in case a returned value is `nil`.
+
+  Inspired by [@expede](https://github.com/expede) and her talk at Code BEAM V 2020.
+
+  Usable in conjunction with other getters to enable function chains:
+
+  ## Example
+
+      1 = [a: 1, b: 2, c: 3] |> Keyword.get(:a)
+      4 = [a: 1, b: 2, c: 3] |> Keyword.get(:d) |> fallback(4)
+  """
+  @spec fallback(nil, any()) :: any()
+  def fallback(nil, value) do
+    value
+  end
+
+  def fallback(value, _) do
+    value
+  end
+end
diff --git a/lib/contentful/query.ex b/lib/contentful/query.ex
index 306d994..4551201 100644
--- a/lib/contentful/query.ex
+++ b/lib/contentful/query.ex
@@ -26,13 +26,18 @@ defmodule Contentful.Query do
   ```
 
   """
+  alias Contentful.Configuration
   alias Contentful.ContentType
   alias Contentful.Delivery
+  alias Contentful.Delivery.Assets
   alias Contentful.Delivery.Entries
   alias Contentful.Delivery.Spaces
+  alias Contentful.Request
   alias Contentful.Space
   alias Contentful.SysData
 
+  @allowed_filter_modifiers [:all, :in, :nin, :ne, :lte, :gte, :lt, :gt, :match, :exists]
+
   @doc """
   adds the `include` parameter to a query.
 
@@ -165,9 +170,9 @@ defmodule Contentful.Query do
           | {:error, atom(), original_message: String.t()}
   def fetch_all(
         queryable,
-        space \\ Delivery.config(:space_id),
-        env \\ Delivery.config(:environment),
-        api_key \\ Delivery.config(:access_token)
+        space \\ Configuration.get(:space_id),
+        env \\ Configuration.get(:environment),
+        api_key \\ Configuration.get(:access_token)
       )
 
   def fetch_all({Spaces, _}, _, _, _) do
@@ -185,13 +190,19 @@ defmodule Contentful.Query do
         env,
         api_key
       ) do
-    url = [
-      space |> Delivery.url(env),
-      queryable.endpoint(),
-      parameters |> Delivery.collection_query_params()
-    ]
+    params = parameters |> Request.collection_query_params()
 
-    {url, api_key |> Delivery.request_headers()}
+    url =
+      [
+        space |> Delivery.url(env),
+        queryable.endpoint()
+      ]
+      |> Enum.join()
+      |> URI.parse()
+      |> add_query_params(params)
+      |> to_string()
+
+    {url, api_key |> Request.headers()}
     |> Delivery.send_request()
     |> Delivery.parse_response(&queryable.resolve_collection_response/1)
   end
@@ -220,9 +231,9 @@ defmodule Contentful.Query do
   def fetch_one(
         queryable,
         id \\ nil,
-        space \\ Delivery.config(:space_id),
-        env \\ Delivery.config(:environment),
-        api_key \\ Delivery.config(:access_token)
+        space \\ Configuration.get(:space_id),
+        env \\ Configuration.get(:environment),
+        api_key \\ Configuration.get(:access_token)
       )
 
   def fetch_one(queryable, id, %Space{sys: %SysData{id: space_id}}, env, api_key) do
@@ -266,11 +277,134 @@ defmodule Contentful.Query do
           queryable
       end
 
-    {url, api_key |> Delivery.request_headers()}
+    {url, api_key |> Request.headers()}
     |> Delivery.send_request()
     |> Delivery.parse_response(&queryable.resolve_entity_response/1)
   end
 
+  @doc """
+  Adds a filter condition to the query.
+
+  This will work for Entries *requiring* a call to `content_type` before:
+
+  ## Example
+
+      import Contentful.Query
+      alias Contentful.Delivery.Entries
+
+      {:ok, entries, total: 1}
+      = Entries
+        |> content_type("dogs")
+        |> by(name: "Hasso", breed: "dalmatian")
+        |> fetch_all
+
+  This will also allow for more complex queries using modifiers:
+
+  ## Example
+
+      import Contentful.Query
+      alias Contentful.Delivery.Entries
+
+      {:ok, entries, total: 100}
+      = Entries
+        |> content_type("dogs")
+        |> by(name: [ne: "Hasso"], breed: "dalmatian")
+        |> fetch_all
+
+  Allowed modifiers are `[:in, :nin, :ne, :lte, :gte, :lt, :gt, :match, :exist]`. See the
+  [official docs](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/equality-operator)
+  for adding search parameters this way.
+
+  Working with `Contentful.Delivery.Assets` requires no `content_type` call:
+
+  ## Example
+
+      import Contentful.Query
+      alias Contentful.Delivery.Assets
+
+      {:ok, assets, total: 1} = Assets |> by(id: "foobar") |> fetch_all
+
+  Calling `by/2` allows for adding multiple conditions to the query:
+
+  ## Example
+
+      import Contentful.Query
+      alias Contentful.Delivery.Assets
+
+      {:ok, assets, total: 200}
+      = Assets
+        |> by(tags: [nin: "maps"])
+        |> fetch_all
+
+  """
+  @spec by(tuple(), list()) :: tuple()
+  def by({Entries, parameters}, new_select_params) do
+    select_params = parameters |> Keyword.take([:select_params])
+
+    content_type_present? = parameters |> Keyword.take([:content_type]) |> length() > 0
+
+    unless content_type_present? do
+      raise %ArgumentError{
+        message: """
+        Filtering for entries requires a content_type, example:
+
+            Entries |> content_type("cats") |> by(name: "Gretchen")
+        """
+      }
+    end
+
+    {Entries,
+     parameters |> Keyword.put(:select_params, select_params |> Keyword.merge(new_select_params))}
+  end
+
+  def by({Assets, parameters}, new_select_params) do
+    select_params = parameters |> Keyword.take([:select_params])
+
+    {Assets,
+     parameters |> Keyword.put(:select_params, select_params |> Keyword.merge(new_select_params))}
+  end
+
+  def by(Entries, select_params) do
+    by({Entries, []}, select_params)
+  end
+
+  def by(Assets, select_params) do
+    by({Assets, []}, select_params)
+  end
+
+  def by(queryable, _select_params) do
+    queryable
+  end
+
+  @doc """
+  allows for full text search over all entries fields. The original nomenclature fromthe API docs is `query`.
+
+  This has been renamed for clarity here.
+
+  ## Example
+
+      import Contentful.Query
+      {Entries, [query: "Nyancat"]} = Entries |> search_full_text("Nyancat")
+
+      # or, with full `fetch_all`
+      {:ok, nyan_cats, total: 616} =
+        Entries
+        |> search_full_text("Nyancat")
+        |> fetch_all
+  """
+  @spec search_full_text(tuple(), term()) :: tuple()
+  def search_full_text({Entries, parameters}, term) do
+    {Entries, parameters |> Keyword.put(:query, term)}
+  end
+
+  def search_full_text(Entries, term) do
+    search_full_text({Entries, []}, term)
+  end
+
+  def search_full_text(queryable, _term) do
+    queryable
+  end
+
   @doc """
   will __resolve__ a query chain by constructing a `Stream.resource` around a possible API response
   allowing for lazy evaluation of queries. Cann be helpful with translating collection calls of
@@ -290,16 +424,16 @@ defmodule Contentful.Query do
       # 10 times total.
       Assets |> limit(100) |> Enum.take(1000)
 
-      # will not work with Spaces, though, as they
+      # will not work with Spaces, though, as they is no collection endpoint
 
   """
   @spec stream(tuple(), String.t(), String.t(), String.t()) ::
           Enumerable.t()
   def stream(
         queryable,
-        space \\ Delivery.config(:space_id),
-        env \\ Delivery.config(:environment),
-        api_key \\ Delivery.config(:access_token)
+        space \\ Configuration.get(:space_id),
+        env \\ Configuration.get(:environment),
+        api_key \\ Configuration.get(:access_token)
       )
 
   def stream(Spaces, _space, _env, _api_key) do
@@ -309,4 +443,16 @@ defmodule Contentful.Query do
   def stream(args, space, env, api_key) do
     Contentful.Stream.stream(args, space, env, api_key)
   end
+
+  def allowed_filter_modifiers do
+    @allowed_filter_modifiers
+  end
+
+  defp add_query_params(uri, []) do
+    uri
+  end
+
+  defp add_query_params(%URI{} = uri, params) do
+    uri |> Map.put(:query, URI.encode_query(params))
+  end
 end
diff --git a/lib/contentful/request.ex b/lib/contentful/request.ex
new file mode 100644
index 0000000..d9f5f39
--- /dev/null
+++ b/lib/contentful/request.ex
@@ -0,0 +1,127 @@
+defmodule Contentful.Request do
+  @moduledoc """
+  Encapsulates functions invloved in making a request towards the Contentful APIs.
+  """
+  alias Contentful.Configuration
+  alias Contentful.Query
+
+  import Contentful.Misc, only: [fallback: 2]
+
+  @agent_name "Contentful Elixir SDK"
+
+  @agent_header [
+    "User-Agent": @agent_name,
+    "X-Contentful-User-Agent": @agent_name
+  ]
+
+  @accept_header [
+    accept: "application/json"
+  ]
+
+  def collection_query_params([]), do: []
+
+  @doc """
+  parses the options for retrieving a collection, usually triggered by a `Contentful.Query.fetch_all/4`.
+  It will drop any option that is not in an allowed set of parameter options.
+
+  ## Examples
+
+      [limit: 50, skip: 25, order: "foobar"]
+        = collection_query_params(limit: 50, baz: "foo", skip: 25, order: "foobar", bar: 42)
+
+  Also provides support for mapping out some of the API specific syntax in field handling.
+
+  ## Examples
+      [{:"sys.id[ne]", "foobar"}] = collection_query_params(select_params: [id: [ne: "foobar"]])
+
+  """
+  @spec collection_query_params(
+          limit: pos_integer(),
+          skip: non_neg_integer(),
+          include: non_neg_integer(),
+          content_type: String.t(),
+          query: String.t(),
+          select_params: map()
+        ) :: list()
+  def collection_query_params(options) do
+    filters =
+      options
+      |> Keyword.get(:select_params)
+      |> fallback([])
+      |> deconstruct_filters()
+
+    options
+    |> Keyword.take([:limit, :skip, :include, :content_type, :query])
+    |> Keyword.merge(filters)
+  end
+
+  @doc """
+  Builds the request headers for a request against the CDA, taking api access tokens into account
+
+  ## Examples
+      my_access_token = "foobarfoob4z"
+      [
+         "Authorization": "Bearer foobarfoob4z",
+         "User-Agent": "Contentful Elixir SDK",
+         "Accept": "application/json"
+       ] = my_access_token |> headers()
+  """
+  @spec headers(String.t()) :: keyword()
+  def headers(api_key) do
+    api_key
+    |> authorization_header()
+    |> Keyword.merge(@agent_header)
+    |> Keyword.merge(@accept_header)
+  end
+
+  defp authorization_header(nil) do
+    api_key_from_configuration() |> authorization_header()
+  end
+
+  defp authorization_header(token) do
+    [authorization: "Bearer #{token}"]
+  end
+
+  defp api_key_from_configuration do
+    Configuration.get(:api_key) |> fallback("___MISSING_API_KEY___")
+  end
+
+  defp deconstruct_filters(filters) do
+    filters
+    |> Enum.map(fn {field, value} = _filter ->
+      mapped_value =
+        case field do
+          :id ->
+            {:"sys.id", value}
+
+          field_name ->
+            {:"fields.#{field_name}", value}
+        end
+
+      case mapped_value do
+        {field, value} when is_binary(value) ->
+          {field, value}
+
+        {field, [{modifier, modifier_value}]} when is_list(modifier_value) ->
+          create_modified_field(field, modifier, Enum.join(modifier_value, ","))
+
+        {field, [{modifier, modifier_value}]} ->
+          create_modified_field(field, modifier, modifier_value)
+      end
+    end)
+  end
+
+  defp create_modified_field(field, modifier, field_value) do
+    unless Query.allowed_filter_modifiers() |> Enum.member?(modifier) do
+      raise %ArgumentError{
+        message: """
+        Invalid modifier for field '#{field}'!
+
+            Allowed modifiers are: #{Query.allowed_filter_modifiers() |> Enum.join(", ")}
+        """
+      }
+    end
+
+    {:"#{field}[#{modifier}]", field_value}
+  end
+end
diff --git a/lib/contentful_delivery/delivery.ex b/lib/contentful_delivery/delivery.ex
index 5fa01a9..2a02675 100644
--- a/lib/contentful_delivery/delivery.ex
+++ b/lib/contentful_delivery/delivery.ex
@@ -123,8 +123,10 @@ defmodule Contentful.Delivery do
   * [Contentful Delivery API docs](https://www.contentful.com/developers/docs/references/content-delivery-api/) (CDA).
   """
 
+  import Contentful.Misc, only: [fallback: 2]
   import HTTPoison, only: [get: 2]
 
+  alias Contentful.Configuration
   alias HTTPoison.Response
 
   @endpoint "cdn.contentful.com"
@@ -132,14 +134,6 @@ defmodule Contentful.Delivery do
   @protocol "https"
   @separator "/"
 
-  @agent_header [
-    "User-Agent": "Contentful Elixir SDK"
-  ]
-
-  @accept_header [
-    accept: "application/json"
-  ]
-
   @doc """
   Gets the json library for the Contentful Delivery API based
   on the config/config.exs.
@@ -217,70 +211,14 @@ defmodule Contentful.Delivery do
     [space |> url(), "environments", env] |> Enum.join(@separator)
   end
 
-  @doc """
-  Builds the request headers for a request against the CDA, taking api access tokens into account
-
-  ## Examples
-      my_access_token = "foobarfoob4z"
-      [
-         "Authorization": "Bearer foobarfoob4z",
-         "User-Agent": "Contentful Elixir SDK",
-         "Accept": "application/json"
-       ] = my_access_token |> request_headers()
-  """
-  @spec request_headers(String.t()) :: keyword()
-  def request_headers(api_key) do
-    api_key
-    |> authorization_header()
-    |> Keyword.merge(@agent_header)
-    |> Keyword.merge(@accept_header)
-  end
-
   @doc """
   Sends a request against the CDA. It's really just a wrapper around `HTTPoison.get/2`
   """
-  @spec send_request(tuple()) :: {:ok, Response.t()}
+  @spec send_request({binary(), any()}) :: {:error, HTTPoison.Error.t()} | {:ok, Response.t()}
   def send_request({url, headers}) do
     get(url, headers)
   end
 
-  @doc """
-  Prevents parsing of empty options.
-
-  ## Examples
-
-      "" = collection_query_params([])
-
-  """
-  def collection_query_params([]) do
-    ""
-  end
-
-  @doc """
-  parses the options for retrieving a collection. It will drop any option that is not in
-  @collection_filters ([:limit, :skip])
-
-  ## Examples
-
-      "?limit=50&skip=25&order=foobar"
-        = collection_query_params(limit: 50, baz: "foo", skip: 25, order: "foobar", bar: 42)
-
-  """
-  @spec collection_query_params(
-          limit: pos_integer(),
-          skip: non_neg_integer(),
-          content_type: String.t(),
-          include: non_neg_integer()
-        ) :: String.t()
-  def collection_query_params(options) do
-    params =
-      options
-      |> Keyword.take([:limit, :skip, :content_type, :include])
-      |> URI.encode_query()
-
-    "?#{params}"
-  end
-
   @doc """
   Parses the response from the CDA and triggers a callback on success
   """
@@ -346,62 +284,19 @@ defmodule Contentful.Delivery do
     {:error, :unknown}
   end
 
-  defp authorization_header(token) when is_nil(token) do
-    api_key_from_configuration() |> authorization_header()
-  end
-
-  defp authorization_header(token) do
-    [authorization: "Bearer #{token}"]
-  end
-
-  defp api_key_from_configuration do
-    config(:api_key) |> fallback("")
-  end
-
   defp environment_from_config do
-    config(:environment) |> fallback("master")
+    Configuration.get(:environment) |> fallback("master")
   end
 
   defp space_from_config do
-    config(:space)
+    Configuration.get(:space)
   end
 
   defp host_from_config do
-    case config(:endpoint) do
+    case Configuration.get(:endpoint) do
       nil -> @endpoint
       :preview -> @preview_endpoint
       value -> value
     end
   end
-
-  @doc """
-  Can be used to retrieve configuration for the `Contentful.Delivery` module
-
-  ## Examples
-      config :contentful, delivery: [
-        my_config: "foobar"
-      ]
-
-      "foobar" = Contentful.Delivery.config(:my_config)
-  """
-  @spec config(atom()) :: any()
-  def config(setting) do
-    config() |> Keyword.get(setting)
-  end
-
-  @doc """
-  loads the configuration for the delivery module from the contentful app configuration
-  """
-  @spec config() :: list(keyword())
-  def config do
-    Application.get_env(:contentful, :delivery, [])
-  end
-
-  defp fallback(nil, value) do
-    value
-  end
-
-  defp fallback(value, _) do
-    value
-  end
 end
diff --git a/lib/contentful_delivery/entries.ex b/lib/contentful_delivery/entries.ex
index 51cb4da..2f40818 100644
--- a/lib/contentful_delivery/entries.ex
+++ b/lib/contentful_delivery/entries.ex
@@ -117,7 +117,7 @@ defmodule Contentful.Delivery.Entries do
           "updatedAt" => updated_at,
           "createdAt" => created_at,
           "locale" => locale,
-          "contentType" => %{"sys" => content_type_id}
+          "contentType" => %{"sys" => %{"id" => content_type_id}}
         }
       }) do
     {:ok,
diff --git a/mix.exs b/mix.exs
index 1674da0..93ad785 100644
--- a/mix.exs
+++ b/mix.exs
@@ -103,6 +103,7 @@ defmodule Contentful.Mixfile do
       {:dialyxir, "~> 1.0.0-rc.7", only: [:dev], runtime: false},
       {:credo, "~> 1.4", only: [:dev, :test], runtime: false},
       {:excoveralls, "~> 0.10", only: :test},
+      {:mix_test_watch, "~> 1.0", only: :dev, runtime: false},
 
       # docs
       {:inch_ex, "2.0.0", only: :docs}
diff --git a/mix.lock b/mix.lock
index 664ae99..ad9537a 100644
--- a/mix.lock
+++ b/mix.lock
@@ -11,6 +11,7 @@
   "excoveralls": {:hex, :excoveralls, "0.13.0", "4e1b7cc4e0351d8d16e9be21b0345a7e165798ee5319c7800b9138ce17e0b38e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "fe2a56c8909564e2e6764765878d7d5e141f2af3bc8ff3b018a68ee2a218fced"},
   "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"},
   "exvcr": {:hex, :exvcr, "0.11.1", "a5e5f57a67538e032e16cfea6cfb1232314fb146e3ceedf1cde4a11f12fb7a58", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "984a4d52d9e01d5f0e28d45718565a41dffab3ac18e029ae45d42f16a2a58a1d"},
+  "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
   "hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
   "httpoison": {:hex, :httpoison, "1.7.0", "abba7d086233c2d8574726227b6c2c4f6e53c4deae7fe5f6de531162ce9929a0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "975cc87c845a103d3d1ea1ccfd68a2700c211a434d8428b10c323dc95dc5b980"},
   "idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
@@ -22,6 +23,7 @@
   "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
   "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
   "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
+  "mix_test_watch": {:hex, :mix_test_watch, "1.0.2", "34900184cbbbc6b6ed616ed3a8ea9b791f9fd2088419352a6d3200525637f785", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "47ac558d8b06f684773972c6d04fcc15590abdb97aeb7666da19fcbfdc441a07"},
   "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
   "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
   "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"},
diff --git a/test/contentful/misc_test.exs b/test/contentful/misc_test.exs
new file mode 100644
index 0000000..a869850
--- /dev/null
+++ b/test/contentful/misc_test.exs
@@ -0,0 +1,5 @@
+defmodule Contentful.MiscTest do
+  use ExUnit.Case
+
+  doctest Contentful.Misc
+end
diff --git a/test/contentful/query_test.exs b/test/contentful/query_test.exs
index 0050f57..10558c6 100644
--- a/test/contentful/query_test.exs
+++ b/test/contentful/query_test.exs
@@ -23,4 +23,41 @@ defmodule Contentful.QueryTest do
       {Entries, include: 2, limit: 4} = Entries |> Query.limit(4) |> Query.include(2)
     end
   end
+
+  describe "by/2" do
+    test "throws an error when used for entries without a content_type call before" do
+      assert_raise(ArgumentError, fn ->
+        Entries |> Query.by(id: "foobar")
+      end)
+    end
+
+    test "throws no error when used for entries with a content_type" do
+      {Entries,
+       [
+         {:select_params, [id: "foobar"]},
+         {:content_type, "car"}
+       ]} = Entries |> Query.content_type("car") |> Query.by(id: "foobar")
+    end
+
+    test "allows passing multiple fields into it" do
+      {Entries,
+       [
+         select_params: [id: "foobar", name: [ne: "Mercedes"]],
+         content_type: "car"
+       ]} =
+        Entries
+        |> Query.content_type("car")
+        |> Query.by(id: "foobar", name: [ne: "Mercedes"])
+    end
+  end
+
+  describe "search_full_text/2" do
+    test "adds a query to the parameters" do
+      {Entries, [query: "foobar"]} = Entries |> Query.search_full_text("foobar")
+    end
+
+    test "gets ignored for everything other than Entries" do
+      Assets = Assets |> Query.search_full_text("barfoo")
+    end
+  end
 end
diff --git a/test/contentful_delivery/assets_test.exs b/test/contentful_delivery/assets_test.exs
index 51af6eb..2a67f05 100644
--- a/test/contentful_delivery/assets_test.exs
+++ b/test/contentful_delivery/assets_test.exs
@@ -8,7 +8,9 @@ defmodule Contentful.Delivery.AssetsTest do
   import Contentful.Query
 
   @space_id "bmehzfuz4raf"
+  @env "master"
   @asset_id "577fpmbIfYD71VCjCpYA84"
+  @access_token nil
 
   setup_all do
     HTTPoison.start()
@@ -49,26 +51,44 @@ defmodule Contentful.Delivery.AssetsTest do
       end
     end
 
-    test "will fetch all published entries for a space, respecting the limit parameter" do
+    test "will fetch all published assets for a space, respecting the limit parameter" do
       use_cassette "multiple assets, limit filter" do
         {:ok, [%Asset{fields: %{title: "bafoo"}}], total: 2} =
           Assets |> limit(1) |> fetch_all(@space_id)
       end
     end
 
-    test "will fetch all published entries for a space, respecting the skip param" do
+    test "will fetch all published assets for a space, respecting the skip param" do
       use_cassette "multiple assets, skip filter" do
         {:ok, [%Asset{fields: %{title: "Foobar"}}], total: 2} =
           Assets |> skip(1) |> fetch_all(@space_id)
       end
     end
 
-    test "will fetch fetch all published entries for a space, respecting both the skip and the limit param" do
+    test "will fetch fetch all published assets for a space, respecting both the skip and the limit param" do
       use_cassette "multiple assets, all filters" do
         {:ok, [%Asset{fields: %{title: "Foobar"}}], total: 2} =
           Assets |> limit(1) |> skip(1) |> fetch_all(@space_id)
       end
     end
+
+    test "will fetch all published assets, filtered by a name" do
+      use_cassette "multiple assets, filtered by name" do
+        {:ok, [%Asset{fields: %{title: "bafoo"}}], total: 1} =
+          Assets
+          |> by(title: "bafoo")
+          |> fetch_all(@space_id, @env, @access_token)
+      end
+    end
+
+    test "will fetch all published assets, filtered by a name, negated" do
+      use_cassette "multiple assets, filtered by name, negated" do
+        {:ok, [%Asset{fields: %{title: nil}}], total: 1} =
+          Assets
+          |> by(title: [ne: "bafoo"])
+          |> fetch_all(@space_id, @env, @access_token)
+      end
+    end
   end
 
   describe ".stream" do
diff --git a/test/contentful_delivery/delivery_test.exs b/test/contentful_delivery/delivery_test.exs
index c2900a5..21b53eb 100644
--- a/test/contentful_delivery/delivery_test.exs
+++ b/test/contentful_delivery/delivery_test.exs
@@ -35,14 +35,4 @@ defmodule Contentful.DeliveryTest do
                Delivery.url("foobar", "baz")
     end
   end
-
-  describe ".request_headers" do
-    test "will construct the necessary headers for making a request with an api token" do
-      assert [
-               authorization: "Bearer api_access_token",
-               "User-Agent": "Contentful Elixir SDK",
-               accept: "application/json"
-             ] == "api_access_token" |> Delivery.request_headers()
-    end
-  end
 end
diff --git a/test/contentful_delivery/entries_test.exs b/test/contentful_delivery/entries_test.exs
index adb4e0a..0b1597d 100644
--- a/test/contentful_delivery/entries_test.exs
+++ b/test/contentful_delivery/entries_test.exs
@@ -2,7 +2,7 @@ defmodule Contentful.Delivery.EntriesTest do
   use ExUnit.Case
 
   alias Contentful.Delivery.Entries
-  alias Contentful.{Entry, Space, SysData}
+  alias Contentful.{ContentType, Entry, Space, SysData}
 
   use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
 
@@ -79,7 +79,7 @@ defmodule Contentful.Delivery.EntriesTest do
       end
     end
 
-    test "will fetch fetch all published entries for a space, respecting both the skip and the limit param" do
+    test "will fetch all published entries for a space, respecting both the skip and the limit param" do
       use_cassette "multiple entries, all filters" do
         {:ok, [%Entry{fields: %{"name" => "Blue steel"}}], total: 2} =
           Entries
@@ -92,5 +92,55 @@ defmodule Contentful.Delivery.EntriesTest do
           )
       end
     end
+
+    test "will fetch all published entries by spaces, filtered by content_type" do
+      use_cassette "multiple entries, filtered by content_type" do
+        {:ok,
+         [
+           %Entry{
+             sys: %SysData{id: "7qCGg4LadgJUcx5cr35Ou9", content_type: %ContentType{id: "category"}}
+           },
+           %Entry{
+             sys: %SysData{id: "4RPjazUzQMqemyNlcD3b9i", content_type: %ContentType{id: "category"}}
+           }
+         ],
+         total: 2} =
+          Entries
+          |> content_type("category")
+          |> fetch_all(@space_id, @env, @access_token)
+      end
+    end
+
+    test "will support select as a way of selecting sys.id" do
+      use_cassette "single entry with select filters" do
+        {:ok,
+         [
+           %Entry{
+             sys: %SysData{id: "7qCGg4LadgJUcx5cr35Ou9", content_type: %ContentType{id: "category"}}
+           }
+         ],
+         total: 1} =
+          Entries
+          |> content_type("category")
+          |> by(id: "7qCGg4LadgJUcx5cr35Ou9")
+          |> fetch_all(@space_id, @env, @access_token)
+      end
+    end
+
+    test "will support negated filtering by field" do
+      use_cassette "some entries not having an id" do
+        {:ok,
+         [
+           %Entry{
+             sys: %SysData{id: "4RPjazUzQMqemyNlcD3b9i", content_type: %ContentType{id: "category"}}
+           }
+         ],
+         total: 1} =
+          Entries
+          |> content_type("category")
+          |> by(id: [ne: "7qCGg4LadgJUcx5cr35Ou9"])
+          |> fetch_all(@space_id, @env, @access_token)
+      end
+    end
   end
 end
diff --git a/test/contentful_delivery/request_test.exs b/test/contentful_delivery/request_test.exs
new file mode 100644
index 0000000..f60a0e6
--- /dev/null
+++ b/test/contentful_delivery/request_test.exs
@@ -0,0 +1,44 @@
+defmodule Contentful.RequestTest do
+  use ExUnit.Case
+
+  alias Contentful.Request
+
+  doctest Contentful.Request
+
+  describe "headers/1" do
+    test "will construct the necessary headers for making a request with an api token" do
+      assert [
+               authorization: "Bearer api_access_token",
+               "User-Agent": "Contentful Elixir SDK",
+               "X-Contentful-User-Agent": "Contentful Elixir SDK",
+               accept: "application/json"
+             ] == "api_access_token" |> Request.headers()
+    end
+  end
+
+  describe "collection_query_params/1" do
+    test "omits arbitrary keywords" do
+      [limit: 1, skip: 2] = Request.collection_query_params(limit: 1, skip: 2)
+    end
+
+    test "raises an error for unknown modifiers" do
+      assert_raise(ArgumentError, fn ->
+        Request.collection_query_params(select_params: [id: [foo: "bar"]])
+      end)
+    end
+
+    test "translates id into sys.id" do
+      ["sys.id": "foo"] = Request.collection_query_params(select_params: [id: "foo"])
+    end
+
+    test "supports modifiers" do
+      ["fields.name[ne]": "bar"] =
+        Request.collection_query_params(select_params: [name: [ne: "bar"]])
+    end
+
+    test "supports translation of array properties" do
+      ["fields.tags[nin]": "foo,bar,barfoo"] =
+        Request.collection_query_params(select_params: [tags: [nin: ["foo", "bar", "barfoo"]]])
+    end
+  end
+end