diff --git a/server/app.ts b/server/app.ts index add0128..efbd18b 100644 --- a/server/app.ts +++ b/server/app.ts @@ -12,6 +12,7 @@ import organizationsRouter from './routers/organizationsRouter'; import plantersRouter from './routers/plantersRouter'; import rawCapturesRouter from './routers/rawCapturesRouter'; import speciesRouter from './routers/speciesRouter'; +import speciesRouterV2 from './routers/speciesRouterV2'; import tokensRouter from './routers/tokensRouter'; import transactionsRouter from './routers/transactionsRouter'; import treesRouter from './routers/treesRouter'; @@ -67,7 +68,7 @@ app.use('/planters', plantersRouter); app.use('/organizations', organizationsRouter); app.use('/v2/organizations', organizationsRouterV2); app.use('/species', speciesRouter); -app.use('/v2/species', speciesRouter); +app.use('/v2/species', speciesRouterV2); app.use('/wallets', walletsRouter); app.use('/v2/wallets', walletsRouter); app.use('/transactions', transactionsRouter); diff --git a/server/infra/database/SpeciesRepositoryV2.ts b/server/infra/database/SpeciesRepositoryV2.ts new file mode 100644 index 0000000..0dbf709 --- /dev/null +++ b/server/infra/database/SpeciesRepositoryV2.ts @@ -0,0 +1,122 @@ +import FilterOptions from 'interfaces/FilterOptions'; +import Species from 'interfaces/Species'; +import BaseRepository from './BaseRepository'; +import Session from './Session'; + +export default class SpeciesRepositoryV2 extends BaseRepository { + constructor(session: Session) { + super('tree_species', session); + } + + 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; + } + +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; + } + + 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}' + ) + + 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; + } + + 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; + } +} \ No newline at end of file diff --git a/server/models/OrganizationV2.ts b/server/models/OrganizationV2.ts index 445e5f9..c3ab7ce 100644 --- a/server/models/OrganizationV2.ts +++ b/server/models/OrganizationV2.ts @@ -19,7 +19,7 @@ function getByFilter( return trees; } if (filter.grower_id){ - log.warn('using planter filter...'); + log.warn('using grower filter...'); const trees = await organizationRepository.getByGrower( filter.grower_id, options, diff --git a/server/models/SpeciesV2.ts b/server/models/SpeciesV2.ts new file mode 100644 index 0000000..45dfc7b --- /dev/null +++ b/server/models/SpeciesV2.ts @@ -0,0 +1,61 @@ +import log from 'loglevel'; +import FilterOptions from 'interfaces/FilterOptions'; +import Species from 'interfaces/Species'; +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; + }; +} + +export default { + getById: delegateRepository('getById'), + getByGrower:delegateRepository('getByGrower'), + getByFilter, +}; diff --git a/server/routers/organizationsRouterV2.ts b/server/routers/organizationsRouterV2.ts index f813a77..dd7cd95 100644 --- a/server/routers/organizationsRouterV2.ts +++ b/server/routers/organizationsRouterV2.ts @@ -51,7 +51,7 @@ router.get( req.query, Joi.object().keys({ planter_id: Joi.number().integer().min(0), - grower_id:Joi.string(), + grower_id:Joi.string().guid(), limit: Joi.number().integer().min(1).max(1000), offset: Joi.number().integer().min(0), }), diff --git a/server/routers/speciesRouterV2.ts b/server/routers/speciesRouterV2.ts new file mode 100644 index 0000000..faa2ea6 --- /dev/null +++ b/server/routers/speciesRouterV2.ts @@ -0,0 +1,78 @@ +import express from 'express'; +import Joi from 'joi'; +import log from 'loglevel'; +import { handlerWrapper } from './utils'; +import Session from '../infra/database/Session'; +import SpeciesRepositoryV2 from '../infra/database/SpeciesRepositoryV2'; +import SpeciesModel from '../models/SpeciesV2'; + +const router = express.Router(); +type Filter = Partial<{ + planter_id: number; + organization_id: number; + wallet_id: string; + grower_id: string; +}>; + +router.get( + '/:id', + handlerWrapper(async (req, res) => { + Joi.assert(req.params.id, Joi.number().required()); + const repo = new SpeciesRepositoryV2(new Session()); + const exe = SpeciesModel.getById(repo); + const result = await exe(req.params.id); + res.send(result); + res.end(); + }), +); + +router.get( + '/', + handlerWrapper(async (req, res) => { + 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), + }), + ); + const { + limit = 20, + offset = 0, + planter_id, + organization_id, + wallet_id, + grower_id, + } = req.query; + const repo = new SpeciesRepositoryV2(new Session()); + const filter: Filter = {}; + if (organization_id) { + filter.organization_id = organization_id; + } else if (planter_id) { + filter.planter_id = planter_id; + } else if (wallet_id) { + filter.wallet_id = wallet_id; + } else if(grower_id){ + filter.grower_id = grower_id; + } + const begin = Date.now(); + const result = await SpeciesModel.getByFilter(repo)(filter, { + limit, + offset, + }); + log.warn('species filter:', filter, 'took time:', Date.now() - begin, 'ms'); + res.send({ + total: null, + offset, + limit, + species: result, + }); + res.end(); + }), +); + +export default router;