Sequelize wrap column with " in where condition - javascript

I have a JSONB column in DB.
I'd like to have request to DB where I can check if some value in this JSON it true or false:
SELECT *
FROM table
WHERE ("json_column"->'data'->>'data2')::boolean = true AND id = '00000000-1111-2222-3333-456789abcdef'
LIMIT 1
So, my sequelize request:
const someVariableWithColumnName = 'data2';
Model.findOne({
where: {
[`$("json_column"->'data'->>'${someVariableWithColumnName}')::boolean$`]: true,
id: someIdVariable,
},
order: [/* some order, doesn't matter */],
})
And sequelize generate bad result like:
SELECT *
FROM table
WHERE "(json_column"."->'data'->>'data2')::boolean" = true AND id = '00000000-1111-2222-3333-456789abcdef'
LIMIT 1
Split my column by . and add " to every element.
Any idea how to get rid of adding " to the column in where condition?
Edit:
Here is my query with sequelize.literal():
const someVariableWithColumnName = 'data2';
Model.findOne({
where: {
[sequelize.literal(`$("json_column"->'data'->>'${someVariableWithColumnName}')::boolean$`)]: true,
id: someIdVariable,
},
order: [/* some order, doesn't matter */],
})

You can use Sequelize.literal() to avoid spurious quotes. IMHO, wrapping the json handling in a db function might also be helpful.

I just came across a similar use case.
I believe you can use the static sequelize.where method in combination with sequelize.literal.
Here is the corresponding documentation in sequelize API reference: https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#static-method-where
And here is an example (although I will admit hard to find) in the regular documentation:
https://sequelize.org/master/manual/model-querying-basics.html#advanced-queries-with-functions--not-just-columns-
In the end for your specific sit try something like this:
const someVariableWithColumnName = 'data2';
Model.findOne({
where: {
[Op.and]: [
// We provide the virtual column sql as the first argument of sequelize.where with sequelize.literal.
// We provide the matching condition as the second argument of sequelize.where, with the usual sequelize syntax.
sequelize.where(sequelize.literal(`$("json_column"->'data'->>'${someVariableWithColumnName}')::boolean$`), { [Op.eq]: true }),
{ id: someIdVariable }
]
})

Related

Filter inside the fetch with mongoDB query operators

how are you? I'm trying to move a filter inside the fetch I'm doing to bring my data from Builder.io and I'm struggling with one of them here. The title search works fine, but the second one don't. My objective is to filter between the entries to catch only the ones that match at least one of the annotationArray items.
The annotationArray can be, for example:
const annotationArray = ['Video', 'Image', 'GPS']
or just
const annotationArray = ['Video']
or whatever.
And the entries have an annotation field that consists in a string where I pass the annotations, like this:
const entries = [{title: 'First', annotation: 'Video, GPS'}, {title: 'Second', annotation: 'GPS'}, {title: 'Third', annotation: 'Video, Image'}]
So, for example, if the annotationArray is ['Video', 'GPS'], I want to fetch all of them. But if it's ['Video'], only the third and first, and so.
Currently I have this code
const sets = await builder.getAll('open-dataset', {
options: { noTargeting: true },
omit: 'data.blocks',
limit: 100,
query: {
data: {
title: { $regex: search, $options: 'i' },
annotation: { $regex: annotationArray && annotationArray.join(' '), $options: 'i' },
}
}
});
The result of annotationArray.join(' ') can be, for example, Video Image GPS or just Image. And annotation Video Image or whatever.
So I need to filter between the entries and fetch only the ones that contain at least one of the annotationArray strings.
My code is failing because currently it only fetches the ones that have all the annotationArray items, and not the ones that have at least one. I don't know how to do it with MondoDB query operators... previously, I had this code with javascript and it worked fine.
const filtered = entries.filter(item => annotationArray.some(data => item.annotation.includes(data)));
can somebody help me? thanks

Mongoose search for items on array containing _id with aggregate [duplicate]

If I have this schema...
person = {
name : String,
favoriteFoods : Array
}
... where the favoriteFoods array is populated with strings. How can I find all persons that have "sushi" as their favorite food using mongoose?
I was hoping for something along the lines of:
PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});
(I know that there is no $contains in mongodb, just explaining what I was expecting to find before knowing the solution)
As favouriteFoods is a simple array of strings, you can just query that field directly:
PersonModel.find({ favouriteFoods: "sushi" }, ...); // favouriteFoods contains "sushi"
But I'd also recommend making the string array explicit in your schema:
person = {
name : String,
favouriteFoods : [String]
}
The relevant documentation can be found here: https://docs.mongodb.com/manual/tutorial/query-arrays/
There is no $contains operator in mongodb.
You can use the answer from JohnnyHK as that works. The closest analogy to contains that mongo has is $in, using this your query would look like:
PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
I feel like $all would be more appropriate in this situation. If you are looking for person that is into sushi you do :
PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})
As you might want to filter more your search, like so :
PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})
$in is like OR and $all like AND. Check this : https://docs.mongodb.com/manual/reference/operator/query/all/
In case that the array contains objects for example if favouriteFoods is an array of objects of the following:
{
name: 'Sushi',
type: 'Japanese'
}
you can use the following query:
PersonModel.find({"favouriteFoods.name": "Sushi"});
In case you need to find documents which contain NULL elements inside an array of sub-documents, I've found this query which works pretty well:
db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})
This query is taken from this post: MongoDb query array with null values
It was a great find and it works much better than my own initial and wrong version (which turned out to work fine only for arrays with one element):
.find({
'MyArrayOfSubDocuments': { $not: { $size: 0 } },
'MyArrayOfSubDocuments._id': { $exists: false }
})
Incase of lookup_food_array is array.
match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}
Incase of lookup_food_array is string.
match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
Though agree with find() is most effective in your usecase. Still there is $match of aggregation framework, to ease the query of a big number of entries and generate a low number of results that hold value to you especially for grouping and creating new files.
PersonModel.aggregate([
{
"$match": {
$and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ] }
},
{ $project : {"_id": 0, "name" : 1} }
]);
There are some ways to achieve this. First one is by $elemMatch operator:
const docs = await Documents.find({category: { $elemMatch: {$eq: 'yourCategory'} }});
// you may need to convert 'yourCategory' to ObjectId
Second one is by $in or $all operators:
const docs = await Documents.find({category: { $in: [yourCategory] }});
or
const docs = await Documents.find({category: { $all: [yourCategory] }});
// you can give more categories with these two approaches
//and again you may need to convert yourCategory to ObjectId
$in is like OR and $all like AND. For further details check this link : https://docs.mongodb.com/manual/reference/operator/query/all/
Third one is by aggregate() function:
const docs = await Documents.aggregate([
{ $unwind: '$category' },
{ $match: { 'category': mongoose.Types.ObjectId(yourCategory) } }
]};
with aggregate() you get only one category id in your category array.
I get this code snippets from my projects where I had to find docs with specific category/categories, so you can easily customize it according to your needs.
For Loopback3 all the examples given did not work for me, or as fast as using REST API anyway. But it helped me to figure out the exact answer I needed.
{"where":{"arrayAttribute":{ "all" :[String]}}}
In case You are searching in an Array of objects, you can use $elemMatch. For example:
PersonModel.find({ favoriteFoods : { $elemMatch: { name: "sushiOrAnytthing" }}});
With populate & $in this code will be useful.
ServiceCategory.find().populate({
path: "services",
match: { zipCodes: {$in: "10400"}},
populate: [
{
path: "offers",
},
],
});
If you'd want to use something like a "contains" operator through javascript, you can always use a Regular expression for that...
eg.
Say you want to retrieve a customer having "Bartolomew" as name
async function getBartolomew() {
const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol
console.log(custStartWith_Bart);
console.log(custEndWith_lomew);
console.log(custContains_rtol);
}
I know this topic is old, but for future people who could wonder the same question, another incredibly inefficient solution could be to do:
PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});
This avoids all optimisations by MongoDB so do not use in production code.

NodeJs is sending empty response

I am using NodeJS and MongoDb as a backend service.I am trying to retrieve distinct data based on condition like I want to get names where order status is pending:
const coll = client.db("My_db").collection("Orders");
coll.distinct("name",{order_status:"pending"},(function(err,docs){
let output = docs.map(r => ({'name':r.name}));
res.send(output);
}));
But it is sending empty array like this:
[
{},
{},
{},
{},
{},
{}
]
Someone please let me know what I am doing wrong.Any help would be appreciated.
THANKS
As per documentation Distinct Mongo DB
Thay result will be array .But you are matching r.name its a object key .
So do with argument only
let output = docs.map(r => ({'name':r}));
You don't need to run map over that response. The distinct function returns the array of distinct values. Try console.log(docs) and check it
Because you have specified field as name, you don't need to use r.name, just use r
You can refer to the link, hope this will help you :)
If your MongoDB is above 2.2 and you want to find multiple fields, you can use aggregate
collection = db.tb;
collection.aggregate([
{"$group": { "_id": { name: "$name", email: "$email" } } }
]);

Update multiple rows in sequelize with different conditions

I'm trying to perform an update command with sequelize on rows in a postgres database. I need to be able to update multiple rows that have different conditions with the same value.
For example, assume I have a user table that contains the following fields:
ID
First Name
Last Name
Gender
Location
createdAt
Assume, I have 4 records in this table, I want to update records with ID - 1 and 4 with a new location say Nigeria.
Something like this: SET field1 = 'foo' WHERE id = 1, SET field1 = 'bar' WHERE id = 2
How can I achieve that with sequelize?
You can update multiple record at a time , but same updates for all records
, if you want to make different updates for different conditons then you have to run that multiple time
Example :
This will update fields1 to foo , where id is 1 or 4
let ids = [1,4];
Your_model.update({ field1 : 'foo' },{ where : { id : ids }});
This will update field1 to foo if id is 1 , and field1 to bar if id is 4
Your_model.update({ field1 : 'foo' },{ where : { id : 1 }});
Your_model.update({ field1 : 'bar' },{ where : { id : 4 }});
Hope this will clear all your doubts.
You can update multiple rows following your conditions, and to do that the operators are very helpful.
Look here: http://docs.sequelizejs.com/manual/querying.html (operators)
const { Op } = Sequelize;
DisplayMedia.update(
{
field: 'bar'
},
{
where: {
id: {
[Op.in]: [1, 10, 15, ..] // this will update all the records
} // with an id from the list
}
}
)
There is all kinds of operators, including the range operators, or like operator ...etc
Also one of the important questions when it come to update, is how to update all rows?
Not including where results in an error "Missing where attribute in the options parameter passed to update".
The answer is in the code bellow: provide a where with an empty object.
await DisplayMediaSequence.update({
default: false
}, {
where: {}, // <-- here
transaction
});
await DisplayMediaSequence.update({
default: true <-- after all turned to false, we now set the new default. (that to show a practical expample) -->
}, {
where: {
id
},
transaction
});
we planning to save different values for same fields in multiple row there is a possible for getting all field values are same in database.
using for loop
const {idArray,group_id} = params;
for(const item of idArray){
const response = await Your_model.findOne({ where:{group_id,user_id:null}, order: [['id', 'DESC']] });
await response.update({user_id:item});
}

Optimization - find on all fields in Mongoose MongoDB schema

We needed to challenge our database approach and need your help
We needed to search a word/phrase in all fields of a Mongoose schema.
Let's say the schema is like this:
var sampleSchema = new Schema({
fieldABC: String,
fieldDEF: String,
fieldGHI: String
});
We need to write a find query which will search for a word in all fields in a document of the collection:
db.sampleCollection.find({
$or: [{
fieldABC: "wordToSearch"
}, {
fieldDEF: "wordToSearch"
}, {
fieldGHI: "wordToSearch"
}]
})
It's possible for us to write the above query but it looks very inefficient - is there some better and faster approach to this?
In the year 2015, it was not supported, is there any change in this?
As suggested by #Veeram
Step 1:
Create a text index
db.sampleCollection.createIndex( { "$**": "text" } )
Step 2:
Use the text index to search the word in concern
db.sampleCollection.find( { $text: { $search: "wordToSearch" } })

Categories