JugglingDB custom query - mysql

I am trying to figure how to create a "repository" file (for the symfony2 users) where i will put all my special behaviour queries.
I have a simple db shema with :
User (email)
RelationType (name)
UserXUserXRelation (fromUser, toUser, relation)
I want to retrieve the user with relation X on my user Y, so in sql it will looks like :
var sql = 'SELECT u.email, u.id
FROM user u
INNER JOIN UserXUser uxu ON uxu.toUser_id = u.id
WHERE uxu.relation_id = 1 AND uxu.fromUser_id = '+id
Where should i create this method ? I tried in the db/shema.js and app/models/user.js without success.
I found here Using arbitrary mySQL Query with JugglingDB? that i should use the shema object to use "query", where and how could I use it ?
Does the callback will look like this :
function(err, data) {...}
Is there some best practice about code separation in that case ?
Additional question : is there a way to bind parameters the PHP PDO way with jugglingdb ?
Thanks for all.

It is possible to execute any arbitrary query using jugglingdb and compoundjs. Providing you are using compoundjs, you can just use the compound.models.user.schema.adapter.query(). The compound object should be parsed into your user model, meaning you have access to many other methods. The way you would use this query in your model would be by creating a method inside the users model with the following code:
var sql = 'SELECT u.email, u.id
FROM user u
INNER JOIN UserXUser uxu ON uxu.toUser_id = u.id
WHERE uxu.relation_id = 1 AND uxu.fromUser_id = '+id
compound.models.user.schema.adapter.query(sql, function(err, data) {
if(error) {
console.log(error)
} else {
//Enjoy your data
}
})
As this overrides the jugglingdb quote escaper, beware of sql injection, make sure your id variable is checked and sanitised.

Related

Node.js: MySQL query with multiple variables

I'm writing a node/express application, using mysql as a database.
One question upfront, that i can't really figure out yet: Is it possible to make multiple querys in one route?
Nevertheless, my problem is, that i want to use multiple variables in the database-query as follows, which does not work.
router.get('/:route_name', (req, res) => {
const route_name = req.params.route_name;
const route_name_join = req.params.route_name+".comp_id";
const queryhelper = [route_name, route_name_join];
const dbquery = "SELECT * FROM ? INNER JOIN users ON ? = users.id;";
sqldb.query(dbquery, queryhelper, function (err, result, fields) {
if (err) throw err;
res.render('categories/category',{
items : result
});
console.log(JSON.stringify(result));
});
I'm expecting to, for example in case of
.../categories/customers
"SELECT * FROM customers INNER JOIN users ON customers.comp_id = users.id;".
If i type in exactly that into dbquery, it does work, but as soon as i'm doing it using '?', i get an error that i'm having a mistake in my mysql syntax at
"SELECT * FROM 'customers' INNER JOIN users ON 'customers.comp_id' = users.id;"
I assume that there is a problem at customers.comp_id, but i simply can't figure out how to fix it.
You need to perform string substitution for this. Parameters can only be used for expressions -- they're replaced with the supplied value as a literal. You can't use a parameter to supply a table or column name.
So do:
const dbquery = `SELECT * FROM ${route_name} INNER JOIN users ON ${route_name_join} = users.id;`;
If these variables are coming from user input, make sure you whitelist them before using them in the query, to prevent SQL-injection.

Multi-parameter search with mysql and node.js

Let me preface by saying I'm very new to SQL (and back end design) in general. So for those annoyed with noob questions, please be gentle.
BACKGROUND:
I'm trying to build a product test database (storing test data for all our products) where I want a user to be able to refine a search to find test data they actually want. For example, they may start by searching for all products of a certain brand name, and then refine it with a product type, and/or refine it with a date range of when the test was done.
PROBLEM:
I'm having a hard time finding information on how to implement multi-parameter searches with mysql and node.js. I know you can do nested queries and joins and such within pure SQL syntax, but it's not abundantly clear to me how I would do this from node.js, especially when certain search criteria aren't guaranteed to be used.
Ex:
CREATE PROCEDURE `procedureName`(
IN brandname VARCHAR(20),
producttype VARCHAR(30))
BEGIN
SELECT * FROM products
WHERE brand = brandname
AND product_type = producttype;
END
I know how to pass data from node.js to this procedure, but what if the user didn't specify a product type? Is there a way to nullify this part of the query? Something like:
AND product_type = ALL;
WHAT I'VE TRIED:
I've also looked into nesting multiple SQL procedures, but passing in dynamic data to the "FROM" clause doesn't seem to be possible. Ex: if I had a brandname procedure, and a product type procedure, I don't know how/if I can pass the results from one procedure to the "FROM" clause of the other to actually refine the search.
One idea was to create tables with the results in each of these procedures, and pass those new table names to subsequent procedures, but that strikes me as an inefficient way to do this (Am I wrong? Is this a completely legit way to do this?).
I'm also looking into building a query string on the node side that would intelligently decide what search criteria have been specified by the front end, and figure out where to put SQL AND's and JOIN's and what-nots. The example below actually works, but this seems like it could get ugly quick as I add more search criteria, along with JOINS to other tables.
// Build a SQL query based on the parameters in a request URL
// Example request URL: http://localhost:3000/search?brand=brandName&type=productType
function qParams(req) {
let q = "SELECT * FROM products WHERE ";
let insert = [];
if(req.query.brand) {
brandname = req.query.brand; // get brandname from url request
q = q + `brand = ?`, // Build brandname part of WHERE clause
insert.push(brandname); // Add brandname to insert array to be used with query.
};
if(req.query.type) {
productType = req.query.type; // get product type from url request
insert.length > 0 ? q = q + ' AND ' : q = q; // Decide if this is the first search criteria, add AND if not.
q = q + 'product_type = ?'; // Add product_type to WHERE clause
insert.push(productType); // Add product_type variable to insert array.
}
// Return query string and variable insert array
return {
q: q,
insert: insert
};
};
// Send Query
async function qSend(req, res) {
const results = await qParams(req); // Call above function, wait for results
// Send query string and variables to MySQL, send response to browser.
con.query(results.q, results.insert, (err, rows) => {
if(err) throw err;
res.send(rows);
res.end;
})
};
// Handle GET request
router.use('/search', qSend);
CONCISE QUESTIONS:
Can I build 1 SQL procedure with all my search criteria as variables, and nullify those variables from node.js if certain criteria aren't used?
Is there way to nest multiple MySQL procedures so I can pick the procedures applicable to the search criteria?
Is creating tables of results in a procedure, and passing those new table names to other procedures a reasonable way to do that?
Building the query from scratch in node is working, but it seems bloated. Is there a better way to do this?
Googling "multi-parameter search mysql nodejs" is not producing useful results for my question, i.e. I'm not asking the right question. What is the right question? What do I need to be researching?
One option is to use coalesce():
SELECT p.*
FROM products p
WHERE
p.brand = COALESCE(:brandname, p.brand)
AND p.product_type = COALESCE(:producttype, p.producttype);
It may be more efficient do explicit null checks on the parameters:
SELECT p.*
FROM products p
WHERE
(:brandname IS NULL OR p.brand = :brandname)
AND (:producttype IS NULL OR p.product_type = :producttype);

Hibernate3 criteria query selecting too many fields

I want to write a simple query to retrieve a list of USER with a simple restriction on CUSTOMER joined table.
I'm only interested by the USER entity.
If I write it using HPQL :
public List<Users> getAssociatedAdminObs(Integer pCustId) {
Criteria crit = getCriteriaForObsAdmin("USER");
crit.createCriteria("clients").add(Restrictions.eq("idCustomer", pCustId));
return crit.list();
StringBuilder hqlQuery = new StringBuilder().append("select u from Users as u join u.customers as c where c.idCustomer=:idCustomer");
Query q = getSessionAndManageFilter().createQuery(hqlQuery.toString());
q.setInteger("idCustomer", pCustId);
return q.list();
}
The SQL generated only Select all the fields from USER entity, as expected.
Now if I write it through Hibernate criteria API :
public List<Users> getAssociatedAdminObs(Integer pCustId) {
Criteria crit = getSession().createCriteria(Users.class);
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
crit.createCriteria("customers").add(Restrictions.eq("idCustomer", pCustId));
return crit.list();
}
The SQL generated Select all the fields from USER entity but also from the CUSTOMER entity.
I'm using hibernate-core 3.3.1.GA.
I know I can use projection to work around the issue but my object will be transient, I also validated it works using a subquery to do my restrictions.
But I'm not happy with using workarounds and I do not understand why it would behave differently between the 2 code examples ?
It doesn't make sense to me to select fields outside of the asked entity.

N-Tiered LinqToSql Question

I am hoping you can help. I am developing a tiered website using Linq to Sql. I created a new class(or object) in DBML designer called memberState. This object is not an actual table in the database. I have this method in my middle layer:
public override IEnumerable(memberState) GetMembersByState(string #state)
{
using (BulletinWizardDataContext context = DataContext)
{
IEnumerable(memberState) mems = (from m in context.Members
join ma in context.MemberAddresses
on m.UserId equals ma.UserId
join s in context.States
on ma.StateId equals s.StateId
where s.StateName == #state
select new memberState
{
userId = m.UserID,
firstName = m.FirstName,
middleInitial = m.MiddleInitial,
lastName = m.LastName,
createDate = m.CreateDate,
modifyDate = m.ModifyDate
}).ToArray(memberState)();
return mems;
}
}
The tables in my joins (Members, States, and MemberAddresses are actual tables in my Database). I created the object memberStates so I could use it in the query above (notice the Select New memberState. When the data is updated on the web page how do I persist the changes back to the Member Table? My Member Table consists of the following columns: UserId, FirstName, MiddleInitial, LastName, CreateDate, ModifyDate. I am not sure how save the changes back to the database.
Thanks,
If I remember correctly, you can create a view from the different tables (Members, States, and MemberAddresses) and add that to the data context. Then any modifications to data in the view object can be saved, and linq to sql will handle the commit correctly as long as all the relationships are clearly setup/defined in both the database and in the data context.
If you have a Member table, the dbml will most likely contain a Member class. To update a member in the database, you will have to create a new Member object, and the Attach it to the BulletinWizardDataContext.Members collection. Something similar to the following code should the trick (I have not tested the code):
using (BulletinWizardDataContext context = DataContext)
{
Member m = new Member() { UserId = userId };
context.Members.Attach(m);
m.FirstName = firstName;
// Set other properties
context.SubmitChanges();
}
Attach must be called before setting the properties. Also, Linq2Sql has some issues with Attach in the case where the properties of your object are set to default values (i.e. 0 for numeric values, false for booleans, null for string etc.). In this case Attach will not generate the correct SQL.
var m = myContext.Members.Single(m=> m.UserID == myMemState.userID);
m.FirstName = myMemState.firstName;
m.MiddleInitial = myMemState.middleInitial;
...
That would be the quick way. It does an additional roundtrip to the db, but will work well. If that's an issue for you, then do Attach like Jakob suggested. For that you have to have to do some extra steps, like reviewing the configuration for optimistic updates and make sure you have the original fields when doing the attach.

Linq to SQL Stored Procedures with Multiple Results

We have followed the approach below to get the data from multiple results using LINQ To SQL
CREATE PROCEDURE dbo.GetPostByID
(
#PostID int
)
AS
SELECT *
FROM Posts AS p
WHERE p.PostID = #PostID
SELECT c.*
FROM Categories AS c
JOIN PostCategories AS pc
ON (pc.CategoryID = c.CategoryID)
WHERE pc.PostID = #PostID
The calling method in the class the inherits from DataContext should look like:
[Database(Name = "Blog")]
public class BlogContext : DataContext
{
...
[Function(Name = "dbo.GetPostByID")]
[ResultType(typeof(Post))]
[ResultType(typeof(Category))]
public IMultipleResults GetPostByID(int postID)
{
IExecuteResult result =
this.ExecuteMethodCall(this,
((MethodInfo)(MethodInfo.GetCurrentMethod())),
postID);
return (IMultipleResults)(result.ReturnValue);
}
}
Notice that the method is decorated not only with the Function attribute that maps to the stored procedure name, but also with the ReturnType attributes with the types of the result sets that the stored procedure returns. Additionally, the method returns an untyped interface of IMultipleResults:
public interface IMultipleResults : IFunctionResult, IDisposable
{
IEnumerable<TElement> GetResult<TElement>();
}
so the program can use this interface in order to retrieve the results:
BlogContext ctx = new BlogContext(...);
IMultipleResults results = ctx.GetPostByID(...);
IEnumerable<Post> posts = results.GetResult<Post>();
IEnumerable<Category> categories = results.GetResult<Category>();
In the above stored procedures we had two select queries
1. Select query without join
2. Select query with Join
But in the above second select query the data which is displayed is from one of the table i.e. from Categories table. But we have used join and want to display the data table with the results from both the tables i.e. from Categories as well as PostCategories.
Please if anybody can let me know how to achieve this using LINQ to SQL
What is the performance trade-off if we use the above approach vis-à-vis implement the above approach with simple SQL
Scott Guthrie (the guy who runs the .Net dev teams at MS) covered how to do this on his blog some months ago much better than I ever could, link here. On that page there is a section titled "Handling Multiple Result Shapes from SPROCs". That explains how to handle multiple results from stored procs of different shapes (or the same shape).
I highly recommend subscribing to his RSS feed. He is pretty much THE authoritative source on all things .Net.
Heya dude - does this work?
IEnumerable<Post> posts;
IEnumerable<Category> categories;
using (BlogContext ctx = new BlogContext(...))
{
ctx.DeferredLoadingEnabled = false; // THIS IS IMPORTANT.
IMultipleResults results = ctx.GetPostByID(...);
posts = results.GetResult<Post>().ToList();
categories = results.GetResult<Category>().ToList();
}
// Now we need to associate each category to the post.
// ASSUMPTION: Each post has only one category (1-1 mapping).
if (posts != null)
{
foreach(var post in posts)
{
int postId = post.PostId;
post.Category = categories
.Where(p => p.PostId == postId)
.SingleOrDefault();
}
}
Ok. lets break this down.
First up, a nice connection inside a using block (so it's disposed of nicely).
Next, we make sure DEFERRED LOADING is off. Otherwise, when u try and do the set (eg. post.Category == blah) it will see that it's null, lazy-load the data (eg. do a rountrip the database) set the data and THEN override the what was just dragged down from the db, with the result of there Where(..) method. phew! Summary: make sure deferred loading is off for the scope of the query.
Last, for each post, iterate and set the category from the second list.
does that help?
EDIT
Fixed it so that it doesn't throw an enumeration error by calling the ToList() methods.
Just curious, if a Post have have one or many Categories, is it possible to instead of using the for loop, to load the Post.PostCategories with the list of Categories (one to many), all in one shot, using a JOIN?
var rslt = from p in results.GetResult<Post>()
join c in results.GetResult<Category>() on p.PostId = c.PostID
...
p.Categories.Add(c)