diadia

興味があることをやってみる。自分のメモを残しておきます。

matchとmatch_phraseクエリの検索結果の違いをメモ

以下のsample1インデックスを作成し、matchやmatch_phraseを使って観察してみる。

インデックスはhtmlタグが存在している場合にタグを削除するhtml_strip, 日本語文章を日本語単語に分けるkuromoji_tokenizerを使っている。

PUT sample1
{
  "settings": {
      "analysis": {
        "analyzer": {
          "kuromoji_standard": {
            "char_filter": [
              "html_strip",
              "icu_normalizer"
            ],
            "tokenizer": "kuromoji_tokenizer",
            "type": "custom"
          }
        }
      }
  },
  "mappings":{
    "properties":{
      "content":{
        "type": "text",
        "analyzer":"kuromoji_standard"
      }
    }
  }
}

ドキュメントの登録

以下3つの文章を登録する。あとで”消費者センター”で検索をかけるための文章になっている。

PUT sample1/_doc/1
{
  "content":"私は消費者センターに連絡する"
}

PUT sample1/_doc/2
{
  "content":"カロリーを消費する"
}

PUT sample1/_doc/3
{
  "content":"消費する者はコンシューマー。中央はセンターという。"
}

また、analyzeAPIの結果では"消費者センター"の単語は以下にように分けられているを確認した。

GET sample1/_analyze
{
  "analyzer": "kuromoji_standard",
  "text":"消費者センター"
}


///////////////////////////////////// 結果 /////////////////////////////////////

{
  "tokens" : [
    {
      "token" : "消費",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "者",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "センター",
      "start_offset" : 3,
      "end_offset" : 7,
      "type" : "word",
      "position" : 2
    }
  ]
}

"消費"、"者"、"センター"で分けられている。
この3単語が検索のキーワードになることが分かったので、 ”カロリーを消費する”なら”消費"で転置インデックスされていることが推定できるし、 ”消費する者はコンシューマー。中央はセンターという。”であれば、”消費”, ”者”, ”センター”で転置インデックスされていることが推定できると思われる。

matchによる検索を試す

結果は”消費者センター”と一致した"私は消費者センターに連絡する"の他、検索にかけた単語すべて(”消費”, ”者”, “センター“)を満たす"消費する者はコンシューマー。中央はセンターという。"

もヒットする。さらに、”者”, ”センター” は一致しないが、”消費”のみ一致した"カロリーを消費する"

もヒットする事がわかる。

これらからmatchはor検索を実行していると思われる。

GET sample1/_search
{
  "query":{
    "match":{
      "content":"消費者センター"
    }
  }
}

/////////////////////////////// 結果 //////////////////////////////////////

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.8630463,
    "hits" : [
      {
        "_index" : "sample1",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.8630463,
        "_source" : {
          "content" : "消費する者はコンシューマー。中央はセンターという。"
        }
      },
      {
        "_index" : "sample1",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8630463,
        "_source" : {
          "content" : "私は消費者センターに連絡する"
        }
      },
      {
        "_index" : "sample1",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.2876821,
        "_source" : {
          "content" : "カロリーを消費する"
        }
      }
    ]
  }
}

match_phraseによる検索を試す

結果は"私は消費者センターに連絡する"のみヒットした。 "消費する者はコンシューマー。中央はセンターという。"といった検索時に使われる単語が全て含まれた文章は排除されているのに注目したい。これを使えば、検索時に使用したキーワードがまるまる入った文章のドキュメントだけを拾ってこれるのでは?と思う。 https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-match-query-phrase.html#query-dsl-match-query-phrase

Full text queries | Elasticsearch Reference [7.x] | Elastic

GET sample1/_search
{
  "query":{
    "match_phrase":{
      "content":"消費者センター"
    }
  }
}

///////////////////////////// 結果 ///////////////////////////////////////

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.8630463,
    "hits" : [
      {
        "_index" : "sample1",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8630463,
        "_source" : {
          "content" : "私は消費者センターに連絡する"
        }
      }
    ]
  }
}

既存のデータをコピーしたり新しいインデックスにデータをコピーしたい

次にデータ移行の際に移行するデータは新インデックスのルール(マッピング)に従ってデータを格納し、検索できるのか検証する。 要するに既存のデータを新インデックスに移し替えると新インデックスのtokenizerやchar_filterに従って検索できるのかを確認する。

// おはようが検索できないインデックスを作成する
PUT my_sample
{
  "settings":{
    "analysis": {
      "analyzer": {
        "my_kuromoji_analyzer": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer"
        }
      }
    }
  },
  "mappings":{
    "properties": {
      "content":{
          "type":"text",
          "analyzer": "my_kuromoji_analyzer"
        }
    }
  }
}
// データ格納
PUT my_sample/_doc/1
{
  "content":"<p>おは<span>よう</span></p>"
}

// これでは”おはよう”は検索できないことを確認できた

次に新たなインデックスを作成する。そのインデックスにデータを流しこむ。

PUT my_new_sample
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "kuromoji_normalize": {                 
            "char_filter": [
              "html_strip",
              "icu_normalizer"                    
            ],
            "tokenizer": "kuromoji_tokenizer",
            "filter": [
              "kuromoji_baseform",
              "kuromoji_part_of_speech",
              "cjk_width",
              "ja_stop",
              "kuromoji_stemmer",
              "lowercase"
            ]
          }
        }
      }
    }
  },
  "mappings":{
    "properties":{
      "content":{
        "type": "text"
      }    
    }
  }
}
// my_sampleのデータをmy_new_sampleに移行する(コピー)
POST _reindex
{
  "source": {
    "index": "my_sample"
  },
  "dest": {
    "index": "my_new_sample"
  }
}

このmy_new_sampleインデックスではおはようは検索できるかを確認する

GET my_new_sample
{
  "query":{
    "match":{
      "content": "おはよう"  
    }
  }
}
// この検索結果はヒットする。
// つまり既存のデータを新インデクスに移行した場合、新インデックスのルールに従ってデータが格納されることが確認された。

htmlタグに邪魔されずに検索したい

内容

  1. 実際にインデックスを作って確かめる
  2. analyzeエンドポイントを使っても確かめてみる

1. 実際にインデックスを作って確かめる

htmlタグがあると困る場合について

次にhtmlタグがついたデータをインデックスに格納するケースを考える。
具体的にはhtmlタグは検索対象ではなく、タグを除いた文章そのものを検索対象としたい場合について取り扱う。

たとえば、上の例で

<p>おは<b>よう</b></p>

のデータを登録すると、”おはよう”では検索できないのです。せっかく日本語文章を検索できるようになったとしてもhtmlタグが存在するとお手上げになってしまいます。
実際に確認してみましょう。

// 日本語の単語を識別するkuromoji_tokenizerをセット
PUT html_tag_sample
{ 
  "settings":{
    "analysis": {
      "analyzer": {
        "my_kuromoji_analyzer": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer"
        }
      }
    }
  },
  "mappings":{
    "properties": {
      "content":{
          "type":"text",
          "analyzer": "my_kuromoji_analyzer"
        }
    }
  }
}
// 当該データを登録する
PUT html_tag_sample/_doc/1
{
  "content":"<p>おは<b>よう</b></p>"
}
// 以下を実施しても検索結果は得られない。
// =>おはようで検索できるはずがタグを挟むことで日本語単語を認識できなくなっている😨
GET html_tag_sample/_search
{
  "query":{
    "match":{
      "content":"おはよう"
    }
  }
}

さらに言えば、pタグやbタグを検索にかけるとヒットします。つまり単語の識別としてhtmlタグも識別していることがわかります。

対処法

この場合、日本語文章に含まれるタグをまず除去してから日本語単語を識別するtokenizerを実行すればうまくいく。 htmlタグを文章から除去するにはhtml_stripを使えば良い。 HTML strip character filter | Elasticsearch Reference [7.11] | Elastic

生データ(文章)にhtml_stripをかけてからをしてからtokenizerを実行しますが、この生データに前処理を担うがchar_filterと呼ばれる。char_filterはtokenizerと同じ次元にある概念です。

したがってchar_filterにhtml_stripをセットする。また、char_filterのセットは0個でも1個でも10個でも良い。 Anatomy of an analyzer | Elasticsearch Reference [7.11] | Elastic

html_stripはマッピングするときに宣言して使うのでtokenizerをセットする要領で使います。

PUT sample
{ 
  "settings":{
    "analysis": {
      "analyzer": {
        "my_kuromoji_analyzer": {
          "type": "custom",
          "char_filter":["html_strip"],
          "tokenizer": "kuromoji_tokenizer"
        }
      }
    }
  },
  "mappings":{
    "properties": {
      "content":{
          "type":"text",
          "analyzer": "my_kuromoji_analyzer"
        }
    }
  }
}

当該データの登録

PUT sample/_doc/2
{
  "content":"<p>おは<b>よう</b></p>"
}
// おはようの検索は成功する
GET sample/_search
{
  "query":{
    "match":{
      "content":"おはよう"
    }
  }
}
// またpタグを検索しても結果は無いと表示される
GET sample/_search
{
  "query":{
    "match":{
      "content":"<p>"
    }
  }
}

これでhtmlタグが挟み込まれることによって単語を識別できなくなる弊害に対処することができました😆

2. analyzeエンドポイントを使っても確かめてみる

sample1とsample2を作成する。sample1はhtml_stripがないanalyzerを作りcontentフィールドにセットし、sample2はhtml_stripがあるanalyzerを作りcontentフィールドにセットしている。

analyzeエンドポイント:

Analyze API | Elasticsearch Reference [7.11] | Elastic

PUT sample1
{
  "settings": {
      "analysis": {
        "analyzer": {
          "kuromoji_standard": {                 
            "tokenizer": "kuromoji_tokenizer",
            "type": "custom"
          }
        }
      }
  },
  "mappings":{
    "properties":{
      "content":{
        "type": "text",
        "analyzer":"kuromoji_standard"
      }    
    }
  }
}



PUT sample2
{
  "settings": {
      "analysis": {
        "analyzer": {
          "kuromoji_html_strip": {                 
            "char_filter": [
              "html_strip"
            ],
            "tokenizer": "kuromoji_tokenizer",
            "type": "custom"
          }
        }
      }
  },
  "mappings":{
    "properties":{
      "content":{
        "type": "text",
        "analyzer":"kuromoji_html_strip"
      }    
    }
  }
}
GET sample1/_analyze
{
  "analyzer": "kuromoji_standard",
  "text":"<p>おは<b>よう</b></p>"
}

//// 結果
{
  "tokens" : [
    {
      "token" : "p",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "お",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "は",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "b",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "よう",
      "start_offset" : 8,
      "end_offset" : 10,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "b",
      "start_offset" : 12,
      "end_offset" : 13,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : "p",
      "start_offset" : 16,
      "end_offset" : 17,
      "type" : "word",
      "position" : 6
    }
  ]
}

結果を見るとすべての文字が分解されているので、日本語単語のまとまりで区切られてはない。つまり検索キーワードに日本語単語が登録されてないことがわかる。

GET sample2/_analyze
{
   "analyzer": "kuromoji_html_strip",
  "text":"<p>おは<b>よう</b></p>"
}


//// 結果
{
  "tokens" : [
    {
      "token" : "おはよう",
      "start_offset" : 3,
      "end_offset" : 14,
      "type" : "word",
      "position" : 0
    }
  ]
}

結果として”おはよう”という単語のまとまりでトークン化できているので、検索時におはようで検索することができると結論付けられる。

elasticsearchで日本語を検索できるようにする

(elasticsearchで検索される英単語と検索されない英単語について - diadiaに続いて)

じゃあ日本語も同じように単語ごとに空白で区切ってから登録すればElasticsearchが使えるねって発想になると思う。しかし生の日本語文章を単語に区切る前処理(この場合では日本語文章の単語と単語の間にスペースを加える自作のスクリプトを実行する)をしてからデータを登録するのは手軽にelasticsearch使えなくて嫌な感じになる。

でもそこで悩む必要はない。そもそもElasticsearchでは単語を識別するための基準がスペース(white space)ではない。単語に区切る基準は、tokenizerと呼ばれ、Tokenizerにどう単語を識別するかを設定して上げれば別の方法で文章から単語を区切ることができる。言い換えれば、tokenizerに日本語文章から日本語単語を識別しますというtokenizerを設定してやればそれで済む話なのです。

(補足ですが英文の場合tokenizerを設定していないですが、デフォルトでスペースを基準に単語に分けるtokenizerが設定されているので英文はtokenizerを明示的にセットしなくても英文章から単語を識別できていた)

日本語文章から単語に区切るtokenizerはkuromoji_tokenizerという。コレはpluginという形で使うので別途インストールが必要になる。 インストールは以下のように実行する。

// elasticsearchのbinディレクトリにて
./elasticsearch-plugin install analysis-kuromoji

Japanese (kuromoji) Analysis Plugin | Elasticsearch Plugins and Integrations [7.11] | Elastic

日本語文章も検索できるようにする

ここでは生の日本語文章を前処理なしでそのままエラスティックサーチに保存させて、日本語の単語から検索できるようにする方法と、それが本当にできるのかを検証する。

// tokenizerに日本語単語を識別するkuromoji_tokenizerを使ったインデックスを作成する
PUT sample2
{ 
  "settings":{
    "analysis": {
      "analyzer": {
        "my_kuromoji_analyzer": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer"
        }
      }
    }
  },
  "mappings":{
    "properties": {
      "content":{
          "type":"text",
          "analyzer": "my_kuromoji_analyzer"
        }
    }
  }
}
// 日本語文章を登録します
// 結果を先にいうと単語と識別されるのは"野菜", "を", "育てる", "お", "ばあちゃん"って事になりました。

PUT sample2/_doc/1
{
  "content":"野菜を育てるおばあちゃん"
}

検索してみる。

// なので、ちゃんで検索をかけると検索結果は無しとなります。
// 育てるのみヒットし、育つではヒットしません。これに対応する方法もあります。

GET sample2/_search
{
  "query":{
    "match":{
      "content":"ちゃん"
    }
  }
}

"野菜", "を", "育てる", "お", "ばあちゃん"で検索をかけると結果が得られます。 しかしながら、"ちゃん"や"育つ"また"野"では検索結果はえられない結果になります。
このことから日本語文章から適切に単語を識別して、転置索引するようになっていると確認する事ができました。
補足、kuromoji_tokenizerだけでは日本語の単語識別は不完全でicu_normalizerというものも使うことをドキュメントには書かれています。

kuromoji analyzer | Elasticsearch Plugins and Integrations [7.11] | Elastic

elasticsearchで検索される英単語と検索されない英単語について

以下ではどのようにデータが格納され、どんなキーワードなら検索できるかを焦点に見る。

例:英文を格納する

エラスティックサーチにおいて英文を格納するケースでは、英文は文章中のスペース(white space)に基づいて単語に分割するとある。
したがってスペースで区切られた単語は検索できる様になるが、スペースがないものに関しては検索できないと結論付けられる。 これを確かめる。

// 格納するインデックスを作成する
// インデックス作成と同時に明示的にMappingを作成
PUT sample
{ 
  "mappings":{
    "properties": {
      "content":{
          "type":"text"
        }
    }
  }
}
// データ(ドキュメント)を格納する
// 以下の場合、単語として"I", "have", "two", "dogs", "you", "threecats"がスペースに基づき単語とみなされる。

PUT sample/_doc/1
{
  "content": "I have two dogs. you have threecats."
}
// 検索は以下のように行う
// contentの値に探したいキーワード(単語)を書く

GET sample/_search
{
  "query":{
    "match":{
      "content":"dogs"
    }
  }
}

// 結果はI, have, two, dogs, you, threecatsでは検索したら検索結果がありとなる。
// 一方で"dog", "cats","three"では検索結果は無しとなる。

スペースで区切られた単語は検索できるが、スペースがないものに関しては検索できないと結論付けられる。 スペースで区切られた文字列をelasticsearchでは単語とみなす。そしてその単語は検索された時に備えて、単語と検索対象(ドキュメント)を保存される。

読書録

パンと牛乳は今すぐやめなさいって本を読んだ。 印象的なところをメモしておく。

小麦製品全部止めるのがベスト。 牛乳や乳製品に含まれるカゼインは消化しにくい。ー>帳と体に負担をかける。

GFCFって言葉があるらしい。グルテンフリー、カゼインフリー。小麦のグルテンと牛乳のカゼインがない食事。

小麦粉のグルテンの質、量によって強力粉、中力粉、薄力粉に分けられる。パンにはグルテンが最も多い強力粉が使われる。

酵素には種類があって、食べ物を消化吸収する時に必要な消化酵素と新陳代謝に必要な代謝酵素がある。これらの酵素は生涯で生成する量が決まっていると言われているらしい。消化酵素を作っているときには代謝酵素はつくられにくいため、代謝が低下し、低体温やこり、つかれが溜まりやすい。また、体に毒がたまり、その毒の症状を受ける。

生野菜やナマの果物には酵素が含まれており、消化しやすい。こういうものを食べると自分で酵素を作って消費する量が減るため自分の酵素を節約しながら消化できる。

elasticsearch/kibana

どんなものか?

素早く全文検索できるところが良いところか?

参考サイト

はじめての Elasticsearch - Qiita

pythonライブラリ

  • elasticsearch-dsl
  • elasticsearch

localでつかうには?

ローカルでは以下で実行する。

bin/elasticsearch 

elasticsearch_dslをつかう

from elasticsearch_dsl import Search
from elasticsearch import Elasticsearch


client = Elasticsearch()
s = Search(using=client, index="my_index")
for d in s.scan():
    print(d)

インデックスを作ってなくてもドキュメントを作るだけでインデックスを作成することができる

statusがyellow, greenってあるけどこれは何を示しているのか?クラスターの状態であるらしい。

ELASTICSEARCHを触ってみて分かったことを記録に留める

格納する文章を単語レベルで分割し、分割した単語を基準に検索できるようにするのがElasticsearchのコンセプトなのだろう。この検索方法を転置インデックスや転置索引と呼ぶようだ。このコンセプトを活かしたければ、文章を適切な単語に分割することが重要だと考えられる。

英文を使って単語をどう識別するか、またその検索結果を調べてみる

elasticsearchで検索される英単語と検索されない英単語について - diadia

日本語文章を使う場合

elasticsearchで日本語を検索できるようにする - diadia

htmlタグがついた文章を扱う場合について

htmlタグに邪魔されずに検索したい - diadia

既存のデータをコピーしたり新しいインデックスにデータをコピーしたい

既存のデータをコピーしたり新しいインデックスにデータをコピーしたい - diadia

matchとmatch_phraseクエリの検索結果の違いをメモ

matchとmatch_phraseクエリの検索結果の違いをメモ - diadia

aliasメモ

elasticsearch alias - diadia

vue Dialogを作ってみる

ポイント: CSSの要素を使うことでダイアログを実装する事ができるってことをしっておくこと。z-indexを使えば良いことさえわかればあとはボタンの制御の問題へ置き換わる。

参考にさせていただいた資料:

vue.js モーダルウィンドウ実装でコンポーネント理解 | アールエフェクト

# ダイアログが表示されるページ

<template>
  <div id='base' :class="{overlay: overlay}">

      <button @click='onClickDialog'>ダイアログを表示する</button>
      <TestDialog id='dialog' v-show='shouldShow'/>

  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import TestDialog from '@/components/TestDialog.vue';


@Component({
  components: { TestDialog },
})
export default class TestDialogUnder extends Vue {
      private overlay = false;
      private shouldShow = false;

      private onClickDialog(): void{
        console.warn('ふぁふぁっふぁ');
        this.shouldShow = !this.shouldShow;
        console.warn(this.shouldShow);
        if (!this.overlay){
          this.overlay = true;
        }else {this.overlay = false}
        
      }
}

</script>


<style>

.overlay{
  /*要素を重ねた時の順番*/
  z-index:1;

  /*画面全体を覆う設定*/
  position:fixed;
  top:0;
  left:0;
  width:100%;
  height:100%;
  background-color:rgba(0,0,0,0.5);

  /*画面の中央に要素を表示させる設定*/
  display: flex;
  align-items: center;
  justify-content: center;

}
#dialog{
  z-index:2;
  width:50%;
  padding: 1em;
  background:#fff;
}
</style>
// TestDialog
 
import { Component, Vue } from 'vue-property-decorator';

@Component({
  components: {},
})
export default class  TestDialog extends Vue {  

}
<template>
<div>
    <p>コレはダイアログです</p>
</div>
</template>


<script lang='ts'>
import TestDialog from '@/components/TestDialog';
export default TestDialog
</script>

動的コンポーネントを試してみる

動的 & 非同期コンポーネント — Vue.js

動的コンポーネントはisを使ってコンポーネントを切り替えて使う。

サンプルコード

# Home.vue

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png"><br />
    <button @click="onClickChangeComponent">コンポーネントを切り替える</button>
    <!--HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/-->
    <MyFirstComponent :myProp='propForMyFirstComponent'/>
    <MySecondComponent :myProp='propForMySecondComponent'/>
    <component :is='myComponent' :myProp="trabajo"></component> #コレ
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import MyFirstComponent from '@/components/MyFirstComponent.vue';
import MySecondComponent from '@/components/MySecondComponent.vue';
@Component({
  components: {
    HelloWorld,
    MyFirstComponent,
    MySecondComponent,
  },
})
export default class Home extends Vue {
      private trabajo = '';
      private propForMyFirstComponent = 'Home.vueからMyFirstComponentに渡すためのプロパティ';
      private propForMySecondComponent = 'Home.vueからMySecondComponentに渡すためのプロパティ';
      private myComponent = MyFirstComponent;

      private onClickChangeComponent(): void{
        if (this.myComponent === MyFirstComponent){
          this.trabajo = 'jajajaj';
          this.myComponent = MySecondComponent;
          return
        }
        this.trabajo = 'fafaffa';
        this.myComponent = MyFirstComponent;
      }
}
</script>

template タグに:isの属性値としてコンポーネントを渡す。 コンポーネントにはPropを使ってデータを渡している。

// MyFirstComponent.ts

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class MyFirstComponent extends Vue{
    // @Prop()
    private firstProperty = '最初のプロパティ';
    private secondProperty = '二番目のプロパティ';

    @Prop()
    private myProp: string;

    public mounted() {
        console.warn(this.myProp);
    }
}
# MyFirstComponent.vue

<template>
    <div style='background-color:yellow;'>
        <p>これは一番目のコンポーネントです。</p>
        {{ firstProperty }}<br/>
        {{ myProp }}
    </div>
</template>


<script lang='ts'>
import MyFirstComponent from '@/components/MyFirstComponent';
export default MyFirstComponent
</script>
// MySecondComponent

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class MySecondComponent extends Vue{
    // @Prop()
    private firstProperty = '最初のプロパティ';
    private secondProperty = '二番目のプロパティ';

    @Prop()
    private myProp: string;

    public mounted() {
        console.warn(this.myProp);
    }
}
# MySecondComponent.vue

<template>
    <div style='background-color: orange'>
        <p>これは二番目のコンポーネントです。</p>
        {{ firstProperty }}<br/>
        {{ myProp }}
    </div>
</template>


<script lang='ts'>
import MySecondComponent from '@/components/MySecondComponent';
export default MySecondComponent
</script>

f:id:torajirousan:20210228205419p:plain

f:id:torajirousan:20210228205412p:plain

propを使う

PROPを使う場面

propを使ってみたのでコードを残しておく。

propは親コンポーネントから子コンポーネントにデータを渡したい時に使われる。  親コンポーネント内にコンポーネントを記述している場合書かれているコンポーネントを子コンポーネントという。

# 親コンポーネント
<template>
<div>
<p>これは親コンポーネント</p>
<SComponent/> # これが子コンポーネント
</div>
</template>

propは、この.vueファイル内でSComponentにデータを渡すことを前提とする。

propを使う際のイメージ

コンポーネントでは子コンポーネントタグ内で変数を渡す。 子コンポーネントでは.ts内ではデータを受け取る変数名をプロパティとしてセットする。

サンプル

## 親コンポーネント

<template>
  <div class="home">
    <MyComponent />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import MyComponent from '@/components/MyComponent.vue'
@Component({
  components: {
    MyComponent,
  },
})
export default class Home extends Vue {
      private propForMyComponent = '親から渡されるプロパティ';
}
</script>
## 子コンポーネント MyComponent.vue
<template>
    <div>
        <p>これは子コンポーネントです。</p>
    </div>
</template>


<script lang='ts'>
import MyComponent from '@/components/MyComponent';
export default MyComponent
</script>
// MyComponent.ts

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class MyComponent extends Vue{}

コンポーネントではデータを子コンポーネントタグ内で渡す

## 親コンポーネント

<template>
  <div class="home">
    <MyComponent  :myProp='propForMyComponent'/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import MyComponent from '@/components/MyComponent.vue'
@Component({
  components: {
    MyComponent,
  },
})
export default class Home extends Vue {
      private propForMyComponent = '親から渡されるプロパティ';
}
</script>

コンポーネントでは受け取る変数の名前をプロパティとしてセットする

## 子コンポーネント MyComponent.vue
<template>
    <div>
        <p>これは子コンポーネントです。</p>
    </div>
</template>


<script lang='ts'>
import MyComponent from '@/components/MyComponent';
export default MyComponent
</script>
// MyComponent.ts

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class MyComponent extends Vue{
    @Prop()
    private myProp: string;

    public mounted() {
        console.warn(this.myProp);
    }
}

エラー:error Type string trivially inferred from a string literal, remove type annotation @typescript-eslint/no-inferrable-types

エラー内容

error  Type string trivially inferred from a string literal, remove type annotation  @typescript-eslint/no-inferrable-types

エラーが出たコード

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class MyComponent extends Vue{
    @Prop()
    private firstProperty: string = '最初のプロパティ';
    private secondProperty: string = '二番目のプロパティ';
}

エラーを消せたコード

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class MyComponent extends Vue{
    @Prop()
    private firstProperty = '最初のプロパティ';
    private secondProperty = '二番目のプロパティ';
}

こちらでエラーについて説明をみた。 【VSCodeのエラー】Type string trivially inferred from a string literal, remove type annotation (no-inferrable-types) | おっくソぶろぐ

要は型推論できるような変数宣言初期化は型を敢えて書かなくて良いってこと。

このlintを消したかっったらこちらを参照すると良い。 今実行しているnode をctrl cで止めて再実行(serve でもbuild)すると同じエラーは出なくなる。

angular - Tslint - type trivially inferred - Why is it bad practice to include the type here? - Stack Overflow

If you came here looking for an eslint solution because tslint is being deprecated, add this rule to your .eslintrc.js file:

module.exports = {
  ...m
  rules: {
    ...,
    "@typescript-eslint/no-inferrable-types": "off",
    ...
  },
};

npmのショートカットコマンドを作る

package.jsonにscriptsがある。ここに追加や修正することで好きなコマンドを作る(ショートカットを作ることができる)。

// package.json

{
  "name": "my-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
...
}

この状態だとnpm serveを実行すると vue-cli-serve serveが実行されることになる。 これをnpm start で実行したい場合は以下のように変更する。

// package.json

{
  "name": "my-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "start": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
...
}

typescript 型の判定方法

typescriptは静的型付け言語なので、型を意識さざるを得ない。
例えばユニオン型で2つの異なるクラスを定義した場合、そのインスタンスはクラスによって使えるプロパティが変わる。 ていうことで、型を判定する条件分岐と判定する方法が重要になるらしい。

判定方法: 型の判定方法は3つある。

  • typeof を使う方法
  • instanceofを使う方法
  • inを使う方法

型の判定方法は3つあるが、typeofはtypescriptで作られた型を判定するしかできない。   今回のケースではクラスの型判定を行いたい。このケースでは、typeof は使うことができない。
したがって instanceofまたはinを使うことになる。

const mojiretsu: string = 'wahahaha';
console.log(typeof mojiretsu === 'string');
// true が返る


class Book {
    title: string;
    price: number;
}

class Blog {
    title: string;
    content: string;
}

type ReadingObject = Book | Blog;
let readingObject = new Book();

console.log('price' in readingObject);
// trueが返る

console.log( readingObject instanceof Book);
// trueが返る