फ़िल्टरिंग
कलेक्शंस संसाधनों की सूची होती हैं। एक डिकपल्ड साइट में, क्लाइंट-साइड पर “New Content” सूची या “My content” सेक्शन जैसी चीज़ें बनाने के लिए आप इन्हीं का उपयोग करते हैं।
हालाँकि, जब आप /jsonapi/node/article
जैसे किसी कलेक्शन एंडपॉइंट पर बिना फ़िल्टर वाला अनुरोध करते हैं, तो आपको वे सभी लेख मिलेंगे जिन्हें देखने की आपको अनुमति है।
बिना फ़िल्टर के, आप केवल अपने लेख या केवल लामा के बारे में लेख नहीं पा सकते।
यह गाइड आपको प्रो की तरह फ़िल्टर बनाना सिखाएगा।
क्विक स्टार्ट
सबसे सरल और सामान्य फ़िल्टर एक key-value फ़िल्टर है:
?filter[field_name]=value&filter[field_other]=value
यह उन सभी संसाधनों का मिलान करता है जिनमें “field_name” “value” के बराबर है और “field_other” भी “value” के बराबर है।
बाकी सबके लिए, आगे पढ़ें!
सारांश
JSON:API मॉड्यूल में फ़िल्टरिंग की सबसे मज़बूत और फीचर-रिच क्षमताएँ हैं। इतना पावर थोड़ी सी सीखने की कर्व के साथ आता है।
इस लेख के अंत तक, आप जटिल क्वेरी बना पाएँगे और ऐसे सवालों पर तर्क कर पाएँगे, जैसे “मैं किसी लेखक के लामा पर लिखे लेखों या पशु जगत के सबसे तेज़ सदस्य पेरिग्रिन फाल्कन के बारे में लेखों की सूची कैसे पाऊँ?”
हम बिलकुल बुनियाद से शुरू करेंगे। उसके बाद कुछ शॉर्टकट दिखाएँगे जो फ़िल्टर लिखना थोड़ा तेज़ और कम verbose बनाते हैं। अंत में, वास्तविक दुनिया से लिए गए कई फ़िल्टर उदाहरण देखेंगे।
यदि आप Drupal में नए नहीं हैं, तो शायद आपने इन कामों के लिए Views मॉड्यूल का इस्तेमाल किया होगा। Drupal Core के साथ आने वाले REST मॉड्यूल के विपरीत, JSON:API Views के परिणाम एक्सपोर्ट नहीं करता। कलेक्शंस, Views में एक्सपोर्ट किए गए “REST displays” के लिए JSON:API का API-फर्स्ट विकल्प हैं।
फ़िल्टर बनाना
JSON:API के फ़िल्टर्स के मौलिक बिल्डिंग ब्लॉक्स conditions और groups हैं। कंडीशंस किसी बात को सत्य ठहराती हैं और ग्रुप्स आपको उन दावों को तार्किक सेट्स में जोड़कर बड़े कंडीशन ग्रुप बनाने देते हैं। ये सेट्स नेस्ट होकर बेहद सूक्ष्म क्वेरी बना सकते हैं। इन नेस्टेड सेट्स को आप पेड़ की तरह समझ सकते हैं:
पारंपरिक अभिव्यक्ति:
a( b() && c( d() || e() ) )
ट्री अभिव्यक्ति:
a
/ \
b & c
/ \
d | e
दोनों अभिव्यक्तियों में:
“d” और “e”, “c” के सदस्य हैं और OR ग्रुप में हैं।
“b” और “c”, “a” के सदस्य हैं और AND ग्रुप में हैं।
तो, एक कंडीशन के अंदर क्या होता है?
आइए लॉजिकल बनें 🖖। याद रखें, एक कंडीशन किसी संसाधन के बारे में TRUE या FALSE बात बताती है और उस पर आपका कोई दावा, जैसे “क्या यह एंटिटी किसी विशेष यूज़र द्वारा बनाई गई थी?” जब किसी संसाधन के लिए कंडीशन FALSE होती है, तो वह संसाधन कलेक्शन में शामिल नहीं होगा।
एक कंडीशन के 3 प्राथमिक हिस्से होते हैं: path, operator और value।
- ‘path’ किसी संसाधन पर एक फ़ील्ड को पहचानता है
- ‘operator’ तुलना करने का तरीका होता है
- ‘value’ वह चीज़ है जिसके विरुद्ध आप तुलना करते हैं
छद्म-कोड में, एक कंडीशन कुछ इस तरह दिखती है:
($field !== 'space')
जहाँ:
$field
संसाधन का वह फ़ील्ड है जिसे उसके ‘path’ से पहचाना गया- ‘operator’ है
!==
- ‘value’ स्ट्रिंग
'space'
है
JSON:API मॉड्यूल में, हम इसे इतना सुंदर नहीं बना सकते क्योंकि हमें इसे URL क्वेरी-स्ट्रिंग के अंदर काम कराना होता है। इसके लिए, हम हर कंडीशन को key/value युग्मों से दर्शाते हैं।
यदि हम किसी यूज़र के first name पर फ़िल्टर कर रहे होते, तो कंडीशन कुछ इस तरह दिखती:
?filter[a-label][condition][path]=field_first_name
&filter[a-label][condition][operator]=%3D <- एन्कोडेड “=” चिन्ह
&filter[a-label][condition][value]=Janis
ध्यान दें कि हमने पहले स्क्वायर-ब्रैकेट्स के अंदर एक लेबल रखा। यह b-label
, this_is_my_super_awesome_label
या फिर 666
जैसा कोई पूर्णांक भी हो सकता था 🤘😅। बात यह है कि हर कंडीशन या ग्रुप का एक पहचानकर्ता होना चाहिए।
लेकिन अगर सिस्टम में बहुत सारी Janis हों तो?
एक और फ़िल्टर जोड़ते हैं ताकि हमें केवल वे Janis मिलें जिनका last name “J” से शुरू होता है:
?filter[first-name-filter][condition][path]=field_first_name
&filter[first-name-filter][condition][operator]=%3D <- एन्कोडेड “=”
&filter[first-name-filter][condition][value]=Janis
&filter[last-name-filter][condition][path]=field_last_name
&filter[last-name-filter][condition][operator]=STARTS_WITH
&filter[last-name-filter][condition][value]=J
शायद Janis का बहुवचन “Janii” होता होगा 🤔…
=
और STARTS_WITH
के अलावा भी कई ऑपरेटर हैं। यह है पूरी सूची, सीधे JSON:API कोडबेस से:
\Drupal\jsonapi\Query\EntityCondition::$allowedOperators = [
'=', '<>',
'>', '>=', '<', '<=',
'STARTS_WITH', 'CONTAINS', 'ENDS_WITH',
'IN', 'NOT IN',
'BETWEEN', 'NOT BETWEEN',
'IS NULL', 'IS NOT NULL',
];
सिंबल ऑपरेटर्स को URL-एन्कोड करना होता है। आप PHP के urlencode()
फ़ंक्शन से सही एन्कोडिंग पा सकते हैं।
कंडीशन ग्रुप्स
अब हम कंडीशंस बनाना जान गए, पर अभी कंडीशंस के ग्रुप बनाना नहीं सीखा। ऊपर जैसा ट्री कैसे बनाएँ?
इसके लिए हमें “group” चाहिए। एक ग्रुप कंडीशंस का सेट है जो “conjunction” से जोड़ा होता है। हर ग्रुप में एक conjunction होता है और conjunction या तो AND होता है या OR।
अब हमारा फ़िल्टर थोड़ा ज़्यादा विशिष्ट हो गया! मान लें, हम उन सभी यूज़र्स को खोजना चाहते हैं जिनका last name “J” से शुरू होता है और जिनका first name या तो “Janis” या “Joan” है।
इसके लिए, हम एक ग्रुप जोड़ते हैं:
?filter[rock-group][group][conjunction]=OR
फिर, हमें अपने फ़िल्टर्स को उस नए ग्रुप का सदस्य बनाना होगा।
इसके लिए हम memberOf
कुंजी जोड़ते हैं। हर कंडीशन और ग्रुप में memberOf
कुंजी हो सकती है।
टिप: ग्रुप्स में भी memberOf
हो सकता है, जैसे कंडीशंस में होता है—यानि हम “ग्रुप्स के ग्रुप्स” बना सकते हैं 🤯!
नोट: जिन फ़िल्टर्स में memberOf
नहीं होता, उन्हें AND conjunction वाले “root” ग्रुप का हिस्सा माना जाता है।
अब सबको जोड़कर देखें:
?filter[rock-group][group][conjunction]=OR
&filter[janis-filter][condition][path]=field_first_name
&filter[janis-filter][condition][operator]=%3D
&filter[janis-filter][condition][value]=Janis
&filter[janis-filter][condition][memberOf]=rock-group
&filter[joan-filter][condition][path]=field_first_name
&filter[joan-filter][condition][operator]=%3D
&filter[joan-filter][condition][value]=Joan
&filter[joan-filter][condition][memberOf]=rock-group
&filter[last-name-filter][condition][path]=field_last_name
&filter[last-name-filter][condition][operator]=STARTS_WITH
&filter[last-name-filter][condition][value]=J
यह परिचित लग रहा है?
लगना चाहिए, हमने इसे ऊपर ट्री के रूप में देखा था:
a a = root-and-group
/ \
/ \ b = last-name-filter
b c c = rock-group
/ \
/ \ d = janis-filter
d e e = joan-filter
आप इन ग्रुप्स को जितना चाहें उतना गहराई तक नेस्ट कर सकते हैं।
पाथ्स
कंडीशंस की एक आख़िरी विशेषता: ‘paths’
Paths संबंध (relationship) मूल्यों के आधार पर फ़िल्टर करने का तरीका देते हैं।
अब तक, हम यूज़र संसाधन पर काल्पनिक field_first_name
और field_last_name
के आधार पर ही फ़िल्टर कर रहे थे।
मान लीजिए हम यूज़र के करियर के नाम पर फ़िल्टर करना चाहते हैं, जहाँ करियर प्रकार अलग संसाधन में स्टोर हैं। हम ऐसा फ़िल्टर जोड़ सकते हैं:
?filter[career][condition][path]=field_career.name
&filter[career][condition][operator]=%3D
&filter[career][condition][value]=Rockstar
Paths, रिश्तों को पार करने के लिए “डॉट नोटेशन” का उपयोग करते हैं।
यदि किसी संसाधन में कोई relationship है, तो आप उस पर फ़िल्टर जोड़ सकते हैं—relationship फ़ील्ड का नाम और उस relationship के फ़ील्ड का नाम जोड़कर—और बीच में .
(डॉट) लगाकर।
आप relationships के relationships (आगे भी) पर भी फ़िल्टर कर सकते हैं, बस और फ़ील्ड नाम व डॉट जोड़ते जाएँ।
टिप: आप relationship के किसी विशेष इंडेक्स पर फ़िल्टर कर सकते हैं—पाथ में कोई non-negative integer रखकर। जैसे some_relationship.1.some_attribute
केवल दूसरे संबंधित संसाधन पर फ़िल्टर करेगा।
टिप: आप किसी फ़ील्ड की उप-प्रॉपर्टीज पर भी फ़िल्टर कर सकते हैं। उदाहरण के लिए, field_phone.country_code
जैसा पाथ काम करेगा, भले ही field_phone
कोई relationship न हो।
टिप: कॉन्फ़िगरेशन प्रॉपर्टीज़ के विरुद्ध फ़िल्टर करते समय, आप पाथ के किसी भी हिस्से के लिए स्टार (*) का उपयोग वाइल्डकार्ड की तरह कर सकते हैं। उदाहरण: /jsonapi/field_config/field_config?filter[dependencies.config.*]=comment.type.comment
उन सभी फ़ील्ड कॉन्फ़िग्स से मेल खाएगा जिनमें ["attributes"]["dependencies"]["config"]
(एक indexed array) में “comment.type.comment” मान मौजूद है।
शॉर्टकट्स
टाइप करने के लिए यह बहुत सारे कैरेक्टर्स हैं। अधिकतर समय आपको इतने जटिल फ़िल्टरों की ज़रूरत नहीं होती; ऐसे मामलों के लिए JSON:API मॉड्यूल कुछ “शॉर्टकट्स” देता है जिससे फ़िल्टर जल्दी लिखे जा सकें।
जब ऑपरेटर =
हो, तो उसे शामिल करने की ज़रूरत नहीं—इसे मान लिया जाता है। इसलिए:
?filter[a-label][condition][path]=field_first_name
&filter[a-label][condition][operator]=%3D <- एन्कोडेड “=” चिन्ह
&filter[a-label][condition][value]=Janis
बन जाता है
?filter[janis-filter][condition][path]=field_first_name
&filter[janis-filter][condition][value]=Janis
यह भी कम होता है कि आपको एक ही फ़ील्ड पर दो बार फ़िल्टर करना पड़े (हालाँकि संभव है)। इसलिए, जब ऑपरेटर =
हो और आपको उसी फ़ील्ड पर दो बार फ़िल्टर नहीं करना, तो path ही पहचानकर्ता हो सकता है। इसलिए:
?filter[janis-filter][condition][path]=field_first_name
&filter[janis-filter][condition][value]=Janis
बन जाता है
?filter[field_first_name][value]=Janis
वह अतिरिक्त value
भी झंझट है। इसलिए आप सबसे सरल समता (equality) जाँच को key-value रूप में घटा सकते हैं:
?filter[field_first_name]=Janis
फ़िल्टर्स और एक्सेस कंट्रोल
पहले एक चेतावनी: फ़िल्टर्स को एक्सेस कंट्रोल समझने की गलती न करें। सिर्फ इसलिए कि आपने किसी ऐसी चीज़ को हटाने के लिए फ़िल्टर लिखा है जिसे यूज़र नहीं देखना चाहिए, इसका अर्थ यह नहीं कि वह अप्राप्य है। हमेशा बैकएंड पर एक्सेस चेक करें।
इस बड़े caveat के साथ, चलिए देखते हैं कि फ़िल्टर्स एक्सेस कंट्रोल का पूरक कैसे बन सकते हैं। परफ़ॉर्मेंस सुधारने के लिए, आपको वह सब फ़िल्टर कर देना चाहिए जिसे आपके यूज़र देख नहीं पाएँगे। JSON:API issue queues में आने वाले सबसे सामान्य सपोर्ट अनुरोधों में से कई इसी एक सरल तरकीब से हल हो जाते हैं!
यदि आपको पता है कि आपके यूज़र unpublished कंटेंट नहीं देख सकते, तो यह फ़िल्टर जोड़ें:
?filter[status][value]=1
इस तरीके से, आप अनावश्यक अनुरोधों की संख्या कम कर देंगे। क्योंकि JSON:API उन संसाधनों का डेटा वापस नहीं करता जिन तक यूज़र की पहुँच नहीं है। आप JSON:API दस्तावेज़ के meta.errors
सेक्शन को देखकर देख सकते हैं कि किन संसाधनों पर इसका प्रभाव पड़ा हो सकता है।
इसलिए, अप्राप्य संसाधनों को पहले से ही जितना हो सके फ़िल्टर कर दें।
फ़िल्टर उदाहरण
1. केवल प्रकाशित नोड्स प्राप्त करें
बहुत सामान्य परिस्थिति यह है कि केवल प्रकाशित नोड्स लोड करने हैं। यह जोड़ना बेहद आसान फ़िल्टर है।
SHORT
filter[status][value]=1
NORMAL
filter[status-filter][condition][path]=status
filter[status-filter][condition][value]=1
2. किसी entity reference के मान से नोड्स प्राप्त करें
एक सामान्य रणनीति है कि कंटेंट को किसी entity reference से फ़िल्टर किया जाए।
SHORT
filter[uid.id][value]=BB09E2CD-9487-44BC-B219-3DC03D6820CD
NORMAL
filter[author-filter][condition][path]=uid.id
filter[author-filter][condition][value]=BB09E2CD-9487-44BC-B219-3DC03D6820CD
JSON API विनिर्देशन के पूर्ण पालन के लिए, जबकि Drupal आंतरिक रूप से uuid
प्रॉपर्टी का उपयोग करता है, JSON API इसके स्थान पर id
का इस्तेमाल करता है।
Drupal 9.3 से, केवल uuid
प्रॉपर्टी से फ़िल्टर करने के बजाय target_id
पर भी फ़िल्टर करना संभव है।
SHORT
filter[field_tags.meta.drupal_internal__target_id]=1
NORMAL
filter[name-filter][condition][path]=field_tags.meta.drupal_internal__target_id
filter[name-filter][condition][value]=1
3. नेस्टेड फ़िल्टर्स: यूज़र “admin” द्वारा बनाए गए नोड्स प्राप्त करें
यूज़र, टैक्सोनॉमी फ़ील्ड्स या किसी भी entity reference फ़ील्ड जैसी संदर्भित एंटिटीज़ के फ़ील्ड्स पर फ़िल्टर करना संभव है। यह आप आसानी से इस नोटेशन से कर सकते हैं: reference_field.nested_field। इस उदाहरण में reference फ़ील्ड यूज़र के लिए uid
है और name
यूज़र एंटिटी का एक फ़ील्ड है।
SHORT
filter[uid.name][value]=admin
NORMAL
filter[name-filter][condition][path]=uid.name
filter[name-filter][condition][value]=admin
4. ऐरे के साथ फ़िल्टरिंग: यूज़र्स [admin, john] द्वारा बनाए गए नोड्स प्राप्त करें
आप किसी फ़िल्टर को खोज के लिए कई मान दे सकते हैं। फ़ील्ड और value की कुंजियों के साथ आप अपनी कंडीशन में एक ऑपरेटर भी जोड़ सकते हैं। आमतौर पर यह “=“ होता है, लेकिन आप “IN”, “NOT IN”, “>”, “<”, “<>”, “BETWEEN” भी उपयोग कर सकते हैं।
इस उदाहरण में हम IN ऑपरेटर का उपयोग करेंगे। ध्यान दें कि मैंने value के पीछे दो स्क्वायर-ब्रैकेट्स जोड़कर इसे array बनाया है।
NORMAL
filter[name-filter][condition][path]=uid.name
filter[name-filter][condition][operator]=IN
filter[name-filter][condition][value][1]=admin
filter[name-filter][condition][value][2]=john
टिप: मल्टीपल वैल्यू फ़िल्टर्स के लिए स्क्वायर-ब्रैकेट्स का उपयोग करते समय, नए मान के लिए सिर्फ खाली स्क्वायर-ब्रैकेट्स न लगाएँ। URL में टाइप करने पर ये काम कर सकते हैं, लेकिन Guzzle और अन्य HTTP क्लाइंट केवल एक ही मान बनाएँगे क्योंकि ऐरे कुंजी समान दिखेगी और पिछला मान ओवरराइड हो जाएगा। बेहतर है कि यूनिक ऐरे एलिमेंट्स बनाने के लिए इंडेक्स का उपयोग करें।
5. ग्रुपिंग फ़िल्टर्स: वे नोड्स पाएं जो प्रकाशित हैं और admin द्वारा बनाए गए हैं
अब ऊपर के कुछ उदाहरणों को मिलाकर यह परिदृश्य बनाते हैं:
WHERE user.name = admin AND node.status = 1;
filter[and-group][group][conjunction]=AND
filter[name-filter][condition][path]=uid.name
filter[name-filter][condition][value]=admin
filter[name-filter][condition][memberOf]=and-group
filter[status-filter][condition][path]=status
filter[status-filter][condition][value]=1
filter[status-filter][condition][memberOf]=and-group
आपको and-group जोड़ने की ज़रूरत नहीं, पर मुझे यह आम तौर पर थोड़ा आसान लगता है।
6. ग्रुप्ड फ़िल्टर्स का ग्रुपिंग: वे नोड्स पाएं जो promoted या sticky हैं और admin द्वारा बनाए गए हैं
जैसा कि ग्रुपिंग सेक्शन में बताया गया, आप ग्रुप्स के अंदर ग्रुप्स रख सकते हैं।
WHERE (user.name = admin) AND (node.sticky = 1 OR node.promoted = 1)
इसके लिए हम sticky और promoted को OR conjunction वाले ग्रुप में रखें। AND conjunction वाला एक ग्रुप बनाएँ और उसमें admin फ़िल्टर तथा promoted/sticky वाला OR ग्रुप दोनों रखें।
# AND और OR ग्रुप बनाएँ
filter[and-group][group][conjunction]=AND
filter[or-group][group][conjunction]=OR
# OR ग्रुप को AND ग्रुप का सदस्य बनाएँ
filter[or-group][group][memberOf]=and-group
# admin फ़िल्टर बनाएँ और AND ग्रुप में रखें
filter[admin-filter][condition][path]=uid.name
filter[admin-filter][condition][value]=admin
filter[admin-filter][condition][memberOf]=and-group
# sticky फ़िल्टर बनाएँ और OR ग्रुप में रखें
filter[sticky-filter][condition][path]=sticky
filter[sticky-filter][condition][value]=1
filter[sticky-filter][condition][memberOf]=or-group
# promoted फ़िल्टर बनाएँ और OR ग्रुप में रखें
filter[promote-filter][condition][path]=promote
filter[promote-filter][condition][value]=1
filter[promote-filter][condition][memberOf]=or-group
7. वे नोड्स फ़िल्टर करें जहाँ ‘title’ में “Foo” CONTAINS
हो
SHORT
filter[title][operator]=CONTAINS&filter[title][value]=Foo
NORMAL
filter[title-filter][condition][path]=title
filter[title-filter][condition][operator]=CONTAINS
filter[title-filter][condition][value]=Foo
8. नॉन-स्टैंडर्ड जटिल फ़ील्ड्स (जैसे addressfield) द्वारा फ़िल्टर करें
LOCALITY द्वारा फ़िल्टर
filter[field_address][condition][path]=field_address.locality
filter[field_address][condition][value]=Mordor
ADDRESS LINE द्वारा फ़िल्टर
filter[address][condition][path]=field_address.address_line1
filter[address][condition][value]=Rings Street
9. टैक्सोनॉमी टर्म मानों (जैसे tags) पर फ़िल्टर करना
फ़िल्टरिंग के लिए आपको vocabulary का मशीन-नेम और वह फ़ील्ड चाहिए जो आपके नोड पर मौजूद है।
filter[taxonomy_term--tags][condition][path]=field_tags.name
filter[taxonomy_term--tags][condition][operator]=IN
filter[taxonomy_term--tags][condition][value][]=tagname
10. दिनांक पर फ़िल्टरिंग (केवल दिनांक, समय नहीं)
दिनांक फ़िल्टर-योग्य हैं। ISO-8601 फ़ॉर्मेट का पालन करने वाली टाइम स्ट्रिंग पास करें।
यह उदाहरण उस Date फ़ील्ड के लिए है जो केवल दिनांक (समय नहीं) के लिए सेट है।
filter[datefilter][condition][path]=field_test_date
filter[datefilter][condition][operator]=%3D
filter[datefilter][condition][value]=2019-06-27
यह उदाहरण उस Date फ़ील्ड के लिए है जो दिनांक और समय दोनों को सपोर्ट करता है।
filter[datefilter][condition][path]=field_test_date
filter[datefilter][condition][operator]=%3D
filter[datefilter][condition][value]=2019-06-27T16%3A00%3A00
ध्यान दें कि timestamp फ़ील्ड्स (जैसे created या changed) पर अभी फ़िल्टरिंग के लिए timestamp का उपयोग करना होगा:
filter[recent][condition][path]=created
filter[recent][condition][operator]=%3D
filter[recent][condition][value]=1591627496
11. खाली ऐरे फ़ील्ड्स पर फ़िल्टरिंग
यह उदाहरण ऐसे Checkboxes/Radio buttons फ़ील्ड के लिए है जिसमें कोई मान चयनित नहीं है। मान लें आपके पास एक फ़ील्ड है जो चेकबॉक्स है। आप वे सभी नोड्स पाना चाहते हैं जिनमें वह मान चेक नहीं है। जब चेक्ड होता है, तो JSON API एक ऐरे लौटाता है:
"my_field":["checked"]
जब अनचेक्ड होता है, तो JSON API एक खाली ऐरे लौटाता है:
"my_field": []
यदि आप वे सभी फ़ील्ड्स पाना चाहते हैं जो अनचेक्ड हैं, तो आपको ऐरे पर बिना value दिए IS NULL
का उपयोग करना होगा:
filter[my-filter][condition][path]=my_field
filter[my-filter][condition][operator]=IS NULL
लेख स्रोत: Drupal Documentation.