diff --git a/server/infra/database/SpeciesRepositoryV2.ts b/server/infra/database/SpeciesRepositoryV2.ts index 19746451..26ef19de 100644 --- a/server/infra/database/SpeciesRepositoryV2.ts +++ b/server/infra/database/SpeciesRepositoryV2.ts @@ -6,146 +6,99 @@ import Session from './Session'; export default class SpeciesRepositoryV2 extends BaseRepository { constructor(session: Session) { super('tree_species', session); + this.tableName = 'herbarium.species'; } - async getByOrganization(organization_id: number, options: FilterOptions) { - const { limit, offset } = options; - const sql = ` - SELECT - species_id as id, total, ts.name, ts.desc - FROM - ( - SELECT - ss.species_id, count(ss.species_id) as total - from webmap.species_stat ss - WHERE - ss.planter_id IN ( - SELECT - id - FROM planter p - WHERE - p.organization_id in ( SELECT entity_id from getEntityRelationshipChildren(${organization_id})) - ) - OR - ss.planting_organization_id = ${organization_id} - GROUP BY ss.species_id - ) s_count - JOIN tree_species ts - ON ts.id = s_count.species_id - ORDER BY total DESC - LIMIT ${limit} - OFFSET ${offset} - `; - const object = await this.session.getDB().raw(sql); - return object.rows; - } + filterWhereBuilder(object, builder) { + const result = builder; + const { + whereNulls = [], + whereNotNulls = [], + whereIns = [], + ...parameters + } = object; - async countByOrganization(organization_id: number) { - const totalSql = ` - SELECT - species_id as id, total, ts.name, ts.desc - FROM - ( - SELECT - ss.species_id, count(ss.species_id) as total - from webmap.species_stat ss - WHERE - ss.planter_id IN ( - SELECT - id - FROM planter p - WHERE - p.organization_id in ( SELECT entity_id from getEntityRelationshipChildren(${organization_id})) - ) - OR - ss.planting_organization_id = ${organization_id} - GROUP BY ss.species_id - ) s_count - JOIN tree_species ts - ON ts.id = s_count.species_id - ORDER BY total DESC - `; - const total = await this.session.getDB().raw(totalSql); - return parseInt(total.rows.length); - } + result.whereNot(`${this.tableName}.status`, 'deleted'); + whereNotNulls.forEach((whereNot) => { + result.whereNotNull(whereNot); + }); - async getByPlanter(planter_id: number, options: FilterOptions) { - const { limit, offset } = options; - const sql = ` - SELECT - species_id as id, total, ts.name, ts.desc - FROM - ( - SELECT - ss.species_id, count(ss.species_id) as total - from webmap.species_stat ss - WHERE - ss.planter_id = ${planter_id} - GROUP BY ss.species_id - ) s_count - JOIN tree_species ts - ON ts.id = s_count.species_id - ORDER BY total DESC - LIMIT ${limit} - OFFSET ${offset} - `; - const object = await this.session.getDB().raw(sql); - return object.rows; - } + whereNulls.forEach((whereNull) => { + result.whereNull(whereNull); + }); - async getByGrower(grower_id: string, options: FilterOptions) { - const { limit, offset } = options; - const sql = ` - SELECT - species_id as id, total, ts.name, ts.desc - FROM - ( - SELECT - ss.species_id, count(ss.species_id) as total - from webmap.species_stat ss - WHERE - ss.planter_id IN ( - SELECT - id - FROM planter p - WHERE - p.grower_account_uuid = '${grower_id}' - ) + whereIns.forEach((whereIn) => { + result.whereIn(whereIn.field, whereIn.values); + }); - GROUP BY - ss.species_id - ) s_count - JOIN tree_species ts - ON ts.id = s_count.species_id - ORDER BY total DESC - LIMIT ${limit} - OFFSET ${offset} - `; - const object = await this.session.getDB().raw(sql); - return object.rows; - } + const filterObject = { ...parameters }; + + if (filterObject.id) { + result.where(`${this.tableName}.id`, '=', filterObject.id); + delete filterObject.id; + } + + if (filterObject.scientific_name) { + result.where( + `${this.tableName}.scientific_name`, + 'ilike', + `%${filterObject.scientific_name}%`, + ); + delete filterObject.scientific_name; + } + + if (filterObject.organization_id) { + result.where( + `${this.tableName}.organization_id`, + '=', + `${filterObject.organization_id}`, + ); + delete filterObject.organization_id; + } + + // if 'captures_amount_max' === 0, 'captures_amount_min' can be only 0. + if (filterObject.captures_amount_max === 0) { + result.whereNull('c.captures_count'); + delete filterObject.captures_amount_min; + delete filterObject.captures_amount_max; + } + + // if 'captures_amount_max' === 0 and 'captures_amount_max' is not defined, all results should be returned. + if ( + filterObject.captures_amount_min === 0 && + !filterObject.captures_amount_max + ) { + delete filterObject.captures_amount_min; + delete filterObject.captures_amount_max; + } + + if (filterObject.captures_amount_min) { + result.where( + `c.captures_count`, + '>=', + `${filterObject.captures_amount_min}`, + ); + delete filterObject.captures_amount_min; + } + + if (filterObject.captures_amount_max) { + result.where( + `c.captures_count`, + '<=', + `${filterObject.captures_amount_max}`, + ); + delete filterObject.captures_amount_max; + } + + if (filterObject.wallet) { + result.where( + `${this.tableName}.wallet`, + 'ilike', + `%${filterObject.wallet}%`, + ); + delete filterObject.wallet; + } - async getByWallet(wallet_id: string, options: FilterOptions) { - const { limit, offset } = options; - const sql = ` - SELECT - species_id as id, total, ts.name, ts.desc - FROM - ( - SELECT - ss.species_id, count(ss.species_id) as total - from webmap.species_stat ss - WHERE - ss.wallet_id::text = '${wallet_id}' - GROUP BY ss.species_id - ) s_count - JOIN tree_species ts - ON ts.id = s_count.species_id - ORDER BY total DESC - LIMIT ${limit} - OFFSET ${offset} - `; - const object = await this.session.getDB().raw(sql); - return object.rows; + result.where(filterObject); } } diff --git a/server/interfaces/SpeciesFilter.ts b/server/interfaces/SpeciesFilter.ts new file mode 100644 index 00000000..66dcebad --- /dev/null +++ b/server/interfaces/SpeciesFilter.ts @@ -0,0 +1,16 @@ +import DbModel from './DbModel'; + +interface SpeciesFilter extends DbModel { + id?: number; + scientific_name?: string; + description?: string; + limit?: number; + offset?: number; + keyword?: string; + morphology?: string; + range?: string; + created_at?: string; + updated_at?: string; +} + +export default SpeciesFilter; diff --git a/server/models/SpeciesV2.ts b/server/models/SpeciesV2.ts index 4ec0a13b..938cf804 100644 --- a/server/models/SpeciesV2.ts +++ b/server/models/SpeciesV2.ts @@ -1,76 +1,27 @@ import log from 'loglevel'; import FilterOptions from 'interfaces/FilterOptions'; import Species from 'interfaces/Species'; +import SpeciesFilter from 'interfaces/SpeciesFilter'; import { delegateRepository } from '../infra/database/delegateRepository'; import SpeciesRepositoryV2 from '../infra/database/SpeciesRepositoryV2'; type Filter = Partial<{ planter_id: number; - organization_id: number; wallet_id: string; grower_id: string; }>; function getByFilter( speciesRepository: SpeciesRepositoryV2, -): (filter: Filter, options: FilterOptions) => Promise { - return async function (filter: Filter, options: FilterOptions) { - if (filter.organization_id) { - log.warn('using org filter...'); - const trees = await speciesRepository.getByOrganization( - filter.organization_id, - options, - ); - return trees; - } - if (filter.planter_id) { - log.warn('using planter filter...'); - const trees = await speciesRepository.getByPlanter( - filter.planter_id, - options, - ); - return trees; - } - - if (filter.wallet_id) { - log.warn('using wallet filter...'); - const trees = await speciesRepository.getByWallet( - filter.wallet_id, - options, - ); - return trees; - } - if (filter.grower_id) { - log.warn('using grower filter...'); - const trees = await speciesRepository.getByGrower( - filter.grower_id, - options, - ); - return trees; - } - - const trees = await speciesRepository.getByFilter(filter, options); - return trees; - }; -} -function countByFilter( - speciesRepository: SpeciesRepositoryV2, -): (filter: Filter) => Promise { - return async function (filter: Filter) { - if (filter.organization_id) { - log.warn('using org filter...'); - const total = await speciesRepository.countByOrganization( - filter.organization_id, - ); - return total; - } - const total = await speciesRepository.countByFilter(filter); - return total; +): (filter: SpeciesFilter, options: FilterOptions) => Promise { + return async function (filter: SpeciesFilter, options: FilterOptions) { + const result = await speciesRepository.getByFilter(filter, options); + return result; }; } + export default { getById: delegateRepository('getById'), getByGrower: delegateRepository('getByGrower'), getByFilter, - countByFilter, }; diff --git a/server/routers/speciesRouterV2.ts b/server/routers/speciesRouterV2.ts index 3ee160a0..1d2e9518 100644 --- a/server/routers/speciesRouterV2.ts +++ b/server/routers/speciesRouterV2.ts @@ -9,7 +9,7 @@ import SpeciesModel from '../models/SpeciesV2'; const router = express.Router(); type Filter = Partial<{ planter_id: number; - organization_id: number; + organization_id: string; wallet_id: string; grower_id: string; }>; @@ -17,7 +17,7 @@ type Filter = Partial<{ router.get( '/:id', handlerWrapper(async (req, res) => { - Joi.assert(req.params.id, Joi.number().required()); + Joi.assert(req.params.id, Joi.string().uuid().required()); const repo = new SpeciesRepositoryV2(new Session()); const exe = SpeciesModel.getById(repo); const result = await exe(req.params.id); @@ -32,12 +32,16 @@ router.get( Joi.assert( req.query, Joi.object().keys({ - organization_id: Joi.number().integer().min(0), - planter_id: Joi.number().integer().min(0), - grower_id: Joi.string().guid(), - wallet_id: Joi.string(), limit: Joi.number().integer().min(1).max(1000), offset: Joi.number().integer().min(0), + keyword: Joi.string(), + id: Joi.string().uuid(), + scientific_name: Joi.string(), + description: Joi.string(), + morphology: Joi.string(), + range: Joi.string(), + created_at: Joi.string(), + updated_at: Joi.string(), }), ); const { @@ -66,7 +70,6 @@ router.get( }); log.warn('species filter:', filter, 'took time:', Date.now() - begin, 'ms'); res.send({ - total: await SpeciesModel.countByFilter(repo)(filter), offset, limit, species: result,