Add condition to "where" clause in Prisma - javascript

I have the following filters in a Prisma query and I need to append a start_date: { equals: null } in addition to the existing start_date: { lt: today }, something like WHERE (id_1, id_2, id_3) IN ([array]) AND (start_date < today OR start_date IS NULL) but I can't find a working solution.
where: {
OR: ids.map((id) => ({
id_1: id.1,
id_2: id.2,
id_3: id.3,
})),
start_date: { lt: today },
},
This options doesn't work:
start_date: { OR: [{ lt: today }, { equals: null }] }
start_date: [{ lt: today }, { equals: null }]
AND: [{ start_date: { lt: today } }, { start_date: { equals: null } }]

As you can only have one OR in an object, combine two filter objects with AND:
{ AND: [{ OR: /* ... */ }, { OR: [{ start_date: null }, { start_date: /*...*/ }] }] }

Related

How to sort any array of a nested data structure by an item's either date related property value or key name?

My purpose is to get from this:
const grouped = [
{
"2022/11": [
{
"2022/11/30": [
{
"breaking-change": [
{
date: "2022-11-30T07:33:07.992955008Z",
reponame: "test-repo",
status: "breaking-change",
},
],
},
],
},
{
"2022/11/29": [
{
compatible: [
{
date: "2022-11-29T14:58:44+01:00",
reponame: "test-repo",
status: "compatible",
},
],
},
],
},
],
},
{
"2022/10": [
{
"2022/10/17": [
{
compatible: [
{
date: "2022-10-17T14:58:44+01:00",
reponame: "test-repo",
status: "compatible",
},
],
},
],
},
],
},
];
This:
[
{
"2022/10": [
{
"2022/10/17": [
{
compatible: [
{
date: "2022-10-17T14:58:44+01:00",
reponame: "test-repo",
status: "compatible",
},
],
},
],
},
],
},
{
"2022/11": [
{
"2022/11/29": [
{
compatible: [
{
date: "2022-12-17T14:58:44+01:00",
reponame: "test-repo",
status: "compatible",
},
],
},
],
},
{
"2022/11/30": [
{
"breaking-change": [
{
date: "2022-11-30T07:33:07.992955008Z",
reponame: "test-repo",
status: "breaking-change",
},
],
},
],
},
],
},
];
So:
"month-objects" ( the example would be the one in the parent array with 2022/11 as a key ) needs to be sorted by key(date/month) in an ascending way;
and also "days-objects" (the example would be the one in the first-level-children array with 2022/11/30 as a key ) needs to be sorted by key(date/month) in an ascending way;
That's what I tried:
const sortedGrouped = grouped.sort(function (a, b) {
return (
new Date(Object.keys(a)[0]).getTime() -
new Date(Object.keys(b)[0]).getTime()
);
});
That way I can only sort parents.
Tried to sort first-level children, but getting an Invalid left-hand side in assignment expression issue:
grouped.forEach((obj) => {
Object.values(obj) = Object.values(obj).sort(function (a, b) {
return (
new Date(Object.keys(a)[0]).getTime() -
new Date(Object.keys(b)[0]).getTime()
);
});
});
This one doesn't work for me either:
const sortByDate = arr => {
const sorter = (a, b) => {
return new Date(Object.keys(a)[0]).getTime() - new Date(Object.keys(b)[0]).getTime();
};
arr.forEach((i)=>Object.values(i).sort(sorter))
return arr;
};
The most generic solution has to based on a recursive approach.
Thus one does
either
try to sort an array by retrieving a Date related value from each of its items,
and keep the recursion going for each item of the just sorted array,
or
keep the recursion going for each value of an object type item.
Which directly leads to two other tasks
a function which from each passed object tries to parse a valid Date instance from
and a helper function which checks whether a passed value is a valid date.
The first mentioned of the latter two functions does so by either assuming an existing date property of the passed value or by going with the first validly parsed date from one of the passed value's keys.
function isValidDate(date) {
const time = date?.getTime?.() ?? Number.NaN;
return (
'number' === typeof time
&& !Number.isNaN(time)
);
}
function parseDateFromItem({ date: dateTime = null, ...itemData }) {
let date;
// parse into a `Date` instance ...
if (dateTime !== null) {
// ... either directly from an available `date` key ...
date = new Date(dateTime);
} else {
// ... or from the first validly parsable property name.
Object
.keys(itemData)
.some(key => {
date = new Date(key);
return isValidDate(date);
});
}
return isValidDate(date) && date || null;
}
function recursivelySortItemsAscendingByParsableDate(data) {
if (Array.isArray(data)) {
data
.sort((a, b) =>
(parseDateFromItem(a) ?? 0) - (parseDateFromItem(b) ?? 0)
);
data
.forEach(recursivelySortItemsAscendingByParsableDate);
} else if (data && 'object' === typeof data) {
Object
.values(data)
.forEach(recursivelySortItemsAscendingByParsableDate);
}
}
const sampleData = [{
"2022/11": [{
"2022/11/30": [{
"breaking-change": [{
date: "2022-11-30T07:33:07.992955008Z",
reponame: "test-repo",
status: "breaking-change",
}],
}],
}, {
"2022/11/29": [{
compatible: [{
date: "2022-11-29T14:58:44+01:00",
reponame: "test-repo",
status: "compatible",
}],
}],
}],
}, {
"2022/10": [{
"2022/10/17": [{
compatible: [{
date: "2022-10-17T14:58:44+01:00",
reponame: "test-repo",
status: "compatible",
}],
}],
}],
}];
recursivelySortItemsAscendingByParsableDate(sampleData);
console.log({ sampleData });
const moreSampleData = [{
"2022/12": [{
"2022/12/17": [{
compatible: [{
date: "2022-12-17T14:58:44+01:00",
reponame: "test-repo",
status: "compatible"
}]
}]
}]
}, {
"2021/11": [{
"2021/11/30": [{
compatible: [{
date: "2021-11-30T07:33:07.992956372Z",
reponame: "test-repo",
status: "compatible"
}]
}]
}]
}, {
"2022/11": [{
"2022/11/30": [{
compatible: [{
date: "2022-11-30T07:33:07.992956372Z",
reponame: "test-repo",
status: "compatible"
}, {
date: "2022-11-25T07:33:07.992955008Z",
reponame: "test-repo",
status: "compatible"
}]
}, {
"breaking-change": [{
date: "2022-11-30T07:33:07.992955008Z",
reponame: "test-repo",
status: "breaking-change"
}]
}, {
"need-an-update": [{
date: "2022-11-30T07:33:07.992955008Z",
reponame: "test-repo",
status: "need-an-update"
}]
}]
}, {
"2022/11/29": [{
compatible: [{
date: "2022-12-17T14:58:44+01:00",
reponame: "test-repo",
status: "compatible"
}]
}]
}]
}];
recursivelySortItemsAscendingByParsableDate(moreSampleData);
console.log({ moreSampleData });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Filtering and counting values of array of objects Javascript [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 months ago.
Improve this question
I have an array of objects that looks like this:
[{
date: "07-14-2022",
report: [{
vehicles: [{
id: "uid",
status: "active",
type: "car"
},
{
id: "uid",
status: "oos",
type: "bus"
},
{
id: "uid",
status: "pm",
type: "bus"
}
]
}]
}]
I'm trying to achieve excluding any status other than "oos" or "pm" (was thinking about first using filter here for the first stage). And then I needed to group all the unique values for type so I could provide a breakdown of total numbers for each. So, in the end it would look like:
bus: 2
This is the first stage I was starting to work on, but having trouble...
getReport() {
this.backend.getAnalyticsReport().subscribe(
response => {
console.log(this.processReport(response))
}
)
}
processReport(obj: any) {
return obj.filter((element: any) => this.filterStatus(element.vehicles))
}
filterStatus(element: any) {
if (element.status === 'oos' || 'pm') {
return true
} else {
return false
}
}
filter first then use reduce to group. This is a trick I've learned from this answer
var data = [{
id: "uid",
status: "active",
type: "car"
},
{
id: "uid",
status: "oos",
type: "bus"
},
{
id: "uid",
status: "pm",
type: "bus"
}
]
var result = data
.filter(item => ['oos', 'pm'].indexOf(item.status) > -1)
.reduce(function(agg, item) {
agg[item.type] = (agg[item.type] || 0) + 1
return agg;
}, {})
console.log(result)
You could do something like this using the Array.reduce() and Array.includes() methods. I modified the data to add in extra car calues that would be counted and added in an extra date to demonstrate how you could get the counts by day.
const data = [{
date: "07-14-2022",
report: [{
vehicles: [{
id: "uid",
status: "active",
type: "car"
},
{
id: "uid",
status: "oos",
type: "bus"
},
{
id: "uid",
status: "pm",
type: "bus"
},
{
id: "uid",
status: "pm",
type: "car"
},
{
id: "uid",
status: "active",
type: "bus"
}
]
}]
},
{
date: "07-15-2022",
report: [{
vehicles: [{
id: "uid",
status: "active",
type: "car"
},
{
id: "uid",
status: "oos",
type: "car"
},
{
id: "uid",
status: "pm",
type: "bus"
},
{
id: "uid",
status: "pm",
type: "bus"
},
{
id: "uid",
status: "active",
type: "bus"
}
]
}]
}
];
const types = ["oos", "pm"];
const results = data.reduce((a, c) => {
a[c.date] = c.report[0].vehicles.reduce((a2, c2) => {
if (types.includes(c2.status)) {
a2[c2.type] ??= 0;
a2[c2.type]++;
}
return a2;
}, {})
return a
}, {});
document.querySelector('pre').innerText = JSON.stringify(results, null, 3);
<pre></pre>

Incorrect date format between Sequelize and Postgres

I have a probleme with postgre results and Sequelize expected results. The same query, but results differents between Sequelize and Postgre, I think is something about timezome. My timezone is Europe/Paris
My query is used to retrieve all summed prices for the week :
SELECT date_trunc('day', "date") AS "date_alias", sum("cost") AS "total"
FROM "finance" AS "Finance"
WHERE "Finance"."date"
BETWEEN '2022-02-13 23:00:00.000 +00:00' AND '2022-02-19 23:00:00.000 +00:00'
GROUP BY "date_alias"
Postgre result look like :
Week data look like:
Sequelize returned results:
Executing (default): SELECT date_trunc('day', "date") AS "date_alias", sum("cost") AS "total" FROM "finance" AS "Finance" WHERE "Finance"."date" BETWEEN '2022-02-13 23:00:00.000 +00:00' AND '2022-02-19 23:00:00.000 +00:00' GROUP BY "date_alias";
[
Finance {
dataValues: { date_alias: 2022-02-14T00:00:00.000Z, total: '76' },
_previousDataValues: { date_alias: 2022-02-14T00:00:00.000Z, total: '76' },
uniqno: 1,
_changed: Set(0) {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [Array]
},
isNewRecord: false
},
Finance {
dataValues: { date_alias: 2022-02-17T00:00:00.000Z, total: '14' },
_previousDataValues: { date_alias: 2022-02-17T00:00:00.000Z, total: '14' },
uniqno: 1,
_changed: Set(0) {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [Array]
},
isNewRecord: false
},
Finance {
dataValues: { date_alias: 2022-02-18T00:00:00.000Z, total: '10' },
_previousDataValues: { date_alias: 2022-02-18T00:00:00.000Z, total: '10' },
uniqno: 1,
_changed: Set(0) {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [Array]
},
isNewRecord: false
}
]
In JSON :
[
{
"date_alias": "2022-02-13T00:00:00.000Z",
"total": "10"
},
{
"date_alias": "2022-02-14T00:00:00.000Z",
"total": "76"
},
{
"date_alias": "2022-02-17T00:00:00.000Z",
"total": "14"
},
{
"date_alias": "2022-02-18T00:00:00.000Z",
"total": "10"
}
]
My controller :
const { Op, Sequelize } = require('sequelize')
const {startOfWeek, lastDayOfWeek, startOfMonth, lastDayOfMonth, lastDayOfYear, startOfYear } = require('date-fns');
module.exports = {
async getAll(req, res) {
try {
//default week
let startedDate = startOfWeek(new Date(), { weekStartsOn: 1 });
let endDate = lastDayOfWeek(new Date(), { weekStartsOn: 1 });
let periodParam = 'day';
if(req.params.period && req.params.period === "month") {
startedDate = startOfMonth(new Date());
endDate = lastDayOfMonth(new Date());
periodParam = 'month';
}
if(req.params.period && req.params.period === "year") {
startedDate = startOfYear(new Date());
endDate = lastDayOfYear(new Date());
periodParam = 'year';
}
let options = {
...(req.params.period && { attributes: [
[ Sequelize.fn('date_trunc', periodParam, Sequelize.col('date')), `date_alias`],
[ Sequelize.fn('sum', Sequelize.col('cost')), 'total']
]}),
...(req.params.period && {group: ['date_alias']}),
where: {
date: {
[Op.between] : [startedDate, endDate],
//[Op.gte]: startedDate,
//[Op.lt]: endDate,
}
},
...(!req.params.period && {order: [
['date', 'ASC']
]}),
...(!req.params.period && {include: {
association: 'taxonomies',
attributes: ['id'],
through: {
attributes: []
}
}
}),
};
const data = await req.Model.findAll(options);
res.json(data);
} catch (err) {
res.status(500).send(err);
}
},
}
Why Sequelize return 2022-02-13T00:00:00.000Z instead of 2022-02-13T23:00:00.000Z ?
My expected result should be :
[
{
"date_alias": "2022-02-13T23:00:00.000Z",
"total": "43"
},
{
"date_alias": "2022-02-14T23:00:00.000Z",
"total": "43"
},
{
"date_alias": "2022-02-17T23:00:00.000Z",
"total": "14"
},
{
"date_alias": "2022-02-18T023:00:00.000Z",
"total": "10"
}
]
I found the solution, I need to configure sequelize like this with dialectOptions
const {Sequelize} = require('sequelize');
const sequelize = new Sequelize(process.env.PG_URL, {
logging: true,
dialectOptions: {
useUTC: false, //for reading from database
dateStrings: true,
typeCast: function (field, next) { // for reading from database
if (field.type === 'DATETIME') {
return field.string()
}
return next()
},
},
timezone: 'Europe/Paris',
});
module.exports = sequelize;

Subtract one object from another one with in array

I want to know the efficient way to subtract two object key[amount] values based on the action key.
data = [
{ _id: { token: 'BEEF', action: 'received' }, amount: 4 },
{ _id: { token: 'BEEF', action: 'sent' }, amount: 2 },
{ _id: { token: 'GFUN', action: 'received' }, amount: 9},
{ _id: { token: 'HOT', action: 'received' }, amount: 6 },
{ _id: { token: 'HOT', action: 'sent' }, amount: 4 },
{ _id: { token: 'LINK', action: 'received' }, amount: 8},
{ _id: { token: 'METM', action: 'sent' }, amount: 7 },
{ _id: { token: 'METM', action: 'received' }, amount: 9},
{ _id: { token: 'ORTC', action: 'received' }, amount: 5},
{ _id: { token: 'ORTC', action: 'sent' }, amount: 3 }
]
desired result after calculation
[
{ token: 'BEEF', amount: 2 },
{ token: 'GFUN', amount: 9},
{ token: 'HOT' , amount: 2 },
{ token: 'LINK', amount: 8},
{ token: 'METM', amount: 2 },
{ token: 'ORTC', amount: 2},
]
Maybe something like this:
const results = [];
const idx = {};
data.forEach(v => {
const token = v['_id']['token'];
if(!token in idx) {
const item = {
token: token,
amount: 0
};
results.push(item);
idx[token] = item;
}
idx[token].amount +=
v['_id']['action'] == 'received' ? v.amount : -v.amount;
});
results is the final array. idx is a lookup into the array in order to make locating the correct item easier.
There's lots of ways to approach this, but here's what I did.
First break the array into two arrays, one for the sent items and one for the received.
const sent = data.filter(o => o._id.action === 'sent');
const recieved = data.filter(o => o._id.action === 'received');
I'm assuming that there is always an entry in received and there may or may not be an entry in sent. So I mapped from the recieved array to the results. For each entry, I find the corresponding entry in sent, and if there is no matching entry, I subtract 0.
const differences = recieved.map( r => {
const s = sent.find(o => o._id.token === r._id.token );
const sentAmount = s ? s.amount : 0;
return {
token: r._id.token,
amount: r.amount - sentAmount,
}
})

MongoDB: How to append to a string in a deep nested array

I'm a hobbyist web programmer who just started learning MongoDB/Mongoose and I can't seem to figure out how to append to a string located in a deep nested array. I am trying to append a string to the end of hours: String. Below is the Schema I'm working with:
const TestSchema = new Schema({
userID: Number,
years: [
{
year: Number,
months: [{
month: Number,
days: [{
day: Number,
hours: String
}]
}]
}
]
});
Here is what I have so far. I tried to extend upon this answer here: https://stackoverflow.com/a/56589089 .But this is giving me a Cannot specify arrayFilters and a pipeline update error.
TestModel.findOneAndUpdate(
{ "years.months.days.day": 12 },
[
{
$set: {
"years.$[index1].months.$[index2].days.$[index3].hours": {
$concat: [
"$years.$[index1].months.$[index2].days.$[index3].hours",
" 44:44:44"
]
}
}
}
],
{
arrayFilters: [
{ "index1.year": 2020 },
{ "index2.month": 7 },
{ "index3.day": 12 }
]
}
).catch(error => {
console.log("error>>" + error);
});
Edit: Below is code with which I created an instance of the model
var test = new TestModel({
userID: 5,
years: [{
year: 2020,
months: [{
month: 7,
days: [{
day: 12,
hours: "4:4:4 5:5:5"
}]
}]
}]
})
test.save().then(function(){
console .log("testSaved>>" + !test.isNew);
});
Here is a screenshot of the data in the db:
Any help would be greatly appreciated.
Update is not supporting both operation together "arrayFilters" and "aggregation pipeline", you need to use only single operation from both,
So here you need to use only update aggregation pipeline, using nested $map,
TestModel.findOneAndUpdate({
years: {
$elemMatch: {
year: 2020,
months: {
$elemMatch: {
month: 7,
days: { $elemMatch: { day: 12 } }
}
}
}
}
},
[{
$set: {
years: {
$map: {
input: "$years",
as: "y",
in: {
$mergeObjects: [
"$$y",
{
months: {
$map: {
input: "$$y.months",
as: "m",
in: {
$mergeObjects: [
"$$m",
{
days: {
$map: {
input: "$$m.days",
as: "d",
in: {
$mergeObjects: [
"$$d",
{
hours: {
$cond: {
if: {
$and: [
{ $eq: ["$$y.year", 2020] },
{ $eq: ["$$m.month", 7] },
{ $eq: ["$$d.day", 12] }
]
},
then: { $concat: ["$$d.hours", " 44:44:44"] },
else: "$$d.hours"
}
}
}
]
}
}
}
}
]
}
}
}
}
]
}
}
}
}
}]
)

Categories