MongoDB
 sql >> डेटाबेस >  >> NoSQL >> MongoDB

टाइपस्क्रिप्ट:नेस्टेड ऑब्जेक्ट की गहरी कुंजी, संबंधित प्रकार के साथ

इस लक्ष्य को प्राप्त करने के लिए हमें सभी अनुमत पथों का क्रमपरिवर्तन बनाने की आवश्यकता है। उदाहरण के लिए:

type Structure = {
    user: {
        name: string,
        surname: string
    }
}

type BlackMagic<T>= T

// user.name | user.surname
type Result=BlackMagic<Structure>

सरणियों और खाली टुपल्स के साथ समस्या और दिलचस्प हो जाती है।

टपल, स्पष्ट लंबाई वाली सरणी, इस तरह से प्रबंधित की जानी चाहिए:

type Structure = {
    user: {
        arr: [1, 2],
    }
}

type BlackMagic<T> = T

// "user.arr" | "user.arr.0" | "user.arr.1"
type Result = BlackMagic<Structure>

तर्क सीधा है। लेकिन हम number[] को कैसे हैंडल कर सकते हैं? ? इस बात की कोई गारंटी नहीं है कि अनुक्रमणिका 1 मौजूद है।

मैंने user.arr.${number} . का उपयोग करने का निर्णय लिया है ।

type Structure = {
    user: {
        arr: number[],
    }
}

type BlackMagic<T> = T

// "user.arr" | `user.arr.${number}`
type Result = BlackMagic<Structure>

हमें अभी भी 1 समस्या है। खाली टपल। शून्य तत्वों वाली सरणी - [] . क्या हमें अनुक्रमण की अनुमति देने की आवश्यकता है? मुझें नहीं पता। मैंने -1 . का उपयोग करने का निर्णय लिया ।

type Structure = {
    user: {
        arr: [],
    }
}

type BlackMagic<T> = T

//  "user.arr" | "user.arr.-1"
type Result = BlackMagic<Structure>

मुझे लगता है कि यहां सबसे महत्वपूर्ण बात कुछ सम्मेलन है। हम कड़े `"नेवर" का भी उपयोग कर सकते हैं। मुझे लगता है कि यह ओपी पर निर्भर है कि इसे कैसे संभालना है।

चूंकि हम जानते हैं कि हमें विभिन्न मामलों को कैसे संभालना है, इसलिए हम अपना कार्यान्वयन शुरू कर सकते हैं। जारी रखने से पहले, हमें कई सहायकों को परिभाषित करने की आवश्यकता है।

type Values<T> = T[keyof T]
{
    // 1 | "John"
    type _ = Values<{ age: 1, name: 'John' }>
}

type IsNever<T> = [T] extends [never] ? true : false;
{
    type _ = IsNever<never> // true 
    type __ = IsNever<true> // false
}

type IsTuple<T> =
    (T extends Array<any> ?
        (T['length'] extends number
            ? (number extends T['length']
                ? false
                : true)
            : true)
        : false)
{
    type _ = IsTuple<[1, 2]> // true
    type __ = IsTuple<number[]> // false
    type ___ = IsTuple<{ length: 2 }> // false
}

type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
{
    type _ = IsEmptyTuple<[]> // true
    type __ = IsEmptyTuple<[1]> // false
    type ___ = IsEmptyTuple<number[]> // false

}

मुझे लगता है कि नामकरण और परीक्षण स्वयं व्याख्यात्मक हैं। कम से कम मैं विश्वास करना चाहता हूँ :D

अब, जब हमारे पास हमारे सभी बर्तन हो जाते हैं, तो हम अपने मुख्य उपयोग को परिभाषित कर सकते हैं:

/**
 * If Cache is empty return Prop without dot,
 * to avoid ".user"
 */
type HandleDot<
    Cache extends string,
    Prop extends string | number
    > =
    Cache extends ''
    ? `${Prop}`
    : `${Cache}.${Prop}`

/**
 * Simple iteration through object properties
 */
type HandleObject<Obj, Cache extends string> = {
    [Prop in keyof Obj]:
    // concat previous Cacha and Prop
    | HandleDot<Cache, Prop & string>
    // with next Cache and Prop
    | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
}[keyof Obj]

type Path<Obj, Cache extends string = ''> =
    // if Obj is primitive
    (Obj extends PropertyKey
        // return Cache
        ? Cache
        // if Obj is Array (can be array, tuple, empty tuple)
        : (Obj extends Array<unknown>
            // and is tuple
            ? (IsTuple<Obj> extends true
                // and tuple is empty
                ? (IsEmptyTuple<Obj> extends true
                    // call recursively Path with `-1` as an allowed index
                    ? Path<PropertyKey, HandleDot<Cache, -1>>
                    // if tuple is not empty we can handle it as regular object
                    : HandleObject<Obj, Cache>)
                // if Obj is regular  array call Path with union of all elements
                : Path<Obj[number], HandleDot<Cache, number>>)
            // if Obj is neither Array nor Tuple nor Primitive - treat is as object    
            : HandleObject<Obj, Cache>)
    )

// "user" | "user.arr" | `user.arr.${number}`
type Test = Extract<Path<Structure>, string>

छोटा सा मुद्दा है। हमें user . जैसे उच्चतम स्तर के प्रॉप्स नहीं लौटाने चाहिए . हमें कम से कम एक बिंदु वाले पथ चाहिए।

दो तरीके हैं:

  • बिना बिंदुओं के सभी प्रॉप्स निकालें
  • स्तर को अनुक्रमित करने के लिए अतिरिक्त सामान्य पैरामीटर प्रदान करें।

दो विकल्पों को लागू करना आसान है।

dot (.) . के साथ सभी प्रॉप्स प्राप्त करें :

type WithDot<T extends string> = T extends `${string}.${string}` ? T : never

जबकि उपरोक्त उपयोग पठनीय और रखरखाव योग्य है, दूसरा थोड़ा कठिन है। हमें Path . दोनों में अतिरिक्त सामान्य पैरामीटर प्रदान करने की आवश्यकता है और HandleObject अन्य प्रश्न / लेख :

type KeysUnion<T, Cache extends string = '', Level extends any[] = []> =
  T extends PropertyKey ? Cache : {
    [P in keyof T]:
    P extends string
    ? Cache extends ''
    ? KeysUnion<T[P], `${P}`, [...Level, 1]>
    : Level['length'] extends 1 // if it is a higher level - proceed
    ? KeysUnion<T[P], `${Cache}.${P}`, [...Level, 1]>
    : Level['length'] extends 2 // stop on second level
    ? Cache | KeysUnion<T[P], `${Cache}`, [...Level, 1]>
    : never
    : never
  }[keyof T]

ईमानदारी से, मुझे नहीं लगता कि किसी के लिए इसे पढ़ना आसान होगा।

हमें एक और चीज लागू करने की जरूरत है। हमें परिकलित पथ द्वारा एक मान प्राप्त करने की आवश्यकता है।


type Acc = Record<string, any>

type ReducerCallback<Accumulator extends Acc, El extends string> =
    El extends keyof Accumulator ? Accumulator[El] : Accumulator

type Reducer<
    Keys extends string,
    Accumulator extends Acc = {}
    > =
    // Key destructure
    Keys extends `${infer Prop}.${infer Rest}`
    // call Reducer with callback, just like in JS
    ? Reducer<Rest, ReducerCallback<Accumulator, Prop>>
    // this is the last part of path because no dot
    : Keys extends `${infer Last}`
    // call reducer with last part
    ? ReducerCallback<Accumulator, Last>
    : never

{
    type _ = Reducer<'user.arr', Structure> // []
    type __ = Reducer<'user', Structure> // { arr: [] }
}

आप Reduce . का उपयोग करने के बारे में अधिक जानकारी प्राप्त कर सकते हैं मेरे ब्लॉग में

पूरा कोड:

type Structure = {
    user: {
        tuple: [42],
        emptyTuple: [],
        array: { age: number }[]
    }
}


type Values<T> = T[keyof T]
{
    // 1 | "John"
    type _ = Values<{ age: 1, name: 'John' }>
}

type IsNever<T> = [T] extends [never] ? true : false;
{
    type _ = IsNever<never> // true 
    type __ = IsNever<true> // false
}

type IsTuple<T> =
    (T extends Array<any> ?
        (T['length'] extends number
            ? (number extends T['length']
                ? false
                : true)
            : true)
        : false)
{
    type _ = IsTuple<[1, 2]> // true
    type __ = IsTuple<number[]> // false
    type ___ = IsTuple<{ length: 2 }> // false
}

type IsEmptyTuple<T extends Array<any>> = T['length'] extends 0 ? true : false
{
    type _ = IsEmptyTuple<[]> // true
    type __ = IsEmptyTuple<[1]> // false
    type ___ = IsEmptyTuple<number[]> // false
}

/**
 * If Cache is empty return Prop without dot,
 * to avoid ".user"
 */
type HandleDot<
    Cache extends string,
    Prop extends string | number
    > =
    Cache extends ''
    ? `${Prop}`
    : `${Cache}.${Prop}`

/**
 * Simple iteration through object properties
 */
type HandleObject<Obj, Cache extends string> = {
    [Prop in keyof Obj]:
    // concat previous Cacha and Prop
    | HandleDot<Cache, Prop & string>
    // with next Cache and Prop
    | Path<Obj[Prop], HandleDot<Cache, Prop & string>>
}[keyof Obj]

type Path<Obj, Cache extends string = ''> =
    (Obj extends PropertyKey
        // return Cache
        ? Cache
        // if Obj is Array (can be array, tuple, empty tuple)
        : (Obj extends Array<unknown>
            // and is tuple
            ? (IsTuple<Obj> extends true
                // and tuple is empty
                ? (IsEmptyTuple<Obj> extends true
                    // call recursively Path with `-1` as an allowed index
                    ? Path<PropertyKey, HandleDot<Cache, -1>>
                    // if tuple is not empty we can handle it as regular object
                    : HandleObject<Obj, Cache>)
                // if Obj is regular  array call Path with union of all elements
                : Path<Obj[number], HandleDot<Cache, number>>)
            // if Obj is neither Array nor Tuple nor Primitive - treat is as object    
            : HandleObject<Obj, Cache>)
    )

type WithDot<T extends string> = T extends `${string}.${string}` ? T : never


// "user" | "user.arr" | `user.arr.${number}`
type Test = WithDot<Extract<Path<Structure>, string>>



type Acc = Record<string, any>

type ReducerCallback<Accumulator extends Acc, El extends string> =
    El extends keyof Accumulator ? Accumulator[El] : El extends '-1' ? never : Accumulator

type Reducer<
    Keys extends string,
    Accumulator extends Acc = {}
    > =
    // Key destructure
    Keys extends `${infer Prop}.${infer Rest}`
    // call Reducer with callback, just like in JS
    ? Reducer<Rest, ReducerCallback<Accumulator, Prop>>
    // this is the last part of path because no dot
    : Keys extends `${infer Last}`
    // call reducer with last part
    ? ReducerCallback<Accumulator, Last>
    : never

{
    type _ = Reducer<'user.arr', Structure> // []
    type __ = Reducer<'user', Structure> // { arr: [] }
}

type BlackMagic<T> = T & {
    [Prop in WithDot<Extract<Path<T>, string>>]: Reducer<Prop, T>
}

type Result = BlackMagic<Structure>

Playground

यह ए> कार्यान्वयन पर विचार करने योग्य है



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. स्प्रिंग डेटा मोंगोडब रिपॉजिटरी का उपयोग करके वैकल्पिक क्वेरी पैरामीटर जोड़ें

  2. CentOS 7 . पर MongoDB स्थापित करना

  3. मैं अपने डेटाबेस का MongoDB डंप कैसे बनाऊं?

  4. क्वेरी ऑपरेटर में MongoDB $

  5. उल्का विधि बनाम नियमों को अस्वीकार/अनुमति दें