How to properly use $match in Mongoose? - javascript

I am using .aggregate and $match to filter dates and then $sample them. Right now I get an empty array. I am using the following code:
const result = await Event.aggregate([
{
$match: { event_added: condition },
},
{ $sample: { size: 10 } },
]);
When I use .find in the code below it works perfectly, and I get all records satisfying the condition.
const result = await Event.find({ event_added: condition });
The condition object is the following:
var condition = { $gt: start, $lt: end };

For Model.find() operations, mongoose can infer the data type based on your schema and converts the types in your query object accordingly.
So if start and end are not Date objects, mongoose will try to cast the values to Date, as specified in your Event schema.
Aggregation objects are more complex and mongoose can not automatically do type casting. If your values are not Date objects you'll have to convert them first before passing them to Model.aggregate()
Generally this should work
const condition = { $gt: new Date(start), $lt: new Date(end) }

Related

Prisma - Optimistic Update using LastUpdated field, is it possible to express with Prisma's safe query builder syntax?

I am working with a PostgreSQL database using Prisma. I have a bulk update command which I want to fail if any of the records have changed since my last read.
My schema:
model OrderItem {
id String #id #default(uuid()) #db.Uuid
quantity Int
lastUpdated DateTime #updatedAt #map("last_updated")
##map("order_item")
}
I have written a query which works, but I built the query manually rather than using Prisma's safe query builder tools.
My query:
type OrderItemType = {
id: string;
quantity: number;
lastUpdated: Date;
}
type OrderItemUpdateDataType = {
quantity: number;
}
const updateByIds = async (
orderItemIdLastUpdatedTuples: ([OrderItemType['id'], OrderItemType['lastUpdated']])[],
orderItemUpdateData: OrderItemUpdateDataType,
) => {
// Optimistic concurrency - try updating based on last known "last updated" state. If mismatch, fail.
await prisma.$transaction(async (prisma) => {
// TODO: Prefer prisma.$queryRaw. Prisma.join() works on id[], but not on [id, lastUpdated][]
const idLastUpdatedPairs = orderItemIdLastUpdatedTuples
.map(([id, lastUpdated]) => `(uuid('${id}'), '${lastUpdated.toISOString()}')`)
.join(', ');
const query = `SELECT * FROM order_item WHERE (id, last_updated) in ( ${idLastUpdatedPairs} )`;
const items = await prisma.$queryRawUnsafe<OrderItem[]>(query);
// If query doesn't match expected update count then another query has outraced and updated since last read.
const itemIds = orderItemIdLastUpdatedTuples.map(([id]) => id);
if (items.length !== orderItemIdLastUpdatedTuples.length) {
throw new ConcurrentUpdateError(`Order Items ${itemIds.join(', ')} were stale. Failed to update.`);
}
await prisma.orderItem.updateMany({
where: { id: { in: itemIds } },
data: orderItemUpdateData,
});
});
};
This function wants to update a set of items. It accepts a list of tuples - id/lastUpdated pairs. It starts an explicit transaction, then performs an unsafe SELECT query to confirm the items to affect haven't been updated, then updates. This is following the guidance of Prisma's docs here - https://www.prisma.io/docs/concepts/components/prisma-client/transactions#interactive-transactions-in-preview
I was hoping to achieve the same results using prisma.$queryRaw rather than prisma.$queryRawUnsafe or even using implicit transactions rather than an explicit transaction wrapper. I wasn't able to find a syntax for expressing "where in tuple" using either of these approaches, though.
I am able to express what I want using implicit transactions when updating a single record. An example here would look like:
const { count } = await prisma.orderItem.updateMany({
where: { id, lastUpdated },
data: orderItemUpdateData,
});
and when using an explicit, safe query I stumbled on joining the array of tuples properly.
From the Prisma documentation, https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#tagged-template-helpers, there exists a Prisma.join command (which happens implicitly when using their tagged template helper syntax) but I wasn't able to generate a valid output when feeding it an array of tuples.
Did I miss anything? Does Prisma support joining a tuple using their safe query template syntax?

How to find if all dates in a array are lesser than current date using loopback 3 filters?

I have an object in my databank which has a array of blocked dates, and I want to create a loopback filter function which retrieves the instances that have expired all their values (IE: all values are lesser than the current date).
I have tried the following query, but it didn't work:
const query = {
where: {
blockedDates: {
nin: [
{ lt: date }
]
}
},
fields: "id"
}
For date filtering, inside where you can pass object with lt or gt defined. For example:
const query = {
where: {
blockedDates: {lt: Date.now()}
}
}

How to filter by property of a reference in Firebase's where()

Background
I have two firebase collections, dates and seats, whose (simplified) schema looks like this:
dates: [
{ time: Timestamp }
]
seats: [
{ date: reference to a date }
]
Of course, there is more information on each collection, but the idea here is that multiple seats can link to the same date.
The task at hand is to fetch only seats with dates that lie in the future.
My first instinct was to use 'in':
const futureDates = await firestore.collection(DATES)
.where('time', '>', new Date())
.get()
const seats = await firestore.collection(SEATS)
.where('date', 'in', futureDates.docs.map(date => date.ref))
.get()
But then I could only use 10 dates at once:
Limitations
Note the following limitations for in and array-contains-any:
in and array-contains-any support up to 10 comparison values.
(https://firebase.google.com/docs/firestore/query-data/queries)
Question
Is it possible to do it more elegantly and access the time property of date directly in the search query of seats?
Something along the lines of:
const seats = await firestore.collection(SEATS)
.where('date.time', '>', new Date()) // This always returns 0 seats though
.get()

How to update a document based on two filter conditions?

I have a document structure that looks like this:
type Document = {
_id: string
title: string
variants: VariantType[]
}
type VariantType = {
timestamp: Int
active: Boolean
content: any[]
}
I'm trying to filter a document based on two filter conditions in one query. First I want to match the _id and then find a specific variant based on a timestamp.
My previous version of the query was filtering based on the active key.
const updatedDocument = await allDocuments
.findOneAndUpdate({ _id: mongoId, 'variants.active': false }, .... };
Changing it to
const updatedDocument = await allDocuments
.findOneAndUpdate({ _id: mongoId, 'variants.timestamp': timestamp }, .... };
returns null.
Can mongo even match a document like this. I saw that there is an $eq query selector but I can't seem to get it working either.
Turns out I the problem was with the timestamp I was sending to the server. I was getting it from the wrong place and it wasn't matching any of the variants.

Mongodb sort by in array

Is there a way to sort by a given array?
something like this:
const somes = await SomeModel.find({}).sort({'_id': {'$in': [ObjectId('sdasdsd), ObjectId('sdasdsd), ObjectId('sdasdsd)]}}).exec()
What i looking for is a way to get a solution, to get all document of the collection and sort by if the document's _id match with one of the given array.
An example:
we have albums collection and songs collection. In albums collection we store the ids of the songs that belongs to the albums.
I want to get the songs, but if the song is in the album take them front of the array.
I solved this as follow, but its looks a bit hacky:
const songs = await SongMode.find({}).skipe(limit * page).limit(limit).exec();
const album = await AlbumModel.findById(id).exec();
if(album) {
songArr = album.songs.slice(limit * page);
for(let song of album.songs) {
songs.unshift(song);
songs.pop();
}
}
This cannot be accomplished using an ordinary .find().sort(). Instead, you will need to use the MongoDB aggregation pipeline (.aggregate()). Specifically, you will need to do the following:
Perform a $projection such that if the _id is $in the array, your new sort_field is given the value 1, otherwise it's given a value of 0.
Perform a $sort such that you're doing a descending sort on the new sort_field.
If you're using MongoDB version 3.4 or greater, then this is easy because of the $addFields operator:
const your_array_of_ids = [
ObjectId('objectid1'),
ObjectId('objectid2'),
ObjectId('objectid3')
];
SomeModel.aggregate([
{ '$addFields': {
'sort_field': { '$cond': {
'if': { '$in': [ '$_id', your_array_of_ids ] },
'then': 1,
'else': 0
}}
}},
{ '$sort': {
'sort_field': -1
}}
]);
If you're using an older version of MongoDB, then the solution is similar, but instead of $addFields you will be using $project. Additionally, you will need to explicitly include all of the other fields you want included, otherwise they will be excluded from the results.

Categories

Resources