Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them; it only takes a minute:

I'm currently using postgres in node to query all users who have a certain tag associated with their account like so (note: I'm using node-postgres):

query = 'SELECT tags.*, pl.email FROM admin.tags tags
LEFT JOIN gameday.player_settings pl
ON tags.player_id = pl.id
WHERE tags.tag = $1'

client.query(
  query,
  [tagName],
  function(err, results) {
    ...

[tagName] is then passed into the WHERE clause.

What I'm aiming to do is instead query by an unknown number of tags and return all users who have all of those tags associated with their account. So instead of [tagName] I'd like to pass in an array of unknown length, [tagNames], but I'm not sure how to accomplish this.

share|improve this question
1  
Where are you passing in the data from? Can you edit to make that clearer, and add an example of the code you need to integrate with? – IMSoP Aug 19 at 11:25
    
@IMSoP, details added above. – MattDionis Aug 19 at 11:40
up vote 2 down vote accepted

You need to turn the question backwards. Instead of:

Which users have all of these tags, you need to ask which users do not have one or more of these tags absent. It's a double negation.

You also need a way to pass the set of tags. The best way to do this, if the client language binding supports it, is as an array-valued query parameter. If the client binding doesn't support array-valued parameters you'll need dynamic SQL.

One formulation might be (untested, since you didn't provide sample schema and data):

SELECT pl.email
FROM gameday.player_settings pl
WHERE NOT EXISTS (
  SELECT 1
  FROM unnest(?) AS wanted_tags(tag)
  LEFT JOIN admin.tags tags
    ON tags.tag = wanted_tags.tag
  WHERE  tags.player_id = pl.id
    AND wanted_tags.tag IS NULL
);

Doing a left join and filtering for IS NULL is called a left anti-join. It keeps the rows where the left-join condition does not match. So in this case, we retain a tag from our wanted_tags array only if there is no matching tag associated with this player. If any tags are left, the WHERE NOT EXISTS returns false, so the player is excluded.

Double-think, isn't it? It's easy to make mistakes with this so test.

Here ? should be your programming language PostgreSQL database binding's query parameter placeholder. I don't know what node.js's is. This will only work if you can pass an array as a query parameter in node. If not, you'll have to use dynamic SQL to generate an ARRAY['x','y','z'] expression or a (VALUES ('x'), ('y'), ('z')) subquery.

P.S. Please provide sample schema and data with questions when possible. http://sqlfiddle.com/ is handy.

share|improve this answer
    
Thank you, and it looks like there is a way to pass an array of values according to the node-postgres FAQ question 11. – MattDionis Aug 19 at 12:09
    
According to this bug report it supports arrays now so the FAQ might be outdated. Confirmation. – Craig Ringer Aug 19 at 12:19
1  
@MattDionis The FAQ suggests that the driver does not support arrays natively (even though those bugs contradict it), and illustrates a way to do it using dynamic SQL. It still uses parameters to avoid SQL injection, but the query string has to be dynamically generated each time. So if possible you should prefer to use node-postgres's support for passing JavaScript arrays as parameters directly. – Craig Ringer Aug 19 at 12:26
1  
@MattDionis I've updated the FAQ. Please test and confirm, since I don't use node-postgres myself. – Craig Ringer Aug 19 at 12:28
    
I'll be testing this out later today and I'll report back. Thank you very much for the help! – MattDionis Aug 19 at 13:07

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.