'Filter'에 해당되는 글 16건

  1. 2022.07.18 [Elasticsearch] Filter Aggregation 사용하기.
  2. 2022.07.14 [Elasticsearch] Text Analysis, Performance Aggs.
  3. 2021.11.18 [Logstash] filter split 예제
  4. 2019.11.06 [Logstash] logstash filter date 조금 알아보기
  5. 2019.11.04 [Logstash] JSON filter plugin
  6. 2018.10.05 [검색일반] stop token filter 와 char filter
  7. 2018.04.24 [Elasticsearch] Synonym filter position 문제 개선
  8. 2018.04.24 [Logstash] Logstash 를 이용한 CSV 파일 Import를 하려면
  9. 2017.07.31 [Lucene] SynonymFilter -> SynonymGraphFilter + FlattenGraphFilter
  10. 2017.07.28 [미미박스 검색이야기] Quick Search - 퀵서치

[Elasticsearch] Filter Aggregation 사용하기.

Elastic/Elasticsearch 2022. 7. 18. 18:50

보통 Query 절에서 문서를 필터링 하고 Aggs 에서 집합/분석 질의를 하게 됩니다.

이 경우 성능적 잇점을 가져 가기 위해서는 보통 아래 두 가지 설정만 잘 사용 하면 됩니다.

 

1. Query 절은 Non-Scoring 질의를 사용하고

2. Size 파라미터 값을 0 으로 사용하고

 

그 이외는 기본적으로 Elasticsearch 에서  Cache 기능이 잘 동작 하기 때문에 크게 고민을 하지 않으셔도 어느 정도의 성능은 나온다고 보시면 됩니다.

 

Bucket Aggregation 중 Query 절에서 Filtering 하는 것과 같은 기능이 있어서 문서 링크와 주의 사항(?) 올려 봅니다.

 

아래 두 질의는 같은 결과를 리턴 합니다.

 

Case 1)

POST /sales/_search?size=0&filter_path=aggregations
{
  "query": { "term": { "type": "t-shirt" } },
  "aggs": {
    "avg_price": { "avg": { "field": "price" } }
  }
}

 

Case 2)

POST /sales/_search?size=0&filter_path=aggregations
{
  "aggs": {
    "t_shirts": {
      "filter": { "term": { "type": "t-shirt" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

여기서 C1, C2 중 어떤게 좀 더 성능적으로 우세 할까요?

공홈 문서에 따르면 Sub Aggregation. 을 가지는 Filter Aggregation 보다는 Top Level Query 를 사용하는게 더 빠르다고 합니다.

그리고 다수의 Filter Aggregation 을 사용 하는 것 보다는 Filters Aggregation 을 사용 하는게 더 빠르다고 하니 참고 해서 사용 하시기 바랍니다.

 

제가 사용 한다고 하면 Top Level Query 에 Filter Query 나 Non-Scoring Query 를 이용해서 사용 할 것 같습니다.

 

 

 

:

[Elasticsearch] Text Analysis, Performance Aggs.

Elastic/Elasticsearch 2022. 7. 14. 19:32

Aggregation 기능을 이용해서 Text 분석을 하고 싶을 때가 있습니다.

X-pack basic 범위에서 가능 하면 더 좋겠죠.

 

아래 공홈 문서 보시고 참고해 보시면 좋을 것 같습니다.

 

 - Text Analysis
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-categorize-text-aggregation.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-multi-terms-aggregation.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-rare-terms-aggregation.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significanttext-aggregation.html

- Aggs Performance
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filter-aggregation.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-filters-aggregation.html

:

[Logstash] filter split 예제

Elastic/Logstash 2021. 11. 18. 12:12

참고문서)

https://www.elastic.co/guide/en/logstash/current/plugins-filters-split.html

https://www.elastic.co/guide/en/logstash/current/plugins-filters-mutate.html#plugins-filters-mutate-split

 

예제코드)

라인단위로 정보가 작성 되어 있을 때

데이터 예시)
1|A|가
2|B|나

filter {
	split { }

	mutate {
		split => { "message" => "|" }
		add_field => {
			"first" => "%{[message][0]}"
			"second" => "%{[message][1]}"
			"third" => "%{[message][2]}"
		}
	}
}

 

:

[Logstash] logstash filter date 조금 알아보기

Elastic/Logstash 2019. 11. 6. 14:34

문의가 들어 왔습니다.

여러 필드에 대해서 date format 이 다른데 어떻게 적용을 해야 하나요?

 

그래서 소스코드를 열어 보고 아래와 같이 해보라고 했습니다.

date {
...
}

date {
...
}

결국 date {...} 를 필드 별로 선언을 해주면 되는 내용입니다.

 

공식 문서)

https://www.elastic.co/guide/en/logstash/current/plugins-filters-date.html

 

Common Options)

https://www.elastic.co/guide/en/logstash/current/plugins-filters-date.html#plugins-filters-date-common-options

 

Date Filter Configuration Options)

Setting

Input type

Required

locale

string

No

match

array

No

tag_on_failure

array

No

target

string

No

timezone

string

No

전체 옵션이 필수가 아니긴 합니다.

그래도 꼭 아셔야 하는 설정은 match, target 입니다.

 

- match 의 첫 번째 값은 field 명이고, 그 이후는 format 들이 되겠습니다.

(공식 문서에 잘 나와 있습니다.)

 

An array with field name first, and format patterns following, [ field, formats... ]

If your time field has multiple possible formats, you can do this:

 

match => [ "logdate", 

    "MMM dd yyyy HH:mm:ss", 

    "MMM d yyyy HH:mm:ss", 

    "ISO8601" ]

 

- target 은 지정을 하지 않게 되면 기본 @timestamp 필드로 설정이 됩니다. 변경 하고자 하면 target 에 원하시는 field name 을 넣으시면 됩니다.

 

예제)

date {
    match => ["time" , "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ss.SSSZ"]
    target => "@timestamp"
}

date {
    match => ["localtime" , "yyyy-MM-dd HH:mm:ssZ"]
    target => "time"
}

DateFilter.java)

더보기
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.logstash.filters;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joda.time.Instant;
import org.logstash.Event;
import org.logstash.ext.JrubyEventExtLibrary.RubyEvent;
import org.logstash.filters.parser.CasualISO8601Parser;
import org.logstash.filters.parser.JodaParser;
import org.logstash.filters.parser.TimestampParser;
import org.logstash.filters.parser.TimestampParserFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class DateFilter {
  private static Logger logger = LogManager.getLogger();
  private final String sourceField;
  private final String[] tagOnFailure;
  private RubyResultHandler successHandler;
  private RubyResultHandler failureHandler;
  private final List<ParserExecutor> executors = new ArrayList<>();
  private final ResultSetter setter;

  public interface RubyResultHandler {
    void handle(RubyEvent event);
  }

  public DateFilter(String sourceField, String targetField, List<String> tagOnFailure, RubyResultHandler successHandler, RubyResultHandler failureHandler) {
    this(sourceField, targetField, tagOnFailure);
    this.successHandler = successHandler;
    this.failureHandler = failureHandler;
  }

  public DateFilter(String sourceField, String targetField, List<String> tagOnFailure) {
    this.sourceField = sourceField;
    this.tagOnFailure = tagOnFailure.toArray(new String[0]);
    if (targetField.equals("@timestamp")) {
      this.setter = new TimestampSetter();
    } else {
      this.setter = new FieldSetter(targetField);
    }
  }

  public void acceptFilterConfig(String format, String locale, String timezone) {
    TimestampParser parser = TimestampParserFactory.makeParser(format, locale, timezone);
    logger.debug("Date filter with format={}, locale={}, timezone={} built as {}", format, locale, timezone, parser.getClass().getName());
    if (parser instanceof JodaParser || parser instanceof CasualISO8601Parser) {
      executors.add(new TextParserExecutor(parser, timezone));
    } else {
      executors.add(new NumericParserExecutor(parser));
    }
  }

 public List<RubyEvent> receive(List<RubyEvent> rubyEvents) {
    for (RubyEvent rubyEvent : rubyEvents) {
      Event event = rubyEvent.getEvent();

      switch (executeParsers(event)) {
        case FIELD_VALUE_IS_NULL_OR_FIELD_NOT_PRESENT:
        case IGNORED:
          continue;
        case SUCCESS:
          if (successHandler != null) {
            successHandler.handle(rubyEvent);
          }
          break;
        case FAIL: // fall through
        default:
          for (String t : tagOnFailure) {
            event.tag(t);
          }
          if (failureHandler != null) {
            failureHandler.handle(rubyEvent);
          }
      }
    }
    return rubyEvents;
  }

  public ParseExecutionResult executeParsers(Event event) {
    Object input = event.getField(sourceField);
    if (event.isCancelled()) { return ParseExecutionResult.IGNORED; }
    if (input == null) { return ParseExecutionResult.FIELD_VALUE_IS_NULL_OR_FIELD_NOT_PRESENT; }

    for (ParserExecutor executor : executors) {
      try {
        Instant instant = executor.execute(input, event);
        setter.set(event, instant);
        return ParseExecutionResult.SUCCESS;
      } catch (IllegalArgumentException | IOException e) {
        // do nothing, try next ParserExecutor
      }
    }
    return ParseExecutionResult.FAIL;
  }
}

 

:

[Logstash] JSON filter plugin

Elastic/Logstash 2019. 11. 4. 14:16

공홈에 올라와 있는 문서의 번역 본 정도로 정리를 해보려고 합니다.

별거 아니지만 JSON filter 를 많이 사용하면서 Validation 에 대한 인식이 부족해서 오류를 발생 시키는 경우가 꽤 많이 있습니다.

기억력을 돕기 위해 작성해 봅니다.

 

공식문서)

https://www.elastic.co/guide/en/logstash/current/plugins-filters-json.html

 

이 내용은 logstash reference 문서 내 filter 항목에 해당 합니다.

용도는 말 그대로 입니다. JSON  parsing 을 하는 filter 입니다.

 

여기서 문제가 들어 오는 JSON 데이터가 항상 validate 할 거라고 생각 하고 구현 하시는 분들이 계시는데 이 부분이 문제가 됩니다.

기본 이라고 생각 하지만 validation 에 대한 개발자의 생각과 경험이 요즘은 다른 것 같더라구요.

 

암튼, 그래서 JSON filter 사용 시 제공하는 Option 에 대해서 숙지 하고 사용하시면 좋겠습니다.

기본적으로는 Common Options 를 먼저 보시는게 좋습니다.

 

JSON Filter Configuration Options)

Setting Input type Required
skip_on_invalid_json boolean No
source string Yes
tag_on_failure array No
target string No
  • skip_on_invalid_json
    json 이 아닌 데이터가 들어 올 경우 에러를 발생 시키지 않고 skip 시키기 위한 옵션 입니다.
    기본 설정 값이 false 이기 때문에 잘 못된 데이터에 대해서 오류가 발생 하게 됩니다.

  • source
    json parsing 을 하기 위한 field 를 지정 하게 됩니다.

  • tag_on_failure
    정상적으로 처리 되지 않았을 경우 tags 라는 filed 에 "_jsonparsefailure" 값이 추가 됩니다.

  •  target
    source field 내 json value 가 target filed 로 등록 되며, 이미 target field 가 있다면 overwrite 됩니다.

위 설정 중에서 skip_on_invalid_json  과 tag_on_failure 만 잘 설정 하셔도 invalid data 에 대한 오류는 잘 넘길 수 있습니다.

간혹 이 오류로 인해서 logstash 가 먹통이 되는 걸 예방 할 수 있기 때문 입니다.

:

[검색일반] stop token filter 와 char filter

ITWeb/검색일반 2018. 10. 5. 14:43

가끔 착각하게 되는 내용이라 기술해 봅니다.


우리가 흔히 이야기 하는 불용어라고 하는 것은 추출된 색인어에 대해서 색인어 처리를 하지 않도록 하는 것입니다.

이것이 stopwords 라고 부르는 것이고 stop token filter 하고 합니다.


analyze 과정에서 char filter 이후 tokenizer 단계에서 사용되는 filter 기능이 되는 것입니다.

다시 말해 색인어 추출 후 적용하는 filter 기능이라고 보시면 됩니다.


그럼 char filter 는 언제 동작 하게 될까요?

이 filter 기능은 색인어 추출 전 그러니까 tokenizer 로 text 가 전달 되기 전에 text 내 불필요한 문자들을 제거 하기 위해 사용을 합니다.

이것이 char filter 가 되겠습니다.


그냥 저 혼자 복습 차원에서 기술해 봤습니다.


https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-stop-tokenfilter.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-charfilters.html


:

[Elasticsearch] Synonym filter position 문제 개선

Elastic/Elasticsearch 2018. 4. 24. 15:52

이미 synonym filter 테스트 관련 글을 공유 했었습니다.

- [Elasticsearch] Synonym filter 테스트


이 테스트에서 발생한 문제는 동의어에 대한 position 정보가 잘 못되는 것입니다.

테스트 환경은 기본 Elasticsearch 6.x 에서 진행 되었습니다. 


아래는 SynonymFilter 코드 내 주석 입니다.


[SynonymFilter.java]

 Matches single or multi word synonyms in a token stream.

 This token stream cannot properly handle position

 increments != 1, ie, you should place this filter before

 filtering out stop words.


그리고 아래는 동의어 처리 시 문제가 발생 하는 부분의 코드 입니다.


[SynonymMap.java]

 /** Sugar: analyzes the text with the analyzer and

 *  separates by {@link SynonymMap#WORD_SEPARATOR}.

 *  reuse and its chars must not be null. */

public CharsRef analyze(String text, CharsRefBuilder reuse) throws IOException {

  try (TokenStream ts = analyzer.tokenStream("", text)) {

    CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class);

    PositionIncrementAttribute posIncAtt = ts.addAttribute(PositionIncrementAttribute.class);

    ts.reset();

    reuse.clear();

    while (ts.incrementToken()) {

      int length = termAtt.length();

      if (length == 0) {

        throw new IllegalArgumentException("term: " + text + " analyzed to a zero-length token");

      }

      if (posIncAtt.getPositionIncrement() != 1) {

        throw new IllegalArgumentException("term: " + text + " analyzed to a token (" + termAtt +

                                           ") with position increment != 1 (got: " + posIncAtt.getPositionIncrement() + ")");

      }

      reuse.grow(reuse.length() + length + 1); /* current + word + separator */

      int end = reuse.length();

      if (reuse.length() > 0) {

        reuse.setCharAt(end++, SynonymMap.WORD_SEPARATOR);

        reuse.setLength(reuse.length() + 1);

      }

      System.arraycopy(termAtt.buffer(), 0, reuse.chars(), end, length);

      reuse.setLength(reuse.length() + length);

    }

    ts.end();

  }

  if (reuse.length() == 0) {

    throw new IllegalArgumentException("term: " + text + " was completely eliminated by analyzer");

  }

  return reuse.get();

}


기본적으로 동의어 처리에 대한 문제는 이미 lucene 레벨에서 개선이 되었습니다.

관련 참고 링크는 아래와 같습니다.


[Reference links]

https://issues.apache.org/jira/browse/LUCENE-6664

http://blog.mikemccandless.com/2012/04/lucenes-tokenstreams-are-actually.html


이와 같이 개선된 synonym filter 를 elasticsearch 에서는 아래와 같이 사용 할 수 있습니다.


[Reference links]

https://www.elastic.co/guide/en/elasticsearch/reference/master/analysis-synonym-graph-tokenfilter.html



[Create index]

PUT /syngtest

{

  "settings": {

    "index.number_of_shards": 1,

    "index.number_of_replicas": 0,

    "index": {

      "analysis": {

        "analyzer": {

          "arirang_custom": {

            "tokenizer": "arirang_tokenizer",

            "filter": [

              "lowercase",

              "trim",

              "arirang_filter",

              "custom_synonym"

            ]

          }

        },

        "filter": {

          "custom_synonym": {

            "type": "synonym_graph",

            "synonyms": [

              "henry,헨리,앙리",

              "신해철,마왕"

            ]

          }

        }

      }

    }

  }

}


[Request analyze]

GET /syngtest/_analyze

{

  "tokenizer": "arirang_tokenizer",

  "filter": [

    "lowercase",

    "trim",

    "arirang_filter",

    "custom_synonym"

  ],

  "text": "신해철은 henry"

}


[Analyzed result]

{

  "tokens": [

    {

      "token": "마왕",

      "start_offset": 0,

      "end_offset": 3,

      "type": "SYNONYM",

      "position": 0

    },

    {

      "token": "신해철",

      "start_offset": 0,

      "end_offset": 3,

      "type": "korean",

      "position": 0

    },

    {

      "token": "헨리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "앙리",

      "start_offset": 5,

      "end_offset": 10,

      "type": "SYNONYM",

      "position": 1

    },

    {

      "token": "henry",

      "start_offset": 5,

      "end_offset": 10,

      "type": "word",

      "position": 1

    }

  ]

}


특별히 코드를 수정 하거나 하지 않고 문제가 해결 된 것을 확인 하실 수 있습니다.

왜 해결 되었는지는 위 synonym graph filter 에 대해서 문서를 보시면 되겠습니다.


:

[Logstash] Logstash 를 이용한 CSV 파일 Import를 하려면

Elastic/Logstash 2018. 4. 24. 11:14

Elastic 사의 공식 문서를 보시면 쉽게 하실 수 있습니다.


기본 flow 는 아래와 같습니다.


CSV -> logstash input file -> Logstash filter csv -> logstash output elasticsearch


각각에 필요한 참조문서는

[Logstash Input File]

https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html


[Logstash Filter CSV]

https://www.elastic.co/guide/en/logstash/current/plugins-filters-csv.html


[Logstash Output Elasticsearch]

https://www.elastic.co/guide/en/logstash/current/plugins-outputs-elasticsearch.html


[Elasticsearch Indices Templates]

https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-templates.html


Template 이 필요한 이유는 csv 파일 데이터에 대한 dynamic mapping 시 의도치 않은 데이터에 대한 형변환 오류를 방지 하기 위함 입니다.

사전에 꼭 정의 하셔서 reindexing 하는 일이 없도록 주의 하시면 좋을 것 같습니다.


:

[Lucene] SynonymFilter -> SynonymGraphFilter + FlattenGraphFilter

ITWeb/검색일반 2017. 7. 31. 18:37

오늘 뭐 좀 보다가 그냥 공유해 봅니다.
https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html


lucene 6.6 에서는 SynonymFilter@Deprecated 되어 있습니다.

대체 filter 는 글에도 나와 있지만 SynonymGraphFilter 인데요.

재밌는건 이넘은 search time 에서 동작 하는 거라 index time 에는 여전히 SynonymFilter 또는 FlattenGraphFilter 를 사용해야 한다는 점입니다.

아직 깊게 분석해보지 않아서 ^^; 


간만에 lucene 코드 까서 이것 저것 테스트 해보니 재밌내요.

그냥 참고 하시라고 올려봤습니다.


:

[미미박스 검색이야기] Quick Search - 퀵서치

ITWeb/미미박스검색이야기 2017. 7. 28. 16:35


기능 적용한지 좀 되었는데 이제야 홍보 합니다. ^^;


퀵서치라는 기능은 기본적으로 모바일 환경에서 검색어를 입력하지 않고 검색 필터 기능을 활용해서 쉽게 원하는 조건의 상품을 검색해서 볼 수 있도록 제공하기 위해 만들어 졌습니다.


더불어 해당 영역은 검색 이외 "바로가기" 기능이나 특정 페이지로 랜딩 시키기 위한 용도로도 활용이 가능 합니다.


구경하기)

http://m.search.memebox.com/





※ 본 글은 회사의 입장과는 전혀 상관이 없으며 개인적인 의견으로 작성된 글 임을 알려 드립니다.


: