Pagination in TypeORM/NestJS - javascript

I have to introduce pagination in findAll() method. I really dont know how to do it. I tried but it is giving so many errors. I used findAndCount() method given by typeorm for that, But I am not sure how it will work.
As of now below method returning all the record. I need to return at a time 10 records. Please suggest what modification I need to do.
async findAll(queryCertificateDto: QueryCertificateDto,page=1): Promise<PaginatedResult> {
let { country, sponser } = queryCertificateDto;
const query = this.certificateRepository.createQueryBuilder('certificate');
if (sponser) {
sponser = sponser.toUpperCase();
query.andWhere('Upper(certificate.sponser)=:sponser', { sponser });
}
if (country) {
country = country.toUpperCase();
query.andWhere('Upper(certificate.country)=:country', { country });
}
const certificates = query.getMany();
return certificates;
}
this is PaginatedResult file.
export class PaginatedResult {
data: any[];
meta: {
total: number;
page: number;
last_page: number;
};
}
I tried changing code of findAll() but where clause is giving error. I am not sure how to handle query.getMany() in pagination.
const take = query.take || 10
const skip = query.skip || 0
const [result, total] = await this.certificateRepository.findAndCount(
{
where: query.getMany(), //this is giving error
take:take,
skip:skip
}
);
return result;
I need to introduce pagination in this method. Any help will be really helpful.

Typeorm has a really nice method specific to your usecase findAndCount
async findAll(queryCertificateDto: QueryCertificateDto): Promise<PaginatedResult> {
const take = queryCertificateDto.take || 10
const skip = queryCertificateDto.skip || 0
const country = queryCertificateDto.keyword || ''
const sponser = queryCertificateDto.sponser || ''
const query = this.certificateRepository.createQueryBuilder('certificate');
const [result, total] = await this.certificateRepository.findAndCount(
{
where: { country: Like('%' + country + '%') AND sponser: Like('%' + sponser + '%') }, order: { name: "DESC" },
take: take,
skip: skip
}
);
return {
data: result,
count: total
};
}
More documentation about Repository class can be found here

You don't need the .getMany() with your where in the last code, the result is an array of the data you need.
From your first code, you can do this:
async findAll(queryCertificateDto: QueryCertificateDto,page=1): Promise<PaginatedResult> {
// let's say limit and offset are passed here too
let { country, sponser, limit, offset } = queryCertificateDto;
const query = this.certificateRepository.createQueryBuilder('certificate');
if (sponser) {
sponser = sponser.toUpperCase();
query.andWhere('certificate.sponser = :sponser', { sponser });
}
if (country) {
country = country.toUpperCase();
query.andWhere('certificate.country = :country', { country });
}
// limit and take mean the same thing, while skip and offset mean the same thing
const certificates = await query
.orderBy("certificate.id", "ASC")
.limit(limit || 10)
.offset(offset || 0)
.getMany();
// if you want to count just replace the `.getMany()` with `.getManyandCount()`;
return certificates;
}```

Related

JavaScript - Targeting an object value to create another variable

So I have an array which looks like this:
[
{ TransactionValues: '50.00' },
{ TransactionValues: '-77.43' },
{ TransactionValues: '-20.23' },
{ TransactionValues: '200.23' }
]
I am trying to find a way to target the monetary value and create a variable based on the sum of these. When I try to target the "50.00" for example I get "Undefined" and it's still an array.
I'm not exactly sure how I can target it specifically, is it possible? Any help would be appreciated
As per the comments here is the full code (be wary I'm still learning so it's not elegant):
var fs = require('fs');
var parse = require('csv-parse');
var transactionValues = []; //Need an array to hold transactions
var currentTrans = [];
var savingsTrans = [];
//constuctor for transactions
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : transactions
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = {
"TransactionValues" : transactions
}
currentTrans.push(cTrans);
}
else {
var sTrans = {
"TransactionValues" : transactions
}
savingsTrans.push(sTrans);
}
};
//parses the csv file, loops each row and adds it to the transactionValue array
var parser = parse({columns: true}, function (err, results) {
console.table(results);
for (const row of results) {
addData(row.AccountID, row.AccountType, row.InitiatorType, row.DateTime, row.TransactionValue );
logTrans(row.AccountType, row.TransactionValue);
}
console.log(transactionValues);
console.log(currentTrans);
console.log(savingsTrans);
});
fs.createReadStream(__dirname+'/testData/customer-1234567-ledger.csv').pipe(parser)
not completely following but at the end of the day you have an array like data below.
you can use filter to target the attribute you want.
you can use map to pull out just the values.
you can use reduce to sum them all up.
run the snippet below to see each step
const data = [
{ TransactionValues: '50.00', AccountType: 'CURRENT' },
{ TransactionValues: '-77.43', AccountType: null},
{ TransactionValues: '-20.23', AccountType: 'CURRENT' },
{ TransactionValues: '200.23', AccountType: null }
];
const CurrentTrans = data.filter((x) => x.AccountType === 'CURRENT');
const SavingTrans = data.filter((x) => x.AccountType !== 'CURRENT');
console.log('CurrentTrans');
console.log(CurrentTrans);
console.log('SavingTrans');
console.log(SavingTrans);
const CurrentTransValues = CurrentTrans.map((x) => parseFloat(x.TransactionValues));
const SavingTransValues = SavingTrans.map((x) => parseFloat(x.TransactionValues));
console.log('CurrentTransValues');
console.log(CurrentTransValues);
console.log('SavingTransValues');
console.log(SavingTransValues);
const TotalCurrentValues = CurrentTransValues.reduce((sum, x) => sum + x);
const TotalSavingValues = SavingTransValues.reduce((sum, x) => sum + x);
console.log('TotalCurrentValues');
console.log(TotalCurrentValues.toFixed(2));
console.log('TotalSavingValues');
console.log(TotalSavingValues.toFixed(2));
So I may have fixed it by using parseFloat in my addData and logTrans functions:
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : parseFloat(transactions)
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = parseFloat(transactions);
currentTrans.push(cTrans);
}
else {
var sTrans = parseFloat(transactions);
savingsTrans.push(sTrans);
}
};
Now that seems to of worked. So I can use the "Sum values of objects in array" as suggested before. Thank you everyone :)

React - Check for duplicates in an array

I'm working on simple list where you can simply add your words to the list.
Main problem is duplicates, I tried many solutions but they weren't even close.
state = {
people: [{ name: null, count: null }]
}
handleSubmit = (e) => {
this.setState(({ count }) => ({
count: count + 1
}));
this.props.addHuman(this.state);
}
addHuman = (human) => {
let people = [...this.state.people, human];
this.setState({
people: people
});
}
I hope for solution which will check if there is any duplicate already in the array
You could make a check if there is someone with the same name already in the array. A better property to check would be an email adresse.
find takes a callback function as parameter. Inside this function, I compare the name properties. If it's a match, find returns true, then a do an early return in the next line and the person isn't added.
addHuman = (human) => {
const exists = this.state.people.find(p => p.name === human.name);
if (exists) return;
let people = [...this.state.people, human];
this.setState({
people: people
});
}
https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/find
if you want to de duplicate the array...you can do it in this way
cont array = [1,1.2,3,5,5];
[...new Set(array)]
This will work as well, there are plenty of ways to achieve the desired outcome.
addHuman = human => {
const updatedPeople = people.includes(human) ? people : [ ...people, human ];
this.setState({
people: updatedPeople
});
}
Remove_Duplicate_recs(filteredRecord)
{
var records = [];
for (let record of filteredRecord)
{
const duplicate_recorde_exists = records.find(r => r.MovementId ===
record.MovementId);
if (duplicate_recorde_exists)
{
continue ;
}
else
{
records.push(record);
}
}
return records;
}

What is the best way for reducing multiple if statement?

I have a helper function that builds object with appropriate query properties. I use this object as a body in my promise request. What is the most elegant way for refactoring multiple if statements? Here is a function:
getQueryParams = (query, pagination, sorting) => {
let queryParam = {}
if (pagination && pagination.pageNumber) {
queryParam.page = `${pagination.pageNumber}`
}
if (pagination && pagination.rowsOnPage) {
queryParam.size = `${pagination.rowsOnPage}`
}
if (query) {
const updatedQuery = encodeURIComponent(query)
queryParam.q = `${updatedQuery}`
}
if (sorting) {
queryParam.sort = `${sorting.isDescending ? '-' : ''}${sorting.name}`
}
return service.get(`/my-url/`, queryParam).then(result => {
return result
})
}
If service checks its parameters (as it should), you could benefit from the default parameters. Something like this:
const getQueryParams = (
query = '',
pagination = {pageNumber: 0, rowsOnPage: 0},
sorting = {isDescending: '', name: ''}
) => {
const queryParam = {
page: pagination.pageNumber,
size: pagination.rowsOnPage,
q: encodeURIComponent(query),
sort: `${sorting.isDescending}${sorting.name}`
}
return ...;
};
A live example to play with at jsfiddle.
This is an idea how it could looks like, but you need to adopt your params before:
const query = new URLSearchParams();
Object.keys(params).forEach(key => {
if (params[key]) {
query.append(key, params[key]);
}
});

filter array to product check in record for events

I have an activity feed, it contains a number of different types of activity for our site.
one type of activity is checkin. which logs when a user checks in and checkouts of a site.
The record entries look like so
Entryable_id | Entry_type | Action | timestamp
1 Ticket Update 12:01
3 Ticket New 12:07
4 CheckIn ClockedIn 14:30
4 CheckIn ClockedOut 17:30
What I want to do is create an array with entries in it like so
Entryable_id | ClockedIn| ClockedOut
4 14:30 17:30
so far what I have is
{
let staffCheckins = []
let checkinRecord = []
if (this.DiaryStore.entries.length) {
this.DiaryStore.entries.forEach(function(val) {
if (val.entryable_type === 'CheckIn') {
staffCheckins.push(val);
return val
}
})
}
staffCheckins.forEach(function(val) {
if (val.action === "ClockedIn") {
checkinRecord[val.entryable_id] = {
clockedIn: val.created_at,
user: val.user
}
}
if (val.action === "ClockedOut") {
checkinRecord[val.entryable_id] = {
clockedOut: val.created_at
}
}
})
console.log(completeCheckin)
},
which gives
1: clockedIn: "2019-07-22T10:26:45.000000Z",
2: clockedIn: "2019-07-22T12:38:02.000000Z"
so I assume that it is not appending to the key when i do
checkinRecord[val.entryable_id] = {clockedOut: val.created_at}
On top of that this all feels like a mess. is there a better way to filter and get what I need?
Thanks
You need to merge attribute, instead of assign to new object
staffCheckins.forEach(function(val) {
if (!checkinRecord[val.entryable_id]) {
checkinRecord[val.entryable_id] = {}
}
if (val.action === "ClockedIn") {
checkinRecord[val.entryable_id] = {
...checkinRecord[val.entryable_id],
clockedIn: val.created_at,
user: val.user
}
} else (val.action === "ClockedOut") {
checkinRecord[val.entryable_id] = {
...checkinRecord[val.entryable_id],
clockedOut: val.created_at
}
}
}
so I haven't gotten to test it because I'm out and about but you could try something like this. If they object entryable_id doesnt exist in the current object in the array, then it will create a new object with the members, otherwise it will find the object and update the fields
{
let staffCheckins = [];
let checkinRecord = [];
if (this.DiaryStore.entries.length) {
staffCheckins = this.DiaryStore.filter(val => val.entryable_type.toLowerCase() === 'checkin');
}
staffCheckins.forEach(function(val, i) {
let { action, entryable_id, created_at, user } = val;
if (!entryable_id in checkinRecord[i]) {
checkinRecord[i] = {
clockedIn: created_at,
clockedOut: created_at,
user
}
}
if (action.toLowerCase() === 'clockedin') {
checkinRecord[i] = {
...checkinRecord[i],
clockedIn: created_at,
user
}
} else if (action.toLowerCase() === 'clockedout') {
checkinRecord[i] = {
...checkinRecord[i],
clockedOut: created_at
}
}
});
}
apologies if I understood wrong but I'm also no currently at my actual computer to test any of it
You could do this whole operation in a filter reduce combination and create a groupBy object using the Entryable_id as keys.
Once loop completes get values array of that object
const checkinGroup = data.filter(({Entry_type}) => Entry_type === 'CheckIn')
.reduce((a, c)=>{
let {Entryable_id:id, Action, timestamp} = c;
a[id] = a[id] || {Entryable_id: id, ClockedIn: null, ClockedOut: null};
a[id][Action] = timestamp;
return a;
},{});
const res = Object.values(checkinGroup)
console.log(res)
<script>
const data = [{
Entryable_id: 1,
Entry_type: 'Ticket',
Action: 'Update',
timestamp: '12:01'
},
{
Entryable_id: 3,
Entry_type: 'Ticket',
Action: 'New',
timestamp: '12:07'
},
{
Entryable_id: 4,
Entry_type: 'CheckIn',
Action: 'ClockedIn',
timestamp: '14:30'
},
{
Entryable_id: 4,
Entry_type: 'CheckIn',
Action: 'ClockedOut',
timestamp: '17:30'
}
]
</script>

How to Tally Up Numerical Values and Produce One Value for all Documents in Mongo/Node

I am trying to do what should be a rather simple operation in my mongoDB/Node environment. Every document in the collection I'm targeting has a field "openBalance", which is a number value. All I want to do is find the totalOpenBalance by adding all of those together.
So far, in reviewing the MongoDB documentation, both $add and $sum seem to be used to perform an operation on the individual documents within the collection, rather than on the collection itself.
This leads me to wonder, is there a different way I should approach this? I've tried numerous constructions, but none work. Here is my function in full:
exports.getClientData = async function (req, res, next) {
let MongoClient = await require('../config/database')();
let db = MongoClient.connection.db;
let search, skip, pagesize, page, ioOnly = false, client;
let docs = [];
let records = 0;
if (_.isUndefined(req.params)) {
skip = parseInt(req.skip) || 0;
search = JSON.parse(req.search);
pagesize = parseInt(req.pagesize) || 0;
page = parseInt(req.page) || 0;
client = req.client || '';
ioOnly = true;
}
else {
skip = parseInt(req.query.skip) || 0;
search = req.body;
pagesize = parseInt(req.query.pagesize) || 0;
page = parseInt(req.query.page) || 0;
client = req.query.client || '';
}
search = {};
if (skip === 0) {
skip = page * pagesize;
}
if (client) {
let arrClient = [];
arrClient = client.split(",");
if (arrClient) {
// convert each ID to a mongo ID
let mongoArrClient = arrClient.map(client => new mongo.ObjectID(client));
if (mongoArrClient) {
search['client._id'] = { $in: mongoArrClient };
}
}
}
console.log(search);
let counter = 0;
let count = await db.collection('view_client_data').find(search).count();
let totalClients = await db.collection('view_client_data').find(search).count({ $sum: "client._id" });
console.log('totalClients', totalClients);
let totalOpenBalance = await db.collection('view_client_data').find(search).count({ $sum: { "$add" : "openBalance" } });
console.log('totalOpenBalance', totalOpenBalance);
db.collection('view_client_data').find(search).skip(skip).limit(pagesize).forEach(function (doc) {
counter ++; {
console.log(doc);
docs.push(doc);
}
}, function (err) {
if (err) {
if (!ioOnly) {
return next(err);
} else {
return res(err);
}
}
if (ioOnly) {
res({ sessionId: sessID, count: count, data: docs, totalClients: totalClients, totalOpenBalance: totalOpenBalance });
}
else {
res.send({ count: count, data: docs, totalClients: totalClients, totalOpenBalance: totalOpenBalance });
}
});
}
As you can see in the above code, I am getting the total number of clients with this code:
let totalClients = await db.collection('view_client_data').find(search).count({ $sum: "client._id" });
console.log('totalClients', totalClients);
That works perfectly, adding up the instances of a client and giving me the total.
Again, to be crystal clear, where I'm running into a problem is in summing up the numerical value for all of the openBalance values. Each document has a field, openBalance. All I want to do is add those up and output them in a variable titled totalOpenBalance and pass that along in the response I send, just like I do for totalClients. I have tried numerous options, including this:
let totalOpenBalance = await db.collection('view_client_data').find(search).count({ $sum: { "$add" : "openBalance" } });
console.log('totalOpenBalance', totalOpenBalance);
and this:
let totalOpenBalance = await db.collection('view_client_data').find(search).aggregate({ $sum: { "$add" : "openBalance" } });
console.log('totalOpenBalance', totalOpenBalance);
... but as I say, none work. Sometimes I get a circular reference error, sometimes an aggregate is not a function error, other times different errors. I've been wracking my brain trying to figure this out -- and I assume it shouldn't be that complicated once I understand the required syntax. How can I get my totalOpenBalance here?
By the way, the documents I'm targeting look something like this:
{
"_id": "3hu40890sf131d361f1ad908",
"client": {
"_id": "4ft9d366121j04563be0b01d6",
"name": {
"first": "John",
"last": "Smith"
}
},
"openBalance": 128,
"lastPurchaseDate": "2018-01-19T00:00:00.000Z"
},
$sum is an accumulator operator that must appear within a $group or $project aggregate pipeline stage. To also incorporate your search filter, you can include a $match stage in your pipeline.
let result = await db.collection('view_client_data').aggregate([
{$match: search},
{$group: {_id: null, totalOpenBalance: {$sum: '$openBalance'}}}
]).next();
console.log(result.totalOpenBalance);
I think $group is what you're looking for.
So for example to calculate all the openBalance fields
db.view_client_data.aggregate(
[
{
$group: {
_id : null
totalOpenBalance: { $sum: "$openBalance" },
}
}
]
)
this should give you an object back like {totalOpenBalance: 900}
Here is the mongodb documentation for some more examples
https://docs.mongodb.com/manual/reference/operator/aggregation/group/#pipe._S_group

Categories

Resources