Repository: deepset-ai/haystack

Issue:
WeaviateDocumentStore.get_all_documents does not return all documents
**Describe the bug**

Doing some experimenting with Weaviate+Haystack and running into what looks like a problem with the way `WeaviateDocumentStore.get_all_documents` is implemented. Does not look like it is actually retrieving all documents, but rather only the first 100.  This results in subsequent calls to `document_store.update_embeddings(retriever, ...)` that does not generate embeddings for all the data, rather just the first 100 documents as well.

**Expected behavior**
`WeaviateDocumentStore.get_all_documents` returns all documents, `document_store.update_embeddings(retriever, ...)` updates embeddings for all documents

**To Reproduce**
Following example shows the problem:
```
    document_store = WeaviateDocumentStore()

    # Write some documents (~500 in this case)
    document_store.write_documents(docs, batch_size=50)

    print(f"get_document_count is {document_store.get_document_count()}")

    docs = document_store.get_all_documents()
    print(f"Actual count of all documents {len(docs)}")

    # only first 100 documents have vectors updated...
    document_store.update_embeddings(retriever, batch_size=200)
```

Output from this shows that all 500 documents are stored, but only the first 100 documents are returned by the method.
```
get_document_count is 502
Actual count of all documents 100
```

Let me know if I'm doing something wrong, but seems like this isn't working currently.  thanks!


**System:**
 - OS: macOS
 - GPU/CPU: CPU
 - Haystack version (commit or version number): 0.10 and verified also occurs in 1.0
 - DocumentStore: Weaviate
 - Reader: N/A
 - Retriever: DensePassageRetriever

Weaviate Version 3.3.0
I'm unsure what the consequences (if any) would be to change the version of Weaviate but while building a demo colab notebook with the folks at Semi Technologies they pointed out that they are on v 3.3.0 and Haystack requires 2.5.0
Would it be a good idea to upgrade?


Discussion:
Hello @cjb06776, thank you for this bug report. I'm going to try replicate this and be back with some feedback asap.
Yes, I confirm I can replicate this issue. Unfortunately I don't have a quick fix for it, but I'll let you know when a patch is out. In the meantime I can only recommend you to temporarily choose another document store, if it's feasible.
As I expected, this is a "feature" of Weaviate: https://weaviate-python-client.readthedocs.io/en/latest/weaviate.data.html?highlight=100#weaviate.data.DataObject.get. I'll have to work it around.

If anybody comes across this and knows of a way to ask Weaviate to remove this limit, by all means please reply to this thread, even if closed. For now I'll perform several queries until the results of `get_document_count` and `get_all_documents` match :confused: 


Resolving patch:
diff --git a/haystack/document_stores/filter_utils.py b/haystack/document_stores/filter_utils.py
--- a/haystack/document_stores/filter_utils.py
+++ b/haystack/document_stores/filter_utils.py
@@ -277,7 +277,10 @@ def _get_weaviate_datatype(
                 data_type = "valueDate"
             # Comparison value is a plain string
             except ValueError:
-                data_type = "valueString"
+                if self.field_name == "content":
+                    data_type = "valueText"
+                else:
+                    data_type = "valueString"
         elif isinstance(value, int):
             data_type = "valueInt"
         elif isinstance(value, float):
diff --git a/haystack/document_stores/weaviate.py b/haystack/document_stores/weaviate.py
--- a/haystack/document_stores/weaviate.py
+++ b/haystack/document_stores/weaviate.py
@@ -1,28 +1,28 @@
-import hashlib
+from typing import Any, Dict, Generator, List, Optional, Union
 import re
 import uuid
-from typing import Dict, Generator, List, Optional, Union
+import json
+import hashlib
+import logging
 from datetime import datetime
 
-import logging
-import json
 import numpy as np
 from tqdm import tqdm
-
-from haystack.schema import Document
-from haystack.document_stores import BaseDocumentStore
-from haystack.document_stores.base import get_batches_from_generator
-from haystack.document_stores.filter_utils import LogicalFilterClause
-from haystack.document_stores.utils import convert_date_to_rfc3339
+import weaviate
 
 try:
     from weaviate import client, AuthClientPassword
-    from weaviate import ObjectsBatchRequest
 except (ImportError, ModuleNotFoundError) as ie:
     from haystack.utils.import_utils import _optional_component_not_installed
 
     _optional_component_not_installed(__name__, "weaviate", ie)
 
+from haystack.schema import Document
+from haystack.document_stores import BaseDocumentStore
+from haystack.document_stores.base import get_batches_from_generator
+from haystack.document_stores.filter_utils import LogicalFilterClause
+from haystack.document_stores.utils import convert_date_to_rfc3339
+
 
 logger = logging.getLogger(__name__)
 UUID_PATTERN = re.compile(r"^[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}$", re.IGNORECASE)
@@ -32,7 +32,7 @@ class WeaviateDocumentStore(BaseDocumentStore):
     """
 
     Weaviate is a cloud-native, modular, real-time vector search engine built to scale your machine learning models.
-    (See https://www.semi.technology/developers/weaviate/current/index.html#what-is-weaviate)
+    (See https://weaviate.io/developers/weaviate/current/index.html#what-is-weaviate)
 
     Some of the key differences in contrast to FAISS & Milvus:
     1. Stores everything in one place: documents, meta data and vectors - so less network overhead when scaling this up
@@ -45,7 +45,7 @@ class WeaviateDocumentStore(BaseDocumentStore):
     https://weaviate-python-client.readthedocs.io/en/docs/weaviate.html
 
     Usage:
-    1. Start a Weaviate server (see https://www.semi.technology/developers/weaviate/current/getting-started/installation.html)
+    1. Start a Weaviate server (see https://weaviate.io/developers/weaviate/current/getting-started/installation.html)
     2. Init a WeaviateDocumentStore in Haystack
 
     Limitations:
@@ -74,7 +74,7 @@ def __init__(
     ):
         """
         :param host: Weaviate server connection URL for storing and processing documents and vectors.
-                             For more details, refer "https://www.semi.technology/developers/weaviate/current/getting-started/installation.html"
+                             For more details, refer "https://weaviate.io/developers/weaviate/current/getting-started/installation.html"
         :param port: port of Weaviate instance
         :param timeout_config: Weaviate Timeout config as a tuple of (retries, time out seconds).
         :param username: username (standard authentication via http_auth)
@@ -88,11 +88,11 @@ def __init__(
                            'cosine' is recommended for Sentence Transformers.
         :param index_type: Index type of any vector object defined in weaviate schema. The vector index type is pluggable.
                            Currently, HSNW is only supported.
-                           See: https://www.semi.technology/developers/weaviate/current/more-resources/performance.html
+                           See: https://weaviate.io/developers/weaviate/current/more-resources/performance.html
         :param custom_schema: Allows to create custom schema in Weaviate, for more details
-                           See https://www.semi.technology/developers/weaviate/current/data-schema/schema-configuration.html
+                           See https://weaviate.io/developers/weaviate/current/data-schema/schema-configuration.html
         :param module_name: Vectorization module to convert data into vectors. Default is "text2vec-trasnformers"
-                            For more details, See https://www.semi.technology/developers/weaviate/current/modules/
+                            For more details, See https://weaviate.io/developers/weaviate/current/modules/
         :param return_embedding: To return document embedding.
         :param embedding_field: Name of field containing an embedding vector.
         :param progress_bar: Whether to show a tqdm progress bar or not.
@@ -265,8 +265,11 @@ def get_document_by_id(
         document = None
 
         id = self._sanitize_id(id=id, index=index)
-
-        result = self.weaviate_client.data_object.get_by_id(id, with_vector=True)
+        result = None
+        try:
+            result = self.weaviate_client.data_object.get_by_id(id, with_vector=True)
+        except weaviate.exceptions.UnexpectedStatusCodeException as usce:
+            logging.debug(f"Weaviate could not get the document requested: {usce}")
         if result:
             document = self._convert_weaviate_result_to_document(result, return_embedding=True)
         return document
@@ -289,7 +292,11 @@ def get_documents_by_id(
         # TODO: better implementation with multiple where filters instead of chatty call below?
         for id in ids:
             id = self._sanitize_id(id=id, index=index)
-            result = self.weaviate_client.data_object.get_by_id(id, with_vector=True)
+            result = None
+            try:
+                result = self.weaviate_client.data_object.get_by_id(id, with_vector=True)
+            except weaviate.exceptions.UnexpectedStatusCodeException as usce:
+                logging.debug(f"Weaviate could not get the document requested: {usce}")
             if result:
                 document = self._convert_weaviate_result_to_document(result, return_embedding=True)
                 documents.append(document)
@@ -458,7 +465,6 @@ def write_documents(
         batched_documents = get_batches_from_generator(document_objects, batch_size)
         with tqdm(total=len(document_objects), disable=not self.progress_bar) as progress_bar:
             for document_batch in batched_documents:
-                docs_batch = ObjectsBatchRequest()
                 for idx, doc in enumerate(document_batch):
                     _doc = {**doc.to_dict(field_map=self._create_document_field_map())}
                     _ = _doc.pop("score", None)
@@ -492,10 +498,11 @@ def write_documents(
                     for date_field in date_fields:
                         _doc[date_field] = convert_date_to_rfc3339(_doc[date_field])
 
-                    docs_batch.add(_doc, class_name=index, uuid=doc_id, vector=vector)
-
+                    self.weaviate_client.batch.add_data_object(
+                        data_object=_doc, class_name=index, uuid=doc_id, vector=vector
+                    )
                 # Ingest a batch of documents
-                results = self.weaviate_client.batch.create(docs_batch)
+                results = self.weaviate_client.batch.create_objects()
                 # Weaviate returns errors for every failed document in the batch
                 if results is not None:
                     for result in results:
@@ -563,15 +570,14 @@ def get_document_count(
         doc_count = 0
         if filters:
             filter_dict = LogicalFilterClause.parse(filters).convert_to_weaviate()
-            result = (
-                self.weaviate_client.query.aggregate(index).with_fields("meta { count }").with_where(filter_dict).do()
-            )
+            result = self.weaviate_client.query.aggregate(index).with_meta_count().with_where(filter_dict).do()
         else:
-            result = self.weaviate_client.query.aggregate(index).with_fields("meta { count }").do()
+            result = self.weaviate_client.query.aggregate(index).with_meta_count().do()
 
         if "data" in result:
             if "Aggregate" in result.get("data"):
-                doc_count = result.get("data").get("Aggregate").get(index)[0]["meta"]["count"]
+                if result.get("data").get("Aggregate").get(index):
+                    doc_count = result.get("data").get("Aggregate").get(index)[0]["meta"]["count"]
 
         return doc_count
 
@@ -586,6 +592,22 @@ def get_all_documents(
         """
         Get documents from the document store.
 
+        Note this limitation from the changelog of Weaviate 1.8.0:
+
+        .. quote::
+            Due to the increasing cost of each page outlined above, there is a limit to
+            how many objects can be retrieved using pagination. By default setting the sum
+            of offset and limit to higher than 10,000 objects, will lead to an error.
+            If you must retrieve more than 10,000 objects, you can increase this limit by
+            setting the environment variable `QUERY_MAXIMUM_RESULTS=<desired-value>`.
+
+            Warning: Setting this to arbitrarily high values can make the memory consumption
+            of a single query explode and single queries can slow down the entire cluster.
+            We recommend setting this value to the lowest possible value that does not
+            interfere with your users' expectations.
+
+        (https://github.com/semi-technologies/weaviate/releases/tag/v1.8.0)
+
         :param index: Name of the index to get the documents from. If None, the
                       DocumentStore's default index (self.index) will be used.
         :param filters: Optional filters to narrow down the search space to documents whose metadata fulfill certain
@@ -651,10 +673,30 @@ def _get_all_documents_in_index(
         else:
             result = self.weaviate_client.query.get(class_name=index, properties=properties).do()
 
-        all_docs = {}
-        if result and "data" in result and "Get" in result.get("data"):
-            if result.get("data").get("Get").get(index):
-                all_docs = result.get("data").get("Get").get(index)
+        # Inherent Weaviate limitation to 100 elements forces us to loop here:
+        #   https://weaviate-python-client.readthedocs.io/en/latest/weaviate.data.html?highlight=100#weaviate.data.DataObject.get
+        base_query = self.weaviate_client.query.get(class_name=index, properties=properties)
+        all_docs: List[Any] = []
+        num_of_documents = self.get_document_count(index=index, filters=filters)
+
+        while len(all_docs) < num_of_documents:
+            query = base_query
+            if filters:
+                filter_dict = LogicalFilterClause.parse(filters).convert_to_weaviate()
+                query = query.with_where(filter_dict)
+
+            if all_docs:
+                # .with_limit() must be used with .with_offset, of the latter won't work properly
+                #   https://weaviate-python-client.readthedocs.io/en/latest/weaviate.gql.html?highlight=offset#weaviate.gql.get.GetBuilder.with_offset
+                query = query.with_limit(100).with_offset(offset=len(all_docs))
+
+            result = query.do()
+
+            if result and "data" in result and "Get" in result.get("data"):
+                if result.get("data").get("Get").get(index):
+                    all_docs += result.get("data").get("Get").get(index)
+            else:
+                raise ValueError(f"Weaviate returned ad exception: {result}")
 
         yield from all_docs
 
@@ -671,6 +713,22 @@ def get_all_documents_generator(
         document store and yielded as individual documents. This method can be used to iteratively process
         a large number of documents without having to load all documents in memory.
 
+        Note this limitation from the changelog of Weaviate 1.8.0:
+
+        .. quote::
+            Due to the increasing cost of each page outlined above, there is a limit to
+            how many objects can be retrieved using pagination. By default setting the sum
+            of offset and limit to higher than 10,000 objects, will lead to an error.
+            If you must retrieve more than 10,000 objects, you can increase this limit by
+            setting the environment variable `QUERY_MAXIMUM_RESULTS=<desired-value>`.
+
+            Warning: Setting this to arbitrarily high values can make the memory consumption
+            of a single query explode and single queries can slow down the entire cluster.
+            We recommend setting this value to the lowest possible value that does not
+            interfere with your users' expectations.
+
+        (https://github.com/semi-technologies/weaviate/releases/tag/v1.8.0)
+
         :param index: Name of the index to get the documents from. If None, the
                       DocumentStore's default index (self.index) will be used.
         :param filters: Optional filters to narrow down the search space to documents whose metadata fulfill certain
@@ -793,7 +851,7 @@ def query(
                             ```
         :param top_k: How many documents to return per query.
         :param custom_query: Custom query that will executed using query.raw method, for more details refer
-                            https://www.semi.technology/developers/weaviate/current/graphql-references/filters.html
+                            https://weaviate.io/developers/weaviate/current/graphql-references/filters.html
         :param index: The name of the index in the DocumentStore from which to retrieve documents
         """
         index = self._sanitize_index_name(index) or self.index
diff --git a/haystack/utils/doc_store.py b/haystack/utils/doc_store.py
--- a/haystack/utils/doc_store.py
+++ b/haystack/utils/doc_store.py
@@ -63,7 +63,7 @@ def launch_weaviate(sleep=15):
     logger.debug("Starting Weaviate ...")
     status = subprocess.run(
         [
-            f"docker start {WEAVIATE_CONTAINER_NAME} > /dev/null 2>&1 || docker run -d -p 8080:8080 --env AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED='true' --env PERSISTENCE_DATA_PATH='/var/lib/weaviate' --name {WEAVIATE_CONTAINER_NAME} semitechnologies/weaviate:1.7.2"
+            f"docker start {WEAVIATE_CONTAINER_NAME} > /dev/null 2>&1 || docker run -d -p 8080:8080 --env AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED='true' --env PERSISTENCE_DATA_PATH='/var/lib/weaviate' --name {WEAVIATE_CONTAINER_NAME} semitechnologies/weaviate:1.11.0"
         ],
         shell=True,
     )


Test patch:
diff --git a/test/conftest.py b/test/conftest.py
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -302,7 +302,7 @@ def weaviate_fixture():
         print("Starting Weaviate servers ...")
         status = subprocess.run(["docker rm haystack_test_weaviate"], shell=True)
         status = subprocess.run(
-            ["docker run -d --name haystack_test_weaviate -p 8080:8080 semitechnologies/weaviate:1.7.2"], shell=True
+            ["docker run -d --name haystack_test_weaviate -p 8080:8080 semitechnologies/weaviate:1.11.0"], shell=True
         )
         if status.returncode:
             raise Exception("Failed to launch Weaviate. Please check docker container logs.")
diff --git a/test/test_document_store.py b/test/test_document_store.py
--- a/test/test_document_store.py
+++ b/test/test_document_store.py
@@ -117,7 +117,7 @@ def test_init_elastic_doc_store_with_index_recreation():
     assert len(labels) == 0
 
 
-def test_write_with_duplicate_doc_ids(document_store):
+def test_write_with_duplicate_doc_ids(document_store: BaseDocumentStore):
     duplicate_documents = [
         Document(content="Doc1", id_hash_keys=["content"]),
         Document(content="Doc1", id_hash_keys=["content"]),
@@ -131,7 +131,7 @@ def test_write_with_duplicate_doc_ids(document_store):
 @pytest.mark.parametrize(
     "document_store", ["elasticsearch", "faiss", "memory", "milvus1", "weaviate", "pinecone"], indirect=True
 )
-def test_write_with_duplicate_doc_ids_custom_index(document_store):
+def test_write_with_duplicate_doc_ids_custom_index(document_store: BaseDocumentStore):
     duplicate_documents = [
         Document(content="Doc1", id_hash_keys=["content"]),
         Document(content="Doc1", id_hash_keys=["content"]),
@@ -164,7 +164,20 @@ def test_get_all_documents_without_filters(document_store_with_docs):
     assert {d.meta["meta_field"] for d in documents} == {"test1", "test2", "test3", "test4", "test5"}
 
 
-def test_get_all_document_filter_duplicate_text_value(document_store):
+def test_get_all_documents_large_quantities(document_store: BaseDocumentStore):
+    # Test to exclude situations like Weaviate not returning more than 100 docs by default
+    #   https://github.com/deepset-ai/haystack/issues/1893
+    docs_to_write = [
+        {"meta": {"name": f"name_{i}"}, "content": f"text_{i}", "embedding": np.random.rand(768).astype(np.float32)}
+        for i in range(1000)
+    ]
+    document_store.write_documents(docs_to_write)
+    documents = document_store.get_all_documents()
+    assert all(isinstance(d, Document) for d in documents)
+    assert len(documents) == len(docs_to_write)
+
+
+def test_get_all_document_filter_duplicate_text_value(document_store: BaseDocumentStore):
     documents = [
         Document(content="Doc1", meta={"f1": "0"}, id_hash_keys=["meta"]),
         Document(content="Doc1", meta={"f1": "1", "meta_id": "0"}, id_hash_keys=["meta"]),
@@ -355,7 +368,7 @@ def test_get_document_by_id(document_store_with_docs):
     assert doc.content == documents[0].content
 
 
-def test_get_documents_by_id(document_store):
+def test_get_documents_by_id(document_store: BaseDocumentStore):
     # generate more documents than the elasticsearch default query size limit of 10
     docs_to_generate = 15
     documents = [{"content": "doc-" + str(i)} for i in range(docs_to_generate)]
@@ -372,7 +385,7 @@ def test_get_documents_by_id(document_store):
     assert set(retrieved_ids) == set(all_ids)
 
 
-def test_get_document_count(document_store):
+def test_get_document_count(document_store: BaseDocumentStore):
     documents = [
         {"content": "text1", "id": "1", "meta_field_for_count": "a"},
         {"content": "text2", "id": "2", "meta_field_for_count": "b"},
@@ -385,7 +398,7 @@ def test_get_document_count(document_store):
     assert document_store.get_document_count(filters={"meta_field_for_count": ["b"]}) == 3
 
 
-def test_get_all_documents_generator(document_store):
+def test_get_all_documents_generator(document_store: BaseDocumentStore):
     documents = [
         {"content": "text1", "id": "1", "meta_field_for_count": "a"},
         {"content": "text2", "id": "2", "meta_field_for_count": "b"},
@@ -421,7 +434,7 @@ def test_update_existing_documents(document_store, update_existing_documents):
         assert stored_docs[0].content == original_docs[0]["content"]
 
 
-def test_write_document_meta(document_store):
+def test_write_document_meta(document_store: BaseDocumentStore):
     documents = [
         {"content": "dict_without_meta", "id": "1"},
         {"content": "dict_with_meta", "meta_field": "test2", "name": "filename2", "id": "2"},
@@ -438,7 +451,7 @@ def test_write_document_meta(document_store):
     assert document_store.get_document_by_id("4").meta["meta_field"] == "test4"
 
 
-def test_write_document_index(document_store):
+def test_write_document_index(document_store: BaseDocumentStore):
     documents = [{"content": "text1", "id": "1"}, {"content": "text2", "id": "2"}]
     document_store.write_documents([documents[0]], index="haystack_test_one")
     assert len(document_store.get_all_documents(index="haystack_test_one")) == 1
@@ -453,7 +466,7 @@ def test_write_document_index(document_store):
 @pytest.mark.parametrize(
     "document_store", ["elasticsearch", "faiss", "memory", "milvus1", "milvus", "weaviate"], indirect=True
 )
-def test_document_with_embeddings(document_store):
+def test_document_with_embeddings(document_store: BaseDocumentStore):
     documents = [
         {"content": "text1", "id": "1", "embedding": np.random.rand(768).astype(np.float32)},
         {"content": "text2", "id": "2", "embedding": np.random.rand(768).astype(np.float64)},
@@ -720,7 +733,7 @@ def test_delete_documents_by_id_with_filters(document_store_with_docs):
 
 # exclude weaviate because it does not support storing labels
 @pytest.mark.parametrize("document_store", ["elasticsearch", "faiss", "memory", "milvus1", "pinecone"], indirect=True)
-def test_labels(document_store):
+def test_labels(document_store: BaseDocumentStore):
     label = Label(
         query="question1",
         answer=Answer(
@@ -808,7 +821,7 @@ def test_labels(document_store):
 
 # exclude weaviate because it does not support storing labels
 @pytest.mark.parametrize("document_store", ["elasticsearch", "faiss", "memory", "milvus1", "pinecone"], indirect=True)
-def test_multilabel(document_store):
+def test_multilabel(document_store: BaseDocumentStore):
     labels = [
         Label(
             id="standard",
@@ -924,7 +937,7 @@ def test_multilabel(document_store):
 
 # exclude weaviate because it does not support storing labels
 @pytest.mark.parametrize("document_store", ["elasticsearch", "faiss", "memory", "milvus1", "pinecone"], indirect=True)
-def test_multilabel_no_answer(document_store):
+def test_multilabel_no_answer(document_store: BaseDocumentStore):
     labels = [
         Label(
             query="question",
@@ -993,7 +1006,7 @@ def test_multilabel_no_answer(document_store):
 # exclude weaviate because it does not support storing labels
 # exclude faiss and milvus as label metadata is not implemented
 @pytest.mark.parametrize("document_store", ["elasticsearch", "memory"], indirect=True)
-def test_multilabel_filter_aggregations(document_store):
+def test_multilabel_filter_aggregations(document_store: BaseDocumentStore):
     labels = [
         Label(
             id="standard",
@@ -1089,7 +1102,7 @@ def test_multilabel_filter_aggregations(document_store):
 # exclude weaviate because it does not support storing labels
 # exclude faiss and milvus as label metadata is not implemented
 @pytest.mark.parametrize("document_store", ["elasticsearch", "memory"], indirect=True)
-def test_multilabel_meta_aggregations(document_store):
+def test_multilabel_meta_aggregations(document_store: BaseDocumentStore):
     labels = [
         Label(
             id="standard",
@@ -1180,7 +1193,7 @@ def test_multilabel_meta_aggregations(document_store):
 
 @pytest.mark.parametrize("document_store", ["elasticsearch", "faiss", "milvus1", "weaviate", "pinecone"], indirect=True)
 # Currently update_document_meta() is not implemented for Memory doc store
-def test_update_meta(document_store):
+def test_update_meta(document_store: BaseDocumentStore):
     documents = [
         Document(content="Doc1", meta={"meta_key_1": "1", "meta_key_2": "1"}),
         Document(content="Doc2", meta={"meta_key_1": "2", "meta_key_2": "2"}),
@@ -1209,7 +1222,7 @@ def test_custom_embedding_field(document_store_type, tmp_path):
 
 
 @pytest.mark.parametrize("document_store", ["elasticsearch"], indirect=True)
-def test_get_meta_values_by_key(document_store):
+def test_get_meta_values_by_key(document_store: BaseDocumentStore):
     documents = [
         Document(content="Doc1", meta={"meta_key_1": "1", "meta_key_2": "11"}),
         Document(content="Doc2", meta={"meta_key_1": "2", "meta_key_2": "22"}),
@@ -1271,7 +1284,7 @@ def test_elasticsearch_delete_index():
 
 
 @pytest.mark.parametrize("document_store", ["elasticsearch"], indirect=True)
-def test_elasticsearch_query_with_filters_and_missing_embeddings(document_store):
+def test_elasticsearch_query_with_filters_and_missing_embeddings(document_store: BaseDocumentStore):
     document_store.write_documents(DOCUMENTS)
     document_without_embedding = Document(
         content="Doc without embedding", meta={"name": "name_7", "year": "2021", "month": "04"}