41

I'm experimenting with postgres jsonb column types, and so far so good. One common query I'm using is like this:

select count(*) from jsonbtest WHERE attributes @> '{"City":"Mesa"}';

How do I reverse that? Is there a different operator or is it simply used as

select count(*) from jsonbtest WHERE NOT attributes @> '{"City":"Mesa"}';
3
  • 8
    No, there is no dedicated operator for that. What's wrong with NOT? Commented Sep 28, 2016 at 0:06
  • @redneb The problem is that NOT simply doesn't work. Using the attributes->>'City' <> 'Mesa' formulation also doesn't work. Commented Mar 30, 2017 at 2:09
  • 4
    @eykanal NOT works pretty well. Commented Apr 28, 2017 at 11:50

5 Answers 5

36

Two way, you can test any json(b) value

  • the ->> operator extract value as text. But this operation slow, if you will use the value only test
  • the @> operator test any json(b) contain any json(b). This is a quick but you are not tested NOT option.

Simply and quick way:

NOT (attribute @> '{"City":"Mesa"}'::jsonb)

I've change attribute->>'City' <> 'Mesa' to NOT (attribute @> '{"City":"Mesa"}'::jsonb) and my ~2.000.000 rows query result time changed 45secs to 25secs.

Sign up to request clarification or add additional context in comments.

4 Comments

The frustrating thing is that if you've added a nice USING GIN(attribute) index, negating the @> contains operator suddenly stops using that index.
NOT doesn't covers situation when you have attribute but this attribute equals NULL
@DmitryChirkin simpe test. null valued object: SELECT NOT('{"City":null}'::jsonb @> '{"City":"Mesa"}'::jsonb); returns as true alternatively test. not contain attribute: SELECT NOT('{"Another Attribute":"Mesa"}'::jsonb @> '{"City":"Mesa"}'::jsonb) returns true i think there is no problem
@nessur You can create the GIN index with jsonb_ops. e.g: CREATE INDEX idx_my_index_jsonb_data_with_ops ON my_table USING GIN (attribut jsonb_ops); You can use this link for options: postgresql.org/docs/current/gin-builtin-opclasses.html
13

This can be achieved with several conditions. It's not elegant but I didn't find another way to do so.

So, first, get every row which simple don't have 'City' attribute and then add 'OR' condition to check for correct field value.

select count(*) from jsonbtest where 
  NOT(attributes ? 'City') 
  OR (attributes ? 'City') is NULL -- this is required if attributes can be null
  OR (attributes->>'City' != 'Mesa')) 

1 Comment

This helped me a lot... I couldn't find anyting about on this use case on the PgSQL site. Thanks!
2

---contains specific key (City), does not contain value ('"Mesa"').

SELECT count(*) FROM jsonbtest  WHERE NOT (attributes  -> 'City'  @> '"Mesa"');

---does not contain specific key value pair: {"City":"Mesa"}

SELECT count(*) FROM jsonbtest  WHERE  NOT (attributes  @> '{"City":"Mesa"}')

Since attributes key can be others except City. So these two queries are different!

Comments

0

I had problem close to this one (NOT actually not reverting the JSON expression), but in my case I was looking for the key-value inside a hierarchy, therefore it was a composition of -> and @> (maybe OP's original problem got simplified when posting).

On such a case, we must consider that (per postgres docs):

The field/element/path extraction operators return NULL, rather than failing, if the JSON input does not have the right structure to match the request; for example if no such key or array element exists.

For example, if one is looking for the key/value pair nested into some hierarchy, but part of that hierarchy doesn't exist, the expression will be NULL instead of false, and NOT NULL is NULL, not true.

A catch-all solution is to wrap the original expression in NOT COALESCE(<original expression>, false).

Comments

-3

You can use the operator <@ this will search where 'City' is not 'Mesa'

select count(*) from jsonbtest WHERE attributes <@ '{"City":"Mesa"}';

1 Comment

That would be the same as select count(*) from jsonbtest WHERE '{"City":"Mesa"}' @> attributes;, checking if {"City": "Mesa"} contains attributes which is not the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.