Skip to content

Implement faceting in search #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions datasets/small_movies.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
"title": "Shazam!",
"poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
"overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
"release_date": 1553299200
"release_date": 1553299200,
"genre": "action"
},
{
"id": "299537",
"title": "Captain Marvel",
"poster": "https://image.tmdb.org/t/p/w1280/AtsgWhDnHTq68L0lLsUrCnM7TjG.jpg",
"overview": "The story follows Carol Danvers as she becomes one of the universe’s most powerful heroes when Earth is caught in the middle of a galactic war between two alien races. Set in the 1990s, Captain Marvel is an all-new adventure from a previously unseen period in the history of the Marvel Cinematic Universe.",
"release_date": 1551830400
"release_date": 1551830400,
"genre": "action"
},
{
"id": "522681",
Expand All @@ -25,7 +27,8 @@
"title": "How to Train Your Dragon: The Hidden World",
"poster": "https://image.tmdb.org/t/p/w1280/xvx4Yhf0DVH8G4LzNISpMfFBDy2.jpg",
"overview": "As Hiccup fulfills his dream of creating a peaceful dragon utopia, Toothless’ discovery of an untamed, elusive mate draws the Night Fury away. When danger mounts at home and Hiccup’s reign as village chief is tested, both dragon and rider must make impossible decisions to save their kind.",
"release_date": 1546473600
"release_date": 1546473600,
"genre": "cartoon"
},
{
"id": "450465",
Expand All @@ -46,14 +49,16 @@
"title": "Dumbo",
"poster": "https://image.tmdb.org/t/p/w1280/279PwJAcelI4VuBtdzrZASqDPQr.jpg",
"overview": "A young elephant, whose oversized ears enable him to fly, helps save a struggling circus, but when the circus plans a new venture, Dumbo and his friends discover dark secrets beneath its shiny veneer.",
"release_date": 1553644800
"release_date": 1553644800,
"genre": "cartoon"
},
{
"id": "299536",
"title": "Avengers: Infinity War",
"poster": "https://image.tmdb.org/t/p/w1280/7WsyChQLEftFiDOVTGkv3hFpyyt.jpg",
"overview": "As the Avengers and their allies have continued to protect the world from threats too large for any one hero to handle, a new danger has emerged from the cosmic shadows: Thanos. A despot of intergalactic infamy, his goal is to collect all six Infinity Stones, artifacts of unimaginable power, and use them to inflict his twisted will on all of reality. Everything the Avengers have fought for has led up to this moment - the fate of Earth and existence itself has never been more uncertain.",
"release_date": 1524618000
"release_date": 1524618000,
"genre": "action"
},
{
"id": "458723",
Expand All @@ -74,21 +79,24 @@
"title": "Cars",
"poster": "https://image.tmdb.org/t/p/w1280/5damnMcRFKSjhCirgX3CMa88MBj.jpg",
"overview": "Lightning McQueen, a hotshot rookie race car driven to succeed, discovers that life is about the journey, not the finish line, when he finds himself unexpectedly detoured in the sleepy Route 66 town of Radiator Springs. On route across the country to the big Piston Cup Championship in California to compete against two seasoned pros, McQueen gets to know the town's offbeat characters.",
"release_date": 1149728400
"release_date": 1149728400,
"genre": "cartoon"
},
{
"id": "299534",
"title": "Avengers: Endgame",
"poster": "https://image.tmdb.org/t/p/w1280/dHjLaIUHXcMBt7YxK1TKWK1end9.jpg",
"overview": "After the devastating events of Avengers: Infinity War, the universe is in ruins due to the efforts of the Mad Titan, Thanos. With the help of remaining allies, the Avengers must assemble once more in order to undo Thanos' actions and restore order to the universe once and for all, no matter what consequences may be in store.",
"release_date": 1556067600
"release_date": 1556067600,
"genre": "action"
},
{
"id": "324857",
"title": "Spider-Man: Into the Spider-Verse",
"poster": "https://image.tmdb.org/t/p/w1280/iiZZdoQBEYBv6id8su7ImL0oCbD.jpg",
"overview": "Miles Morales is juggling his life between being a high school student and being a spider-man. When Wilson 'Kingpin' Fisk uses a super collider, others from across the Spider-Verse are transported to this dimension.",
"release_date": 1544140800
"release_date": 1544140800,
"genre": "action"
},
{
"id": "157433",
Expand Down Expand Up @@ -137,7 +145,8 @@
"title": "Aquaman",
"poster": "https://image.tmdb.org/t/p/w1280/5Kg76ldv7VxeX9YlcQXiowHgdX6.jpg",
"overview": "Once home to the most advanced civilization on Earth, Atlantis is now an underwater kingdom ruled by the power-hungry King Orm. With a vast army at his disposal, Orm plans to conquer the remaining oceanic people and then the surface world. Standing in his way is Arthur Curry, Orm's half-human, half-Atlantean brother and true heir to the throne.",
"release_date": 1544140800
"release_date": 1544140800,
"genre": "action"
},
{
"id": "512196",
Expand Down Expand Up @@ -186,7 +195,8 @@
"title": "Fantastic Beasts: The Crimes of Grindelwald",
"poster": "https://image.tmdb.org/t/p/w1280/fMMrl8fD9gRCFJvsx0SuFwkEOop.jpg",
"overview": "Gellert Grindelwald has escaped imprisonment and has begun gathering followers to his cause—elevating wizards above all non-magical beings. The only one capable of putting a stop to him is the wizard he once called his closest friend, Albus Dumbledore. However, Dumbledore will need to seek help from the wizard who had thwarted Grindelwald once before, his former student Newt Scamander, who agrees to help, unaware of the dangers that lie ahead. Lines are drawn as love and loyalty are tested, even among the truest friends and family, in an increasingly divided wizarding world.",
"release_date": 1542153600
"release_date": 1542153600,
"genre": "fantasy"
},
{
"id": "399579",
Expand Down
17 changes: 11 additions & 6 deletions meilisearch/index.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import urllib
from datetime import datetime
from time import sleep
Expand Down Expand Up @@ -228,7 +229,8 @@ def get_stats(self):
)
)

def search(self, query, opt_params=None):
# pylint: disable=dangerous-default-value
def search(self, query, opt_params={}):
"""Search in meilisearch

Parameters
Expand All @@ -243,13 +245,16 @@ def search(self, query, opt_params=None):
results: `dict`
Dictionnary with hits, offset, limit, processingTime and initial query
"""
if opt_params is None:
opt_params = {}
search_param = {'q': query}
# Query parameters parsing
for key in opt_params:
if isinstance(opt_params[key], list):
if key in ('facetsDistribution', 'facetFilters'):
opt_params[key] = json.dumps(opt_params[key])
elif isinstance(opt_params[key], list):
opt_params[key] = ','.join(opt_params[key])
params = {**search_param, **opt_params}
params = {
'q': query,
**opt_params
}
return self.http.get(
'{}/{}/{}?{}'.format(
self.config.paths.index,
Expand Down
59 changes: 56 additions & 3 deletions meilisearch/tests/index/test_index_search_meilisearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_custom_search(self):
assert '_formatted' in response['hits'][0]
assert 'dragon' in response['hits'][0]['_formatted']['title'].lower()

def test_basic_search_params_with_wildcard(self):
def test_custom_search_params_with_wildcard(self):
"""Tests search with '*' in query params"""
response = self.index.search(
'a',
Expand All @@ -69,7 +69,7 @@ def test_basic_search_params_with_wildcard(self):
assert '_formatted' in response['hits'][0]
assert "title" in response['hits'][0]['_formatted']

def test_basic_search_params_with_simple_string(self):
def test_custom_search_params_with_simple_string(self):
"""Tests search with simple string in query params"""
response = self.index.search(
'a',
Expand All @@ -86,7 +86,7 @@ def test_basic_search_params_with_simple_string(self):
assert 'title' in response['hits'][0]['_formatted']
assert not 'release_date' in response['hits'][0]['_formatted']

def test_basic_search_params_with_string_list(self):
def test_custom_search_params_with_string_list(self):
"""Tests search with string list in query params"""
response = self.index.search(
'a',
Expand All @@ -103,3 +103,56 @@ def test_basic_search_params_with_string_list(self):
assert not 'release_date' in response['hits'][0]
assert 'title' in response['hits'][0]['_formatted']
assert not 'overview' in response['hits'][0]['_formatted']

def test_custom_search_params_with_facets_distribution(self):
update = self.index.update_attributes_for_faceting(['genre'])
self.index.wait_for_pending_update(update['updateId'])
response = self.index.search(
'world',
{
'facetsDistribution': ['genre']
}
)
assert isinstance(response, object)
assert len(response['hits']) == 12
assert 'facetsDistribution' in response
assert 'exhaustiveFacetsCount' in response
assert response['exhaustiveFacetsCount']
assert 'genre' in response['facetsDistribution']
assert response['facetsDistribution']['genre']['cartoon'] == 1
assert response['facetsDistribution']['genre']['action'] == 3
assert response['facetsDistribution']['genre']['fantasy'] == 1

def test_custom_search_params_with_facet_filters(self):
update = self.index.update_attributes_for_faceting(['genre'])
self.index.wait_for_pending_update(update['updateId'])
response = self.index.search(
'world',
{
'facetFilters': [['genre:action']]
}
)
assert isinstance(response, object)
assert len(response['hits']) == 3
assert 'facetsDistribution' not in response
assert 'exhaustiveFacetsCount' not in response

def test_custom_search_params_with_many_params(self):
update = self.index.update_attributes_for_faceting(['genre'])
self.index.wait_for_pending_update(update['updateId'])
response = self.index.search(
'world',
{
'facetFilters': [['genre:action']],
'attributesToRetrieve': ['title', 'poster']
}
)
assert isinstance(response, object)
assert len(response['hits']) == 3
assert 'facetsDistribution' not in response
assert 'exhaustiveFacetsCount' not in response
assert 'title' in response['hits'][0]
assert 'poster' in response['hits'][0]
assert 'overview' not in response['hits'][0]
assert 'release_date' not in response['hits'][0]
assert response['hits'][0]['title'] == 'Avengers: Infinity War'
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class TestDisplayedAttributes:

client = meilisearch.Client(BASE_URL, MASTER_KEY)
index = None
displayed_attributes = ['id', 'release_date', 'title', 'poster', 'overview']
displayed_attributes = ['id', 'release_date', 'title', 'poster', 'overview', 'genre']
dataset_file = None
dataset_json = None

Expand Down