React Admin 및 Sequelize의 필터 연산자

TLDR


필터에서 연산자를 전달하려면 React 구성 요소의 원본 속성에 연산자 유형을 추가한 다음 백엔드에서 연산자를 해석하고where 조건을 만듭니다.
필터와 다른 구성 요소가 선택한 연산자에서 값을 추출하여 동적으로 만들 수 있는 사용자 정의 필터를 작성할 수도 있습니다.
            <NumberInput
                resource="users"
                label="Age"
                source="age|op=lte"
            />

            <SelectArrayInput
                resource="users"
                source="city|op=In"
                choices={cities}
            />

스택


우리의 최신 프로젝트에서 우리가 사용하는 것은 React Admin이다. 이것은 웹 프레임워크로 매우 강력하고 고급 응용 프로그램을 합리적이고 신속하게 구축할 수 있다.
그리고 React 프로그램에서 발생하는 모든 때를 처리하기 위해 AWS API 스위치와 lambda 그룹이 있습니다.
모든 내용을 Aurora Serverless에 저장하고 백엔드에서 Sequelize를 사용하여 데이터베이스에 기록한다. 이것은 대상 관계 매핑기(ORM)로 대상 문법을 데이터베이스 모델에 쉽게 매핑할 수 있다.
React Admin은 사용자 입력을 수집하고 사용자가 선택한 API를 호출하여 엔드포인트에 바인딩하여 반환하는 데이터 속성을 제공하는 맞춤형 격자와 필터 구성 요소를 제공합니다.
Filter Inputs의 React 구성 요소는 다음과 같습니다.
 <NumberInput
                resource="users"
                label="User ID"
                source="id"
            />

            <SelectArrayInput
                resource="users"
                source="city"
                choices={cities}
            />
그 중에서 Resource는 단점입니다. 소스는 그에 따라 선별할 속성입니다.
이 구성 요소의 onChange 탐지기는 엔드포인트에 전달되는 유효 로드를 생성합니다. URL이 인코딩되지 않은 유효 로드는 다음과 같습니다.
{
filter:  {
   "cities": ["NY", "LA", "WA"],
   "name": "ma",
   "age": 18
  }
}
name 속성을 Like 검색에 연결하거나, 그룹 값을 IN에 연결할 수 있는 조건은 매우 간단하지만, 우리는 곧 코드의 유연성과 읽기 가능성에 문제가 생겼다.

문맥


어느 순간, 우리는 데이터베이스에서 불러온 데이터에 대해 더 높은 필터를 추가해야 한다.
  • X가 아닌 요소를 검색하세요.
  • Y보다 큰 요소 검색
    etc
  • MySQL에서 이러한 Where 조건을 작성하는 것은 매우 간단하며, Sequelize도 마찬가지다.
    SELECT city, name, age FROM Users WHERE id IN (NY, LA, WA)
    
    SELECT city, name, age FROM Users WHERE name like %ma%.  # will return anything like Matt, Mark, Annamarie, Samantha, Thelma, Naima. 
    
    SELECT city, name, age FROM Users WHERE age < 18 
    
    Sequelize에서는 다음과 같이 표시됩니다.
    
        Users.findAll({
            where: {
                id: {
                    [Op.in]: [NY, LA, WA],
                }
            })
    
        Users.findAll({
            where: {
                name: {
                    [Op.substring]: "ma"
                }
            })
    
        Users.findAll({
            where: {
                age: {
                    [Op.lte]: 18
                }
            })
    
    물론 이것들은 조건과 결합할 수 있다.

    문제.


    React 구성 요소의 운영자는 어떻게 처리합니까?
    내 생각은 이렇다.

    AG-Grid Simple filters에서 찾았어요.그러나 React Admin 은 이러한 구성 요소를 제공하지 않으며, 운영자 정보를 포함하는 방식으로 유효 부하를 설정할 수 없습니다. 대체로 다음과 같습니다.
    "groupOp": "AND",
      "rules": [
        {
          "field": "age",
          "op": "lowerThan",
          "data": 18
        },
        {
          "field": "city",
          "op": "in",
          "data": ["NY", "LA"]
        }
      ],
    
    Sequelize는 전체 목록 Operators 을 제공하지만, 이것은 React Admin 중 유일한 가능성입니다.
    a generic text search 이 방법을 사용합니다.
    filter={"q":"lorem "}  
    
    그 중에서 q는 백엔드에서 특정한 속성과 특정한 like(startsWith,endsWith,contains) 등을 비춰야 한다.
    편하지만 그렇게 편하지는 않아요...
    실제로 API가 특정 연산자를 사용하여 검색하는 방법은 표준이 없으며 Reactadmin은 이에 대해 아무것도 모르기 때문에 필터가 이런 기능을 처리할 수 없습니다.
    React Admin의 빌더가 onStackOverflow도 설명했지만 이 문제를 처리하는 방법에 대한 팁도 제공합니다.

    Composing the source so that it contains the operator

    filter: { status_id_gt: 2 }

    솔루션


    이상적이지는 않지만, 나를 시작하게 하기에는 충분하다.(소스, 속성 및 연산자를 밑줄로 구분하고 싶지 않습니다. 너무 많은 경우 열이나 속성에 밑줄이 포함되어 있기 때문입니다.)
    백엔드에서 속성을 조작부호에서 안전하게 분리할 수 있는 특정한 구분자 (|op=) 를 선택했습니다. 그리고 사용 가능한 Sequelize 조작부호에 상수를 추가할 수 있습니다.
    const filters = {
            'name|op=substring': 'ma',
            'age|op=lt': [18],
            'cities': {
                'id|op=in': ['NY', 'LA']
            },
            'status|op=notIn': ["pending", "deleted"]
        }
    
    
    이렇게 하면 간단한 방법으로 해석됩니다.
    const {Op} = require('sequelize')
    
    const parseOperator = (key, value, separator = '|op=') => {
        if (key.includes(separator)) {
            const [prop, op] = key.split(separator)
            if (!Object.prototype.hasOwnProperty.call(Op, op)) {
                throw new Error(`Invalid Filter Condition/Operator (${op})`)
            }
            return {
                property: prop,
                operator: Op[op],
                value
            }
        }
    // this is handling the default - which is an Equal if value is it's a string or number, or an IN if we received an Array.
        return {
            property: key,
            operator: Array.isArray(value) ? Op.in : Op.eq,
            value
        }
    }
    
    그리고 프로그램에서 받은 필터 대상의 항목마다
    Object.entries(filter).reduce((acc, current) => {
                const [key, value] = current
                const {property, operator, value: by} = parseOperator(key, value)
                acc.where[property] = {
                    [operator]: by
                }
                return acc
            }, {
                where: {}
            }
        )
    
    이 대상을 자주 사용하는 Model.findAll() 방법에 전달하면 정확한 연산자가 있는 정확한 Sequelize 문장을 얻을 수 있습니다.

    이제 어떡하지?


    나는 여전히 상술한 구성 요소의 완전한 실현을 고려하고 있다. 사용자는 그들이 사용하고자 하는 조작부호를 선택할 수 있지만, 현재 이것은 우리의 요구가 아니기 때문에, 나는 '하드코딩' 조작부호를 사용하는 기본적인 실현을 견지하지만, 더욱 큰 유연성을 허용한다.
    이 기능이 필요할 때마다 입력한 필터 구성 요소를 사용자 정의 구성 요소로 포장하면 사용자가 조작부호의 유형을 선택하고 백엔드에서 해석될 문자열을 합성할 수 있습니다.
                <NumberInput
                    resource="users"
                    label="Age"
                    source="age|op={$SELECTED_OPERATOR}"
                />
    
                <SelectArrayInput
                    resource="users"
                    source="city|op={$SELECTED_OPERATOR}"
                    choices={cities}
                />
    
    도움이 되었으면 좋겠습니다.

    좋은 웹페이지 즐겨찾기