عوامل JSONB واستعلامات الاحتواء
استخدم عوامل الاحتواء والمسار التي تستطيع فهارس GIN تسريعها فعليًا.
عوامل JSONB واستعلامات الاحتواء درس مجاني في PostgreSQL Performance & Query Optimization على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PostgreSQL Performance & Query Optimization، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PostgreSQL Performance & Query Optimization 4 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
Why Operator Choice Decides Index Use
In PostgreSQL, a column of type jsonb can be searched many different ways, but not every operator can use an index. Performance here is almost entirely about choosing operators that a GIN index can accelerate.
- A GIN index (Generalized Inverted Index) stores the keys and values inside your JSON documents so lookups skip the full table.
- The two operators that matter most are containment (
@>) and key existence (?,?|,?&).
This lesson teaches exactly which operators those are, and how to write queries that stay index-friendly.
The Containment Operator @>
The containment operator @> asks: does the left JSONB contain the right JSONB? The right side is a fragment, and Postgres checks that every key/value in it appears in the left document.
'{"a":1,"b":2}' @> '{"a":1}'is true.'{"a":1}' @> '{"a":1,"b":2}'is false (the right side has more).
This is the workhorse for filtering rows: WHERE data @> '{"status":"active"}' finds every row whose JSON includes that pair.
SELECT '{"a":1,"b":2}'::jsonb @> '{"a":1}'::jsonb AS contains_a,
'{"a":1}'::jsonb @> '{"a":1,"b":2}'::jsonb AS contains_both;Building a GIN Index for Containment
A plain GIN index on a jsonb column supports both containment and key-existence operators. This is the index you reach for first.
- The default
jsonb_opsoperator class indexes every key and value. - It accelerates
@>,?,?|, and?&.
Create it once, and containment filters that previously scanned the whole table become bitmap index scans.
CREATE INDEX idx_events_data
ON events
USING GIN (data);Containment Filters in WHERE
Once the GIN index exists, write the filter as a containment check so the planner can use it. Matching a nested fragment works too, because containment is recursive.
- Top-level match:
data @> '{"status":"active"}'. - Nested match:
data @> '{"user":{"plan":"pro"}}'.
Notice we pass a JSON object literal on the right, not a column reference or function call. That literal shape is what makes the query index-eligible.
SELECT id, created_at
FROM events
WHERE data @> '{"user":{"plan":"pro"}}'
ORDER BY created_at DESC
LIMIT 50;Key Existence Operators ? ?| ?&
Sometimes you only care whether a key is present, regardless of its value. The existence operators handle this and are also GIN-accelerated.
data ? 'email'— true if the top-level keyemailexists.data ?| array['phone','email']— true if any of these keys exist.data ?& array['phone','email']— true if all of these keys exist.
Important: ? checks top-level keys only, and for arrays it checks whether the string is an element.
SELECT '{"email":"x@y.z","phone":"123"}'::jsonb ? 'email' AS has_email,
'{"email":"x@y.z"}'::jsonb ?| array['phone','email'] AS has_any,
'{"email":"x@y.z"}'::jsonb ?& array['phone','email'] AS has_all;The Trap: Path Extraction Operators -> and ->>
The extraction operators look convenient but are not accelerated by a standard GIN index:
data -> 'status'returns the value asjsonb.data ->> 'status'returns the value astext.
A query like WHERE data ->> 'status' = 'active' forces a sequential scan on a plain GIN index, because the index does not index extracted scalar comparisons. Prefer the containment form data @> '{"status":"active"}' instead.
-- Slow on a plain GIN index (seq scan):
SELECT * FROM events WHERE data ->> 'status' = 'active';
-- Fast equivalent (uses GIN):
SELECT * FROM events WHERE data @> '{"status":"active"}';Rescuing ->> with an Expression Index
If you genuinely need range or pattern comparisons on one field, a B-tree expression index on the extracted text is the right tool — not GIN.
- Index the exact expression you query.
- Then comparisons like
=,<,>, andBETWEENcan use it.
The query's expression must match the indexed expression character for character, or the planner ignores the index.
CREATE INDEX idx_events_status
ON events ((data ->> 'status'));
-- Now this can use the B-tree index:
SELECT * FROM events WHERE (data ->> 'status') = 'active';jsonb_path_ops: Smaller, Faster, Containment-Only
The alternative operator class jsonb_path_ops indexes hashed root-to-leaf paths instead of every key.
- It produces a smaller index and is typically faster for
@>queries. - Trade-off: it supports only containment (
@>), not the existence operators?,?|,?&.
Choose jsonb_path_ops when your workload is dominated by containment filtering and you never need key-existence searches.
CREATE INDEX idx_events_data_path
ON events
USING GIN (data jsonb_path_ops);Containment Against Arrays
Containment also matches inside JSON arrays, which makes it ideal for tag-style data. To ask "does this array contain a value," wrap the value in an array on the right side.
'["a","b","c"]' @> '["b"]'is true.- For a tagged document:
data @> '{"tags":["urgent"]}'finds rows whosetagsarray includesurgent.
This stays fully index-eligible on a GIN index, so tag filtering scales well.
SELECT '["a","b","c"]'::jsonb @> '["b"]'::jsonb AS has_b,
'{"tags":["urgent","billing"]}'::jsonb
@> '{"tags":["urgent"]}'::jsonb AS is_urgent;Verify With EXPLAIN
Never assume the index is used — confirm it. Run EXPLAIN and look for a Bitmap Index Scan on your GIN index. A Seq Scan means your operator or expression defeated the index.
- Good sign:
Bitmap Index Scan on idx_events_data. - Bad sign:
Seq Scan on eventswith a JSON filter.
Use EXPLAIN (ANALYZE, BUFFERS) to also see real timing and how many pages were read.
EXPLAIN (ANALYZE, BUFFERS)
SELECT id FROM events
WHERE data @> '{"status":"active"}';Putting It Together: A Decision Rule
Use this quick rule when writing a JSONB filter:
- Matching key/value or nested fragment? Use
@>with a GIN index. - Only checking a key is present? Use
?/?|/?&with defaultjsonb_opsGIN. - Containment-only workload, want the smallest index? Use
jsonb_path_opsGIN. - Range or pattern on one scalar field? Use a B-tree expression index on
->>.
Avoid ->> equality filters without a matching expression index — they trigger sequential scans.
Quick Check
You have a default jsonb_ops GIN index on events.data. Which WHERE clause can use that index?
Recap
You learned which JSONB operators actually benefit from indexing:
- @> (containment) is the primary GIN-accelerated filter, including nested objects and arrays.
- ?, ?|, ?& (key existence) are GIN-accelerated, but only with the default
jsonb_opsclass, and check top-level keys. - jsonb_path_ops gives a smaller, faster containment-only index.
- -> and ->> extraction filters do NOT use a plain GIN index; rewrite as
@>or add a B-tree expression index. - Always confirm with
EXPLAINthat you get a Bitmap Index Scan, not a Seq Scan.
الأسئلة الشائعة
هل درس «عوامل JSONB واستعلامات الاحتواء» مجاني؟
نعم — نص درس «عوامل JSONB واستعلامات الاحتواء» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PostgreSQL Performance & Query Optimization، انتقل إلى CoddyKit PRO. تتضمن دورة PostgreSQL Performance & Query Optimization 4 دروس في المجموع.
ماذا ستتعلم في «عوامل JSONB واستعلامات الاحتواء»؟
استخدم عوامل الاحتواء والمسار التي تستطيع فهارس GIN تسريعها فعليًا. تتمرن على PostgreSQL Performance & Query Optimization مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ PostgreSQL Performance & Query Optimization؟
لا تُشترط خبرة سابقة. PostgreSQL Performance & Query Optimization على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «عوامل JSONB واستعلامات الاحتواء»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس PostgreSQL Performance & Query Optimization هذا؟
نعم. كل درس في PostgreSQL Performance & Query Optimization يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- عوامل JSONB واستعلامات الاحتواء
- مقارنة GIN بفهرس التعبير على JSONB
- الاستعلام عن JSONB باستخدام JSONPath
- متى نطبّع البيانات خارج JSONB