From 2f701156b3d267d2995dabc326153413c1304a2e Mon Sep 17 00:00:00 2001 From: Correl Date: Wed, 21 Feb 2024 22:42:15 -0500 Subject: [PATCH] Add exact name match search syntax --- README.org | 1 + tutor/database.py | 10 ++++++++-- tutor/search.py | 10 ++++++++-- tutor/server.py | 1 + tutor/templates/openapi.yaml | 21 +++++++++++++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/README.org b/README.org index 82b0fff..15cbf08 100644 --- a/README.org +++ b/README.org @@ -11,6 +11,7 @@ cards with certain properties. - ~bolt~ :: Find all cards with "bolt" in the name - ~"God of"~ :: Find all cards with "God of" in the name +- ~!"urza's incubator"~ :: Find all cards with the exact name "Urza's Incubator" - ~t:legendary t:creature c:jund~ :: Find all legendary creatures with a color identity of red/blue/green - ~color<=ubg~ :: Find all spells that are blue, black, green, or any diff --git a/tutor/database.py b/tutor/database.py index a72e4fe..0be9747 100644 --- a/tutor/database.py +++ b/tutor/database.py @@ -118,8 +118,14 @@ async def advanced_search( for i, criterion in enumerate(search.criteria): param = f"param_{i}" if isinstance(criterion, tutor.search.Name): - constraints.append(f"cards.name ILIKE %({param})s") - params[param] = f"%{criterion.text}%" + if criterion.operator == tutor.search.Operator.lte: + logger.info("Search NAME LTE") + constraints.append(f"cards.name ILIKE %({param})s") + params[param] = f"%{criterion.text}%" + if criterion.operator == tutor.search.Operator.matches: + logger.info("Search NAME MATCHES") + constraints.append(f"cards.name ILIKE %({param})s") + params[param] = f"{criterion.text}" if isinstance(criterion, tutor.search.Type): constraints.append(f"cards.type_line ILIKE %({param})s") params[param] = f"%{criterion.text}%" diff --git a/tutor/search.py b/tutor/search.py index abe2162..d623dcc 100644 --- a/tutor/search.py +++ b/tutor/search.py @@ -51,7 +51,7 @@ class Collection(Criterion): @dataclasses.dataclass class Name(Criterion): - operator: Operator = Operator.matches + operator: Operator = Operator.lte text: parsy.string = "" @@ -180,7 +180,13 @@ collection = parsy.seq( text=string_literal, ).combine_dict(Collection) -name = parsy.seq(text=string_literal).combine_dict(Name) + +exact_name = parsy.seq(_prefix=lstring_from("!"), text=string_literal).combine_dict( + functools.partial(Name, operator=Operator.matches) +) +partial_name = parsy.seq(text=string_literal).combine_dict(Name) + +name = exact_name | partial_name criterion = colors | expansion | rarity | type_line | oracle | collection | name diff --git a/tutor/server.py b/tutor/server.py index 4aadf30..e459bf9 100644 --- a/tutor/server.py +++ b/tutor/server.py @@ -212,6 +212,7 @@ class SearchHandler(RequestHandler): limit = int(self.get_argument("limit", "10")) sort_by = self.get_argument("sort_by", "rarity") search = tutor.search.search.parse(query) + logger.debug(search) copies = await tutor.database.advanced_search( cursor, search, diff --git a/tutor/templates/openapi.yaml b/tutor/templates/openapi.yaml index 38e4fdd..197fef2 100644 --- a/tutor/templates/openapi.yaml +++ b/tutor/templates/openapi.yaml @@ -108,21 +108,30 @@ paths: `bolt` : Find all cards with "bolt" in the name + `"God of"` : Find all cards with "God of" in the name + + `!"urza's incubator"` + : Find all cards with the exact name "Urza's Incubator" + + `t:legendary t:creature c:jund` : Find all legendary creatures with a color identity of red/blue/green + `color<=ubg` : Find all spells that are blue, black, green, or any combination thereof. + `color:red set:stx rarity>=rare` : Find all red cards in Strixhaven that are rare or mythic + `t:enchantment o:"enters the battlefield"` : Find all enchantments with ETB effects @@ -134,28 +143,35 @@ paths: Keywords : `c`, `color` + Operators : `:` (matches), `>=` (greater than or equal to), `<=` (less than or equal to) + Matches cards of the chosen color or colors. + Single colors : `w` or `white`, `u` or `blue`, `b` or `black, =g` or `green`, `r` or `red` + Any combination of abbreviated single colors : e.g.: `rg`, `uw`, or `wubgr` + Ravnican guilds : `boros` (white/red), `golgari` (green/black), `selesnya` (green/white), `dimir` (blue/black), `orzhov` (white/black), `izzet` (blue/red), `gruul` (red/green), `azorius` (white/blue), `rakdos` (black/red), `simic` (green/blue) + Alaran shards : `bant` (white/green/blue), `esper` (blue/white/black), `grixis` (black/blue/red), `jund` (red/black/green), `naya` (green/red/white) + Tarkirian wedges : `abzan` (white/black/green), `jeskai` (white/blue/red), `sultai` (blue/black/green), `mardu` (white/black/red), `temur` @@ -166,6 +182,7 @@ paths: Keywords : `s`, `set`, `e`, `expansion` + Operators : `:` (matches) @@ -174,6 +191,7 @@ paths: Keywords : `r`, `rarity` + Operators : `:` (matches), `>=` (greater than or equal to), `<=` (less than or equal to) @@ -183,6 +201,7 @@ paths: Keywords : `t`, `type` + Operators : `:` (matches) @@ -191,6 +210,7 @@ paths: Keywords : `o`, `oracle` + Operators : `:` (matches) @@ -199,6 +219,7 @@ paths: Keywords : `collection` + Operators : `:` (matches) responses: