Skip to content

Commit fba15ea

Browse files
committed
chore: refine station query
1 parent cb32405 commit fba15ea

File tree

1 file changed

+165
-108
lines changed

1 file changed

+165
-108
lines changed

server/src/models/Station.js

Lines changed: 165 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,45 @@ class Station extends Model {
8282
onlyGmaxStationed,
8383
onlyInactiveStations,
8484
} = args.filters
85+
86+
const battleLevelFilters = new Set()
87+
const battleComboFilters = new Map()
88+
if (onlyMaxBattles && onlyBattleTier === 'all') {
89+
Object.entries(args.filters || {}).forEach(([key, value]) => {
90+
if (!value) return
91+
if (key.startsWith('j')) {
92+
const parsedLevel = Number(key.slice(1))
93+
if (Number.isFinite(parsedLevel)) {
94+
battleLevelFilters.add(parsedLevel)
95+
}
96+
return
97+
}
98+
if (/^\d+-/.test(key)) {
99+
const [idPart, formPart] = key.split('-', 2)
100+
const pokemonId = Number(idPart)
101+
if (!Number.isFinite(pokemonId)) return
102+
let formValue = null
103+
if (formPart && formPart !== 'null') {
104+
const parsedForm = Number(formPart)
105+
if (!Number.isFinite(parsedForm)) return
106+
formValue = parsedForm
107+
}
108+
const comboKey = `${pokemonId}-${formValue ?? 'null'}`
109+
if (!battleComboFilters.has(comboKey)) {
110+
battleComboFilters.set(comboKey, { pokemonId, form: formValue })
111+
}
112+
}
113+
})
114+
}
115+
const battleLevels = [...battleLevelFilters]
116+
const battleCombos = [...battleComboFilters.values()]
117+
118+
if (!onlyAllStations && !onlyInactiveStations && !perms.dynamax) {
119+
return []
120+
}
121+
85122
const ts = getEpoch()
86-
const select = [
123+
const baseSelect = [
87124
'id',
88125
'name',
89126
'lat',
@@ -93,8 +130,7 @@ class Station extends Model {
93130
'end_time',
94131
]
95132

96-
const query = this.query()
97-
applyManualIdFilter(query, {
133+
const manualFilterOptions = {
98134
manualId: args.filters.onlyManualId,
99135
latColumn: 'lat',
100136
lonColumn: 'lon',
@@ -105,30 +141,16 @@ class Station extends Model {
105141
minLon: args.minLon,
106142
maxLon: args.maxLon,
107143
},
108-
})
144+
}
145+
146+
const select = [...baseSelect]
147+
148+
const query = this.query()
149+
applyManualIdFilter(query, manualFilterOptions)
109150
const now = Date.now() / 1000
110151
const activeCutoff = now - stationUpdateLimit * 60 * 60
111152
const inactiveCutoff = now - stationInactiveLimitDays * 24 * 60 * 60
112153

113-
if (onlyInactiveStations) {
114-
query.andWhere((builder) => {
115-
builder
116-
.where((active) =>
117-
active
118-
.where('end_time', '>', ts)
119-
.andWhere('updated', '>', activeCutoff),
120-
)
121-
.orWhere((inactive) =>
122-
inactive
123-
.where('end_time', '<=', ts)
124-
.andWhere('updated', '>', inactiveCutoff),
125-
)
126-
})
127-
} else {
128-
query.andWhere('end_time', '>', ts).andWhere('updated', '>', activeCutoff)
129-
}
130-
// .where('is_inactive', false)
131-
132154
if (perms.dynamax && (onlyMaxBattles || onlyGmaxStationed)) {
133155
select.push(
134156
'is_battle_available',
@@ -151,58 +173,116 @@ class Station extends Model {
151173
if (hasBattlePokemonStats) {
152174
select.push('battle_pokemon_stamina', 'battle_pokemon_cp_multiplier')
153175
}
176+
}
154177

155-
if (!onlyAllStations) {
156-
query.whereNotNull('battle_pokemon_id').andWhere('battle_end', '>', ts)
178+
const applyStationFilters = (builder) => {
179+
if (onlyAllStations) return
180+
if (!perms.dynamax) {
181+
builder.whereRaw('0 = 1')
182+
return
183+
}
157184

158-
query.andWhere((station) => {
159-
if (hasStationedGmax || !onlyGmaxStationed)
160-
station.where((battle) => {
185+
builder.andWhere((station) => {
186+
let applied = false
187+
188+
if (onlyMaxBattles) {
189+
const hasBattleConditions =
190+
onlyBattleTier !== 'all' ||
191+
battleLevels.length > 0 ||
192+
battleCombos.length > 0
193+
if (hasBattleConditions) {
194+
const method = applied ? 'orWhere' : 'where'
195+
station[method]((battle) => {
196+
battle
197+
.whereNotNull('battle_pokemon_id')
198+
.andWhere('battle_end', '>', ts)
161199
if (onlyBattleTier === 'all') {
162-
const battleBosses = new Set()
163-
const battleForms = new Set()
164-
const battleLevels = new Set()
165-
166-
Object.keys(args.filters).forEach((key) => {
167-
switch (key.charAt(0)) {
168-
case 'o':
169-
break
170-
case 'j':
171-
battleLevels.add(key.slice(1))
172-
break
173-
default:
174-
{
175-
const [id, form] = key.split('-')
176-
if (id) battleBosses.add(id)
177-
if (form) battleForms.add(form)
200+
battle.andWhere((match) => {
201+
let matchApplied = false
202+
if (battleLevels.length) {
203+
const levelMethod = matchApplied ? 'orWhereIn' : 'whereIn'
204+
match[levelMethod]('battle_level', battleLevels)
205+
matchApplied = true
206+
}
207+
battleCombos.forEach(({ pokemonId, form }) => {
208+
const comboMethod = matchApplied ? 'orWhere' : 'where'
209+
match[comboMethod]((combo) => {
210+
combo.where('battle_pokemon_id', pokemonId)
211+
if (form === null) {
212+
combo.andWhereNull('battle_pokemon_form')
213+
} else {
214+
combo.andWhere('battle_pokemon_form', form)
178215
}
179-
break
216+
})
217+
matchApplied = true
218+
})
219+
if (!matchApplied) {
220+
match.whereRaw('0 = 1')
180221
}
181222
})
182-
if (battleBosses.size) {
183-
battle.andWhere('battle_pokemon_id', 'in', [...battleBosses])
184-
}
185-
if (battleForms.size) {
186-
battle.andWhere('battle_pokemon_form', 'in', [...battleForms])
187-
}
188-
if (battleLevels.size) {
189-
battle.andWhere('battle_level', 'in', [...battleLevels])
190-
}
191223
} else {
192224
battle.andWhere('battle_level', onlyBattleTier)
193225
}
194226
})
195-
if (hasStationedGmax && onlyGmaxStationed)
196-
station.orWhere('total_stationed_gmax', '>', 0)
227+
applied = true
228+
}
229+
}
230+
231+
if (onlyGmaxStationed) {
232+
if (hasStationedGmax) {
233+
const method = applied ? 'orWhere' : 'where'
234+
station[method]('total_stationed_gmax', '>', 0)
235+
applied = true
236+
} else {
237+
const method = applied ? 'orWhere' : 'where'
238+
station[method]((gmax) => {
239+
gmax.whereRaw(
240+
"JSON_SEARCH(COALESCE(stationed_pokemon, '[]'), 'one', ?, NULL, '$[*].bread_mode') IS NOT NULL",
241+
['2'],
242+
)
243+
gmax.orWhereRaw(
244+
"JSON_SEARCH(COALESCE(stationed_pokemon, '[]'), 'one', ?, NULL, '$[*].bread_mode') IS NOT NULL",
245+
['3'],
246+
)
247+
})
248+
applied = true
249+
}
250+
}
251+
252+
if (!applied) {
253+
station.whereRaw('0 = 1')
254+
}
255+
})
256+
}
257+
258+
query.select(select)
259+
260+
if (onlyInactiveStations) {
261+
query.andWhere((builder) => {
262+
builder.where((active) => {
263+
active
264+
.where('end_time', '>', ts)
265+
.andWhere('updated', '>', activeCutoff)
266+
applyStationFilters(active)
197267
})
198-
}
268+
// Battle data etc of inactive stations should be ignored since they are outdated by design
269+
builder.orWhere((inactive) =>
270+
inactive
271+
.where('end_time', '<=', ts)
272+
.andWhere('updated', '>', inactiveCutoff),
273+
)
274+
})
275+
} else {
276+
query.andWhere('end_time', '>', ts).andWhere('updated', '>', activeCutoff)
277+
applyStationFilters(query)
199278
}
200279

201280
if (!getAreaSql(query, areaRestrictions, onlyAreas, isMad)) {
202281
return []
203282
}
204283

205-
const stations = await query.select(select)
284+
/** @type {import('@rm/types').FullStation[]} */
285+
const stations = await query
206286

207287
let pokemonData = null
208288
if (hasBattlePokemonStats && perms.dynamax) {
@@ -224,56 +304,33 @@ class Station extends Model {
224304
}
225305
}
226306

227-
return stations
228-
.map((station) => {
229-
if (station.is_battle_available && station.battle_pokemon_id === null) {
230-
station.is_battle_available = false
231-
}
232-
if (station.total_stationed_pokemon === null) {
233-
station.total_stationed_pokemon = 0
234-
}
235-
if (
236-
station.stationed_pokemon &&
237-
(station.total_stationed_gmax === undefined ||
238-
station.total_stationed_gmax === null)
239-
) {
240-
const list =
241-
typeof station.stationed_pokemon === 'string'
242-
? JSON.parse(station.stationed_pokemon)
243-
: station.stationed_pokemon || []
244-
let count = 0
245-
if (list)
246-
for (let i = 0; i < list.length; ++i)
247-
if (list[i].bread_mode === 2 || list[i].bread_mode === 3) ++count
248-
station.total_stationed_gmax = count
249-
}
250-
station.battle_pokemon_estimated_cp = pokemonData
251-
? estimateStationCp(pokemonData, station)
252-
: null
253-
return station
254-
})
255-
.filter((station) => {
256-
if (Number.isFinite(station.end_time) && station.end_time <= ts) {
257-
return onlyInactiveStations
258-
}
259-
260-
const matchesBattleFilter =
261-
onlyMaxBattles &&
262-
(onlyBattleTier === 'all'
263-
? args.filters[`j${station.battle_level}`] ||
264-
args.filters[
265-
`${station.battle_pokemon_id}-${station.battle_pokemon_form}`
266-
]
267-
: onlyBattleTier === station.battle_level)
268-
269-
const matchesGmaxFilter =
270-
onlyGmaxStationed && station.total_stationed_gmax > 0
271-
272-
return (
273-
onlyAllStations ||
274-
(perms.dynamax && (matchesBattleFilter || matchesGmaxFilter))
275-
)
276-
})
307+
return stations.map((station) => {
308+
if (station.is_battle_available && station.battle_pokemon_id === null) {
309+
station.is_battle_available = false
310+
}
311+
if (station.total_stationed_pokemon === null) {
312+
station.total_stationed_pokemon = 0
313+
}
314+
if (
315+
station.stationed_pokemon &&
316+
(station.total_stationed_gmax === undefined ||
317+
station.total_stationed_gmax === null)
318+
) {
319+
const list =
320+
typeof station.stationed_pokemon === 'string'
321+
? JSON.parse(station.stationed_pokemon)
322+
: station.stationed_pokemon || []
323+
let count = 0
324+
if (list)
325+
for (let i = 0; i < list.length; ++i)
326+
if (list[i].bread_mode === 2 || list[i].bread_mode === 3) ++count
327+
station.total_stationed_gmax = count
328+
}
329+
station.battle_pokemon_estimated_cp = pokemonData
330+
? estimateStationCp(pokemonData, station)
331+
: null
332+
return station
333+
})
277334
}
278335

279336
/**

0 commit comments

Comments
 (0)