12 दिस॰ 2024·8 मिनट पढ़ने में

PostgreSQL में ऑर्ग चार्ट मॉडल करना: एडजेसेंसी लिस्ट बनाम क्लोज़र टेबल

Adjacency lists और closure tables की तुलना करके PostgreSQL में ऑर्ग चार्ट मॉडल करें—फ़िल्टरिंग, रिपोर्टिंग और परमिशन चेक्स के स्पष्ट उदाहरणों के साथ।

PostgreSQL में ऑर्ग चार्ट मॉडल करना: एडजेसेंसी लिस्ट बनाम क्लोज़र टेबल

एक ऑर्ग चार्ट को किस चीज़ का समर्थन करना चाहिए

एक ऑर्ग चार्ट यह बताता है कि कौन किसको रिपोर्ट करता है और टीमें किस तरह विभागों में रैप-अप होती हैं। PostgreSQL में ऑर्ग चार्ट मॉडल करते समय आप केवल हर व्यक्ति पर manager_id नहीं स्टोर कर रहे होते — आप असल काम का समर्थन कर रहे होते हैं: ऑर्ग ब्राउज़िंग, रिपोर्टिंग और एक्सेस नियम।

अधिकांश उपयोगकर्ता तुरंत महसूस करने के लिए तीन चीज़ें उम्मीद करते हैं: ऑर्ग का एक्सप्लोर करना, लोगों को ढूँढना, और परिणामों को "मेरे क्षेत्र" में फ़िल्टर करना। वे यह भी चाहते हैं कि अपडेट सुरक्षित हों। जब कोई मैनेजर बदलता है, तो चार्ट हर जगह अपडेट होना चाहिए बिना रिपोर्ट्स या परमिशन्स को तोड़े।

व्यवहार में, एक अच्छा मॉडल कुछ बार-बार आने वाले सवालों के जवाब देने में सक्षम होना चाहिए:

  • किसी व्यक्ति की चेन ऑफ कमांड क्या है (शीर्ष तक)?
  • इस मैनेजर के नीचे कौन है (डायरेक्ट रिपोर्ट्स और पूरा सबट्री)?
  • डैशबोर्ड्स के लिए लोग किस तरह टीमों और विभागों में ग्रुप होते हैं?
  • रिइंऑर्ग बिना glitches के कैसे होते हैं?
  • ऑर्ग संरचना के आधार पर कौन क्या देख सकता है?

यह आसान पेड़ से भी ज़्यादा जटिल हो जाता है क्योंकि संगठन अक्सर बदलते हैं। टीमें विभागों के बीच मूव करती हैं, मैनेजर ग्रुप स्वैप करते हैं, और कुछ व्यूज़ पूरी तरह से "लोग लोगों को रिपोर्ट करते हैं" पर आधारित नहीं होते। उदाहरण के लिए: एक व्यक्ति किसी टीम का सदस्य है, और टीमें विभागों की मालिक होती हैं। परमिशन्स एक और परत जोड़ते हैं: ऑर्ग का आकार आपकी सुरक्षा मॉडल का हिस्सा बन जाता है, सिर्फ़ एक डायग्राम नहीं।

कुछ शब्द डिज़ाइन को स्पष्ट रखने में मदद करते हैं:

  • नोड हायरार्की में एक आइटम होता है (एक व्यक्ति, एक टीम, या एक विभाग)।
  • पेरेंट सीधे ऊपर स्थित नोड होता है (एक मैनेजर, या वह विभाग जो टीम का मालिक है)।
  • एनसेस्टर कोई भी ऊपर वाला नोड है किसी भी दूरी पर (आपके मैनेजर का मैनेजर)।
  • डिसेंडेंट कोई भी निचला नोड है किसी भी दूरी पर (आपके नीचे हर कोई)।

उदाहरण: अगर Sales एक नए VP के तहत चला जाता है, तो दो चीज़ें तुरंत सत्य रहनी चाहिए। डैशबोर्ड्स अभी भी "संपूर्ण Sales" को फ़िल्टर करें, और नए VP की परमिशन्स अपने आप Sales को कवर करें।

टेबल डिज़ाइन चुनने से पहले निर्णय

स्कीमा तय करने से पहले यह स्पष्ट करें कि आपका ऐप हर दिन किन सवालों का उत्तर देना चाहिए। "कौन किसको रिपोर्ट करता है?" केवल शुरुआत है। कई ऑर्ग चार्ट्स को यह भी दिखाना होता है कि कौन विभाग का नेतृत्व करता है, कौन टीम के लिए टाइम-ऑफ़ को अप्रूव करता है, और कौन रिपोर्ट देख सकता है।

अपने स्क्रीन और परमिशन चेक्स के सटीक सवाल लिख लें। अगर आप सवाल नहीं बता सकते, तो आपको ऐसा स्कीमा मिलेगा जो सही दिखता है पर क्वेरी करने में मुश्किल होता है।

वे फैसले जो सब कुछ आकार देते हैं:

  • किन क्वेरीज को तेज़ होना चाहिए: डायरेक्ट मैनेजर, CEO तक चेन, किसी नेता के नीचे पूरा सबट्री, या "विभाग X के सभी लोग"?
  • क्या यह एक स्ट्रिक्ट ट्री है (एक मैनेजर) या मैट्रिक्स ऑर्ग (एक से ज़्यादा मैनेजर या लीड)?
  • क्या विभाग लोग वाली उसी हायरार्की के नोड हैं, या एक अलग एट्रीब्यूट (जैसे हर व्यक्ति पर department_id) हैं?
  • क्या कोई एक से ज़्यादा टीमों का सदस्य हो सकता है (शेयर्ड सर्विसेज, स्क्वैड्स)?
  • परमिशन्स कैसे फ्लो करते हैं: डाउन-ट्री, अप-ट्री, या दोनों?

ये चुनौतियाँ यह परिभाषित करती हैं कि "सही" डेटा कैसा दिखेगा। अगर Alex दोनों Support और Onboarding का नेतृत्व करता है, तो एकल manager_id या "प्रत्येक टीम के लिए एक लीड" का नियम काम नहीं कर सकता। हो सकता है आपको एक जॉइन टेबल (लीडर टू टीम) चाहिए या एक स्पष्ट नीति जैसे "एक प्राथमिक टीम, साथ में डॉटेड-लाइन टीमें"।

विभाग एक और फ़र्क बनाते हैं। अगर विभाग नोड्स हैं, तो आप व्यक्त कर सकते हैं "विभाग A में टीम B है और टीम B में व्यक्ति C है"। अगर विभाग अलग हैं, तो आप department_id = X से फ़िल्टर करेंगे, जो शुरू करने के लिए सरल है पर तब टूट सकता है जब टीमें विभागों में फैली हों।

अंत में, परमिशन्स को साधारण भाषा में परिभाषित करें। "एक मैनेजर अपनी टीम के सभी लोगों के लिए वेतन देख सकता है, पर पीयर्स नहीं" एक डाउन-ट्री नियम है। "कोई भी अपनी मैनेजमेंट चेन देख सकता है" एक अप-ट्री नियम है। इसे जल्दी तय करें क्योंकि यह तय करेगा कि कौन सा हायरार्की मॉडल सहज लगेगा और कौन सा महँगी क्वेरीज के लिए मजबूर करेगा।

एडजेसेंसी लिस्ट: मैनेजर और टीम्स के लिए सरल स्कीमा

अगर आप कम हिस्से वाले मॉडल चाहते हैं, तो एडजेसेंसी लिस्ट क्लासिक शुरुआती बिंदु है। हर व्यक्ति अपने डायरेक्ट मैनेजर के लिए एक पॉइंटर स्टोर करता है, और ये पॉइंटर मिलकर ट्री बनाते हैं।

एक न्यूनतम सेटअप कुछ इस तरह दिखता है:

create table departments (
  id bigserial primary key,
  name text not null unique
);

create table teams (
  id bigserial primary key,
  department_id bigint not null references departments(id),
  name text not null,
  unique (department_id, name)
);

create table employees (
  id bigserial primary key,
  full_name text not null,
  team_id bigint references teams(id),
  manager_id bigint references employees(id)
);

आप अलग तालिकाएँ छोड़कर department_name और team_name को employees पर कॉलम के रूप में भी रख सकते हैं। यह शुरू करने में तेज़ है, पर साफ़ रखने में मुश्किल (टाइपो, टीम का नाम बदलना, और असंगत रिपोर्टिंग)। अलग तालिकाएँ फ़िल्टरिंग और परमिशन नियमों को सुसंगत रूप से व्यक्त करना आसान बनाती हैं।

पहले ही गार्डरेल जोड़ें। खराब हायरार्की डेटा बाद में ठीक करना दर्दनाक होता है। कम से कम, सेल्फ-मैनेजमेंट रोकें (manager_id <> id)। यह भी तय करें कि क्या एक मैनेजर उसी टीम या विभाग के बाहर हो सकता है, और क्या आपको सॉफ्ट-डिलीट्स या ऐतिहासिक बदलाव (ऑडिटिंग रिपोर्टिंग लाइन्स के लिए) चाहिए।

एडजेसेंसी लिस्ट के साथ, अधिकांश बदलाव सरल लिखाई होते हैं: एक मैनेजर बदलने पर employees.manager_id अपडेट होता है, और टीम मूव करने पर employees.team_id अपडेट होता है (अक्सर मैनेजर के साथ)। पकड़ यह है कि एक छोटा लेखन बहुत बड़े डाउनस्ट्रीम प्रभाव ला सकता है। रिपोर्टिंग रोलअप बदलते हैं, और कोई भी "मैनेजर सभी रिपोर्ट्स देख सकता है" नियम अब नई चेन का पालन करना होगा।

यह सादगी एडजेसेंसी लिस्ट की सबसे बड़ी ताकत है। इसकी कमजोरी तब दिखती है जब आप अक्सर "इस मैनेजर के नीचे सभी" के आधार पर फ़िल्टर करते हैं, क्योंकि आप आमतौर पर हर बार ट्री को वॉक करने के लिए रिकर्सिव क्वेरीज पर निर्भर रहते हैं।

एडजेसेंसी लिस्ट: फ़िल्टरिंग और रिपोर्टिंग के सामान्य क्वेरीज

एडजेसेंसी लिस्ट के साथ, कई उपयोगी ऑर्ग चार्ट सवाल रिकर्सिव क्वेरीज बन जाते हैं। अगर आप PostgreSQL में ऑर्ग चार्ट इस तरह मॉडल कर रहे हैं, तो ये पैटर्न आप लगातार चलाएँगे।

डायरेक्ट रिपोर्ट्स (एक स्तर)

सर्वसाधारण केस है एक मैनेजर की तत्काल टीम:

SELECT id, full_name, title
FROM employees
WHERE manager_id = $1
ORDER BY full_name;

यह तेज़ और पढ़ने में आसान है, पर यह केवल एक स्तर तक जाता है।

चेन ऑफ कमांड (ऊपर की तरफ)

किसी का कौन किसको रिपोर्ट करता है (मैनेजर, मैनेजर का मैनेजर वगैरह) दिखाने के लिए एक रिकर्सिव CTE का उपयोग करें:

WITH RECURSIVE chain AS (
  SELECT id, full_name, manager_id, 0 AS depth
  FROM employees
  WHERE id = $1

  UNION ALL

  SELECT e.id, e.full_name, e.manager_id, c.depth + 1
  FROM employees e
  JOIN chain c ON e.id = c.manager_id
)
SELECT *
FROM chain
ORDER BY depth;

यह अप्रूवल्स, एस्‍केलेशन पाथ्स, और मैनेजर ब्रेडक्रम्ब्स का समर्थन करता है।

पूरा सबट्री (नीचे की तरफ)

किसी नेता के नीचे सभी लोगों (सभी स्तर) को पाने के लिए रिकर्सन पलट दें:

WITH RECURSIVE subtree AS (
  SELECT id, full_name, manager_id, department_id, 0 AS depth
  FROM employees
  WHERE id = $1

  UNION ALL

  SELECT e.id, e.full_name, e.manager_id, e.department_id, s.depth + 1
  FROM employees e
  JOIN subtree s ON e.manager_id = s.id
)
SELECT *
FROM subtree
ORDER BY depth, full_name;

एक सामान्य रिपोर्ट है "लीडर Y के नीचे विभाग X के सभी लोग":

WITH RECURSIVE subtree AS (
  SELECT id, department_id
  FROM employees
  WHERE id = $1
  UNION ALL
  SELECT e.id, e.department_id
  FROM employees e
  JOIN subtree s ON e.manager_id = s.id
)
SELECT e.*
FROM employees e
JOIN subtree s ON s.id = e.id
WHERE e.department_id = $2;

एडजेसेंसी लिस्ट क्वेरीज परमिशन्स के लिए रिस्की हो सकती हैं क्योंकि एक्सेस चेक अक्सर पूरे पाथ पर निर्भर करते हैं (क्या व्यूअर इस व्यक्ति का एनसेस्टर है?). अगर कोई एंडपॉइंट रिकर्सन भूल जाता है या फ़िल्टर्स गलत जगह पर लागू करता है, तो पंक्तियाँ लीक हो सकती हैं। साथ ही साइकिल्स और मिसिंग मैनेजर्स जैसे डेटा इश्यूज़ पर नज़र रखें। एक गलत रिकॉर्ड रिकर्सन को तोड़ सकता है या आश्चर्यजनक परिणाम दे सकता है, इसलिए परमिशन क्वेरीज में सावधानियां और अच्छे कॉन्स्ट्रेंट्स चाहिए।

क्लोज़र टेबल: यह पूरी हायरार्की कैसे स्टोर करती है

अपने जनरेटेड कोड के मालिक बनें
जब ज़रूरत हो, वास्तविक सोर्स कोड एक्सपोर्ट करने का विकल्प रखें।
कोड एक्सपोर्ट करें

क्लोज़र टेबल हर एनसेस्टर-डिसेंडेंट रिश्ता स्टोर करती है, केवल डायरेक्ट मैनेजर लिंक नहीं। आप ट्री को एक-एक कदम चलाने की बजाय सीधे पूछ सकते हैं: "किसके नीचे कौन है?" और एक साधारण जॉइन से पूरा जवाब मिल जाएगा।

आम तौर पर आप दो तालिकाएँ रखते हैं: एक नोड्स के लिए (लोग या टीमें) और एक हायरार्की पाथ्स के लिए।

-- nodes
employees (
  id bigserial primary key,
  name text not null,
  manager_id bigint null references employees(id)
)

-- closure
employee_closure (
  ancestor_id bigint not null references employees(id),
  descendant_id bigint not null references employees(id),
  depth int not null,
  primary key (ancestor_id, descendant_id)
)

क्लोज़र टेबल जोड़े स्टोर करती है जैसे (Alice, Bob) मतलब "Alice Bob की एनसेस्टर है"। यह एक रो भी स्टोर करती है जहाँ ancestor_id = descendant_id और depth = 0। शुरू में यह सेल्फ-रो अजीब लगती है, पर यह कई क्वेरीज को साफ़ बनाती है।

depth बताता है कि दो नोड्स कितनी दूर हैं: depth = 1 डायरेक्ट मैनेजर है, depth = 2 मैनेजर का मैनेजर, और इसी तरह। यह मायने रखता है जब डायरेक्ट रिपोर्ट्स को अप्रत्यक्ष से अलग ट्रीट करना हो।

मुख्य लाभ अनुमाननीय, तेज़ रीड्स हैं:

  • पूरा सबट्री लुकअप तेज़ होते हैं (एक डायरेक्टर के नीचे सब कोई)।
  • चेन ऑफ कमांड सरल है (किसी के ऊपर के सभी मैनेजर्स)।
  • आप depth का इस्तेमाल कर के डायरेक्ट बनाम इन्डायरेक्ट रिलेशनशिप अलग कर सकते हैं।

लागत अपडेट्स पर है। अगर Bob का मैनेजर Alice से Dana में बदलता है, तो आपको Bob और Bob के नीचे सबके लिए क्लोज़र रोज़ पुनर्निर्माण करने होंगे। सामान्य तरीका है: पुराने एनसेस्टर पाथ्स उस सबट्री के लिए डिलीट करें, फिर Dana के एनसेस्टर्स को Bob के सबट्री के हर नोड के साथ मिलाकर नए पाथ्स इंसर्ट करें और depth रीकैल्कुलेट करें।

क्लोज़र टेबल: तेज़ फ़िल्टरिंग के सामान्य क्वेरीज

हायरेरकी परमिशन्स मान्य करें
सरल चेक के साथ हर स्क्रीन पर “कौन किसे देख सकता है” को कंसिस्टेंट रखें।
एक्सेस टेस्ट करें

क्लोज़र टेबल पहले से हर एनसेस्टर-डिसेंडेंट जोड़ी स्टोर करती है (अक्सर org_closure(ancestor_id, descendant_id, depth) के रूप में)। इससे ऑर्ग फ़िल्टर्स तेज़ होते हैं क्योंकि अधिकांश सवाल एक जॉइन बन जाते हैं।

किसी मैनेजर के नीचे सब लोगों की सूची करने के लिए एक बार जॉइन करें और depth से फ़िल्टर करें:

-- Descendants (पूरी सबट्री)
SELECT e.*
FROM employees e
JOIN org_closure c
  ON c.descendant_id = e.id
WHERE c.ancestor_id = :manager_id
  AND c.depth > 0;

-- सिर्फ डायरेक्ट रिपोर्ट्स
SELECT e.*
FROM employees e
JOIN org_closure c
  ON c.descendant_id = e.id
WHERE c.ancestor_id = :manager_id
  AND c.depth = 1;

चेन ऑफ कमांड (किसी कर्मचारी के सभी एनसेस्टर्स) के लिए जॉइन पलट दें:

SELECT m.*
FROM employees m
JOIN org_closure c
  ON c.ancestor_id = m.id
WHERE c.descendant_id = :employee_id
  AND c.depth > 0
ORDER BY c.depth;

फ़िल्टरिंग अनुमाननीय बन जाती है। उदाहरण: "लीडर X के नीचे सभी, पर केवल विभाग Y के" :

SELECT e.*
FROM employees e
JOIN org_closure c ON c.descendant_id = e.id
WHERE c.ancestor_id = :leader_id
  AND e.department_id = :department_id;

क्योंकि हायरार्की प्रीकम्प्यूटेड है, काउंट्स भी सीधी बात हैं (कोई रिकर्सन नहीं)। यह डैशबोर्ड्स और परमिशन-स्कोप्ड टोटल्स में मदद करता है, और यह पेजिनेशन और सर्च के साथ अच्छा खेलता है क्योंकि आप ORDER BY, LIMIT/OFFSET और फ़िल्टर्स सीधे डिसेंडेंट सेट पर लागू कर सकते हैं।

हर मॉडल का परमिशन्स और एक्सेस चेक पर असर

एक सामान्य ऑर्ग नियम सरल है: एक मैनेजर अपने नीचे के सारे लोगों को देख सकता (और कभी-कभी संपादित भी कर सकता) है।-schema जिसको आप चुनते हैं वह यह तय करता है कि आपको कितनी बार यह पता लगाने की लागत चुकानी है कि "कौन किसके नीचे है"।

एडजेसेंसी लिस्ट के साथ, परमिशन चेक अक्सर रिकर्सन की ज़रूरत होती है। अगर कोई उपयोगकर्ता 200 कर्मचारियों की सूची खोलता है, तो आप आमतौर पर डिसेंडेंट सेट एक रिकर्सिव CTE से बनाते हैं और टार्गेट रोज़ को उसके खिलाफ फ़िल्टर करते हैं।

क्लोज़र टेबल के साथ, वही नियम अक्सर एक साधारण अस्तित्व जाँच से किया जा सकता है: "क्या वर्तमान उपयोगकर्ता इस कर्मचारी का एनसेस्टर है?" अगर हाँ, तो अलाउ।

-- क्लोज़र टेबल परमिशन चेक (सैद्धांतिक)
SELECT 1
FROM org_closure c
WHERE c.ancestor_id = :viewer_id
  AND c.descendant_id = :employee_id
LIMIT 1;

यह सादगी तब मायने रखती है जब आप रो-लेवल सिक्योरिटी (RLS) जोड़ते हैं, जहाँ हर क्वेरी अपने आप एक नियम शामिल करती है जैसे "सिर्फ़ वही पंक्तियाँ लौटाएँ जो व्यूअर देख सकता है"। एडजेसेंसी लिस्ट के साथ, नियम अक्सर रिकर्सन एम्बेड करते हैं और ट्यून करना कठिन होता है। क्लोज़र टेबल के साथ, पॉलिसी अक्सर सरल EXISTS (...) चेक होती है।

एज केस जहाँ परमिशन लॉजिक अक्सर टूटता है:

  • डॉटेड-लाइन रिपोर्टिंग: एक व्यक्ति के पास प्रभावी रूप से दो मैनेजर हों।
  • असिस्टेंट और डेलीगेट्स: एक्सेस हायरार्की पर आधारित नहीं है, इसलिए स्पष्ट ग्रांट स्टोर करें (अक्सर एक्सपायरी के साथ)।
  • अस्थायी एक्सेस: टाइम-बाउंड परमिशन्स को ऑर्ग स्ट्रक्चर में न बुनें।
  • क्रॉस-टीम प्रोजेक्ट्स: एक्सेस प्रोजेक्ट सदस्यता द्वारा दें, न कि मैनेजमेंट चेन द्वारा।

अगर आप इसे AppMaster में बना रहे हैं, तो क्लोज़र टेबल अक्सर विज़ुअल डेटा मॉडल से अच्छी तरह मैप होती है और वेब/मोबाइल ऐप्स में एक्सेस चेक खुद सरल रखते हैं।

ट्रेडऑफ़: स्पीड, जटिलता, और मेंटेनेंस

ऑर्ग चार्ट APIs जेनरेट करें
अपने ऑर्ग स्ट्रक्चर को प्रोडक्शन-रेडी APIs में बदलें बिना हैंड-कोडिंग के।
बैकएंड जेनरेट करें

सबसे बड़ा चुनाव यह है कि आप किसके लिए ऑप्टिमाइज़ करेंगे: सरल राइट्स और छोटा स्कीमा, या "किसका नीचे कौन है" और परमिशन चेक्स के लिए तेज़ रीड्स।

एडजेसेंसी लिस्ट तालिका छोटी और अपडेट आसान रखती है। लागत रीड्स पर आती है: पूरा सबट्री आमतौर पर रिकर्सन मांगता है। यह ठीक हो सकता है अगर आपकी ऑर्ग छोटी है, आपका UI केवल कुछ स्तर लोड करता है, या हायरार्की-आधारित फ़िल्टर्स कुछ ही जगहों पर उपयोग होते हैं।

क्लोज़र टेबल ट्रेडऑफ़ पलट देती है। रीड्स तेज़ हो जाते हैं क्योंकि आप नियमित जॉइन से "सभी डिसेंडेंट" का उत्तर दे सकते हैं। लिखना अधिक जटिल होता है क्योंकि एक मूव या रिइंऑर्ग के लिए कई रिलेशनशिप रोज़ को इंसर्ट/डिलीट करना पड़ सकता है।

वास्तविक काम में ट्रेडऑफ आम तौर पर इस तरह दिखता है:

  • रीड प्रदर्शन: एडजेसेंसी को रिकर्सन चाहिए; क्लोज़र ज्यादातर जॉइन हैं और जैसे-जैसे ऑर्ग बढ़े तेज़ रहते हैं।
  • राइट जटिलता: एडजेसेंसी एक parent_id अपडेट करता है; क्लोज़र एक मूव पर कई रोज़ अपडेट करता है।
  • डेटा आकार: एडजेसेंसी लोगों/टीमों के साथ बढ़ता है; क्लोज़र रिश्तों के साथ बढ़ता है (सबसे खराब मामले में, गहरे ट्री के लिए लगभग N^2)।

दोनों मॉडलों में इंडेक्सिंग मायने रखती है, पर लक्ष्य अलग होता है:

  • एडजेसेंसी लिस्ट: पेरेंट पॉइंटर (manager_id) पर इंडेक्स करें, साथ ही सामान्य फ़िल्टर्स जैसे एक "active" फ्लैग।
  • क्लोज़र टेबल: (ancestor_id, descendant_id) पर इंडेक्स और सामान्य लुकअप के लिए descendant_id पर भी इंडेक्स।

एक सरल नियम: अगर आप शायद ही कभी हायरार्की से फ़िल्टर करते हैं और परमिशन चेक्स सिर्फ़ "मैनेजर डायरेक्ट रिपोर्ट्स देखता है" हैं, तो एडजेसेंसी लिस्ट अक्सर काफी है। अगर आप नियमित रूप से "VP X के नीचे सब लोग" रिपोर्ट चलाते हैं, विभाग ट्री से फ़िल्टर करते हैं, या कई स्क्रीन पर हायरार्की-आधारित परमिशन्स लागू करते हैं, तो क्लोज़र टेबल अक्सर अतिरिक्त मेंटेनेंस का भुगतान कर देती है।

स्टेप-बाय-स्टेप: एडजेसेंसी लिस्ट से क्लोज़र टेबल पर जाना

आपको पहले दिन मॉडल चुनना ज़रूरी नहीं है। एक सुरक्षित रास्ता है कि आप अपनी एडजेसेंसी लिस्ट (manager_id या parent_id) रखें और उसके साथ एक क्लोज़र टेबल जोड़ें, फिर धीरे-धीरे रीड्स माइग्रेट करें। इससे जोखिम घटता है जबकि आप यह सत्यापित करते हैं कि नया हायरार्की वास्तविक क्वेरीज और परमिशन चेक्स में कैसे व्यवहार करता है।

शुरूआत में क्लोज़र टेबल बनायें (अक्सर org_closure कहलाती है) जिसमें ancestor_id, descendant_id, और depth जैसे कॉलम हों। इसे अपने वर्तमान employees या teams टेबल से अलग रखें ताकि आप बैकफिल और वैलिडेट कर सकें बिना मौजूदा फ़ीचर्स को छुए।

एक व्यावहारिक रोलआउट:

  • क्लोज़र टेबल और इंडेक्स बनायें जबकि एडजेसेंसी लिस्ट सोर्स-ऑफ-ट्रूथ बनी रहे।
  • वर्तमान मैनेजर रिलेशनशिप से क्लोज़र पंक्तियाँ बैकफिल करें, जिसमें सेल्फ रो (depth = 0) भी शामिल हो।
  • स्पॉट चेक्स से वैलिडेट करें: कुछ मैनेजर चुनें और दोनों मॉडलों में एक जैसा सबऑर्डिनेट सेट दिखता है या नहीं देखें।
  • पहले रीड पाथ्स स्विच करें: रिपोर्ट्स, फ़िल्टर और हायरार्की परमिशन्स क्लोज़र टेबल से पढ़ें, फिर लिखने वाले पाथ को बदलें।
  • हर लिखाई पर क्लोज़र को अपडेट रखें (re-parent, hire, move team)। एक बार स्थिर होने पर रिकर्सन-आधारित क्वेरीज रिटायर कर दें।

जब आप वैलिडेट करें, तो उन मामलों पर ध्यान दें जो अक्सर एक्सेस नियमों को तोड़ते हैं: मैनेजर चेंजेस, शीर्ष स्तर के लीडर्स, और जिन उपयोगकर्ताओं का कोई मैनेजर नहीं है।

अगर आप AppMaster में बना रहे हैं, तो आप पुराने एंडपॉइंट्स चलते रहते हुए नए एंडपॉइंट्स जोड़ सकते हैं जो क्लोज़र से पढ़ते हैं, और परिणाम मिलते ही स्विच कर दें।

आम गलतियाँ जो ऑर्ग फ़िल्टरिंग या परमिशन्स को तोड़ देती हैं

ऑर्ग-ड्रिवन लॉजिक ऑटोमेट करें
रूटिंग, एस्केलेशन्स और डेलीगेशन्स के लिए ड्रैग-एंड-ड्रॉप बिज़नेस लॉजिक इस्तेमाल करें।
लॉजिक ऑटोमेट करें

ऑर्ग फीचर्स तोड़ने का सबसे तेज़ तरीका है हायरार्की को असंगति होने देना। डेटा पंक्ति-दर-पंक्ति सही दिख सकती है, पर छोटी गलतियाँ गलत फ़िल्टर, धीमे पेज, या परमिशन लीक पैदा कर सकती हैं।

एक क्लासिक समस्या साइकिल बनाना है: A B को मैनेज करता है, और बाद में किसी ने B को A का मैनेजर सेट कर दिया (या 3-4 लोगों के माध्यम से लंबा लूप)। रिकर्सिव क्वेरीज अनिश्चितकाल तक चल सकती हैं, डुप्लिकेट रोज़ लौट सकती हैं, या टाइम आउट हो सकती हैं। क्लोज़र टेबल के साथ भी साइकिल्स एनसेस्टर/डिसेंडेंट रोज़ को दूषित कर सकती हैं।

एक और आम समस्या क्लोज़र ड्रिफ्ट है: आपने किसी का मैनेजर बदल दिया पर केवल डायरेक्ट रिलेशनशिप अपडेट की और सबट्री के क्लोज़र रोज़ को फिर से बनाना भूल गए। तब "इस VP के नीचे सब लोग" जैसे फ़िल्टर पुराने और नए संरचना का मिश्रण लौटाते हैं। यह पकड़ना मुश्किल है क्योंकि व्यक्तिगत प्रोफ़ाइल पेज अभी भी सही दिख सकते हैं।

ऑर्ग चार्ट तब भी गड़बड़ होते हैं जब विभागों और रिपोर्टिंग लाइनों को बिना स्पष्ट नियम के मिलाया जाता है। विभाग अक्सर प्रशासनिक समूह होते हैं, जबकि रिपोर्टिंग लाइनों का मतलब मैनेजर्स से है। अगर आप दोनों को एक ही ट्री समझते हैं, तो आप अजीब व्यवहार का सामना कर सकते हैं जैसे कि "विभाग मूव" अप्रत्याशित रूप से एक्सेस बदल देगा।

सबसे ज़्यादा बार परमिशन्स तब फेल होते हैं जब चेक सिर्फ़ डायरेक्ट मैनेजर को देखते हैं। अगर आप viewer is manager of employee पर एक्सेस देते हैं, तो आप पूरी चेन को मिस कर देते हैं। नतीजा या तो ओवर-ब्लॉकिंग होगा (स्किप-लेवल मैनेजर्स अपनी टीम नहीं देख पाते) या ओवर-शेयरिंग (किसी को अस्थायी डायरेक्ट मैनेजर बनाकर एक्सेस मिल सकता है)।

धीमे लिस्ट पेज अक्सर हर रिक्वेस्ट पर रिकर्सिव फ़िल्टरिंग चलाने से आते हैं (हर इनबॉक्स, हर टिकट लिस्ट, हर कर्मचारी सर्च)। अगर वही फ़िल्टर हर जगह इस्तेमाल हो रहा है, तो या तो आप प्रीकम्प्यूटेड पाथ (क्लोज़र टेबल) चाहते हैं या अनुमत कर्मचारी IDs का कैश्ड सेट।

कुछ व्यावहारिक सुरक्षा उपाय:

  • मैनेजर बदलाव सेव करने से पहले वैलिडेशन करके साइकिल्स ब्लॉक करें।
  • "विभाग" का अर्थ तय करें, और इसे रिपोर्टिंग से अलग रखें।
  • अगर आप क्लोज़र टेबल इस्तेमाल करते हैं, तो मैनेजर बदलाव पर डिसेंडेंट रोज़ को रीबिल्ड करें।
  • परमिशन नियम पूरी चेन के लिए लिखें, सिर्फ डायरेक्ट मैनेजर के लिए नहीं।
  • सूची पेज्स के लिए हर बार रिकर्सन की बजाय ऑर्ग स्कोप्स प्रीकम्प्यूट करें।

अगर आप AppMaster में एडमिन पैनल बना रहे हैं, तो "मैनेजर बदलें" को संवेदनशील वर्कफ़्लो समझें: इसे वैलिडेट करें, संबंधित हायरार्की डेटा अपडेट करें, और तभी फ़िल्टर और एक्सेस को प्रभावी होने दें।

शिप करने से पहले जल्दी चेकलिस्ट

एक ऑर्ग चार्ट डायरेक्टरी शिप करें
सर्च, फ़िल्टर और मैनेजर ड्रिलडाउन के साथ एक वेब ऑर्ग ब्राउज़र बनाएं।
बिल्डिंग शुरू करें

अपने ऑर्ग चार्ट को "डन" कहने से पहले सुनिश्चित करें कि आप एक्सेस को साधारण शब्दों में समझा सकें। अगर कोई पूछे, "कौन कर्मचारी X को देख सकता है, और क्यों?", तो आप एक नियम और एक क्वेरी (या व्यू) दिखा कर इसका प्रमाण दे सकें।

परफॉर्मेंस अगला वास्तविक परीक्षण है। एडजेसेंसी लिस्ट के साथ, "किसी मैनेजर के नीचे सभी दिखाओ" एक रिकर्सिव क्वेरी बन जाती है जिसकी स्पीड गहराई और इंडेक्सिंग पर निर्भर करती है। क्लोज़र टेबल के साथ, रीड्स आमतौर पर तेज़ होते हैं, पर आपको हर बदलाव पर टेबल सही रखने के लिए अपने राइट पाथ पर भरोसा करना होगा।

एक संक्षिप्त प्री-शिप चेकलिस्ट:

  • एक कर्मचारी चुनें और विज़िबिलिटी को एंड-टू-एंड ट्रेस करें: कौन सी चेन एक्सेस देती है, और कौन सा रोल उसे नकारता है।
  • अपनी अपेक्षित साइज के साथ एक मैनेजर सबट्री क्वेरी बेंचमार्क करें (उदाहरण: 5 लेवल गहराई और 50,000 कर्मचारी)।
  • गलत लिखाइयों को ब्लॉक करें: साइकिल्स, सेल्फ-मैनेजमेंट, और अनऑरफ़न नोड्स को कॉन्स्ट्रेंट्स और ट्रांज़ैक्शन चेक्स से रोकें।
  • रिइंऑर्ग सुरक्षा की जाँच करें: मूव्स, मर्जेस, मैनेजर बदलाव, और बीच में विफल होने पर रोलबैक टेस्ट करें।
  • परमिशन टेस्ट जोड़ें जो वास्तविक भूमिकाओं (HR, मैनेजर, टीम लीड, सपोर्ट) के लिए अलाउ और डिनाइड एक्सेस दोनों का Assertion करें।

एक व्यावहारिक परिदृश्य वैलिडेट करें: एक सपोर्ट एजेंट केवल अपने असाइन किए गए विभाग के कर्मचारियों को देख सकता है, जबकि एक मैनेजर अपने पूरे सबट्री को देख सकता है। अगर आप PostgreSQL में ऑर्ग चार्ट मॉडल कर सकते हैं और दोनों नियमों को टेस्ट्स के साथ सिद्ध कर सकते हैं, तो आप शिप करने के क़रीब हैं।

अगर आप यह इंटरनल टूल AppMaster में बना रहे हैं, तो इन चेक्स को उन एंडपॉइंट्स के चारों ओर ऑटोमेटेड टेस्ट के रूप में रखें जो ऑर्ग लिस्ट और कर्मचारी प्रोफाइल लौटाते हैं, सिर्फ़ डेटाबेस क्वेरीज के रूप में नहीं।

उदाहरण परिदृश्य और अगले कदम

मान लें एक कंपनी में तीन विभाग हैं: Sales, Support, और Engineering। हर विभाग की दो टीमें हैं, और हर टीम का एक लीड है। Sales लीड A अपनी टीम के लिए डिस्काउंट अप्रूव कर सकता है, Support लीड B अपने विभाग के सारे टिकट देख सकता है, और VP of Engineering Engineering के नीचे सब कुछ देख सकता है।

फिर एक रिइंऑर्ग होता है: एक Support टीम Sales के तहत चली जाती है, और एक नया मैनेजर Sales डायरेक्टर और दो टीम लीड्स के बीच में जोड़ दिया जाता है। अगले दिन, किसी ने एक्सेस की रिक्वेस्ट की: "Jamie (एक Sales एनालिस्ट) को Sales विभाग के सभी कस्टमर अकाउंट्स देखने दें, पर Engineering नहीं।"

अगर आपने PostgreSQL में ऑर्ग चार्ट एडजेसेंसी लिस्ट के साथ मॉडल किया है, तो स्कीमा सरल है, पर ऐप वर्क आपके क्वेरीज और परमिशन चेक्स में चला जाता है। "Sales के नीचे सभी" जैसे फ़िल्टर आमतौर पर रिकर्सन मांगते हैं। एक बार जब आप अप्रूवल्स जोड़ते हैं (जैसे "केवल चेन में मैनेजर्स अप्रूव कर सकते हैं"), तो रिइंऑर्ग के बाद एज केस मायने रखने लगते हैं।

क्लोज़र टेबल के साथ, रिइंऑर्ग पर लिखाई अधिक होती है (एनसेस्टर/डिसेंडेंट रोज़ अपडेट करना), पर रीड साइड सरल हो जाता है। फ़िल्टरिंग और परमिशन्स अक्सर सिंपल जॉइन बन जाते हैं: "क्या यह यूजर उस कर्मचारी का एनसेस्टर है?" या "क्या यह टीम इस विभाग सबट्री में है?"।

यह सीधे स्क्रीन पर दिखता है जो लोग बनाते हैं: विभाग-स्कोप्ड लोगों के पिकर्स, अनुरोधकर्ता के ऊपर निकटतम मैनेजर को अप्रूवल रूटिंग, विभाग डैशबोर्ड्स के लिए एडमिन व्यूज़, और ऑडिट्स जो बताते हैं कि किसी दिए दिन एक्सेस क्यों मौजूद था।

अगले कदम:

  1. परमिशन नियम साधारण शब्दों में लिखें (कौन क्या देख सकता है, और क्यों)।
  2. वह मॉडल चुनें जो सबसे आम चेक्स से मेल खाता हो (तेज़ रीड्स बनाम सरल राइट्स)।
  3. एक इंटरनल एडमिन टूल बनाएं जो रिइंऑर्ग्स, एक्सेस रिक्वेस्ट्स, और अप्रूवल्स को एंड-टू-एंड टेस्ट करने दे।

अगर आप उन ऑर्ग-अवेयर एडमिन पैनल्स और पोर्टल्स को जल्दी बनाना चाहते हैं तो AppMaster (appmaster.io) व्यावहारिक विकल्प हो सकता है: यह आपको PostgreSQL-बैक्ड डेटा मॉडल करने, विज़ुअल बिज़नेस प्रोसेस में अप्रूवल लॉजिक लागू करने, और एक ही बैकएंड से वेब और नेटिव मोबाइल ऐप्स देने देता है।

सामान्य प्रश्न

Adjacency list vs closure table — मुझे कब कौन सा इस्तेमाल करना चाहिए?

एडजेसेंसी लिस्ट तब अच्छा है जब आपकी ऑर्ग छोटी हो, अपडेट अक्सर हों, और अधिकांश स्क्रीन सिर्फ डायरेक्ट रिपोर्ट्स या कुछ स्तर ही दिखाती हों। क्लोज़र टेबल तब बेहतर है जब आप लगातार “किसी लीडर के नीचे सब लोग” जैसे सवाल पूछते हों, विभाग-आधारित फ़िल्टरिंग करते हों, या कई जगहों पर हायरार्की-आधारित परमिशन्स लागू करते हों — क्योंकि रीड्स सिंपल जॉइन से तेज़ और अनुमाननीय होते हैं।

PostgreSQL में “कौन किसे रिपोर्ट करता है” स्टोर करने का सबसे सरल तरीका क्या है?

सबसे सादा तरीका है employees(manager_id) रखना और डायरेक्ट रिपोर्ट्स के लिए WHERE manager_id = ? जैसा सिंपल क्वेरी इस्तेमाल करना। केवल उन फीचर्स के लिए रिकर्सिव क्वेरीज जोड़ें जिन्हें सचमुच पूरी एनसेस्टी या सबट्री की ज़रूरत है, जैसे अप्रूवल्स, “मेरी टीम” फ़िल्टर या स्किप-लेवल डैशबोर्ड।

मैं साइकिल (A manages B और B manages A) कैसे रोकूं?

सेल्फ-मैनेजमेंट रोकने के लिए एक चेक लगाएं जैसे manager_id <> id, और अपडेट से पहले वैलिडेट करें ताकि आप किसी ऐसे मैनेजर को न असाइन करें जो पहले से ही कर्मचारी के सबट्री में हो। व्यवहार में सबसे सुरक्षित तरीका है कि मैनेजर बदलने से पहले एनसेस्ट्रि की जाँच करें, क्योंकि एक सर्किल रिपटीशन वाली क्वेरीज को बिगाड़ सकती है और परमिशन लॉजिक करप्ट कर सकती है।

क्या विभागों को लोगों वाली उसी हायरार्की में नोड की तरह रखना चाहिए?

अच्छा डिफ़ॉल्ट यही है कि विभागों को एक व्यवस्थापकीय समूह मानें और रिपोर्टिंग लाइनों को अलग मैनेजर ट्री के रूप में रखें। इससे “विभाग मूव” से गलती से रिपोर्टिंग बदलने से बचा जा सकता है, और “Sales में सभी” जैसे फ़िल्टर तब भी स्पष्ट रहते हैं जब रिपोर्टिंग लाइनों का विभाग सीमा से मेल न हो।

अगर किसी का दो मैनेजर हों (मैट्रिक्स ऑर्ग), तो मैं उसे कैसे मॉडल करूँ?

अक्सर आप कर्मचारी पर एक प्राथमिक रिपोर्टिंग मैनेजर स्टोर करते हैं और डॉटेड-लाइन रिश्तों को अलग रखते हैं, जैसे सेकेंडरी मैनेजर टेबल या “टीम लीड” मैपिंग। इससे बेसिक हायरार्की क्वेरीज टूटती नहीं और आप प्रोजेक्ट एक्सेस या अप्रूवल डेलीगेशन जैसी विशेष नियम लागू कर पाते हैं।

जब कोई मैनेजर बदलता है तो क्लोज़र टेबल में क्या अपडेट करना चाहिए?

मूव होने पर पुराने ancestor पाथ्स डिलीट करें और फिर नए मैनेजर के अनस्स्टर्स को उस सबट्री के हर नोड के साथ जोड़कर नए पाथ्स इंसर्ट करें, और depth को फिर से कैलकुलेट करें। इसे एक ट्रांज़ैक्शन में करें ताकि बीच में फेल होने पर क्लोज़र टेबल आधा-अपडेटेड न रह जाए।

ऑर्ग चार्ट क्वेरीज के लिए कौन से इंडेक्स सबसे ज़रूरी हैं?

एडजेसेंसी लिस्ट के लिए, employees(manager_id) पर इंडेक्स करें क्योंकि लगभग हर ऑर्ग क्वेरी वहीं से शुरू होती है, और सामान्य फ़िल्टर जैसे team_id या department_id पर भी इंडेक्स जोड़ें। क्लोज़र टेबल के लिए प्रमुख इंडेक्स (ancestor_id, descendant_id) पर होना चाहिए और अलग से descendant_id पर भी इंडेक्स रखें ताकि “कौन इस रो को देख सकता है?” वाले चेक तेज़ हों।

“एक मैनेजर अपने नीचे के सभी लोगों को देख सकता है” को कैसे सुरक्षित रूप से लागू करूँ?

एक सामान्य पैटर्न है क्लोज़र टेबल पर EXISTS करना: तब एक्सेस दें जब व्यूअर टार्गेट कर्मचारी का ancestor हो। यह रिले-लेवल सिक्योरिटी (RLS) के साथ अच्छा काम करता है क्योंकि डेटाबेस नियम को लगातार लागू कर सकता है, बजाय इसके कि हर API एंडपॉइंट वही रिकर्सिव लॉजिक दोहराए।

-- क्लोज़र टेबल पर आधारित परमिशन चेक (सैद्धांतिक)
SELECT 1
FROM org_closure c
WHERE c.ancestor_id = :viewer_id
  AND c.descendant_id = :employee_id
LIMIT 1;
रिइंऑर्ग हिस्ट्री और ऑडिट ट्रेल्स मैं कैसे संभालूँ?

इतिहास स्पष्ट रूप से स्टोर करें, आम तौर पर एक अलग टेबल में जो मैनेजर चेंजेस को प्रभावी तिथियों के साथ रिकॉर्ड करे, बजाय इसके कि आप वर्तमान मैनेजर को ओवरराइट कर दें और पिछला रिकॉर्ड खो दें। इससे आप यह बता पाएंगे कि किसी ख़ास तारीख पर कौन किसके अधीन था बिना अनुमान लगाए।

Adjacency list से क्लोज़र टेबल में बिना ऐप तोड़े मैं कैसे माइग्रेट करूँ?

अपने मौजूदा manager_id को सोर्स-ऑफ-ट्रूथ रखें, क्लोज़र टेबल साथ में बनाएं और मौजूदा ट्री से क्लोज़र पंक्तियों को बैकफिल करें। पहले रीड पाथ्स बदलें (फ़िल्टर, डैशबोर्ड, परमिशन चेक), फिर लिखने के समय दोनों को अपडेट करें, और तब ही रिकर्सन-आधारित क्वेरीज रिटायर करें जब आप वास्तविक परिदृश्यों में मिलान की पुष्टि कर लें।

शुरू करना आसान
कुछ बनाएं अद्भुत

फ्री प्लान के साथ ऐपमास्टर के साथ प्रयोग करें।
जब आप तैयार होंगे तब आप उचित सदस्यता चुन सकते हैं।

शुरू हो जाओ