diff --git a/src/doc/tutorials/complex_data_models.rst b/src/doc/tutorials/complex_data_models.rst
index b33bc9ae49e9a850a73e32e396735652ecaab7d7..168cf3b9f0d6839ed8f78beb01ae24fb9d489e88 100644
--- a/src/doc/tutorials/complex_data_models.rst
+++ b/src/doc/tutorials/complex_data_models.rst
@@ -51,18 +51,18 @@ Examples
    # Very complex part of the data model:
    # Case 1: File added to another file
    f2.add_property(p1, value=f1)  # this adds a file property with value first file
-		                  # to the second file
+                          # to the second file
 
    # Case 2: Property added to a property
    p2.add_property(p3, value=27)  # this adds an integer property with value 27 to the
-		                  # double property
+                          # double property
 
    # Case 3: Reference property added to a property
    # The property p2 now has two sub properties, one is pointing to
    # record p2 which itself has the property p2, therefore this can be
    # considered a loop in the data model.
    p2.add_property(p4, value=r2)  # this adds a reference property pointing to
-		                  # record 2 to the double property
+                          # record 2 to the double property
 
    # Insert a container containing all the newly created entities:
    c = db.Container().extend([rt1, rt2, r1, r2, f1, p1, p2, p3, f2, p4])
@@ -87,46 +87,42 @@ entities. A short example:
 
    import linkahead as db
 
-   # Setup a record with four properties
+   # Setup a record with six properties
    r = db.Record()
-   p1 = db.Property(id=101, name="Property 1")
-   p2 = db.Property(id=102, name="Property 2")
-   p3_1 = db.Property(id=103, name="Property 3")
-   p3_2 = db.Property(id=103, name="Property 3")
-   p4 = db.Property(name="Property")
-   p5 = db.Property(name="Property")
-   r.add_property(p1).add_property(p2).add_property(p3_1)
-   r.add_property(p3_2).add_property(p4).add_property(p5)
+   p1_1 = db.Property(id=101, name="Property 1")
+   p1_2 = db.Property(name="Property 1")
+   p2_1 = db.Property(id=102, name="Property 2")
+   p2_2 = db.Property(id=102)
+   p2_3 = db.Property(id=102, name="Other Property")
+   p3 = db.Property(id=104, name="Other Property")
+   r.add_property(p1_1).add_property(p1_2).add_property(p2_1)
+   r.add_property(p2_2).add_property(p2_3).add_property(p3)
    properties = r.properties
 
-   # As r only has one property with id 101, this returns a list containing only p1
+   # As r only has one property with id 101, this returns a list containing only p1_1
    properties.filter(pid=101)
-   # Result: [p1]
-
-   # Filtering with name="Property" returns both p4 and p5, as they share their name
-   properties.filter(name="Property")
-   # Result: [p4, p5]
-
-   # Filtering with name="Property 1" and id=102 returns both p1 and p2, because
-   # any property matching either criterion is returned:
-   properties.filter(name="Property 1", pid="102")
-   # Result: [p1, p2]
-
-   p6 = db.Property(name="Property 2")
-   r.add_property(p6)
-   # If we want to find properties matching one specific property, we can also filter using
-   # the entity itself. In this case, only properties matching both name and id are returned,
-   # as long as both are set.
-   properties.filter(p2)
-   # Result: [p2]
-   # As p6 does not have an id yet, both candidates matching its name are returned
-   properties.filter(p6)
-   # Result: [p2, p6]
-   # Similarly if we match using name and id parameters, all candidates matching either are returned
-   properties.filter(name=p2.name, pid=p2.id)
-   # Result: [p2, p6], because p2 and p6 share a name
-   # And if both name and id match, there may also be several results when matching an entity
-   properties.filter(p3_1)
-   # Result: [p3_1, p3_2], because they share both their name and id
+   # Result: [p1_1]
+
+   # Filtering with name="Property 1" returns both p1_1 and p1_2, as they share their name
+   properties.filter(name="Property 1")
+   # Result: [p1_1, p1_2]
+
+   #  If both name and pid are given, matching is based only on pid for all entities that have an id
+   properties.filter(pid="102", name="Other Property")
+   # Result: [p2_1, p2_2, p2_3]
+
+   # However, filtering with name="Property 1" and id=101 returns both p1_1 and p1_2, because
+   # p1_2 does not have an id and matches the name
+   properties.filter(pid="101", name="Property 1")
+   # Result: [p1_1, p1_2]
+
+   # We can also filter using an entity, in which case the name and id of the entity are used:
+   properties.filter(pid="102", name="Property 2") == properties.filter(p2_1)
+   # Result: True
+
+   # If we only need properties that match both id and name, we can set the parameter
+   # conjunction to True:
+   properties.filter(pid="102", name="Property 2", conjunction=True)
+   # Result: [p2_1]
 
 The filter function of ParentList works analogously.
diff --git a/src/linkahead/apiutils.py b/src/linkahead/apiutils.py
index d51171c7c59fd0ae8ee202db224f2597f3e9cdae..17bd5af4b223b9d0db84b2124b0393e07ba2f80c 100644
--- a/src/linkahead/apiutils.py
+++ b/src/linkahead/apiutils.py
@@ -251,12 +251,16 @@ def compare_entities(entity0: Optional[Entity] = None,
     #    - Should there be a more detailed comparison of parents without id?
     #    - Revisit filter - do we care about RecordType when matching?
     #      How to treat None?
+    #    - Should matching of parents also take the recordtype into account
+    #      for parents that have a name but no id?
     # Suggestions for enhancements:
     #    - For the comparison of entities in value and properties, consider
     #      keeping a list of traversed entities, not only look at first layer
     #    - Make the empty_diff functionality faster by adding a parameter to
     #      this function so that it returns after the first found difference?
     #    - Add parameter to restrict diff to some characteristics
+    #    - Implement comparison of date where one is a string and the other is
+    #      datetime
     if entity0 is None and old_entity is None:
         raise ValueError("Please provide the first entity as first argument (`entity0`)")
     if entity1 is None and new_entity is None:
@@ -264,12 +268,12 @@ def compare_entities(entity0: Optional[Entity] = None,
     if old_entity is not None:
         warnings.warn("Please use 'entity0' instead of 'old_entity'.", DeprecationWarning)
         if entity0 is not None:
-            raise ValueError("You cannot use both, entity0 and old_entity")
+            raise ValueError("You cannot use both entity0 and old_entity")
         entity0 = old_entity
     if new_entity is not None:
         warnings.warn("Please use 'entity1' instead of 'new_entity'.", DeprecationWarning)
         if entity1 is not None:
-            raise ValueError("You cannot use both, entity1 and new_entity")
+            raise ValueError("You cannot use both entity1 and new_entity")
         entity1 = new_entity
 
     diff: tuple = ({"properties": {}, "parents": []},
@@ -371,7 +375,7 @@ def compare_entities(entity0: Optional[Entity] = None,
 
     # compare properties
     for prop in entity0.properties:
-        matching = entity1.properties.filter(prop, check_wrapped=False)
+        matching = entity1.properties.filter(name=prop.name, pid=prop.id)
         if len(matching) == 0:
             # entity1 has prop, entity0 does not
             diff[0]["properties"][prop.name] = {}
diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py
index dd0718c79717bb7a983d8da2b59b9c73ecbd96f3..1dbeb802311c7afaea2340af15e49537520ef57f 100644
--- a/src/linkahead/common/models.py
+++ b/src/linkahead/common/models.py
@@ -40,22 +40,22 @@ import sys
 import warnings
 from builtins import str
 from copy import deepcopy
-from enum import Enum
 from datetime import date, datetime
+from enum import Enum
 from functools import cmp_to_key
 from hashlib import sha512
 from os import listdir
 from os.path import isdir
 from random import randint
 from tempfile import NamedTemporaryFile
-from typing import TYPE_CHECKING
-from typing import Any, Final, Literal, Optional, TextIO, Union
+from typing import TYPE_CHECKING, Any, Final, Literal, Optional, TextIO, Union
 
 if TYPE_CHECKING:
-    from .datatype import DATATYPE
-    from tempfile import _TemporaryFileWrapper
     from io import BufferedWriter
     from os import PathLike
+    from tempfile import _TemporaryFileWrapper
+
+    from .datatype import DATATYPE
     QueryDict = dict[str, Optional[str]]
 
 from warnings import warn
@@ -65,36 +65,17 @@ from lxml import etree
 from ..configuration import get_config
 from ..connection.connection import get_connection
 from ..connection.encode import MultipartParam, multipart_encode
-from ..exceptions import (
-    AmbiguousEntityError,
-    AuthorizationError,
-    ConsistencyError,
-    EmptyUniqueQueryError,
-    EntityDoesNotExistError,
-    EntityError,
-    EntityHasNoAclError,
-    EntityHasNoDatatypeError,
-    HTTPURITooLongError,
-    LinkAheadConnectionError,
-    LinkAheadException,
-    MismatchingEntitiesError,
-    PagingConsistencyError,
-    QueryNotUniqueError,
-    TransactionError,
-    UniqueNamesError,
-    UnqualifiedParentsError,
-    UnqualifiedPropertiesError,
-)
-from .datatype import (
-    BOOLEAN,
-    DATETIME,
-    DOUBLE,
-    INTEGER,
-    TEXT,
-    get_list_datatype,
-    is_list_datatype,
-    is_reference,
-)
+from ..exceptions import (AmbiguousEntityError, AuthorizationError,
+                          ConsistencyError, EmptyUniqueQueryError,
+                          EntityDoesNotExistError, EntityError,
+                          EntityHasNoAclError, EntityHasNoDatatypeError,
+                          HTTPURITooLongError, LinkAheadConnectionError,
+                          LinkAheadException, MismatchingEntitiesError,
+                          PagingConsistencyError, QueryNotUniqueError,
+                          TransactionError, UniqueNamesError,
+                          UnqualifiedParentsError, UnqualifiedPropertiesError)
+from .datatype import (BOOLEAN, DATETIME, DOUBLE, INTEGER, TEXT,
+                       get_list_datatype, is_list_datatype, is_reference)
 from .state import State
 from .timezone import TimeZone
 from .utils import uuid, xml2str
@@ -2522,38 +2503,31 @@ class PropertyList(list):
 
         return xml2str(xml)
 
-    def filter(self, prop: Optional[Property] = None, pid: Union[None, str, int] = None,
-               name: Optional[str] = None, check_wrapped: bool = True) -> list:
+    def filter(self, prop: Optional[Property] = None,
+               pid: Union[None, str, int] = None,
+               name: Optional[str] = None,
+               conjunction: bool = False) -> list:
         """
         Return all Properties from the given PropertyList that match the
         selection criteria.
 
-        You can provide name or ID and all matching elements will be returned.
-        If both name and ID are given, elements matching either criterion will
-        be returned.
-
-        If a Property is given, neither name nor ID may be set. In this case,
-        only elements matching both name and ID of the Property are returned.
-
-        Also checks the original Properties wrapped within the elements of
-        PropertyList and will return the original Property if both wrapper and
-        original match.
+        Please refer to the documentation of _filter_entity_list for a detailed
+        description of behaviour.
 
         Params
         ------
         listobject        : Iterable(Property)
                             List to be filtered
         prop              : Property
-                            Property to match name and ID with. Cannot be
-                            set simultaneously with ID or name.
+                            Property to match name and ID with. Cannot be set
+                            simultaneously with ID or name.
         pid               : str, int
                             Property ID to match
         name              : str
                             Property name to match
-        check_wrapped     : bool, default: True
-                            If set to False, only the wrapper elements
-                            contained in the given PropertyList will be
-                            checked, not the original Properties they wrap.
+        conjunction       : bool, defaults to False
+                            Set to return only entities that match both id and name
+                            if both are given.
 
         Returns
         -------
@@ -2561,7 +2535,7 @@ class PropertyList(list):
                            List containing all matching Properties
         """
         return _filter_entity_list(self, pid=pid, name=name, entity=prop,
-                                   check_wrapped=check_wrapped)
+                                   conjunction=conjunction)
 
     def _get_entity_by_cuid(self, cuid: str):
         '''
@@ -2693,22 +2667,16 @@ class ParentList(list):
 
         return xml2str(xml)
 
-    def filter(self, parent: Optional[Parent] = None, pid: Union[None, str, int] = None,
-               name: Optional[str] = None, check_wrapped: bool = True) -> list:
+    def filter(self, parent: Optional[Parent] = None,
+               pid: Union[None, str, int] = None,
+               name: Optional[str] = None,
+               conjunction: bool = False) -> list:
         """
         Return all Parents from the given ParentList that match the selection
         criteria.
 
-        You can provide name or ID and all matching elements will be returned.
-        If both name and ID are given, elements matching either criterion will
-        be returned.
-
-        If a Parent is given, neither name nor ID may be set. In this case,
-        only elements matching both name and ID of the Parent are returned.
-
-        Also checks the original Parents wrapped within the elements of
-        ParentList, will return the original Parent if both wrapper and
-        original match.
+        Please refer to the documentation of _filter_entity_list for a detailed
+        description of behaviour.
 
         Params
         ------
@@ -2721,10 +2689,9 @@ class ParentList(list):
         name              : str
                             Parent name to match
                             simultaneously with ID or name.
-        check_wrapped     : bool, default: True
-                            If set to False, only the wrapper elements
-                            contained in the given ParentList will be
-                            checked, not the original Parents they wrap.
+        conjunction       : bool, defaults to False
+                            Set to return only entities that match both id and name
+                            if both are given.
 
         Returns
         -------
@@ -2732,7 +2699,7 @@ class ParentList(list):
                            List containing all matching Parents
         """
         return _filter_entity_list(self, pid=pid, name=name, entity=parent,
-                                   check_wrapped=check_wrapped)
+                                   conjunction=conjunction)
 
     def remove(self, parent: Union[Entity, int, str]):
         """
@@ -5498,24 +5465,33 @@ def delete(ids: Union[list[int], range], raise_exception_on_error: bool = True):
     return c.delete(raise_exception_on_error=raise_exception_on_error)
 
 
-def _filter_entity_list(listobject, entity: Optional[Entity] = None, pid: Union[None, str, int] = None,
-                        name: Optional[str] = None, check_wrapped: bool = True) -> list:
+def _filter_entity_list(listobject: list[Entity],
+                        entity: Optional[Entity] = None,
+                        pid: Union[None, str, int] = None,
+                        name: Optional[str] = None,
+                        conjunction: bool = False) -> list:
     """
-    Return all elements from the given list that match the selection criteria.
+    Returns a subset of entities from the list based on whether their id and
+    name matches the selection criterion.
+
+    If both pid and name are given, entities from the list are first matched
+    based on id. If they do not have an id, they are matched based on name.
+    If only one parameter is given, only this parameter is considered.
 
-    You can provide name or ID and all matching elements will be returned.
-    If both name and ID are given, elements matching either criterion will be
-    returned.
+    If an Entity is given, neither name nor ID may be set. In this case, pid
+    and name are determined by the attributes of given entity.
 
-    If an Entity is given, neither name nor ID may be set. In this case, only
-    elements matching both name and ID of the Entity are returned, as long as
-    name and ID are both set.
+    This results in the following selection criteria:
+    If an entity in the list
+    - has both name and id, it is returned if the id matches the given not-None
+      value for pid. If no pid was given, it is returned if the name matches.
+    - has an id, but no name, it will be returned only if it matches the given
+      not-None value
+    - has no id, but a name, it will be returned if the name matches the given
+      not-None value
+    - has neither id nor name, it will never be returned
 
-    In case the elements contained in the given list are wrapped, the function
-    in its default configuration checks both the wrapped and wrapper Entity
-    against the match criteria, and will return the wrapped Entity if both
-    match. Note that this is currently not iterative, meaning that only the
-    first layer of wrapped entity is considered.
+    As IDs can be strings, integer IDs are cast to string for the comparison.
 
     Params
     ------
@@ -5528,10 +5504,9 @@ def _filter_entity_list(listobject, entity: Optional[Entity] = None, pid: Union[
                         Entity ID to match
     name              : str
                         Entity name to match
-    check_wrapped     : bool, default: True
-                        If set to False, only the wrapper elements
-                        contained in the given list will be checked and
-                        returned, not the original Entities they wrap.
+    conjunction       : bool, defaults to False
+                        Set to true to return only entities that match both id
+                        and name if both are given.
 
     Returns
     -------
@@ -5539,57 +5514,40 @@ def _filter_entity_list(listobject, entity: Optional[Entity] = None, pid: Union[
                        A List containing all matching Entities
     """
     # Check correct input params and setup
-    match_entity = False
     if entity is not None:
         if pid is not None or name is not None:
-            raise ValueError("Please provide either Entity, pid or name.")
+            raise ValueError("If an entity is given, pid and name must not be set.")
         pid = entity.id
         name = entity.name
-        match_entity = True
+    if pid is None and name is None:
+        if entity is None:
+            raise ValueError("One of entity, pid or name must be set.")
+        else:
+            raise ValueError("A given entity must have at least one of name and id.")
+    if pid is None or name is None:
+        conjunction = False
 
     # Iterate through list and match based on given criteria
     matches = []
-    potentials = list(zip(listobject.copy(), [False]*len(listobject)))
-    for candidate, wrapped_is_checked in potentials:
-        name_match, pid_match, original_candidate = False, False, None
-
-        # Parents/Properties may be wrapped - if wanted, try to match original
-        # Note: if we want to check all wrapped Entities, this should be
-        #       switched. First check the wrap, then append wrapped. In this
-        #       case we also don't need wrapped_checked, but preferentially
-        #       append the wrapper.
-        if check_wrapped and not wrapped_is_checked:
-            try:
-                if candidate._wrapped_entity is not None:
-                    original_candidate = candidate
-                    candidate = candidate._wrapped_entity
-            except AttributeError:
-                pass
+    for candidate in listobject:
+        name_match, pid_match = False, False
 
         # Check whether name/pid match
-        # pid and candidate.id might be int and str, so cast to str (None safe)
-        if pid is not None and str(candidate.id) == str(pid):
-            pid_match = True
-        elif match_entity and pid is None:
-            # Without a pid we cannot match
+        # Comparison is only possible if both are not None
+        pid_none = pid is None or candidate.id is None
+        # Cast to string in case one is f.e. "12" and the other is 12
+        if not pid_none and str(candidate.id) == str(pid):
             pid_match = True
-        if (name is not None and candidate.name is not None
-                and candidate.name.lower() == name.lower()):
-            name_match = True
-        elif match_entity and name is None:
-            # Without a name we cannot match
+        name_none = name is None or candidate.name is None
+        if not name_none and str(candidate.name).lower() == str(name).lower():
             name_match = True
 
-        # If the criteria are satisfied, append the match. Otherwise, check
-        # the wrapper if applicable
-        # ToDo: Check whether it would make sense to also check the RecordType
-        #       for equality when match_entity is true to offset potentially
-        #       missing id
-        if name_match and pid_match:
+        # If the criteria are satisfied, append the match.
+        if pid_match and name_match:
             matches.append(candidate)
-        elif not match_entity and (name_match or pid_match):
-            matches.append(candidate)
-        else:
-            if original_candidate is not None:
-                potentials.append((original_candidate, True))
+        elif not conjunction:
+            if pid_match:
+                matches.append(candidate)
+            if pid_none and name_match:
+                matches.append(candidate)
     return matches
diff --git a/unittests/test_apiutils.py b/unittests/test_apiutils.py
index 5b99ebc335ed2cb1dfffcda18e6d35d06f3a559d..2fb946c518d940bd505622284070a0f5fafdf12f 100644
--- a/unittests/test_apiutils.py
+++ b/unittests/test_apiutils.py
@@ -261,17 +261,16 @@ def test_compare_entities_units():
 
 
 def test_compare_entities_battery():
-    par1, par2, par3 = db.Record(), db.Record(), db.RecordType()
+    par1, par3 = db.Record(name=""), db.RecordType(name="")
     r1, r2, r3 = db.Record(), db.Record(), db.Record()
-    prop1 = db.Property()
     prop2 = db.Property(name="Property 2")
-    prop3 = db.Property()
+    prop3 = db.Property(name="")
 
     # Basic tests for Properties
     prop_settings = {"datatype": db.REFERENCE, "description": "desc of prop",
                      "value": db.Record().add_parent(par3), "unit": '°'}
-    t1 = db.Record().add_parent(db.RecordType())
-    t2 = db.Record().add_parent(db.RecordType())
+    t1 = db.Record().add_parent(db.RecordType(id=1))
+    t2 = db.Record().add_parent(db.RecordType(id=1))
     # Change datatype
     t1.add_property(db.Property(name="datatype", **prop_settings))
     prop_settings["datatype"] = par3
@@ -336,10 +335,10 @@ def test_compare_entities_battery():
 
     # Basic tests for special attributes
     prop_settings = {"id": 42, "name": "Property",
-                     "datatype": db.LIST(db.REFERENCE), "value": [db.Record()],
+                     "datatype": db.LIST(db.REFERENCE), "value": [db.Record(name="")],
                      "unit": '€', "description": "desc of prop"}
     alt_settings = {"id": 64, "name": "Property 2",
-                    "datatype": db.LIST(db.TEXT), "value": [db.RecordType()],
+                    "datatype": db.LIST(db.TEXT), "value": [db.RecordType(name="")],
                     "unit": '€€', "description": " ę Ě ப ཾ ཿ ∛ ∜ ㅿ ㆀ 값 "}
     t5 = db.Property(**prop_settings)
     t6 = db.Property(**prop_settings)
@@ -408,10 +407,10 @@ def test_compare_entities_battery():
     assert not empty_diff(db.File(path='ABC', file=StringIO("ABC")),
                           db.File(path='ABC', file=StringIO("Other")))
     # Importance
-    assert empty_diff(db.Property().add_property(prop1),
-                      db.Property().add_property(prop1))
-    assert not empty_diff(db.Property().add_property(prop1, importance=db.SUGGESTED),
-                          db.Property().add_property(prop1, importance=db.OBLIGATORY))
+    assert empty_diff(db.Property().add_property(prop2),
+                      db.Property().add_property(prop2))
+    assert not empty_diff(db.Property().add_property(prop2, importance=db.SUGGESTED),
+                          db.Property().add_property(prop2, importance=db.OBLIGATORY))
     # Mixed Lists
     assert empty_diff(db.Property(value=[1, 2, 'a', r1]),
                       db.Property(value=[1, 2, 'a', r1]))
@@ -435,7 +434,7 @@ def test_compare_entities_battery():
     assert not empty_diff(prop4, prop4_c, entity_name_id_equivalency=False)
     assert empty_diff(prop4, prop4_c, entity_name_id_equivalency=True)
     # Order invariance
-    t7 = db.Property(**prop_settings).add_parent(par1).add_property(prop1)
+    t7 = db.Property(**prop_settings).add_parent(par1).add_property(prop2)
     t8 = db.Property(**alt_settings).add_parent(par3).add_property(prop3)
     diffs_0 = compare_entities(t7, t8), compare_entities(t7, t8, True)
     diffs_1 = compare_entities(t8, t7)[::-1], compare_entities(t8, t7, True)[::-1]
@@ -444,6 +443,7 @@ def test_compare_entities_battery():
                      "value": db.Record().add_parent(par3), "unit": '°'}
     t1.add_property(db.Property(name="description", **prop_settings))
     t2.add_property(db.Property(name="description", **prop_settings))
+    # Order invariance for multi-property - either both fail or same result
     try:
         diffs_0 = compare_entities(t1, t2), compare_entities(t1, t2, True)
     except Exception as e:
@@ -453,6 +453,28 @@ def test_compare_entities_battery():
     except Exception as e:
         diffs_1 = type(e)
     assert diffs_0 == diffs_1
+    # Property types
+    t09, t10 = db.RecordType(), db.RecordType()
+    for t, ex in [(db.INTEGER, [-12, 0]), (db.DATETIME, ["2030-01-01", "1012-02-29"]),
+                  (db.DOUBLE, [13.23, 7.1]), (db.BOOLEAN, [True, False])]:
+        t09.add_property(db.Property(name=f"{t}:{ex[0]}", datatype=t, value=ex[0]))
+        t10.add_property(db.Property(name=f"{t}:{ex[0]}", datatype=t, value=ex[0]))
+        t09.add_property(name=f"{t}:{ex[1]}", datatype=t, value=ex[1])
+        t10.add_property(name=f"{t}:{ex[1]}", datatype=t, value=ex[1])
+    assert empty_diff(t09, t10)
+    t09.add_property(name=f"diff", value=1)
+    t10.add_property(name=f"diff", value=2)
+    assert not empty_diff(t09, t10)
+    # Default values
+    t09, t10 = db.Record(), db.Record()
+    t09.add_property(db.Property(name=f"A1"), value="A")
+    t10.add_property(name=f"A1", value="A")
+    t09.add_property(db.Property(id=12, name=f"A2"), value="A")
+    t10.add_property(id=12, name=f"A2", value="A")
+    t09.add_property(db.Property(id=15), value="A")
+    t10.add_property(id=15, value="A")
+    assert empty_diff(t09, t10)
+    # ToDo: extended tests for references
 
 
 def test_compare_special_properties():
diff --git a/unittests/test_entity.py b/unittests/test_entity.py
index 66cffbefe207820ddde1463f62a88788beb7df9a..2127ce028f4de55b8ef0ca704c1e69959c24ba82 100644
--- a/unittests/test_entity.py
+++ b/unittests/test_entity.py
@@ -25,14 +25,14 @@
 import os
 # pylint: disable=missing-docstring
 import unittest
-from pytest import raises
 
 import linkahead
-from linkahead import (INTEGER, Entity, Property, Record, RecordType, Parent,
+from linkahead import (INTEGER, Entity, Parent, Property, Record, RecordType,
                        configure_connection)
 from linkahead.common.models import SPECIAL_ATTRIBUTES
 from linkahead.connection.mockup import MockUpServerConnection
 from lxml import etree
+from pytest import raises
 
 UNITTESTDIR = os.path.dirname(os.path.abspath(__file__))
 
@@ -164,13 +164,13 @@ def test_property_list():
 def test_filter():
     rt1 = RecordType(id=100)
     rt2 = RecordType(id=101, name="RT")
-    rt3 = RecordType()
+    rt3 = RecordType(name="")
     p1 = Property(id=100)
     p2 = Property(id=100)
     p3 = Property(id=101, name="RT")
     p4 = Property(id=102, name="P")
     p5 = Property(id=103, name="P")
-    p6 = Property()
+    p6 = Property(name="")
     r1 = Record(id=100)
     r2 = Record(id=100)
     r3 = Record(id=101, name="RT")
@@ -180,61 +180,105 @@ def test_filter():
     test_ents = [rt1, rt2, rt3, p1, p2, p3, p4, p5, p6, r1, r2, r3, r4, r5, r6]
 
     # Setup
-    t1 = Property()
-    t1_props, t1_pars = t1.properties, t1.parents
-    t2 = Record()
-    t2_props, t2_pars = t2.properties, t2.parents
-    t3 = RecordType()
-    t3_props, t3_pars = t3.properties, t3.parents
-    test_colls = [t1_props, t1_pars, t2_props, t2_pars, t3_props, t3_pars]
-    for coll in test_colls:
-        for ent in test_ents:
-            assert ent not in coll
-            assert ent not in coll.filter(ent)
+    for entity in [Property(name=""), Record(name=""), RecordType(name="")]:
+        for coll in [entity.properties, entity.parents]:
+            for ent in test_ents:
+                assert ent not in coll
+                assert ent not in coll.filter(ent)
 
-    # Checks with each type
-    for t, t_props, t_pars in [(t1, t1_props, t1_pars), (t2, t2_props, t2_pars),
-                               (t3, t3_props, t3_pars)]:
+        # Checks with each type
+        t, t_props, t_pars = entity, entity.properties, entity.parents
         # Properties
         # Basic Checks
         t.add_property(p1)
+        tp1 = t.properties[-1]
         t.add_property(p3)
-        assert p1 in t_props.filter(pid=100)
-        assert p1 in t_props.filter(pid="100")
-        assert p1 not in t_props.filter(pid=101, name="RT")
+        tp3 = t.properties[-1]
+        assert len(t_props.filter(pid=100)) == 1
+        assert tp1 in t_props.filter(pid=100)
+        assert len(t_props.filter(pid="100")) == 1
+        assert tp1 in t_props.filter(pid="100")
+        assert len(t_props.filter(pid=101, name="RT")) == 1
+        assert tp3 in t_props.filter(pid=101, name="RT")
         for entity in [rt1, p2, r1, r2]:
             assert entity not in t_props.filter(pid=100)
-            assert p1 in t_props.filter(entity)
+            assert tp1 in t_props.filter(entity)
         # Check that direct addition (not wrapped) works
         t_props.append(p2)
-        assert p2 in t_props.filter(pid=100)
-        assert p2 not in t_props.filter(pid=101, name="RT")
+        tp2 = t_props[-1]
+        assert tp2 in t_props.filter(pid=100)
+        assert tp2 not in t_props.filter(pid=101, name="RT")
         for entity in [rt1, r1, r2]:
             assert entity not in t_props.filter(pid=100)
-            assert p2 in t_props.filter(entity)
+            assert tp2 in t_props.filter(entity)
 
         # Parents
         # Filtering with both name and id
         t.add_parent(r3)
+        tr3 = t.parents[-1]
         t.add_parent(r5)
-        assert r3 in t_pars.filter(pid=101)
-        assert r5 not in t_pars.filter(pid=101)
-        assert r3 not in t_pars.filter(name="R")
-        assert r5 in t_pars.filter(name="R")
-        assert r3 in t_pars.filter(pid=101, name="R")
-        assert r5 in t_pars.filter(pid=101, name="R")
-        assert r3 in t_pars.filter(pid=104, name="RT")
-        assert r5 in t_pars.filter(pid=104, name="RT")
-        assert r3 not in t_pars.filter(pid=105, name="T")
-        assert r5 not in t_pars.filter(pid=105, name="T")
+        tr5 = t.parents[-1]
+        assert tr3 in t_pars.filter(pid=101)
+        assert tr5 not in t_pars.filter(pid=101)
+        assert tr3 not in t_pars.filter(name="R")
+        assert tr5 in t_pars.filter(name="R")
+        assert tr3 in t_pars.filter(pid=101, name="R")
+        assert tr5 not in t_pars.filter(pid=101, name="R")
+        assert tr3 not in t_pars.filter(pid=104, name="RT")
+        assert tr5 in t_pars.filter(pid=104, name="RT")
+        assert tr3 not in t_pars.filter(pid=105, name="T")
+        assert tr5 not in t_pars.filter(pid=105, name="T")
         # Works also without id / name and with duplicate parents
         for ent in test_ents:
             t.add_parent(ent)
-        for ent in test_ents:
+        for ent in t_pars:
             assert ent in t_pars.filter(ent)
-        for ent in [rt1, p1, p2, r1, r2]:
-            filtered = t_pars.filter(ent)
-            for ent2 in [rt1, p1, p2, r1, r2]:
-                assert ent2 in filtered
-            assert ent in t_pars.filter(pid=100)
-            assert ent in t_pars.filter(pid="100")
+
+    # Grid-Based
+    r7 = Record()
+    r7.add_property(Property()).add_property(name="A").add_property(name="B")
+    r7.add_property(id=27).add_property(id=27, name="A").add_property(id=27, name="B")
+    r7.add_property(id=43).add_property(id=43, name="A").add_property(id=43, name="B")
+    assert len(r7.properties.filter(pid=27)) == 3
+    assert len(r7.properties.filter(pid=43)) == 3
+    assert len(r7.properties.filter(pid=43, conjunction=True)) == 3
+    assert len(r7.properties.filter(name="A")) == 3
+    assert len(r7.properties.filter(name="B")) == 3
+    assert len(r7.properties.filter(name="B", conjunction=True)) == 3
+    assert len(r7.properties.filter(pid=1, name="A")) == 1
+    assert len(r7.properties.filter(pid=1, name="A", conjunction=True)) == 0
+    assert len(r7.properties.filter(pid=27, name="B")) == 4
+    assert len(r7.properties.filter(pid=27, name="B", conjunction=True)) == 1
+    assert len(r7.properties.filter(pid=27, name="C")) == 3
+    assert len(r7.properties.filter(pid=27, name="C", conjunction=True)) == 0
+    # Entity based filtering behaves the same
+    assert (r7.properties.filter(pid=27) ==
+            r7.properties.filter(Property(id=27)))
+    assert (r7.properties.filter(pid=43, conjunction=True) ==
+            r7.properties.filter(Property(id=43), conjunction=True))
+    assert (r7.properties.filter(name="A") ==
+            r7.properties.filter(Property(name="A")))
+    assert (r7.properties.filter(name="B") ==
+            r7.properties.filter(Property(name="B")))
+    assert (r7.properties.filter(name="B", conjunction=True) ==
+            r7.properties.filter(Property(name="B"), conjunction=True))
+    assert (r7.properties.filter(pid=1, name="A") ==
+            r7.properties.filter(Property(id=1, name="A")))
+    assert (r7.properties.filter(pid=1, name="A", conjunction=True) ==
+            r7.properties.filter(Property(id=1, name="A"), conjunction=True))
+    assert (r7.properties.filter(pid=27, name="B") ==
+            r7.properties.filter(Property(id=27, name="B")))
+    assert (r7.properties.filter(pid=27, name="B", conjunction=True) ==
+            r7.properties.filter(Property(id=27, name="B"), conjunction=True))
+    assert (r7.properties.filter(pid=27, name="C") ==
+            r7.properties.filter(Property(id=27, name="C")))
+    assert (r7.properties.filter(pid=27, name="C", conjunction=True) ==
+            r7.properties.filter(Property(id=27, name="C"), conjunction=True))
+    # Name only matching and name overwrite
+    r8 = Record().add_property(name="A").add_property(name="B").add_property(name="B")
+    r8.add_property(Property(name="A"), name="B")
+    r8.add_property(Property(name="A", id=12), name="C")
+    assert len(r8.properties.filter(name="A")) == 1
+    assert len(r8.properties.filter(name="B")) == 3
+    assert len(r8.properties.filter(name="C")) == 1
+    assert len(r8.properties.filter(pid=12)) == 1