hanseom 2025. 3. 29. 03:00
반응형

  Elasticsearch에서 JOIN은 문서 간의 관계를 설정하고 이를 통해 문서를 연결하여 검색할 수 있는 기능입니다. 단, Elasticsearch의 JOIN은 RDBMS와 달리 성능에 영향을 미칠 수 있습니다. 특히, has_child or has_parent 쿼리는 성능에 부담을 줄 수 있으므로, 데이터 모델링 시 신중히 고려해야 합니다.

 

Parent-Child 관계

  Parent-Child 관계는 한 문서가 부모가 되고, 다른 문서가 자식이 되는 구조입니다. 이 관계는 인덱스 내에서 설정되며, 부모와 자식 문서는 반드시 같은 샤드에 저장되어야 합니다.

  • has_child 쿼리: 자식 문서가 특정 조건을 만족할 때 부모 문서를 반환합니다.
  • has_parent 쿼리: 부모 문서가 특정 조건을 만족할 때 자식 문서를 반환합니다.

 

Join 데이터 타입

  Elasticsearch 6.x부터는 Join 데이터 타입을 사용하여 Parent-Child 관계를 설정할 수 있습니다. 이 타입은 문서 내에서 가능한 관계 집합을 정의하며, 각 관계는 부모 이름과 자식 이름으로 구성됩니다.

 

  다음 library 인덱스는 부모(author)-자식(book) 관계를 가집니다.

PUT /library
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "author": "book"  // Define 'author' as parent and 'book' as child
        }
      },
      "name": { "type": "text" },       // Common field for names
      "title": { "type": "text" },      // Field for book titles
      "genre": { "type": "keyword" },   // Field for book genres
      "year": { "type": "integer" }     // Field for publication year
    }
  }
}

 

  부모 Documents를 생성합니다.

POST /library/_doc/1
{
  "my_join_field": "author",  // Indicating this is a parent document
  "name": "J.K. Rowling"
}

POST /library/_doc/2
{
  "my_join_field": "author",  // Indicating this is a parent document
  "name": "George R.R. Martin"
}

 

  자식 Documents를 생성합니다. 부모와 자식 문서는 반드시 같은 샤드에 저장되어야 하기 때문에 부모 문서의 ID를 자식 문서의 routing 값으로 사용합니다. (Elasticsearch에서 문서를 저장할 때 기본적으로 문서의 ID를 해싱하여 샤드를 결정합니다. 그러므로 부모 문서를 저장할 때는 별도의 라우팅 값을 지정하지 않아도 됩니다.)

POST /library/_doc/3?routing=1
{
  "my_join_field": {
    "name": "book",
    "parent": "1"  // Link to the parent author (J.K. Rowling)
  },
  "title": "Harry Potter and the Philosopher's Stone",
  "genre": "Fantasy",
  "year": 1997
}

POST /library/_doc/4?routing=1
{
  "my_join_field": {
    "name": "book",
    "parent": "1"  // Link to the parent author (J.K. Rowling)
  },
  "title": "Harry Potter and the Chamber of Secrets",
  "genre": "Fantasy",
  "year": 1998
}

POST /library/_doc/5?routing=2
{
  "my_join_field": {
    "name": "book",
    "parent": "2"  // Link to the parent author (George R.R. Martin)
  },
  "title": "A Game of Thrones",
  "genre": "Fantasy",
  "year": 1996
}

POST /library/_doc/6?routing=2
{
  "my_join_field": {
    "name": "book",
    "parent": "2"  // Link to the parent author (George R.R. Martin)
  },
  "title": "A Clash of Kings",
  "genre": "Fantasy",
  "year": 1998
}

 

has_parent 쿼리

GET /library/_search
{
  "query": {
    "has_parent": {
      "parent_type": "author",
      "query": {
        "match": {
          "name": "J.K. Rowling"
        }
      }
    }
  }
}

 

has_child 쿼리

GET /library/_search
{
  "query": {
    "has_child": {
      "type": "book",
      "query": {
        "match": {
          "title": "Game"
        }
      }
    }
  }
}

GET /library/_search
{
  "query": {
    "has_child": {
      "type": "book",
      "query": {
        "term": {
          "genre": "Fantasy"
        }
      }
    }
  }
}

 

Parent ID로 Child 문서 검색

GET /library/_search
{
  "query": {
    "parent_id": {
      "type": "book",
      "id": 1
    }
  }
}

 

Inner Hit

GET /library/_search
{
  "query": {
    "has_parent": {
      "inner_hits": {},
      "parent_type": "author",
      "query": {
        "match": {
          "name": "J.K. Rowling"
        }
      }
    }
  }
}

GET /library/_search
{
  "query": {
    "has_child": {
      "inner_hits": {},
      "type": "book",
      "query": {
        "match": {
          "title": "Game"
        }
      }
    }
  }
}

 

[참고자료]

반응형