Skip to content

Commit

Permalink
Support facets in multi-valued objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Cito committed Aug 6, 2024
1 parent da578de commit 9397a52
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 27 deletions.
6 changes: 6 additions & 0 deletions src/mass/adapters/outbound/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ def pipeline_facet_sort_and_paginate(
if not name:
name = name_from_key(facet.key)
segment[name] = [
{
"$unwind": {
"path": prefix,
"preserveNullAndEmptyArrays": True,
}
},
{
"$unwind": {
"path": f"{prefix}.{specified_field}",
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/test_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ searchable_classes:
- key: species
- key: eats
name: Food
- key: friends.name
name: Friend
selected_fields:
- key: name
resource_change_event_topic: searchable_resources
Expand Down
58 changes: 58 additions & 0 deletions tests/fixtures/test_data/FilteringTests.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
"eats": [
"bananas"
],
"friends": [
{
"name": "Hector"
},
{
"name": "Jack"
}
],
"id_": "1",
"name": "Jack",
"species": "monkey"
Expand All @@ -13,6 +21,17 @@
"dog food",
"treats"
],
"friends": [
{
"name": "Elle"
},
{
"name": "Jack"
},
{
"name": "Paulette"
}
],
"id_": "2",
"name": "Bruiser",
"species": "dog"
Expand All @@ -22,6 +41,17 @@
"spaghetti",
"meatballs"
],
"friends": [
{
"name": "Jog"
},
{
"name": "Tramp"
},
{
"name": "Trusty"
}
],
"id_": "3",
"name": "Lady",
"species": "dog"
Expand All @@ -34,6 +64,20 @@
"spaghetti",
"treats"
],
"friends": [
{
"name": "Arlene"
},
{
"name": "Jack"
},
{
"name": "Jon"
},
{
"name": "Odie"
}
],
"id_": "4",
"name": "Garfield",
"species": "cat"
Expand All @@ -43,6 +87,20 @@
"fish",
"shrimp"
],
"friends": [
{
"name": "Buddy"
},
{
"name": "Jon"
},
{
"name": "Peter"
},
{
"name": "Sandy"
}
],
"id_": "5",
"name": "Flipper",
"species": "dolphin"
Expand Down
95 changes: 88 additions & 7 deletions tests/test_filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@

from tests.fixtures.joint import JointFixture, QueryParams

pytestmark = pytest.mark.asyncio()

CLASS_NAME = "FilteringTests"


@pytest.mark.asyncio
async def test_facets(joint_fixture: JointFixture):
"""Test that the facets are returned properly"""
params: QueryParams = {"class_name": CLASS_NAME}

results = await joint_fixture.call_search_endpoint(params)

facets = results.facets
assert len(facets) == 2
assert len(facets) == 3

facet = facets[0]
assert facet.key == "species"
Expand All @@ -55,13 +56,33 @@ async def test_facets(joint_fixture: JointFixture):
}
assert list(options) == sorted(options)

facet = facets[2]
assert facet.key == "friends.name"
assert facet.name == "Friend"
options = {option.value: option.count for option in facet.options}
assert options == {
"Arlene": 1,
"Buddy": 1,
"Elle": 1,
"Hector": 1,
"Jack": 3,
"Jog": 1,
"Jon": 2,
"Odie": 1,
"Paulette": 1,
"Peter": 1,
"Sandy": 1,
"Tramp": 1,
"Trusty": 1,
}
assert list(options) == sorted(options)


@pytest.mark.parametrize(
"species,names",
[("mouse", []), ("cat", ["Garfield"]), ("dog", ["Bruiser", "Lady"])],
ids=[0, 1, 2],
)
@pytest.mark.asyncio
async def test_single_valued_with_with_single_filter(
species: str, names: list[str], joint_fixture: JointFixture
):
Expand All @@ -80,7 +101,7 @@ async def test_single_valued_with_with_single_filter(

# Check that the facet only contains the filtered values
facets = results.facets
assert len(facets) == 2
assert len(facets) == 3
facet = facets[0]
assert facet.key == "species"
assert facet.name == "Species"
Expand All @@ -99,7 +120,6 @@ async def test_single_valued_with_with_single_filter(
[("broccoli", []), ("bananas", ["Jack"]), ("fish", ["Garfield", "Flipper"])],
ids=[0, 1, 2],
)
@pytest.mark.asyncio
async def test_multi_valued_with_with_single_filter(
food: str, names: list[str], joint_fixture: JointFixture
):
Expand All @@ -118,7 +138,7 @@ async def test_multi_valued_with_with_single_filter(

# Check that the facet only contains the filtered values
facets = results.facets
assert len(facets) == 2
assert len(facets) == 3
facet = facets[1]
assert facet.key == "eats"
assert facet.name == "Food"
Expand All @@ -141,7 +161,6 @@ async def test_multi_valued_with_with_single_filter(
assert not options


@pytest.mark.asyncio
async def test_multiple_filters(joint_fixture: JointFixture):
"""Test the combination of multiple filters.
Expand All @@ -159,3 +178,65 @@ async def test_multiple_filters(joint_fixture: JointFixture):
# Only Jack and Garfield fulfill these conditions
returned_names = [resource.content["name"] for resource in results.hits]
assert returned_names == ["Jack", "Garfield"]


@pytest.mark.parametrize(
"friend,names",
[
("Oscar", []),
("Paulette", ["Bruiser"]),
("Jon", ["Garfield", "Flipper"]),
("Jack", ["Jack", "Bruiser", "Garfield"]),
],
ids=[0, 1, 2, 3],
)
async def test_filter_for_common_friend(
friend: str, names: list[str], joint_fixture: JointFixture
):
"""Test that we can search for animals with a common friend"""
params: QueryParams = {
"class_name": CLASS_NAME,
"filter_by": "friends.name",
"value": friend,
}

results = await joint_fixture.call_search_endpoint(params)

# Check that the expected names are returned
returned_names = [resource.content["name"] for resource in results.hits]
assert returned_names == names

# Check that the facet contains the friend in question
facets = results.facets
assert len(facets) == 3
facet = facets[2]
assert facet.key == "friends.name"
assert facet.name == "Friend"
if names:
assert any(option.value == friend for option in facet.options)


async def test_filter_for_couple_of_friends(joint_fixture: JointFixture):
"""Test that we can search for animals who have one of the specified friends"""
params: QueryParams = {
"class_name": CLASS_NAME,
"filter_by": ["friends.name"] * 3,
"value": ["Hector", "Sandy", "Tramp"],
}

results = await joint_fixture.call_search_endpoint(params)

# only Jack, Lady and Flipper have Hector, Sandy or Tramp as friend
returned_names = [resource.content["name"] for resource in results.hits]
assert returned_names == ["Jack", "Lady", "Flipper"]

# Check that the facet contains the selected friends and all their co-friends
facets = results.facets
assert len(facets) == 3
facet = facets[2]
assert facet.key == "friends.name"
assert facet.name == "Friend"
option_values = [option.value for option in facet.options]
co_friends = "Buddy Hector Jack Jog Jon Peter Sandy Tramp Trusty".split()
assert option_values == co_friends
assert all(option.count == 1 for option in facet.options)
2 changes: 1 addition & 1 deletion tests/test_index_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@


@pytest.mark.parametrize("create_index_manually", [False, True], ids=["auto", "manual"])
@pytest.mark.asyncio
@pytest.mark.asyncio()
async def test_index_creation(joint_fixture: JointFixture, create_index_manually: bool):
"""Test the index creation function."""
# indexes will have been created in fixture setup, so we actually need to del those
Expand Down
2 changes: 1 addition & 1 deletion tests/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"Deletion failure due to non-existent accession",
],
)
@pytest.mark.asyncio
@pytest.mark.asyncio()
async def test_event_sub_logging(
joint_fixture: JointFixture,
caplog,
Expand Down
9 changes: 2 additions & 7 deletions tests/test_relevance.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from mass.core import models
from tests.fixtures.joint import JointFixture, QueryParams

pytestmark = pytest.mark.asyncio()

CLASS_NAME: str = "RelevanceTests"
RELEVANCE_SORT = models.SortingParameter(
field="score", order=models.SortOrder.RELEVANCE
Expand Down Expand Up @@ -98,7 +100,6 @@ def sorted_reference_results(
return [result["_id"] for result in sorted_results]


@pytest.mark.asyncio
async def test_happy_relevance(joint_fixture: JointFixture):
"""Make sure default works as expected"""
query = "test"
Expand All @@ -115,7 +116,6 @@ async def test_happy_relevance(joint_fixture: JointFixture):
assert [hit.id_ for hit in results.hits] == reference_ids


@pytest.mark.asyncio
async def test_happy_relevance_descending_id(joint_fixture: JointFixture):
"""Make sure default Pydantic model parameter works as expected"""
query = "test"
Expand All @@ -136,7 +136,6 @@ async def test_happy_relevance_descending_id(joint_fixture: JointFixture):
assert [hit.id_ for hit in results.hits] == reference_ids


@pytest.mark.asyncio
async def test_with_absent_term(joint_fixture: JointFixture):
"""Make sure nothing is pulled back with an absent term (sanity check)"""
params: QueryParams = {"class_name": CLASS_NAME, "query": "doesnotexistinourtests"}
Expand All @@ -146,7 +145,6 @@ async def test_with_absent_term(joint_fixture: JointFixture):
assert results.count == 0


@pytest.mark.asyncio
async def test_limited_term(joint_fixture: JointFixture):
"""Make sure only results with the term are retrieved"""
query = "alternative"
Expand All @@ -160,7 +158,6 @@ async def test_limited_term(joint_fixture: JointFixture):
assert [hit.id_ for hit in results.hits] == reference_ids


@pytest.mark.asyncio
async def test_two_words(joint_fixture: JointFixture):
"""Test with two different terms that appear in different fields"""
query = "alternative test"
Expand All @@ -174,7 +171,6 @@ async def test_two_words(joint_fixture: JointFixture):
assert [hit.id_ for hit in results.hits] == reference_ids


@pytest.mark.asyncio
async def test_with_filters(joint_fixture: JointFixture):
"""Test with filters applied but no sorting parameters"""
query = "test"
Expand All @@ -196,7 +192,6 @@ async def test_with_filters(joint_fixture: JointFixture):
assert [hit.id_ for hit in results.hits] == reference_ids


@pytest.mark.asyncio
async def test_with_filters_and_sorts(joint_fixture: JointFixture):
"""Test with filters applied and at least one sorting parameter (not relevance)"""
query = "test"
Expand Down
Loading

0 comments on commit 9397a52

Please sign in to comment.