Using dynamic search parameters with Sequelize.js - javascript

I'm trying to follow the Sequelize tutorial on their website.
I have reached the following line of code.
Project.findAll({where: ["id > ?", 25]}).success(function(projects) {
// projects will be an array of Projects having a greater id than 25
})
If I tweak it slightly as follows
Project.findAll({where: ["title like '%awe%'"]}).success(function(projects) {
for (var i=0; i<projects.length; i++) {
console.log(projects[i].title + " " + projects[i].description);
}
});
everything works fine. However when I try to make the search parameter dynamic as follows
Project.findAll({where: ["title like '%?%'", 'awe']}).success(function(projects) {
for (var i=0; i<projects.length; i++) {
console.log(projects[i].title + " " + projects[i].description);
}
});
It no longer returns any results. How can I fix this?

Now on Sequelize you can try this
{ where: { columnName: { $like: '%awe%' } } }
See http://docs.sequelizejs.com/en/latest/docs/querying/#operators for updated syntax

I think you would do that like this:
where: ["title like ?", '%' + 'awe' + '%']
So if you were doing this with an actual variable you'd use:
Project.findAll({where: ["title like ?", '%' + x + '%']}).success(function(projects) {
for (var i=0; i<projects.length; i++) {
console.log(projects[i].title + " " + projects[i].description);
}
});

Please try this code
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
{ where: { columnName: { [Op.like]: '%awe%' } } }

I would do it in this way:
Project.findAll({where: {title: {like: '%' + x + '%'}, id: {gt: 10}}).success(function(projects) {
for (var i=0; i<projects.length; i++) {
console.log(projects[i].title + " " + projects[i].description);
}
});
In this way you can have nicely more WHERE clausas

It might be cleaner to leverage the Sequelize.Utils.format function

The accepted answer of ["columnName like ?", '%' + x + '%'] for the where clause results in this error in Sequelize 4.41.1: "Support for literal replacements in the where object has been removed."
Assuming: modelName.findAll({ where : { columnName : { searchCriteria } } });
Using [Op.like]: '%awe%' or $like: '%awe%' } as the searchCriteria (where 'awe' is the value you want to find in columnName) both result in SQL with a LIKE clause of LIKE '\"%awe%\"'. Notice the extra quotation marks. [Op.like] and $like are aliases of each other and neither answer the OP's question because they don't allow dynamic search parameters.
Using [Op.like] : `%${parameter}%` as the searchCriteria (where 'parameter' is the parameter whose value you want to find in columnName) resulted in SQL with a LIKE clause of LIKE '\"%findMe\"' when parameter= 'findMe'. Again, notice the extra quotation marks. No results.
An answer in another StackOverflow post suggested using [Op.like]: [`%${parameter}%`] for the searchCriteria (where 'parameter' is the parameter whose value you want to find in columnName). Notice the square brackets! This resulted in SQL with a LIKE clause of LIKE '[\"%findMe%\"]' when parameter= 'findMe'. Again notice the extra quotation marks and the square brackets. No results.
For me, the solution was to use a raw query:
Sequelize.query('SELECT * FROM tableName WHERE columnName LIKE "%searchCriteria%"');

Related

Discord.js searching on key terms

I'm trying to create a Discord bot for trading within certain games. So far I have most of the basic commands working--!create creates a trade listing in an SQL database, !find finds one--but it only finds it on the exact same word. What I'm trying to do is make the search less specific so the terms don't have to be exactly equal to show results.
My current code is pretty convoluted and, needless to say, very broken:
var searchTerms = args[1].split(" ");
var output = {};
for (var id in userData) {
for (var offer in userData[id].offers) {
var score = 0;
for (var key in searchTerms) {
if (offer.includes(key)) {
score ++;
}
}
if (score >= searchTerms.length / 2) {
output[id] = userData[id].offers[offer] + " - " + ((score / searchTerms.length) * 100) + "%";
}
}
}
if (output == {}) {
bot.sendMessage({
to: channelID,
message: 'No matching offers found.'
});
} else {
msg = ""
for (id in output) {
msg += '<#' + id + '> - ' + output[id] + " "
}
bot.sendMessage({
to: channelID,
message: Object.keys(output).length + ' offers found: ' + msg
});
}
I'm new to Javascript so I'm not really sure how to get this working. Any tips are appreciated, thanks!
It looks like what you're trying to implement is an mechanism called Fuzzy Search, which user can find similar results using typo or approximate strings.
( Reference: https://en.wikipedia.org/wiki/Approximate_string_matching )
It not really an easy feature for a programming beginner to implement on your own, either the database have to support some kind of fuzzy query, or you'll have to get all the data from database first, and use a JavaScript fuzzy search library to accomplish that.
If you still want to do it, I recommend using Fuse.js, which is able to accomplish fuzzy search in a few lines
//list to be searched
var books = [{
'ISBN': 'A',
'title': "Old Man's War",
'author': 'John Scalzi'
}, {
'ISBN': 'B',
'title': 'The Lock Artist',
'author': 'Steve Hamilton'
}]
// init the search
var options = {
keys: ['title', 'author'],
id: 'ISBN'
}
var fuse = new Fuse(books, options)
fuse.search('old')
// result
[
"A"
]
Fuzzy search is a complex computer science problem, if you want to know more about it and how Fuse.js is implemented, here are a few useful links
An intro to fuzzy string matching
source code of Fuse.js
bitap algorithm (used by fuse.js)

Custom query parameters format serializer and extractor [JS]

I have a JSON object of the type
[
{
at: "own",
op: "in_between",
va: ["str", "jan"]
},
{
a: 'alas',
op: 'cont',
va: [10, 20]
},
.
.
.
]
I want to pass this as a GET query parameter. Now I could just stringify it and pass it as something like this
?q={"0":{"at":"own","op":"in_between","va":["str","jan"]},"1":{"at":"alas","op":"cont","va":[10,20]}}
However I would prefer to serialize it in a different format. Something like
q[]=at:"own"|op:"in_between"|va:["str","jan"]&q[]=at:"alas"|op:"cont"|va:[10,20]
(I saw a this kind of format being used in Amazon's search filters. Any other format suggestions are welcome. My main goal is to shorten the URL)
So i was able to serialize it by just concatenating to a string
let q = "";
data.forEach(function(i) {
q = q.concat(`q[]=at:"${i.at}"|op:"${i.op}"|va:[${i.val}]&`);
});
return q.slice(0,-1);
Similarly I have an extractor
let qArray = q.split('&');
let qParse = [];
qArray.forEach(function(i) {
i = JSON.parse('{' + i.substring(4).split('|').join(',') + '}');
q.push(i);
});
However this only works well for q[1] where q[1]['va'] has an integer array. It needs to also work for q[0] with the string values
Also is there any better way of serializing and extracting it in these kinds of forms?
Thanks in advance!
As said previously in the comments, I was wondering if CSV wouldn't work for what you want. It's kind of easy to parse. Would this work (assuming filters is your array) ? :
let params = "?q=;at,op,va;";
filters.forEach(function(fil) {
params += fil.at + "," + fil.op + "," + JSON.stringify(fil.va) + ";";
})
If you want to store queries to make percentage, you'd just have to remove first three characters. I'm also supposing that all your dictionnaries follow the same structure. Hope this works for you

Pg-promise performance boost : Multiple inserts with multiple update parameters

I am implementing Vitaly's pg-promise performance patterns, as advised here and there.
Here is my code :
for (var i=0;i<chunkedData.length;i++){
var insertData = chunkedData[i].map(function (d) {
return {
application_id: d.application_id,
country_id: d.country_id,
collection_id: collectionId
};
});
// Would need to make a loop here, and thus turning the result into an array
var updateData = {
application_id: chunkedData[i][j].application_id,
country_id: chunkedData[i][j].country_id,
collection_id: collectionId
};
var query = h.insert(insertData, cs) +
" ON CONFLICT ON CONSTRAINT application_average_ranking_application_id_country_id_colle_key DO UPDATE SET " +
h.sets(updateData, cs);
db.none(query)
.then(data => {
console.log('success');
})
.catch(error=> {
console.log('insert error : ' + error);
});
}
My problem is that insertData is an Array of Objects, and the library's insert helper builds an insert request using that Array, as specified in pg-promise API. Whereas updateData must be a simple Object.
I would like that when :
ON CONFLICT ON CONSTRAINT constraintName DO UPDATE
is triggered, the update values match the corresponding object in 'insertData' array.
How can I work around that problem ?
I've tried to put everything in a loop, but it leaks memory like crazy, and well, I lose the benefits of the pattern...
EDIT :
I want my query to be the equivalent of :
var inserts = data.map(entry => {
return t.none(" INSERT INTO application_average_ranking (application_id,country_id,collection_id) VALUES ($1,$2,$3)" +
" ON CONFLICT ON CONSTRAINT application_average_ranking_application_id_country_id_colle_key" +
" DO UPDATE SET country_id=$2,collection_id=$3",
[entry.application_id,entry.country_id,collectionId]
);
});
In that case when Update is called, the parameters refer to values originally proposed for insertion.
Your task requires a static SQL to implement that kind of logic, by using EXCLUDED as the table reference with rows excluded due to the conflict:
var sqlConflict = " ON CONFLICT ON CONSTRAINT" +
" application_average_ranking_application_id_country_id_colle_key" +
" DO UPDATE SET application_id = excluded.application_id" +
" country_id = excluded.country_id, collection_id = excluded.collection_id";
var insertData = chunkedData.map(function (d) {
return {
application_id: d.application_id,
country_id: d.country_id,
collection_id: collectionId
};
});
var query = h.insert(insertData, cs) + sqlConflict;
db.none(query)
.then(data => {
console.log('success');
})
.catch(error=> {
console.log('insert error : ' + error);
});
UPDATE
And in case your static list of excluded fields is too long and you want to simplify it, you can can always rely on flexibility of the helpers methods:
// or pull them from an object using `Object.keys(obj)`:
var cols = ['application_id', 'country_id', 'collection_id'];
var sets = pgp.helpers.sets({}, cols.map(c=> ({
name: c, mod: '^', def: 'excluded.' + pgp.as.name(c)
})));
console.log(sets);
//=> "application_id"=excluded."application_id","country_id"=excluded."country_id",
// "collection_id"=excluded."collection_id"
// or its simple JavaScript equivalent:
var sets = cols.map(c=> {
var name = pgp.as.name(c);
return name + '=excluded.' + name;
}).join();
UPDATE
With version 7.3.0 of the library and later, you should use method assignColumns to generate all of the excluded sets, like this:
cs.assignColumns({from: 'EXCLUDED'})
//=> "application_id"=EXCLUDED."application_id","country_id"=EXCLUDED."country_id","collection_id"=EXCLUDED."collection_id"
or, if you want to skip application_id, then you can do:
cs.assignColumns({from: 'EXCLUDED', skip: 'application_id'})
//=> "country_id"=EXCLUDED."country_id","collection_id"=EXCLUDED."collection_id"
See ColumnSet.assignColumns
Don't use h.sets(). Just write the conflict_action yourself. Handbook says
The SET and WHERE clauses in ON CONFLICT DO UPDATE have access to the existing row using the table's name (or an alias), and to rows proposed for insertion using the special excluded table.
Postgres - Insert

Sails.js Waterline UPDATE: How to handle multiple updates

I have an array of objects that I want to update, thus a multiple update. I am looking for the best way to implement this. Each object includes the same properties, but the properties for each object have different values.
Do I implement something like this?
var people = [
{'firstName': 'Brian', 'clockedIn': '2016-4-12', 'type': 'developer'},
{'firstName': 'Taylor', 'clockedIn': '2016-4-14', 'type': 'developer'},
{'firstName': 'Jake', 'clockedIn': '2016-4-14', 'type': 'manager'}
];
PersonModel.update({'type': 'developer'}, people, function(err, records) {
// ...
// ...
});
If I do something like the previous code, what exactly does it do? Does it automatically try to match the primary key of each record in the people array, update any of the properties that are listed, and then pass the records that were updated into the callback function?
I noticed in the Sails.js documentation here that the second argument for the update function can be an object OR an array, I am just unclear if I can use it like this. The documentation is unclear.
If I cannot use it like this and I wanted to try an iterative or recursive approach, how would you suggest I implement it?
What is your adapter ?
I had to do this with MySQL, I make the query my self, and then call PersonModel.query(myQuery)
I follow this answer to make it (example with two field) :
values = [
{
id: 1,
param_1: 'NewValueParam1',
param_2: 'NewValueParam2'
},
{
id: 2,
param_1: 'AnotherNewValueParam1',
param_2: 'AnotherNewValueParam2'
}
];
function multiValuesUpdate(values) {
var request = "UPDATE MyTable SET ";
var part_param_1 = "`param_1` = (CASE `id` ";
var part_param_2 = "`param_2` = (CASE `id` ";
var part_ids = "WHERE `id` IN (";
_.forEach(values, function (v) {
part_param_1 += "WHEN '" + v.id + "' THEN '" + v.param_1 + "' ";
part_param_2 += "WHEN '" + v.id + "' THEN '" + v.param_2 + "' ";
part_ids += v.id + ",";
});
part_param_1 += "ELSE `param_1` END), "; // Be careful with the comma !
part_param_2 += "ELSE `param_2` END) ";
part_ids = part_ids.slice(0, -1) + ");"; // Remove the last "," and add ");"
request += part_param_1 + part_param_2 + part_ids;
return request;
}
console.log(multiValuesUpdate(values));
Output :
"UPDATE MyTable
SET `param_1` = (
CASE `id`
WHEN '1' THEN 'NewValueParam1'
WHEN '2' THEN 'AnotherNewValueParam1'
ELSE `param_1`
END),
`param_2` = (
CASE `id`
WHEN '1' THEN 'NewValueParam2'
WHEN '2' THEN 'AnotherNewValueParam2'
ELSE `param_2`
END)
WHERE `id` IN (1,2);"

displaying json data in javascript not working

I have created a var and passed JSON data(comma seperated values) to it, but when I want to display json data - it only returns null. Here's the code:
<script type="text/javascript">
var data1 = [
{order:"145",country:"Dubai",employee:"permanent",customer:"self"}
];
document.write(data1);
</script>
You can either do it like this:
var data1 = [{order:"145",country:"Dubai",employee:"permanent",customer:"self"} ];
data1.forEach(function(data){
document.write(data.order);
document.write(data.country);
document.write(data.employee);
document.write(data.customer);
});
or you can do it like this
var data1 = [
{order:"145",country:"Dubai",employee:"permanent",customer:"self"}
];
$.each(data1[0], function(key, value){
document.write(key + " " + value);
});
Either way, storing just one object in the list makes this answer a bit redundant unless I show you how to loop over multiple objects.
var data1 = [
{order:"145",country:"Dubai",employee:"permanent",customer:"self"},
{order:"212",country:"Abu-Dhabi",employee:"permanent",customer:"Tom"}
];
data1.forEach(function(data){
$.each(data, function(key, value){
document.write(key+" "+value);
});
});
I'm using a mix of jQuery here aswell, which might not be optimal but atleast it serves to show that there are multiple ways to accomplishing what you need.
Also, the forEach() method on arrays is a MDN developed method so it might not be crossbrowser compliant, just a heads up!
If you want pure JS this is one of the ways to go
var data1 = [
{order:"145",country:"Dubai",employee:"permanent",customer:"self"},
{order:"212",country:"Abu-Dhabi",employee:"permanent",customer:"Tom"}
];
for(json in data1){
for(objs in data1[json]){
document.write(objs + " : " + data1[json][objs]);
}
}
For simple and quick printing of JSON, one can do something like below and pretty much same goes for objects as well;
var json = {
"title" : "something",
"status" : true,
"socialMedia": [{
"facebook": 'http://facebook.com/something'
}, {
"twitter": 'http://twitter.com/something'
}, {
"flickr": 'http://flickr.com/something'
}, {
"youtube": 'http://youtube.com/something'
}]
};
and now to print on screen, a simple for in loop is enough, but please not e, it won't print array instead will print [object Object]. for simplicity of answer, i won't go in deep to print arrays key and value in screen.
Hope that this will be usefull for someone. Cheers!
for(var i in json) {
document.writeln('<strong>' + i + '</strong>' +json[i] + '<br>');
console.log(i + ' ' + json[i])
}

Categories

Resources