-
[ELK Stack] Elasticsearch 검색 - Highlight, Nested FieldELK Stack 2020. 10. 20. 19:22반응형
Elasticsearch(ES)에 저장된 data는 _search REST API를 이용하여 검색할수 있습니다.
시스템의 검색기능을 구현하면서 찾아봤던 자료들을 정리하려고 합니다.
검색관련해서는 ELK official document를 참고하시면 되겠습니다.
www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
Query DSL | Elasticsearch Reference [7.9] | Elastic
Elasticsearch provides a full Query DSL (Domain Specific Language) based on JSON to define queries. Think of the Query DSL as an AST (Abstract Syntax Tree) of queries, consisting of two types of clauses: Leaf query clauses Leaf query clauses look for a par
www.elastic.co
1. javascript에서 _search API 사용
저는 nodejs 서버에서 "node-fetch" module로 하기와 같이 ES REST API를 call하였습니다.
serverip, index, auth는 각자의 값을 넣으면 되겠습니다. const qeuryES = async (serverip, index, auth, param) => { const data = await fetch(`${serverip}/${index}/_search?scroll=1m`, { method: "POST", headers: { "Authorization": auth, "Content-Type": "application/json", }, body: JSON.stringify({ size: 10000, ...param }) }).then(res => res.json()); let docs = []; const { _scroll_id, hits: { total: { value: total }, hits } } = data; docs = docs.concat(hits); return docs; }
2. 배열 검색 - Nested field
www.elastic.co/guide/en/elasticsearch/reference/current/nested.html
Nested field type | Elasticsearch Reference [7.10] | Elastic
When ingesting key-value pairs with a large, arbitrary set of keys, you might consider modeling each key-value pair as its own nested document with key and value fields. Instead, consider using the flattened data type, which maps an entire object as a sing
www.elastic.co
Elasticsearch에서 배열에 있는 object에 대해 하기와 같이 처리합니다.
{ "group" : "fans1", "user" : [ { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] }, { "group" : "fans2", "user" : [ { "first" : "Alice", "last" : "Smith" }, { "first" : "John", "last" : "White" } ] } 위의 user field의 배열은 하기와 같이 변경되어 저장됩니다. { "group" : "fans1", "user.first" : [ "alice", "john" ], "user.last" : [ "smith", "white" ] }, { "group" : "fans2", "user.first" : [ "alice", "john" ], "user.last" : [ "smith", "white" ] }
위와 같이 fans1 과 fans2 다른 user 배열을 가지고 있지만 동일하게 변경되어 저장됩니다.
때문에 검색시 fans1를 찾으려고 John Smith를 검색하면 fans1과 fans2모두 나타나게 되겠죠.
위와 같은 상황에서는 하기와 같이 user field를 indexing 할때 nested field로 저장하면 해당 문제가 해결됩니다.
{ "mappings": { "properties": { "user": { "type": "nested" } } } }
주의할점은 검색시 하기와 같이 nested로 검색을 해야 합니다.
{ "query": { "nested": { "path": "user", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" }}, { "match": { "user.last": "Smith" }} ] } } } } }
3. Highlight
www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html
Highlighting | Elasticsearch Reference [7.10] | Elastic
Highlighters don’t reflect the boolean logic of a query when extracting terms to highlight. Thus, for some complex boolean queries (e.g nested boolean queries, queries using minimum_should_match etc.), parts of documents may be highlighted that don’t c
www.elastic.co
highlight된 문구를 받으려면 _search API 호출시 body에 highlight option을 추가하면 됩니다.
하기와 같이 test_highlight index에 2개 data를 추가합니다.
PUT test_highlight/_doc/1 { "sports": "football", "description": "Football is a family of team sports that involve, to varying degrees, kicking a ball to score a goal. Unqualified, the word football normally means the form of", "stars": ["Messi", "Ronaldo", "Ibrahimovic", "Neymar"] } PUT test_highlight/_doc/2 { "sports": "basketball", "description": "Basketball, colloquially referred to as hoops, is a team sport in which two teams, most commonly of five players each, opposing one another on a rectangular", "stars": ["LeBron James", "Stephen Curry", "Kevin Durant"] }
하기와 같이 _search API를 호출하면 highlight가 return됩니다.
GET test_highlight/_search { "query": { "query_string": { "fields": ["sports", "stars", "description"], "query": "*ball*" } }, "highlight": { "fragment_size": 150, "fields": { "stars": {}, "sports": {}, "description": {} } } } 결과: { "took" : 7, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "test_highlight", "_type" : "_doc", "_id" : "1", "_score" : 1.0, "_source" : { "sports" : "football", "description" : "Football is a family of team sports that involve, to varying degrees, kicking a ball to score a goal. Unqualified, the word football normally means the form of", "stars" : [ "Messi", "Ronaldo", "Ibrahimovic", "Neymar", "football star1" ] }, "highlight" : { "sports" : [ "<em>football</em>" ], "description" : [ "<em>Football</em> is a family of team sports that involve, to varying degrees, kicking a <em>ball</em> to score a goal.", "Unqualified, the word <em>football</em> normally means the form of" ], "stars" : [ "<em>football</em> star1" ] } }, { "_index" : "test_highlight", "_type" : "_doc", "_id" : "2", "_score" : 1.0, "_source" : { "sports" : "basketball", "description" : "Basketball, colloquially referred to as hoops, is a team sport in which two teams, most commonly of five players each, opposing one another on a rectangular", "stars" : [ "LeBron James", "Stephen Curry", "Kevin Durant", "basketball star1" ] }, "highlight" : { "sports" : [ "<em>basketball</em>" ], "description" : [ "<em>Basketball</em>, colloquially referred to as hoops, is a team sport in which two teams, most commonly of five players each, opposing one another on a rectangular" ], "stars" : [ "<em>basketball</em> star1" ] } } ] } }
hit된 object에 highlight field가 추가되어서 return됨을 확인할 수 있습니다.
또한 default로 <em>이 추가되어서 return 됩니다.
그리고 highlight return 값을 보면 알수있듯이 기존 field가 배열이 아니더라도 highlight는 배열로 return 됩니다.
stars와 같은 배열에 대해 highlight할 때 위의 결과와 같이 highlight 된 item만 return 됩니다. 따라서 front-end에서 [LeBron James, Stephen Curry, Kevin Durant, <em>basketball star1</em>] 로 보이고 싶으면 원래 배열에서 highlight된 item을 찾아서 처리해주는 방법밖에 없습니다(제가 찾아봤을 때에는 없었습니다...ㅠㅠ).
저는 하기와 같이 stars 배열의 highlight에 대해 tag가 없이 값만 받아와서 기존 배열에서 해당 item을 찾아서 수동으로 <em>을 주고 front-end에 return 하였습니다.
GET test_highlight/_search { "query": { "query_string": { "fields": ["sports", "stars", "description"], "query": "*ball*" } }, "highlight": { "fragment_size": 150, "fields": { "stars": { "number_of_fragments": 0, "pre_tags": "", "post_tags": "" }, "sports": {}, "description": {} } } }
pre_tags, post_tags로 highlight된 문구에 대한 tab를 수정할 수 있습니다.
query 에 대해서는 다음 페이지에서 설명하겠습니다.
2020/12/01 - [ELK Stack] - [ELK Stack] [초보자] Elasticsearch 검색(search) - 2
반응형'ELK Stack' 카테고리의 다른 글
[ELK Stack] Filebeat 소개 및 사용 (0) 2022.08.31 [ELK Stack] Elasticsearch cluster 롤링 업그레이드 관련(7.2 -> 7.17.2) (0) 2022.08.25 [ELK Stack]Elasticsearch 검색 - Full Text Query (0) 2020.12.01 [ELK Stack] Elasticsearch 검색(search) - Compound Query (0) 2020.12.01 [ELK Stack] [초보자] Logstash 시작하기 (0) 2020.10.18