How to do pattern matching query in node-oracledb - node.js

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.

Related

Is there a way to convert a graphql query string into a GraphQLResolveInfo object?

I have written a piece of software that parses and formats the fourth parameter of a graphql resolver function (the info object) to be used elsewhere. I would like to write unit tests for this software. Specifically, I do not want to build the GraphQLResolveInfo object myself, because doing that would be very cumbersome, error-prone and hard to maintain. Instead, I want to write human-readable query strings and convert them to GraphQLResolveInfo objects so I can pass those to my software.
After extensive googling and reading of the graphql-js source code, I have not found a simple way to do what they are doing internally. I'm really hoping that I am missing something.
What I am not trying to do is use the graphql-tag library, because that just generates an AST which has a very different format from the GraphQLResolveInfo type.
Has anyone done this before? Help would be much appreciated!
I will keep monitoring this question to see if a better answer comes along, but I've finally managed to solve my particular issue by creating as close an approximation of the GraphQLResolveInfo object as I need for my particular use case.
The GraphQLResolveInfo object is composed of several attributes, two of which are called fieldNodes and fragments. Both are in fact parts of the same AST that graphql-tag generates from a query string. These are the only parts of the GraphQLResolveInfo object that concern the software I wrote, the rest of it is ignored.
So here is what I did:
import gql from 'graphql-tag';
// The converter function
const convertQueryToResolveInfo = (query) => {
const operation = query.definitions
.find(({ kind }) => kind === 'OperationDefinition');
const fragments = query.definitions
.filter(({ kind }) => kind === 'FragmentDefinition')
.reduce((result, current) => ({
...result,
[current.name.value]: current,
}), {});
return {
fieldNodes: operation.selectionSet.selections,
fragments,
};
};
// An example call
const query = gql`
query {
foo {
bar
}
}
`;
const info = convertQueryToResolveInfo(query);
From the AST generated by graphql-tag, I extract and modify the operation and fragment definitions so that they look the way they do within the GraphQLResolveInfo object. This is by no means perfect and may be subject to change in the future depending on how my software evolves, but it is a relatively brief solution for my particular problem.

Making Mongoose/MongoDB query helper plugin return zero results

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!

How to use AQL functions inside user defined functions?

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.

Passing parameters to db.query with arangojs

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).

Query value gets quoted automatically before sending it to MongoDB?

The following is confusing me a lot. I have been spending quite a bit of time trying to understand why collection.find() doesn't work with regex passed as an object. The regex match is coming over HTTP wrapped in the body of a POST request. Then I try to gather the query (in string format) and perform the query. The problem seems to be that unless the regex is written inside Node without quotes, it won't work. That is, it must be a literal without quotes.
For example, the following works fine:
var query1 = {
company: {
'$regex': /goog/
}
};
collection.find(query1, {}).toArray(function (err, docs) {
// Got results back. Awesome.
});
However, if the data comes wrapped in an object, it doesn't return anything. I suspect it's because the value gets quoted behind the scenes (i.e. "/goog/"):
// Assume
var query2 = {
company: {
'$regex': query.company
}
};
collection.find(query2, {}).toArray(function (err, docs) {
// Got nothing back.
});
I have tested it with the mongo shell and I can confirm the following:
// Returns 5 results
db.getCollection("contacts").find( { "company": /goog/ } )
// Doesn't match anything
db.getCollection("contacts").find( { "company": "/goog/" } )
Furthermore, I just discovered the following: if I write the value with quotes
// Works fine
var companyRegex = {'$regex': /goog/};
var query3 = {
company: companyRegex
};
So technically, a "literal" regex without quotes wrapped in an object works fine. But if it's a string, it won't work. Even after trying to replace the double-quotes and single-quotes with nothing (i.e. essentially removing them.)
Any idea how can I get the regex match be passed verbatim to find()? I've researched it, finding lots of potential solutions, alas it's not working for me.
Thanks in advance!
Let me focus on one line of your post. This is where the problem might be:
The regex match is coming over HTTP wrapped in the body of a POST request.
This seems problematic because:
The only structures that survive serialization between client/server are:
boolean
number
string
null *
objects and arrays containing these basic types
objects and arrays containing object and arrays [of more obj/array] of these basic types
Regexp, Date, Function, and a host of others require reconstruction, which means
passing a string or pair of strings for the match and option components of the Regexp and running Regexp() on the receiving end to reconstruct.
Regexp gets a bit messy because Regexp.toString() and Regexp() do not appear to be inverses of each others: /someMatch/.toString() is "/someMatch/" but RegExp("/someMatch/") is //someMatch// and what was needed instead to rebuild the regexp was just RegExp("someMatch"), which is /someMatch/. I hope this helps.
JSON.stringify(/someMatch/) is {} (at least on Chrome).
So instead of trying to build a general transport, I recommend re instantiating a particular field as a regexp.
* Irrelevant note: (null is fine but undefined is peculiar. JSON won't stringify undefineds in objects and turns undefined into null in Arrays. I recognize this isn't part of your problem, just trying to be complete in describing what can be serialized.)

Resources