I'm having problems sending parameters with the ArangoJS library and was wondering if anyone could help.
With the example below, it is possible to execute db.query if parameter values are in the query, but as soon as I try to use bindVars I get silent errors and I can't extract any error details.
var db = require('arangojs')("http://127.0.0.1:8529");
/*
The '_system' database contains a collection called 'test' that contains one document:
{
"a": 1,
"b": 2
}
*/
// This works
db.query('FOR t IN test FILTER t.a == 1 RETURN t')
.then((cursor) => {
cursor.all()
.then(vals => {
console.log("\nNo bindVars");
console.log(vals);
});
});
// This does not work
db.query("FOR t IN #first FILTER t.a == #second RETURN t", { first: "test", second: 1 })
.then((cursor) => {
cursor.all()
.then(vals => {
console.log("\nUsing bindVars");
console.log(vals);
});
});
I'm new to Node.js and ArangoDB and would love to be able to use properly parameterized queries.
I'm also assuming that this use of parameters protects you from SQL Injection style attacks?
Thanks!
The problem isn't with the JavaScript driver or Node, the problem is with the query itself:
FOR t IN #first FILTER t.a == #second RETURN t
In AQL collection names can't be injected with ordinary bind parameters. This is because you're not actually trying to use the parameter as a string value but to refer to a collection with that name. To quote the AQL documentation:
A special type of bind parameter exists for injecting collection names. This type of bind parameter has a name prefixed with an additional # symbol (thus when using the bind parameter in a query, two # symbols must be used).
In other words, in AQL it has to be called ##first (instead of #first) and in the bind parameters argument to db.query it has to be called #first (instead of just first).
When using arangojs it's actually possible to avoid this entirely by using the aqlQuery template handler:
var aqlQuery = require('arangojs').aqlQuery;
var first = db.collection('test');
var second = 1;
db.query(aqlQuery`
FOR t IN ${first}
FILTER t.a == ${second}
RETURN t
`).then(
cursor => cursor.all()
).then(vals => {
console.log('Using aqlQuery');
console.log(vals);
});
This way you don't have to think about bind parameter syntax when writing queries and can write more complex queries without having to mess with extremely long strings. Note that it will recognize arangojs collection instances and handle them accordingly. Using a string instead of a collection instance would result in the same problems as in your example.
Additionally note that the template handler also exists in the arangosh shell and in ArangoDB itself (e.g. when using Foxx).
Related
I am trying to execute a simple query with pattern matching using LIKE in the WHERE clause using node-oracledb NodeJS library, however I can't seem to figure out how to do it. This doesn't seem to work
const baseQuery =
`select item, item_desc
from item_master
where item_level=tran_level
and item like '%:item%'`;
const binds = { item: '550' };
const result = await conn.execute(baseQuery, binds, {});
It keeps throwing the error below-
[Error: ORA-01036: illegal variable name/number] {
errorNum: 1036,
offset: 0
}
I've read the node-oracledb documentation but this seemingly simple use case of pattern match queries doesn't seem to be documented anywhere. Is pattern matching supported? if yes, then what am I doing wrong?
This was answered in your cross post at https://github.com/oracle/node-oracledb/issues/1195
Use a normal bind variable in the SQL statement (which is important for scalability and security, so that data is never treated as part of the SQL statement). Then concatenate whatever pattern matching syntax you want to the data.
To quote sla100's answer in the GitHub issue:
SQL:
and item like :item
JS:
const binds = { item: '%550%' };
You can do this with LIKE comparisons and also REGEXP_LIKE. The node-oracledb documentation also has examples of doing this in WHERE IN.
details:mongodb,mongoose,nodejs
example :
schema.virtual(''name').get(function() => {
return this.anything;
})
how the this key work to point document?
Is get method return document?
Might be a typo in the post but your string starts with two quotes.
You are mixing up the syntax for regular functions and arrow functions.
Do this:
schema.virtual('name').get(function() {
return this.anything;
});
You cannot use an arrow function in this situation anyway since arrow functions do not allow the rebinding of the this context.
I'm developing a Mongoose Query Helper plugin that provides the chainable method .search(query). On certain conditions, I want the query to return zero results, no matter how the other methods in the query builder chain behave. Turns out this isn't so easy as I have assumed.
Basically, I have the following code:
schema.query.search = function search(query) {
if ("query is invalid") {
// return no results => no easy way to achieve that?
}
return this.find(query);
};
Now, I want SomeModel.find({}).search(someQuery).exec() to return no results in case the query is invalid. I first tried to return this.limit(0), but turns out a limit of 0 is equivalent to setting no limit.
As a temporary solution, I do return this.find({ nonExistingField: 'something' }) which always results in no results, but this does seem a bit awkward and is probably also not so optimal in terms of performance as it triggers a search when no search is needed.
Thanks in advance for your help!
Is it possible to use standard AQL functions inside a user-defined function ?
I tried to call IS_IN_POLYGON() inside a custom function and got this error at execution time :
Query: AQL: in function 'GEO::IS_IN_MULTIPOLYGON()': user function runtime error: ReferenceError: IS_IN_POLYGON is not defined at (…)
Is there any prefix / require() / anything, that should be used to access standard AQL functions ?
ArangoDB version : 3.2.4
Engine : RocksDB
Yes, one can use AQL functions, including other UDFs, inside UDF functions.
Here is a complete example, using the AQL function LENGTH():
aqlfunctions.register('TEST::test', function(collection) {
'use strict';
const db = require('#arangodb').db;
const AQL_FUNCTION = db._query;
return (typeof collection == "string")
? AQL_QUERY('RETURN LENGTH(' + collection + ')').toArray()[0]
: return typeof collection;
}, false);
To use a UDF function, simply include its namespace (e.g. ARRAY::PRODUCT) in the way you'd use in an AQL query.
Caveat: UDFs must be side-effect free. They are not supposed to alter the database state in any way.
The ArangoDB documentation is not entirely clear what complications may arise if the return value of a UDF depends in any way on the database state (as in the above example).
Answering my own question here.
One can use AQL functions inside a user defined function this way :
(example for fictitious SOME_AQL_FUNCTION() returning a boolean)
let result = AQL_QUERY("RETURN SOME_AQL_FUNCTION(…)").json[0];
Found that reading some test code on ArangoDB's GitHub.
An user can post multiple comments in a thread, and I try to get list of threads (distinct) that an user has make comment to it, like :-
// comment table (relation table)
id, thread_id, user_id
select comment.thread_id, count(*)
from user
inner join comment on user.id=comment.user_id
where user.id = ?
group by comment.thread_id;
This is pretty easy in MySQL.
But to convert to couchdb :-
// map
function(doc)
{
emit(doc.user_id, doc.thread_id);
}
// reduce
function (key, thread_id)
{
return thread_id;
}
If I using the above map function, I will hit into an error like :-
"error": "reduce_overflow_error",
"reason": "Reduce output must shrink more rapidly: Current output: ...
I think I have applied the reduce function in wrong manner.
If using another way, like :-
// map
function (doc)
{
emit([doc.user_id, doc.thread_id], 1);
}
// reduce
function(keys, values)
{
return sum(values);
}
The group=true result is look exactly what mysql group-by does.
However, I'm unable to get ALL the list of thread by an user (given I only have the user_id during query time)
Third way, I can discard use of map reduce, and directly apply :-
emit(doc.user_id, doc.thread_id);
And do an PHP array like
foreach ( ... )
{
$threads[$thread_id] = TRUE;
}
array_keys($threads);
However, this is quite bloated and less efficient.
Second method look more accurate :-
key=[user_id, *] <-- it does not work, believe only work on exact match
key=[user_id, thread_id] <-- return one row
Is there a way to get all result without knowing the thread_id ?
(ps: I new to couchdb, and I might have describe the scenario in a bad manner)
Some reference I gotten via #jasonsmith :- http://guide.couchdb.org/draft/cookbook.html
As a rule of thumb, the reduce function should reduce to a single scalar value. That is, an integer; a string; or a small, fixed-size list or object that includes an aggregated value (or values) from the values argument. It should never just return values or similar. CouchDB will give you a warning if you try to use reduce “the wrong way”:
Follow closely to what this docs saying :-
http://wiki.apache.org/couchdb/View_Snippets#Generating_a_list_of_unique_values
// map
function(doc)
{
emit([doc.user_id, doc.thread_id], null);
}
// reduce
function (keys, values)
{
return null;
}
Query :-
?startkey=["$uid"]&endkey=["$uid",{}]&group=true
And the result now is accurate,
so the problem is lied on the reduce function and how the query being construct.