From f2106ab37b94ebc82a4997cd5c37a892d95c835c Mon Sep 17 00:00:00 2001 From: ZanSara Date: Thu, 13 Apr 2023 09:36:23 +0200 Subject: [PATCH] feat: initial implementation of `MemoryDocumentStore` for new Pipelines (#4447) * add stub implementation * reimplementation * test files * docstore tests * tests for document * better testing * remove mmh3 * readme * only store, no retrieval yet * linting * review feedback * initial filters implementation * working on filters * linters * filtering works and is isolated by document store * simplify filters * comments * improve filters matching code * review feedback * pylint * move logic into_create_id * mypy --- haystack/preview/README.md | 1 + haystack/preview/__init__.py | 1 + haystack/preview/dataclasses/__init__.py | 1 + haystack/preview/dataclasses/document.py | 110 +++++++ haystack/preview/document_stores/__init__.py | 2 + haystack/preview/document_stores/errors.py | 10 + .../document_stores/memory/__init__.py | 1 + .../document_stores/memory/_filters.py | 255 ++++++++++++++++ .../document_stores/memory/document_store.py | 144 +++++++++ haystack/preview/pipeline.py | 2 +- haystack/preview/utils/import_utils.py | 24 ++ test/preview/dataclasses/test_dataclasses.py | 152 ++++++++++ test/preview/document_stores/_base.py | 286 ++++++++++++++++++ test/preview/document_stores/test_memory.py | 38 +++ test/preview/test_files/images/apple.jpg | Bin 0 -> 69286 bytes 15 files changed, 1026 insertions(+), 1 deletion(-) create mode 100644 haystack/preview/README.md create mode 100644 haystack/preview/dataclasses/__init__.py create mode 100644 haystack/preview/dataclasses/document.py create mode 100644 haystack/preview/document_stores/__init__.py create mode 100644 haystack/preview/document_stores/errors.py create mode 100644 haystack/preview/document_stores/memory/__init__.py create mode 100644 haystack/preview/document_stores/memory/_filters.py create mode 100644 haystack/preview/document_stores/memory/document_store.py create mode 100644 haystack/preview/utils/import_utils.py create mode 100644 test/preview/dataclasses/test_dataclasses.py create mode 100644 test/preview/document_stores/_base.py create mode 100644 test/preview/document_stores/test_memory.py create mode 100644 test/preview/test_files/images/apple.jpg diff --git a/haystack/preview/README.md b/haystack/preview/README.md new file mode 100644 index 000000000..6ae5c7c30 --- /dev/null +++ b/haystack/preview/README.md @@ -0,0 +1 @@ +# Haystack - Preview features diff --git a/haystack/preview/__init__.py b/haystack/preview/__init__.py index 5149a73e6..f2178f3d3 100644 --- a/haystack/preview/__init__.py +++ b/haystack/preview/__init__.py @@ -1,2 +1,3 @@ from canals import node +from haystack.preview.dataclasses import Document from haystack.preview.pipeline import Pipeline, PipelineError, NoSuchStoreError, load_pipelines, save_pipelines diff --git a/haystack/preview/dataclasses/__init__.py b/haystack/preview/dataclasses/__init__.py new file mode 100644 index 000000000..6fcd1f335 --- /dev/null +++ b/haystack/preview/dataclasses/__init__.py @@ -0,0 +1 @@ +from haystack.preview.dataclasses.document import Document diff --git a/haystack/preview/dataclasses/document.py b/haystack/preview/dataclasses/document.py new file mode 100644 index 000000000..f269ffd56 --- /dev/null +++ b/haystack/preview/dataclasses/document.py @@ -0,0 +1,110 @@ +from typing import List, Any, Dict, Literal, Optional, TYPE_CHECKING + +import json +import hashlib +import logging +from pathlib import Path +from dataclasses import asdict, dataclass, field + +from haystack.preview.utils.import_utils import optional_import + +# We need to do this dance because ndarray is an optional dependency used as a type by dataclass +if TYPE_CHECKING: + from numpy import ndarray +else: + ndarray = optional_import("numpy", "ndarray", "You won't be able to use embeddings.", __name__) + +DataFrame = optional_import("pandas", "DataFrame", "You won't be able to use table related features.", __name__) + + +logger = logging.getLogger(__name__) +ContentType = Literal["text", "table", "image", "audio"] +PYTHON_TYPES_FOR_CONTENT: Dict[ContentType, type] = {"text": str, "table": DataFrame, "image": Path, "audio": Path} + + +def _create_id( + classname: str, content: Any, metadata: Optional[Dict[str, Any]] = None, id_hash_keys: Optional[List[str]] = None +): + """ + Creates a hash of the content given that acts as the document's ID. + """ + content_to_hash = f"{classname}:{content}" + if id_hash_keys: + if not metadata: + raise ValueError("If 'id_hash_keys' is provided, you must provide 'metadata' too.") + content_to_hash = ":".join([content_to_hash, *[str(metadata.get(key, "")) for key in id_hash_keys]]) + return hashlib.sha256(str(content_to_hash).encode("utf-8")).hexdigest() + + +@dataclass(frozen=True) +class Document: + """ + Base data class containing some data to be queried. + Can contain text snippets, tables, file paths to files like images or audios. + Documents can be sorted by score, serialized to/from dictionary and JSON, and are immutable. + + Immutability is due to the fact that the document's ID depends on its content, so upon changing the content, also + the ID should change. To avoid keeping IDs in sync with the content by using properties, and asking docstores to + be aware of this corner case, we decide to make Documents immutable and remove the issue. If you need to modify a + Document, consider using `to_dict()`, modifying the dict, and then create a new Document object using + `Document.from_dict()`. + + Note that `id_hash_keys` are referring to keys in the metadata. `content` is always included in the id hash. + In case of file-based documents (images, audios), the content that is hashed is the file paths, + so if the file is moved, the hash is different, but if the file is modified without renaming it, the has will + not differ. + """ + + id: str = field(default_factory=str) + content: Any = field(default_factory=lambda: None) + content_type: ContentType = "text" + metadata: Dict[str, Any] = field(default_factory=dict, hash=False) + id_hash_keys: List[str] = field(default_factory=lambda: [], hash=False) + score: Optional[float] = field(default=None, compare=True) + embedding: Optional[ndarray] = field(default=None, repr=False) + + def __str__(self): + return f"{self.__class__.__name__}('{self.content}')" + + def __post_init__(self): + """ + Generate the ID based on the init parameters and make sure that content_type + matches the actual type of content. + """ + # Validate content_type + if not isinstance(self.content, PYTHON_TYPES_FOR_CONTENT[self.content_type]): + raise ValueError( + f"The type of content ({type(self.content)}) does not match the " + f"content type: '{self.content_type}' expects '{PYTHON_TYPES_FOR_CONTENT[self.content_type]}'." + ) + # Check if id_hash_keys are all present in the meta + for key in self.id_hash_keys: + if key not in self.metadata: + raise ValueError( + f"'{key}' must be present in the metadata of the Document if you want to use it to generate the ID." + ) + # Generate the ID + hashed_content = _create_id( + classname=self.__class__.__name__, + content=str(self.content), + metadata=self.metadata, + id_hash_keys=self.id_hash_keys, + ) + + # Note: we need to set the id this way because the dataclass is frozen. See the docstring. + object.__setattr__(self, "id", hashed_content) + + def to_dict(self): + return asdict(self) + + def to_json(self, **json_kwargs): + return json.dumps(self.to_dict(), *json_kwargs) + + @classmethod + def from_dict(cls, dictionary): + return cls(**dictionary) + + @classmethod + def from_json(cls, data, **json_kwargs): + dictionary = json.loads(data, **json_kwargs) + return cls.from_dict(dictionary=dictionary) diff --git a/haystack/preview/document_stores/__init__.py b/haystack/preview/document_stores/__init__.py new file mode 100644 index 000000000..6d70bca62 --- /dev/null +++ b/haystack/preview/document_stores/__init__.py @@ -0,0 +1,2 @@ +from haystack.preview.document_stores.memory.document_store import MemoryDocumentStore +from haystack.preview.document_stores.errors import StoreError, DuplicateDocumentError, MissingDocumentError diff --git a/haystack/preview/document_stores/errors.py b/haystack/preview/document_stores/errors.py new file mode 100644 index 000000000..8c29ff6a8 --- /dev/null +++ b/haystack/preview/document_stores/errors.py @@ -0,0 +1,10 @@ +class StoreError(Exception): + pass + + +class DuplicateDocumentError(StoreError): + pass + + +class MissingDocumentError(StoreError): + pass diff --git a/haystack/preview/document_stores/memory/__init__.py b/haystack/preview/document_stores/memory/__init__.py new file mode 100644 index 000000000..3b3739985 --- /dev/null +++ b/haystack/preview/document_stores/memory/__init__.py @@ -0,0 +1 @@ +from haystack.preview.document_stores.memory.document_store import MemoryDocumentStore diff --git a/haystack/preview/document_stores/memory/_filters.py b/haystack/preview/document_stores/memory/_filters.py new file mode 100644 index 000000000..02db3525e --- /dev/null +++ b/haystack/preview/document_stores/memory/_filters.py @@ -0,0 +1,255 @@ +from typing import List, Any + +from haystack.preview.dataclasses import Document + + +def not_operation(conditions: List[Any], document: Document, _current_key: str): + """ + Applies a NOT to all the nested conditions. + + :param conditions: the filters dictionary. + :param document: the document to test. + :param _current_key: internal, don't use. + :return: True if the document matches the negated filters, False otherwise + """ + return not and_operation(conditions=conditions, document=document, _current_key=_current_key) + + +def and_operation(conditions: List[Any], document: Document, _current_key: str): + """ + Applies an AND to all the nested conditions. + + :param conditions: the filters dictionary. + :param document: the document to test. + :param _current_key: internal, don't use. + :return: True if the document matches all the filters, False otherwise + """ + for condition in conditions: + if not _match(conditions=condition, document=document, _current_key=_current_key): + return False + return True + + +def or_operation(conditions: List[Any], document: Document, _current_key: str): + """ + Applies an OR to all the nested conditions. + + :param conditions: the filters dictionary. + :param document: the document to test. + :param _current_key: internal, don't use. + :return: True if the document matches ano of the filters, False otherwise + """ + for condition in conditions: + if _match(conditions=condition, document=document, _current_key=_current_key): + return True + return False + + +def eq_operation(fields, field_name, value): + """ + Checks for equality between the document's metadata value and a fixed value. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the values are equal, False otherwise + """ + if not field_name in fields: + return False + return fields[field_name] == value + + +def in_operation(fields, field_name, value): + """ + Checks for whether the document's metadata value is present into the given list. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the document's value is included in the given list, False otherwise + """ + if not field_name in fields: + return False + return fields[field_name] in value + + +def ne_operation(fields, field_name, value): + """ + Checks for inequality between the document's metadata value and a fixed value. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the values are different, False otherwise + """ + if not field_name in fields: + return True + return fields[field_name] != value + + +def nin_operation(fields, field_name, value): + """ + Checks whether the document's metadata value is absent from the given list. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the document's value is not included in the given list, False otherwise + """ + if not field_name in fields: + return True + return fields[field_name] not in value + + +def gt_operation(fields, field_name, value): + """ + Checks whether the document's metadata value is (strictly) larger than the given value. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the document's value is strictly larger than the fixed value, False otherwise + """ + if not field_name in fields: + return False + return fields[field_name] > value + + +def gte_operation(fields, field_name, value): + """ + Checks whether the document's metadata value is larger than or equal to the given value. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the document's value is larger than or equal to the fixed value, False otherwise + """ + if not field_name in fields: + return False + return fields[field_name] >= value + + +def lt_operation(fields, field_name, value): + """ + Checks whether the document's metadata value is (strictly) smaller than the given value. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the document's value is strictly smaller than the fixed value, False otherwise + """ + if not field_name in fields: + return False + return fields[field_name] < value + + +def lte_operation(fields, field_name, value): + """ + Checks whether the document's metadata value is smaller than or equal to the given value. + + :param fields: all the document's metadata + :param field_name: the field to test + :param value; the fixed value to compare against + :return: True if the document's value is smaller than or equal to the fixed value, False otherwise + """ + if not field_name in fields: + return False + return fields[field_name] <= value + + +LOGICAL_STATEMENTS = {"$not": not_operation, "$and": and_operation, "$or": or_operation} +OPERATORS = { + "$eq": eq_operation, + "$in": in_operation, + "$ne": ne_operation, + "$nin": nin_operation, + "$gt": gt_operation, + "$gte": gte_operation, + "$lt": lt_operation, + "$lte": lte_operation, +} +RESERVED_KEYS = [*LOGICAL_STATEMENTS.keys(), *OPERATORS.keys()] + + +def match(conditions: Any, document: Document): + """ + This method applies the filters to any given document and returns True when the documents + metadata matches the filters, False otherwise. + + :param conditions: the filters dictionary. + :param document: the document to test. + :return: True if the document matches the filters, False otherwise + """ + if isinstance(conditions, list): + # The default operation for a list of sibling conditions is $and + return _match(conditions=conditions, document=document, _current_key="$and") + + if isinstance(conditions, dict): + if len(conditions.keys()) > 1: + # The default operation for a list of sibling conditions is $and + return _match(conditions=conditions, document=document, _current_key="$and") + + field_key, field_value = list(conditions.items())[0] + return _match(conditions=field_value, document=document, _current_key=field_key) + + raise ValueError("Filters must be dictionaries or lists. See the examples in the documentation.") + + +def _match(conditions: Any, document: Document, _current_key: str): + """ + Recursive implementation of match(). + """ + if isinstance(conditions, list): + # The default operation for a list of sibling conditions is $and + return _match(conditions={"$and": conditions}, document=document, _current_key=_current_key) + + if isinstance(conditions, dict): + # Check for malformed filters, like {"name": {"year": "2020"}} + if _current_key not in RESERVED_KEYS and any(key not in RESERVED_KEYS for key in conditions.keys()): + raise ValueError( + f"This filter ({_current_key}, {conditions}) seems to be malformed. Comparisons with dictionaries are " + "not currently supported. Check the documentation to learn more about filters syntax." + ) + + # The default operation for a list of sibling conditions is $and + if len(conditions.keys()) > 1: + return and_operation( + conditions=_conditions_as_list(conditions), document=document, _current_key=_current_key + ) + + field_key, field_value = list(conditions.items())[0] + + if field_key in LOGICAL_STATEMENTS.keys(): + # It's a nested logical statement ($and, $or, $not) + return LOGICAL_STATEMENTS[field_key]( + conditions=_conditions_as_list(field_value), document=document, _current_key=_current_key + ) + if field_key in OPERATORS.keys(): + # It's a comparison operator ($eq, $in, $gte, ...) + if not _current_key: + raise ValueError( + "Filters can't start with an operator like $eq and $in. You have to specify the field name first. " + "See the examples in the documentation." + ) + return OPERATORS[field_key](fields=document.metadata, field_name=_current_key, value=field_value) + + if isinstance(field_value, list): + # The default operator for a {key: [value1, value2]} filter is $in + return in_operation(fields=document.metadata, field_name=field_key, value=field_value) + + # The default operator for a {key: value} filter is $eq + return eq_operation(fields=document.metadata, field_name=_current_key, value=conditions) + + +def _conditions_as_list(conditions: Any) -> List[Any]: + """ + Make sure all nested conditions are not dictionaries or single values, but always lists. + + :param conditions: the conditions to transform into a list + :returns: a list of filters + """ + if isinstance(conditions, list): + return conditions + if isinstance(conditions, dict): + return [{key: value} for key, value in conditions.items()] + return [conditions] diff --git a/haystack/preview/document_stores/memory/document_store.py b/haystack/preview/document_stores/memory/document_store.py new file mode 100644 index 000000000..a3eadb9a7 --- /dev/null +++ b/haystack/preview/document_stores/memory/document_store.py @@ -0,0 +1,144 @@ +from typing import Literal, Any, Dict, List, Optional, Iterable + +import logging + +from haystack.preview.dataclasses import Document +from haystack.preview.document_stores.memory._filters import match +from haystack.preview.document_stores.errors import DuplicateDocumentError, MissingDocumentError + + +logger = logging.getLogger(__name__) +DuplicatePolicy = Literal["skip", "overwrite", "fail"] + + +class MemoryDocumentStore: + """ + Stores data in-memory. It's ephemeral and cannot be saved to disk. + """ + + def __init__(self): + """ + Initializes the store. + """ + self.storage = {} + + def count_documents(self) -> int: + """ + Returns the number of how many documents are present in the document store. + """ + return len(self.storage.keys()) + + def filter_documents(self, filters: Optional[Dict[str, Any]] = None) -> List[Document]: + """ + Returns the documents that match the filters provided. + + Filters are defined as nested dictionaries. The keys of the dictionaries can be a logical operator (`"$and"`, + `"$or"`, `"$not"`), a comparison operator (`"$eq"`, `$ne`, `"$in"`, `$nin`, `"$gt"`, `"$gte"`, `"$lt"`, + `"$lte"`) or a metadata field name. + + Logical operator keys take a dictionary of metadata field names and/or logical operators as value. Metadata + field names take a dictionary of comparison operators as value. Comparison operator keys take a single value or + (in case of `"$in"`) a list of values as value. If no logical operator is provided, `"$and"` is used as default + operation. If no comparison operator is provided, `"$eq"` (or `"$in"` if the comparison value is a list) is used + as default operation. + + Example: + + ```python + filters = { + "$and": { + "type": {"$eq": "article"}, + "date": {"$gte": "2015-01-01", "$lt": "2021-01-01"}, + "rating": {"$gte": 3}, + "$or": { + "genre": {"$in": ["economy", "politics"]}, + "publisher": {"$eq": "nytimes"} + } + } + } + # or simpler using default operators + filters = { + "type": "article", + "date": {"$gte": "2015-01-01", "$lt": "2021-01-01"}, + "rating": {"$gte": 3}, + "$or": { + "genre": ["economy", "politics"], + "publisher": "nytimes" + } + } + ``` + + To use the same logical operator multiple times on the same level, logical operators can take a list of + dictionaries as value. + + Example: + + ```python + filters = { + "$or": [ + { + "$and": { + "Type": "News Paper", + "Date": { + "$lt": "2019-01-01" + } + } + }, + { + "$and": { + "Type": "Blog Post", + "Date": { + "$gte": "2019-01-01" + } + } + } + ] + } + ``` + + :param filters: the filters to apply to the document list. + :return: a list of Documents that match the given filters. + """ + if filters: + return [doc for doc in self.storage.values() if match(conditions=filters, document=doc)] + return list(self.storage.values()) + + def write_documents(self, documents: List[Document], duplicates: DuplicatePolicy = "fail") -> None: + """ + Writes (or overwrites) documents into the store. + + :param documents: a list of documents. + :param duplicates: documents with the same ID count as duplicates. When duplicates are met, + the store can: + - skip: keep the existing document and ignore the new one. + - overwrite: remove the old document and write the new one. + - fail: an error is raised + :raises DuplicateError: Exception trigger on duplicate document if `duplicates="fail"` + :return: None + """ + if ( + not isinstance(documents, Iterable) + or isinstance(documents, str) + or any(not isinstance(doc, Document) for doc in documents) + ): + raise ValueError("Please provide a list of Documents.") + + for document in documents: + if document.id in self.storage.keys(): + if duplicates == "fail": + raise DuplicateDocumentError(f"ID '{document.id}' already exists.") + if duplicates == "skip": + logger.warning("ID '%s' already exists", document.id) + self.storage[document.id] = document + + def delete_documents(self, document_ids: List[str]) -> None: + """ + Deletes all documents with a matching document_ids from the document store. + Fails with `MissingDocumentError` if no document with this id is present in the store. + + :param object_ids: the object_ids to delete + """ + for doc_id in document_ids: + if not doc_id in self.storage.keys(): + raise MissingDocumentError(f"ID '{doc_id}' not found, cannot delete it.") + del self.storage[doc_id] diff --git a/haystack/preview/pipeline.py b/haystack/preview/pipeline.py index e7db4a2ee..e31427e7c 100644 --- a/haystack/preview/pipeline.py +++ b/haystack/preview/pipeline.py @@ -21,7 +21,7 @@ class Pipeline(CanalsPipeline): def __init__(self): super().__init__() - self.stores: Dict[str, object] = {} + self.stores = {} def add_store(self, name: str, store: object) -> None: """ diff --git a/haystack/preview/utils/import_utils.py b/haystack/preview/utils/import_utils.py new file mode 100644 index 000000000..282ce816e --- /dev/null +++ b/haystack/preview/utils/import_utils.py @@ -0,0 +1,24 @@ +from typing import Optional, Any +import importlib +import logging + + +def optional_import(import_path: str, import_target: Optional[str], error_msg: str, importer_module: str) -> Any: + """ + Imports an optional dependency. Emits a DEBUG log if the dependency is missing. + """ + try: + module = importlib.import_module(import_path) + if import_target: + return getattr(module, import_target) + return module + except ImportError as exc: + logging.getLogger(importer_module).debug( + "%s%s%s can't be imported: %s Error raised: %s", + import_path, + "." if import_target else "", + import_target, + error_msg, + exc, + ) + return None diff --git a/test/preview/dataclasses/test_dataclasses.py b/test/preview/dataclasses/test_dataclasses.py new file mode 100644 index 000000000..dbcee208f --- /dev/null +++ b/test/preview/dataclasses/test_dataclasses.py @@ -0,0 +1,152 @@ +from pathlib import Path +import hashlib +import pandas as pd +import numpy as np + +from haystack.preview import Document +from haystack.preview.dataclasses.document import _create_id + + +def test_default_text_document_to_dict(): + assert Document(content="test content").to_dict() == { + "id": _create_id(classname=Document.__name__, content="test content"), + "content": "test content", + "content_type": "text", + "metadata": {}, + "id_hash_keys": [], + "score": None, + "embedding": None, + } + + +def test_default_text_document_from_dict(): + assert Document.from_dict( + { + "id": _create_id(classname=Document.__name__, content="test content"), + "content": "test content", + "content_type": "text", + "metadata": {}, + "id_hash_keys": [], + "score": None, + "embedding": None, + } + ) == Document(content="test content") + + +def test_default_table_document_to_dict(): + df = pd.DataFrame([1, 2]) + dictionary = Document(content=df, content_type="table").to_dict() + + dataframe = dictionary.pop("content") + assert dataframe.equals(df) + + assert dictionary == { + "id": _create_id(classname=Document.__name__, content=df), + "content_type": "table", + "metadata": {}, + "id_hash_keys": [], + "score": None, + "embedding": None, + } + + +def test_default_table_document_from_dict(): + df = pd.DataFrame([1, 2]) + assert Document.from_dict( + { + "id": _create_id(classname=Document.__name__, content=df), + "content": df, + "content_type": "table", + "metadata": {}, + "id_hash_keys": [], + "score": None, + "embedding": None, + } + ) == Document(content=df, content_type="table") + + +def test_default_image_document_to_dict(): + path = Path(__file__).parent / "test_files" / "apple.jpg" + assert Document(content=path, content_type="image").to_dict() == { + "id": _create_id(classname=Document.__name__, content=path), + "content": path, + "content_type": "image", + "metadata": {}, + "id_hash_keys": [], + "score": None, + "embedding": None, + } + + +def test_default_image_document_from_dict(): + path = Path(__file__).parent / "test_files" / "apple.jpg" + assert Document.from_dict( + { + "id": _create_id(classname=Document.__name__, content=path), + "content": path, + "content_type": "image", + "metadata": {}, + "id_hash_keys": [], + "score": None, + "embedding": None, + } + ) == Document(content=path, content_type="image") + + +def test_document_with_most_attributes_to_dict(): + """ + This tests also id_hash_keys + """ + doc = Document( + content="test content", + content_type="text", + metadata={"some": "values", "test": 10}, + id_hash_keys=["test"], + score=0.99, + embedding=np.zeros([10, 10]), + ) + dictionary = doc.to_dict() + + embedding = dictionary.pop("embedding") + assert (embedding == np.zeros([10, 10])).all() + + assert dictionary == { + "id": _create_id( + classname=Document.__name__, + content="test content", + id_hash_keys=["test"], + metadata={"some": "values", "test": 10}, + ), + "content": "test content", + "content_type": "text", + "metadata": {"some": "values", "test": 10}, + "id_hash_keys": ["test"], + "score": 0.99, + } + + +def test_document_with_most_attributes_from_dict(): + embedding = np.zeros([10, 10]) + assert Document.from_dict( + { + "id": _create_id( + classname=Document.__name__, + content="test content", + id_hash_keys=["test"], + metadata={"some": "values", "test": 10}, + ), + "content": "test content", + "content_type": "text", + "metadata": {"some": "values", "test": 10}, + "id_hash_keys": ["test"], + "score": 0.99, + "embedding": embedding, + } + ) == Document( + content="test content", + content_type="text", + metadata={"some": "values", "test": 10}, + id_hash_keys=["test"], + score=0.99, + embedding=embedding, + ) diff --git a/test/preview/document_stores/_base.py b/test/preview/document_stores/_base.py new file mode 100644 index 000000000..f11f0ce10 --- /dev/null +++ b/test/preview/document_stores/_base.py @@ -0,0 +1,286 @@ +import pytest + +import numpy as np + +from haystack.preview.dataclasses import Document +from haystack.preview.document_stores import MissingDocumentError, DuplicateDocumentError + + +class DocumentStoreBaseTests: + @pytest.fixture + def docstore(self): + raise NotImplementedError() + + @pytest.fixture + def filterable_docs(self): + documents = [] + for i in range(3): + documents.append( + Document( + content=f"A Foo Document {i}", + metadata={"name": f"name_{i}", "year": "2020", "month": "01", "number": 2}, + embedding=np.random.rand(768).astype(np.float32), + ) + ) + documents.append( + Document( + content=f"A Bar Document {i}", + metadata={"name": f"name_{i}", "year": "2021", "month": "02", "number": -2}, + embedding=np.random.rand(768).astype(np.float32), + ) + ) + documents.append( + Document( + content=f"A Foobar Document {i}", + metadata={"name": f"name_{i}", "year": "2000", "month": "03", "number": -10}, + embedding=np.random.rand(768).astype(np.float32), + ) + ) + documents.append( + Document( + content=f"Document {i} without embedding", + metadata={"name": f"name_{i}", "no_embedding": True, "month": "03"}, + ) + ) + + return documents + + def test_count_empty(self, docstore): + assert docstore.count_documents() == 0 + + def test_count_not_empty(self, docstore): + self.direct_write( + docstore, [Document(content="test doc 1"), Document(content="test doc 2"), Document(content="test doc 3")] + ) + assert docstore.count_documents() == 3 + + def test_no_filter_empty(self, docstore): + assert docstore.filter_documents() == [] + assert docstore.filter_documents(filters={}) == [] + + def test_no_filter_not_empty(self, docstore): + docs = [Document(content="test doc")] + self.direct_write(docstore, docs) + assert docstore.filter_documents() == docs + assert docstore.filter_documents(filters={}) == docs + + def test_filter_simple_value(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": "2020"}) + assert len(result) == 3 + + def test_filter_simple_list(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": ["2020"]}) + assert all(doc.metadata["year"] == "2020" for doc in result) + result = docstore.filter_documents(filters={"year": ["2020", "2021"]}) + assert all(doc.metadata["year"] in ["2020", "2021"] for doc in result) + + def test_incorrect_filter_name(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"non_existing_meta_field": ["whatever"]}) + assert len(result) == 0 + + def test_incorrect_filter_type(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + with pytest.raises(ValueError, match="dictionaries or lists"): + docstore.filter_documents(filters="something odd") + + def test_incorrect_filter_value(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": ["nope"]}) + assert len(result) == 0 + + def test_incorrect_filter_nesting(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + with pytest.raises(ValueError, match="malformed"): + docstore.filter_documents(filters={"number": {"year": "2020"}}) + with pytest.raises(ValueError, match="malformed"): + docstore.filter_documents(filters={"number": {"year": {"month": "01"}}}) + + def test_eq_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": {"$eq": "2020"}}) + assert all(doc.metadata["year"] == "2020" for doc in result) + result = docstore.filter_documents(filters={"year": "2020"}) + assert all(doc.metadata["year"] == "2020" for doc in result) + + def test_in_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": {"$in": ["2020", "2021", "n.a."]}}) + assert all(doc.metadata["year"] in ["2020", "2021"] for doc in result) + result = docstore.filter_documents(filters={"year": ["2020", "2021", "n.a."]}) + assert all(doc.metadata["year"] in ["2020", "2021"] for doc in result) + + def test_ne_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": {"$ne": "2020"}}) + assert all(doc.metadata.get("year", None) != "2020" for doc in result) + + def test_nin_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": {"$nin": ["2020", "2021", "n.a."]}}) + assert all(doc.metadata.get("year", None) not in ["2020", "2021"] for doc in result) + + def test_gt_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"number": {"$gt": 0.0}}) + assert all(doc.metadata["number"] > 0 for doc in result) + + def test_gte_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"number": {"$gte": -2.0}}) + assert all(doc.metadata["number"] >= -2.0 for doc in result) + + def test_lt_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"number": {"$lt": 0.0}}) + assert all(doc.metadata["number"] < 0 for doc in result) + + def test_lte_filter(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"number": {"$lte": 2.0}}) + assert all(doc.metadata["number"] <= 2.0 for doc in result) + + def test_filter_simple_explicit_and(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": {"$and": {"$lte": "2021", "$gte": "2020"}}}) + assert all(int(doc.metadata["year"]) >= 2020 and int(doc.metadata["year"]) <= 2021 for doc in result) + result = docstore.filter_documents(filters={"year": {"$and": [{"$lte": "2021"}, {"$gte": "2020"}]}}) + assert all(int(doc.metadata["year"]) >= 2020 and int(doc.metadata["year"]) <= 2021 for doc in result) + + def test_filter_simple_implicit_and(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + result = docstore.filter_documents(filters={"year": {"$lte": "2021", "$gte": "2020"}}) + assert all(int(doc.metadata["year"]) >= 2020 and int(doc.metadata["year"]) <= 2021 for doc in result) + + def test_filter_nested_explicit_and(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters = {"$and": {"year": {"$and": {"$lte": "2021", "$gte": "2020"}}, "name": {"$in": ["name_0", "name_1"]}}} + result = docstore.filter_documents(filters=filters) + assert all( + int(doc.metadata["year"]) >= 2020 + and int(doc.metadata["year"]) <= 2021 + and doc.metadata["name"] in ["name_0", "name_1"] + for doc in result + ) + + def test_filter_nested_implicit_and(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters_simplified = {"year": {"$lte": "2021", "$gte": "2020"}, "name": ["name_0", "name_1"]} + result = docstore.filter_documents(filters=filters_simplified) + assert all( + int(doc.metadata["year"]) >= 2020 + and int(doc.metadata["year"]) <= 2021 + and doc.metadata["name"] in ["name_0", "name_1"] + for doc in result + ) + + def test_filter_simple_or(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters = {"$or": {"name": {"$in": ["name_0", "name_1"]}, "number": {"$lt": 1.0}}} + result = docstore.filter_documents(filters=filters) + assert all(doc.metadata["name"] in ["name_0", "name_1"] or doc.metadata["number"] < 1.0 for doc in result) + + def test_filter_nested_or(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters = {"$or": {"name": {"$or": [{"$eq": "name_0"}, {"$eq": "name_1"}]}, "number": {"$lt": 1.0}}} + result = docstore.filter_documents(filters=filters) + assert all(doc.metadata["name"] in ["name_0", "name_1"] or doc.metadata["number"] < 1.0 for doc in result) + + def test_filter_nested_and_or(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters_simplified = { + "year": {"$lte": "2021", "$gte": "2020"}, + "$or": {"name": {"$in": ["name_0", "name_1"]}, "number": {"$lt": 1.0}}, + } + result = docstore.filter_documents(filters=filters_simplified) + assert all( + (int(doc.metadata["year"]) >= 2020 and int(doc.metadata["year"]) <= 2021) + and (doc.metadata["name"] in ["name_0", "name_1"] or doc.metadata["number"] < 1.0) + for doc in result + ) + + def test_filter_nested_or_and(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters_simplified = { + "$or": { + "number": {"$lt": 1.0}, + "$and": {"name": {"$in": ["name_0", "name_1"]}, "$not": {"month": {"$eq": "01"}}}, + } + } + result = docstore.filter_documents(filters=filters_simplified) + assert all( + doc.metadata.get("number", 2) < 1.0 + or (doc.metadata["name"] in ["name_0", "name_1"] and doc.metadata["month"] != "01") + for doc in result + ) + + def test_filter_nested_multiple_identical_operators_same_level(self, docstore, filterable_docs): + self.direct_write(docstore, filterable_docs) + filters = { + "$or": [ + {"$and": {"name": {"$in": ["name_0", "name_1"]}, "year": {"$gte": "2020"}}}, + {"$and": {"name": {"$in": ["name_0", "name_1"]}, "year": {"$lt": "2021"}}}, + ] + } + result = docstore.filter_documents(filters=filters) + assert all(doc.metadata["name"] in ["name_0", "name_1"] for doc in result) + + def test_write(self, docstore): + doc = Document(content="test doc") + docstore.write_documents(documents=[doc]) + assert self.direct_access(docstore, doc_id=doc.id) == doc + + def test_write_duplicate_fail(self, docstore): + doc = Document(content="test doc") + self.direct_write(docstore, [doc]) + with pytest.raises(DuplicateDocumentError, match=f"ID '{doc.id}' already exists."): + docstore.write_documents(documents=[doc]) + assert self.direct_access(docstore, doc_id=doc.id) == doc + + def test_write_duplicate_skip(self, docstore): + doc = Document(content="test doc") + self.direct_write(docstore, [doc]) + docstore.write_documents(documents=[doc], duplicates="skip") + assert self.direct_access(docstore, doc_id=doc.id) == doc + + def test_write_duplicate_overwrite(self, docstore): + doc1 = Document(content="test doc 1") + doc2 = Document(content="test doc 2") + object.__setattr__(doc2, "id", doc1.id) # Make two docs with different content but same ID + + self.direct_write(docstore, [doc2]) + assert self.direct_access(docstore, doc_id=doc1.id) == doc2 + docstore.write_documents(documents=[doc1], duplicates="overwrite") + assert self.direct_access(docstore, doc_id=doc1.id) == doc1 + + def test_write_not_docs(self, docstore): + with pytest.raises(ValueError, match="Please provide a list of Documents"): + docstore.write_documents(["not a document for sure"]) + + def test_write_not_list(self, docstore): + with pytest.raises(ValueError, match="Please provide a list of Documents"): + docstore.write_documents("not a list actually") + + def test_delete_empty(self, docstore): + with pytest.raises(MissingDocumentError): + docstore.delete_documents(["test"]) + + def test_delete_not_empty(self, docstore): + doc = Document(content="test doc") + self.direct_write(docstore, [doc]) + + docstore.delete_documents([doc.id]) + + with pytest.raises(Exception): + assert self.direct_access(docstore, doc_id=doc.id) + + def test_delete_not_empty_nonexisting(self, docstore): + doc = Document(content="test doc") + self.direct_write(docstore, [doc]) + + with pytest.raises(MissingDocumentError): + docstore.delete_documents(["non_existing"]) + + assert self.direct_access(docstore, doc_id=doc.id) == doc diff --git a/test/preview/document_stores/test_memory.py b/test/preview/document_stores/test_memory.py new file mode 100644 index 000000000..68b5c3c95 --- /dev/null +++ b/test/preview/document_stores/test_memory.py @@ -0,0 +1,38 @@ +import pytest +from haystack.preview.document_stores import MemoryDocumentStore + +from test.preview.document_stores._base import DocumentStoreBaseTests + + +class TestMemoryDocumentStore(DocumentStoreBaseTests): + """ + Test MemoryDocumentStore's specific features + """ + + @pytest.fixture + def docstore(self) -> MemoryDocumentStore: + return MemoryDocumentStore() + + def direct_access(self, docstore, doc_id): + """ + Bypass `filter_documents()` + """ + return docstore.storage[doc_id] + + def direct_write(self, docstore, documents): + """ + Bypass `write_documents()` + """ + for doc in documents: + docstore.storage[doc.id] = doc + + def direct_delete(self, docstore, ids): + """ + Bypass `delete_documents()` + """ + for doc_id in ids: + del docstore.storage[doc_id] + + # + # Test retrieval + # diff --git a/test/preview/test_files/images/apple.jpg b/test/preview/test_files/images/apple.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f9023fea2cf935f2786880010e662074495b3eec GIT binary patch literal 69286 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

3q(kEeHIL}j@>1~3Iwh(vpa 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