JSON सरणी में एक तत्व खोजने के लिए सूचकांक


84

मेरे पास एक तालिका है जो इस तरह दिखती है:

CREATE TABLE tracks (id SERIAL, artists JSON);

INSERT INTO tracks (id, artists) 
  VALUES (1, '[{"name": "blink-182"}]');

INSERT INTO tracks (id, artists) 
  VALUES (2, '[{"name": "The Dirty Heads"}, {"name": "Louis Richards"}]');

कई अन्य स्तंभ हैं जो इस प्रश्न के लिए प्रासंगिक नहीं हैं। उन्हें JSON के रूप में संग्रहीत करने का एक कारण है।

मैं जो करने की कोशिश कर रहा हूं वह एक विशिष्ट कलाकार का नाम (सटीक मिलान) वाला ट्रैक देखने वाला है ।

मैं इस क्वेरी का उपयोग कर रहा हूं:

SELECT * FROM tracks 
  WHERE 'ARTIST NAME' IN
    (SELECT value->>'name' FROM json_array_elements(artists))

उदाहरण के लिए

SELECT * FROM tracks
  WHERE 'The Dirty Heads' IN 
    (SELECT value->>'name' FROM json_array_elements(artists))

हालाँकि, यह एक पूर्ण तालिका स्कैन करता है, और यह बहुत तेज़ नहीं है। मैंने एक फ़ंक्शन का उपयोग करके GIN इंडेक्स बनाने की कोशिश की names_as_array(artists), और उपयोग किया 'ARTIST NAME' = ANY names_as_array(artists), हालांकि इंडेक्स का उपयोग नहीं किया गया है और क्वेरी वास्तव में काफी धीमी है।


मैंने इस पर आधारित एक अनुवर्ती प्रश्न किया है: dba.stackexchange.com/questions/71546/…
Ken Li

जवाबों:


138

jsonb Postgres में 9.4+

नए बाइनरी JSON डेटा प्रकार के साथ jsonb, Postgres 9.4 ने काफी हद तक बेहतर सूचकांक विकल्प पेश किए । अब आप एक jsonbसरणी पर सीधे GIN सूचकांक रख सकते हैं :

CREATE TABLE tracks (id serial, artists jsonb);
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);

सरणी को परिवर्तित करने के लिए फ़ंक्शन की आवश्यकता नहीं है। यह एक प्रश्न का समर्थन करेगा:

SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';

@>नया jsonb"ऑपरेटर" होता है , जो GIN इंडेक्स का उपयोग कर सकता है। (प्रकार के लिए नहीं json, केवल jsonb!)

या आप jsonb_path_opsसूचकांक के लिए अधिक विशिष्ट, गैर-डिफ़ॉल्ट GIN ऑपरेटर वर्ग का उपयोग करते हैं:

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (artists jsonb_path_ops);

समान क्वेरी।

वर्तमान में jsonb_path_opsकेवल @>ऑपरेटर का समर्थन करता है । लेकिन यह आमतौर पर बहुत छोटा और तेज होता है। अधिक इंडेक्स विकल्प हैं, मैनुअल में विवरण


तो artists बस के रूप में दिखाया गया है उदाहरण के केवल नाम रखती है, इसे और अधिक एक कम अनावश्यक JSON मूल्य के साथ शुरू करने के लिए स्टोर करने के लिए कुशल होगा मान पाठ के रूप में पुरातन और अनावश्यक कुंजी स्तंभ नाम में हो सकता है।

JSON वस्तुओं और आदिम प्रकारों के बीच अंतर पर ध्यान दें:

CREATE TABLE tracks (id serial, artistnames jsonb);
INSERT INTO tracks  VALUES (2, '["The Dirty Heads", "Louis Richards"]');

CREATE INDEX tracks_artistnames_gin_idx ON tracks USING gin (artistnames);

प्रश्न:

SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';

?ऑब्जेक्ट वैल्यू के लिए काम नहीं करता है , बस चाबियाँ और सरणी तत्व
या (अधिक कुशल अगर नाम अक्सर दोहराया जाता है):

CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING  gin (artistnames jsonb_path_ops);

प्रश्न:

SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;

json पोस्टग्रेट्स 9.3+ में

यह एक IMMUTABLE समारोह के साथ काम करना चाहिए :

CREATE OR REPLACE FUNCTION json2arr(_j json, _key text)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT elem->>_key FROM json_array_elements(_j) elem)';

यह कार्यात्मक सूचकांक बनाएं :

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (json2arr(artists, 'name'));

और इस तरह एक क्वेरी का उपयोग करें। WHEREखंड में अभिव्यक्ति को सूचकांक में एक से मेल खाना है:

SELECT * FROM tracks
WHERE  '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));

टिप्पणियों में प्रतिक्रिया के साथ अपडेट किया गया। GIN इंडेक्स को सपोर्ट करने के लिए हमें ऐरे ऑपरेटर्स का उपयोग करना होगा। ऑपरेटर "के अंतर्गत होता है" इस मामले में।
<@

फ़ंक्शन अस्थिरता पर नोट्स

यदि आप नहींIMMUTABLE भी हैं तो भी आप अपने कार्य की घोषणा कर सकते हैं। अधिकांश कार्य केवल हुआ करते थे , नहीं । इसे बदलने के लिए हैकर्स की सूची पर चर्चा हुई। अब ज्यादातर हैं । इससे जाँच करें:json_array_elements()
JSONSTABLEIMMUTABLEIMMUTABLE

SELECT p.proname, p.provolatile
FROM   pg_proc p
JOIN   pg_namespace n ON n.oid = p.pronamespace
WHERE  n.nspname = 'pg_catalog'
AND    p.proname ~~* '%json%';

कार्यात्मक सूचकांक केवल कार्यों के साथ काम करते IMMUTABLEहैं।


2
यह काम नहीं करता है क्योंकि रिटर्न SETOFएक इंडेक्स में इस्तेमाल नहीं किया जा सकता है। इसे हटाकर, मैं इंडेक्स बना सकता हूं, हालांकि यह क्वेरी प्लानर द्वारा उपयोग नहीं किया जाता है। इसके अलावा, दोनों json_array_elements और array_aggIMMUTABLE
JeffS

2
@ टिप्पणी: क्षमा करें, मैं स्तंभ नाम और कुंजी नाम मिला रहा था। निश्चित और अधिक जोड़ा गया।
एरविन ब्रान्डेसटेटर

1
@PyWebDesign: jsonb कंटेंट क्वेश्चन आम तौर पर सम्‍मिलित ऑब्जेक्ट के समान संरचना से मेल खाना चाहिए (इसलिए किसी सरणी के अंदर किसी ऑब्जेक्ट की खोज करने का मतलब है कि आपको किसी सरणी के अंदर ऑब्जेक्ट का उपयोग करके क्वेरी करनी चाहिए)। एक सरणी के अंदर आदिम प्रकारों के लिए एक विशेष अपवाद है; यहाँ और अधिक विवरण: stackoverflow.com/a/29947194/818187
potatosalad

3
@PyWebDesign: अब मैं देख रहा हूं, एक उदाहरण में सरणी परत गायब थी। फिक्स्ड। सूचकांक केवल एक बड़ी तालिका में उपयोग किया जा रहा है ताकि यह अनुक्रमिक स्कैन की तुलना में पोस्टग्रेज के लिए सस्ता हो।
एरविन ब्रान्डेसटेटर

2
@PyWebDesign: अपने सत्र में SET enable_seqscan = off;(केवल डीबगिंग उद्देश्यों के लिए) stackoverflow.com/questions/14554302/… चलाएं ।
इरविन ब्रान्डस्टेट्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.