Skip to content
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

feat: Add better sorting/filtering/pagination #458

Merged
merged 7 commits into from
Jan 3, 2024
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
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ jobs:
cd server
bun install --frozen-lockfile

- name: Validate the data
run: bun run validate
- name: Validate the data & the server
run: |
bun run validate
cd server
bun run validate
95 changes: 71 additions & 24 deletions meta/definitions/graphql.gql
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,62 @@ directive @locale (
lang: String!
) on FIELD

# Queries to use on the DB
"""
Every queries available on the GraphQL API

If you have more queries that you would like added, make a new issue here

https://github.com/tcgdex/cards-database/issues/new/choose
"""
type Query {
cards(filters: CardsFilters, pagination: Pagination): [Card]
sets: [Set]
series: [Serie]
"""Find the cards"""
cards(filters: CardsFilters, pagination: Pagination, sort: Sort): [Card]

"""Find the sets"""
sets(filters: SetFilters, pagination: Pagination, sort: Sort): [Set]

"""Find the series"""
series(filters: SerieFilters, pagination: Pagination, sort: Sort): [Serie]

"""Find one card (using the id and set is deprecated)"""
card(
id: ID!,
set: String
set: String,
"""The new way to filter"""
filters: CardsFilters
): Card

"""Find one set (using the id is deprecated)"""
set(
id: ID!
id: ID!,
"""The new way to filter"""
filters: SetFilters
): Set

"""Find one serie (using the id is deprecated)"""
serie(
id: ID!
id: ID!,
"""The new way to filter"""
filters: SerieFilters
): Serie

}

# Pagination input
"""Paginate the datas you fetch"""
input Pagination {
page: Float!
count: Float!
"""Indicate the page number (from 1)"""
page: Int!
"""Indicate the number of items in one page"""
itemsPerPage: Int
count: Float @deprecated(reason: "use itemsPerPage instead")
}

"""Change how the data is sorted"""
input Sort {
"""Indicate which field it will sort using"""
field: String!
"""Indicate how it is sorted ("ASC" or "DESC) (default: "ASC")"""
order: String
}

##################
Expand All @@ -41,45 +75,45 @@ input CardsFilters {
description: String
energyType: String
evolveFrom: String
hp: Float
hp: Int
id: ID
localId: String
dexId: Float
dexId: Int
illustrator: String
image: String
level: Float
level: Int
levelId: String
name: String
rarity: String
regulationMark: String
stage: String
suffix: String
trainerType: String
retreat: Float
retreat: Int
}

type Card {
abilities: [AbilitiesListItem]
attacks: [AttacksListItem]
category: String!
description: String
dexId: [Float]
dexId: [Int]
effect: String
energyType: String
evolveFrom: String
hp: Float
hp: Int
id: String!
illustrator: String
image: String
item: Item
legal: Legal!
level: Float
level: Int
localId: String!
name: String!
rarity: String!
regulationMark: String
resistances: [WeakResListItem]
retreat: Float
retreat: Int
set: Set!
stage: String
suffix: String
Expand Down Expand Up @@ -143,13 +177,21 @@ type Set {
tcgOnline: String
}

input SetFilters {
id: String
name: String
serie: String
releaseDate: String
tcgOnline: String
}

type CardCount {
firstEd: Float
holo: Float
normal: Float
official: Float!
reverse: Float
total: Float!
firstEd: Int
holo: Int
normal: Int
official: Int!
reverse: Int
total: Int!
}

##################
Expand All @@ -163,6 +205,11 @@ type Serie {
sets: [Set]!
}

input SerieFilters {
id: String
name: String
}

##################
# StringEndpoint #
##################
Expand Down
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"scripts": {
"compile": "bun compiler/index.ts",
"dev": "bun --watch --hot src/index.ts",
"validate": "tsc --noEmit --project ./tsconfig.json",
"start": "bun src/index.ts"
},
"license": "MIT",
Expand Down
41 changes: 13 additions & 28 deletions server/src/V2/Components/Card.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { objectLoop } from '@dzeio/object-util'
import { Card as SDKCard, CardResume, SupportedLanguages } from '@tcgdex/sdk'
import { Pagination } from '../../interfaces'
import { lightCheck } from '../../util'
import { CardResume, Card as SDKCard, SupportedLanguages } from '@tcgdex/sdk'
import { Query } from '../../interfaces'
import { handlePagination, handleSort, handleValidation } from '../../util'
import Set from './Set'

type LocalCard = Omit<SDKCard, 'set'> & {set: () => Set}
Expand Down Expand Up @@ -55,40 +55,25 @@ export default class Card implements LocalCard {
})
}


public set(): Set {
return Set.findOne(this.lang, {id: this.card.set.id}) as Set
return Set.findOne(this.lang, {filters: { id: this.card.set.id }}) as Set
}

public static find(lang: SupportedLanguages, params: Partial<Record<keyof SDKCard, any>> = {}, pagination?: Pagination) {
let list : Array<SDKCard> = (require(`../../../generated/${lang}/cards.json`) as Array<SDKCard>)
.filter((c) => objectLoop(params, (it, key) => {
return lightCheck(c[key as 'localId'], it)
}))
if (pagination) {
list = list
.splice(pagination.count * pagination.page - 1, pagination.count)
}
return list.map((it) => new Card(lang, it))
public static getAll(lang: SupportedLanguages): Array<SDKCard> {
return require(`../../../generated/${lang}/cards.json`)
}

public static raw(lang: SupportedLanguages): Array<SDKCard> {
return require(`../../../generated/${lang}/cards.json`)
public static find(lang: SupportedLanguages, query: Query<SDKCard>) {
return handlePagination(handleSort(handleValidation(this.getAll(lang), query), query), query)
.map((it) => new Card(lang, it))
}

public static findOne(lang: SupportedLanguages, params: Partial<Record<keyof SDKCard, any>> = {}) {
const res = (require(`../../../generated/${lang}/cards.json`) as Array<SDKCard>).find((c) => {
return objectLoop(params, (it, key) => {
if (key === 'set' && typeof it === 'string') {
return (c['set'].id === it || lightCheck(c['set'].name, it))
}
return lightCheck(c[key as 'localId'], it)
})
})
if (!res) {
public static findOne(lang: SupportedLanguages, query: Query<SDKCard>) {
const res = handleValidation(this.getAll(lang), query)
if (res.length === 0) {
return undefined
}
return new Card(lang, res)
return new Card(lang, res[0])
}

public resume(): CardResume {
Expand Down
38 changes: 14 additions & 24 deletions server/src/V2/Components/Serie.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { objectLoop } from '@dzeio/object-util'
import { Serie as SDKSerie, SerieResume, SupportedLanguages } from '@tcgdex/sdk'
import { Pagination } from '../../interfaces'
import { lightCheck } from '../../util'
import { Query } from '../../interfaces'
import { handlePagination, handleSort, handleValidation } from '../../util'
import Set from './Set'

type LocalSerie = Omit<SDKSerie, 'sets'> & {sets: () => Array<Set>}
Expand All @@ -25,34 +25,24 @@ export default class Serie implements LocalSerie {
}

public sets(): Array<Set> {
return this.serie.sets.map((s) => Set.findOne(this.lang, {id: s.id}) as Set)
return this.serie.sets.map((s) => Set.findOne(this.lang, {filters: { id: s.id }}) as Set)
}

public static find(lang: SupportedLanguages, params: Partial<Record<keyof SDKSerie, any>> = {}, pagination?: Pagination) {
let list = (require(`../../../generated/${lang}/series.json`) as Array<SDKSerie>)
.filter((c) => objectLoop(params, (it, key) => {
if (key === 'id') return c[key] === it
return lightCheck(c[key as 'id'], it)
}))
if (pagination) {
list = list
.splice(pagination.count * pagination.page - 1, pagination.count)
}
return list.map((it) => new Serie(lang, it))
public static getAll(lang: SupportedLanguages): Array<SDKSerie> {
return require(`../../../generated/${lang}/series.json`)
}

public static find(lang: SupportedLanguages, query: Query<SDKSerie>) {
return handlePagination(handleSort(handleValidation(this.getAll(lang), query), query), query)
.map((it) => new Serie(lang, it))
}

public static findOne(lang: SupportedLanguages, params: Partial<Record<keyof Serie, any>> = {}): Serie | undefined {
const res = (require(`../../../generated/${lang}/series.json`) as Array<SDKSerie>)
.find((c) => {
return objectLoop(params, (it, key) => {
if (key === 'id') return c[key] === it
return lightCheck(c[key as 'id'], it)
})
})
if (!res) {
public static findOne(lang: SupportedLanguages, query: Query<SDKSerie>) {
const res = handleValidation(this.getAll(lang), query)
if (res.length === 0) {
return undefined
}
return new Serie(lang, res)
return new Serie(lang, res[0])
}

public resume(): SerieResume {
Expand Down
47 changes: 15 additions & 32 deletions server/src/V2/Components/Set.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { objectLoop } from '@dzeio/object-util'
import { Set as SDKSet, SetResume, SupportedLanguages } from '@tcgdex/sdk'
import { Pagination } from '../../interfaces'
import { lightCheck } from '../../util'
import { Query } from '../../interfaces'
import { handlePagination, handleSort, handleValidation } from '../../util'
import Card from './Card'
import Serie from './Serie'

Expand Down Expand Up @@ -39,45 +39,28 @@ export default class Set implements LocalSet {
symbol?: string | undefined

public serie(): Serie {
return Serie.findOne(this.lang, {id: this.set.serie.id}) as Serie
return Serie.findOne(this.lang, {filters: { id: this.set.serie.id }}) as Serie
}

public cards(): Array<Card> {
return this.set.cards.map((s) => Card.findOne(this.lang, {id: s.id}) as Card)
return this.set.cards.map((s) => Card.findOne(this.lang, { filters: { id: s.id }}) as Card)
}

public static find(lang: SupportedLanguages, params: Partial<Record<keyof SDKSet, any>> = {}, pagination?: Pagination) {
let list = (require(`../../../generated/${lang}/sets.json`) as Array<SDKSet>)
.filter((c) => objectLoop(params, (it, key) => {
if (key === 'id' || key === 'name') {
return c[key as 'id'].toLowerCase() === it.toLowerCase()
} else if (typeof it === 'string') {
return c[key as 'id'].toLowerCase().includes(it.toLowerCase())
}
return lightCheck(c[key as 'id'], it)
}))
if (pagination) {
list = list
.splice(pagination.count * pagination.page - 1, pagination.count)
}
return list.map((it) => new Set(lang, it))
public static getAll(lang: SupportedLanguages): Array<SDKSet> {
return require(`../../../generated/${lang}/sets.json`)
}

public static findOne(lang: SupportedLanguages, params: Partial<Record<keyof Set, any>> = {}) {
const res = (require(`../../../generated/${lang}/sets.json`) as Array<SDKSet>).find((c) => {
return objectLoop(params, (it, key) => {
if (key === 'id' || key === 'name') {
return c[key as 'id'].toLowerCase() === it.toLowerCase()
} else if (typeof it === 'string') {
return c[key as 'id'].toLowerCase().includes(it.toLowerCase())
}
return lightCheck(c[key as 'id'], it)
})
})
if (!res) {
public static find(lang: SupportedLanguages, query: Query<SDKSet>) {
return handlePagination(handleSort(handleValidation(this.getAll(lang), query), query), query)
.map((it) => new Set(lang, it))
}

public static findOne(lang: SupportedLanguages, query: Query<SDKSet>) {
const res = handleValidation(this.getAll(lang), query)
if (res.length === 0) {
return undefined
}
return new Set(lang, res)
return new Set(lang, res[0])
}

public resume(): SetResume {
Expand Down
Loading