Skip to content
Open
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
23 changes: 23 additions & 0 deletions meta/definitions/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ paths:
operationId: cards
parameters:
- $ref: '#/components/parameters/filter'
- $ref: '#/components/parameters/anyName'
- $ref: '#/components/parameters/sortField'
- $ref: '#/components/parameters/sortOrder'
- $ref: '#/components/parameters/paginationPage'
Expand Down Expand Up @@ -1001,6 +1002,28 @@ components:
type: string
example: "{\"name\": \"eq:Furret\", \"hp\": \"lte:60\"}"

anyName:
name: anyName
in: query
required: false
description: |
Search cards by name across all languages.

Unlike the `name` filter which only matches the name in the requested language,
`anyName` searches every language's card names and returns the matching cards
in the requested language.

Supports the same operators as other filters:
- Default/`like:` - Contains match (case insensitive)
- `eq:` - Exact match
- `not:`/`notlike:` - Does not contain
- `neq:` - Not equal

Example: searching `anyName=Dracaufeu` on `/v2/en/cards` returns Charizard.
schema:
type: [string, null]
example: "Dracaufeu"

sortField:
name: "sort:field"
in: query
Expand Down
23 changes: 22 additions & 1 deletion server/src/V2/Components/Card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import zhcn from '../../../generated/zh-cn/cards.json'
import zhtw from '../../../generated/zh-tw/cards.json'
import { getCardMarketPrice } from '../../libs/providers/cardmarket'
import { getTCGPlayerPrice } from '../../libs/providers/tcgplayer'
import { executeQuery, type Query } from '../../libs/QueryEngine/filter'
import { executeQuery, type Query, type QueryValues } from '../../libs/QueryEngine/filter'

// any is CompiledCard that is currently not mapped correctly
const list: Record<`${string | any}${SupportedLanguages | string}`, any> = {}
Expand Down Expand Up @@ -148,6 +148,27 @@ export async function findCards(lang: SupportedLanguages, query: Query<SDKCard>)
return executeQuery(await getAllCards(lang), query).data
}

/**
* Returns the set of card IDs (lowercased) whose name matches the given query value
* in ANY language's compiled card list.
*
* This allows callers to search by a card's name regardless of which language
* the name was provided in (e.g. searching "Dracaufeu" in /v2/en/cards still
* returns Charizard).
*/
export function getMatchingIdsByAnyName(nameQueryValue: QueryValues<string>): Set<string> {
const matchingIds = new Set<string>()

for (const langCards of Object.values(cards)) {
const matched = executeQuery(langCards as Array<{ id: string; name: string }>, { name: nameQueryValue })
for (const card of matched.data) {
matchingIds.add(card.id.toLowerCase())
}
}

return matchingIds
}

export async function findOneCard(lang: SupportedLanguages, query: Query<SDKCard>) {
const res = await findCards(lang, query)
if (res.length === 0) {
Expand Down
9 changes: 8 additions & 1 deletion server/src/V2/endpoints/jsonEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Errors, sendError } from '../../libs/Errors'
import type { Query } from '../../libs/QueryEngine/filter'
import { recordToQuery } from '../../libs/QueryEngine/parsers'
import { betterSorter, checkLanguage, unique } from '../../util'
import { getAllCards, findOneCard, findCards, toBrief, getCardById, getCompiledCard } from '../Components/Card'
import { getAllCards, findOneCard, findCards, toBrief, getCardById, getCompiledCard, getMatchingIdsByAnyName } from '../Components/Card'
import { findOneSet, findSets, setToBrief } from '../Components/Set'
import { findOneSerie, findSeries, serieToBrief } from '../Components/Serie'
import { listSKUs } from '../../libs/providers/tcgplayer'
Expand Down Expand Up @@ -130,6 +130,13 @@ server

switch (endpoint) {
case 'cards': {
if ('anyName' in query) {
const anyNameQuery = query.anyName
delete query.anyName
const matchingIds = Array.from(getMatchingIdsByAnyName(anyNameQuery as any))
// @ts-expect-error id is not in the SDKCard query type but works at runtime
query.id = { $in: matchingIds }
}
if ('set' in query) {
const tmp = query.set
delete query.set
Expand Down