I am using pg-promise to execute the following query
Here is the raw query variable
const query = `SELECT * FROM feed_items WHERE feed_item_id=$1 AND '{$2}' <# tags`
tags is an array and I want to check if item is present in the array
I keep getting this error bind message supplies 2 parameters, but prepared statement "" requires 1 despite supplying 2 values
Can someone please suggest where I am going wrong
Prepared Statements won't let you do it, because it is too limited. But pg-promise native formatting is quite flexible, and you can do it in several ways...
Via ':value' filter, you can use either '{$2#}' or {$2:value}
Via Custom Type Formatting, you can use $2 directly, while wrapping the value into the following:
const wrap = a => ({rawType: true, toPostgres: () => pgp.as.format('{$1#}', [a])});
or like this:
const wrap = a => ({rawType: true, toPostgres: () => pgp.as.format('{$1:value}', [a])});
or even like this:
const wrap = a => ({rawType: true, toPostgres: () => `'{${pgp.as.value(a)}}'`});
example:*
await db.any('SELECT ... $1 <# tags', [wrap(123)]);
//=> SELECT ... '{123}' <#tags'
Related
I have a table with two columns, 'first_name' and 'last_name' and I want to join both tables so that I can search a query against them with a LIKE query and using % wildcards.
I was able to do this when I used a string literal, however it is not working when I'm trying to use positional bindings. I am returned with nothing.
Is there a way to join the two columns without concat and a whereRaw function? And how would I write the binding correctly?
const searchUser = (query) => {
const name = query.toLowerCase();
return knex('users')
.select('*')
.whereRaw('concat(LOWER("first_name"), \' \' , LOWER("last_name")) LIKE \'%??%\'', [name]);
};
It appears that you may be trying to query two separate columns with the same value?
What you could do here is a orWhere chain which links multiple where statements together where it matches if just one is true.
For example:
const searchUser = (query) => {
return knex('users')
.select('*')
.where(knex.raw('first_name ILIKE ?', `%${query}%`))
.orWhere(knex.raw('last_name ILIKE ?', `%${query}%`));
};
This also uses "ILIKE" which gets you the same case insensitive matching that you're achieving with the LOWER function.
You may also find value using a named binding rather than positional bindings. This would look like this:
const searchUser = (query) => {
const bindings = { query: `%${query}%` };
return knex('users')
.select('*')
.where(knex.raw('first_name ILIKE :query', bindings))
.orWhere(knex.raw('last_name ILIKE :query', bindings));
};
Consider below code:
const items = await Item.query().where(
"type",
"like",
raw("'??'", [`%${term}%`])
);
I'm not getting any errors with the code above, but the database returns an empty result set. The created SQL query is below:
select "items".* from "items" where type LIKE '%"mobiles"%'
Please look at the like mobiles in the above SQL '%"mobiles"%' "" are treated as part of the value and returns an empty result set.
How can avoid "" in the query above?
Edit: Please note that I'm using ObjectionJS as well which uses Knex.
?? are supposed to be used for a column name.
I've 2 suggestions for you,
use the query without raw at all,
const items = await Item.query().where('type', 'like', `%${term}%`);
use single ?,
const items = await Item.query().where('type', 'like', raw("'?'", [`%${term}%`]));
I can't find out if it's possible to invoke AQL queries set up on the ArangoDb web UI in Foxx script - rather like stored procedures can be called by services of a RDMS.
So far all examples I've seen show the AQL embedded into the Foxx services JavaScript.
const result = db._query([name of query defined in Db], {
"#arg": arg-value
}).toArray();
I would expect the query defined on the ArangoDb to run and pass argument value from Foxx service. but the _query method seems to only accept a query string.
The queries are stored as properties of the user and as such are available via the users-API:
var _ = require("lodash"); // helper to group the queries
var users = require('#arangodb/users');
var myQueries = _.groupBy(users.document(users.currentUser())['extra']['queries'], 'name');
const result = db._query(myQueries['name of query defined in Db'], ...);
lodash is available per default, no need to install it.
This is not clearly documented anywhere -- I took a look at the network traffic of the query-editor, to find the queries under 'extra/queries' of the user; than I searched for a js user-API and found an article on 'User Managment'. I'd say the lack of documentation is a clear 'caveat emptor' -- it might change without notice; but this should get you going.
Additionally, thinking a bit, I only tested this in arangosh, not a foxx app -- maybe the users module isn't available there. If it works you will probably have to replace the users.currentUser() call with the username as string.
Given the ease this approach lends to breaking your application, I wouldn't recommend to use this (if it works...) outside of R&D and/or prototyping.
Edit: I implemented this to test it:
router.post(
'/getQueries',
/**
* #param {Object} req
* #param {Object} res
*/
function(req, res) {
var _ = require("lodash"); // helper to group the queries
var users = require('#arangodb/users');
var myQueries = _.groupBy(users.document(req.body.user, ['extra']['queries'], 'name'));
res.json({success: (myQueries !== null), queries: myQueries, error: null});
}).summary('Return queries stored for the given user').body(joi.object().keys({
'user': joi.string().required()
}).unknown(true), ['json']);
This works, the queries are objects with of the following form:
[
"value" => """
FOR x IN #params\n
RETURN x
""",
"parameter" => [
"params" => "",
],
"name" => "x_test",
],
so a foxx service can indeed access and execute queries stored for a user, but I still strongly suggest to not do this in production.
If I do this...
Context.Orders.Select(o => o.User.UserId);
... I get an exception because User is null. I can use Include instead,
Context.Orders.Include(o => o.User).Select(o => o.User.UserId);
... but shouldn't User be loaded automatically?
EDIT:
The first snippet of code doesn't work when the Select is applied to the result of a function. Which type should the function return in order to tack the Select onto the database query?
I've tried IEnumerable<Order> and IQueryable<Order>.
I have two tables in my database and one of the tables is associated with my Accounts table.
So in my Schema Result for Account.pm I added the following line.
__PACKAGE__->has_many('subjects', 'MyApp::DBIC::Schema::Subject', {'foreight.account_id' => 'self.account_id'});
Then in my controller I make a search like this.
$c->stash->{search_results} = $c->model('DB::Account')->search(
{ -or => [
firstname => {like => '%'.$search_term.'%'},
'subjects.subject_title' => {like => '%'.$search_term.'%'},
]
},
{
join => 'subjects',
rows => '3',
},
{
order_by => 'first name ASC',
page => 1,
rows => 10,
}
);
It does not output any errors, but I can't figure out how to output the results on my view file. Is this a correct method of making relations between two tables?
My goal: provided a search_term, search two tables and output the result in view file. My SQL would look something like this:
SELECT FROM Accounts,Subjects WHERE Accounts.firstname=$search_term OR Subjects.subject_title=$search_term LEFT JOIN Subjects ON Accounts.account_id=Subject.account_id
And would want to output the result in view file, as I stated above.
I am fairly new to Perl and some of the documentations don't make that much sense to me, still. So any help and tips are appreciated.
The join looks OK to me, but it would make sense to try a simplified version without the join to check that everything else is OK.
The behaviour of DBIx::Class::ResultSet::search differs depending on the context in which it's called. If it's called in list context then it executes the database query and returns an array of MyApp::DBIC::Schema::Account objects. For example:
my #accounts = $c->model('DB::Account')->search();
In your case you're calling search in scalar context, which means that rather than returning an array it will return a DBIx::Class::ResultSet object (or a subclass thereof), and crucially it won't actually execute a db query. For that to happen you need to call the all method on your resultset. So, assuming you're using the default template toolkit view you probably want something like this:
[% FOREACH search_result IN search_results.all %]
[% search_result.first_name %]
[% END %]
This 'lazy' behaviour of DBIx::Class is actually very useful, and in my opinion somewhat undersold in the documentation. It means you can keep a resultset in a variable and keep executing different search calls on it without actually hitting the DB, it can allow much nicer code in cases where you want to conditionally build up a complex query. See the DBIx::Class::Resultset documentation for further details.
You have error in your query:
Try:
$c->stash->{search_results} = $c->model('DB::Account')->search(
{ -or => [
firstname => {like => '%'.$search_term.'%'},
'subjects.subject_title' => {like => '%'.$search_term.'%'},
]
},
{
join => 'subjects',
order_by => 'firstname ASC',
page => 1,
rows => 10,
}
);