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
どんなものか?
素早く全文検索できるところが良いところか?
参考サイト
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タグがついた文章を扱う場合について
既存のデータをコピーしたり新しいインデックスにデータをコピーしたい
既存のデータをコピーしたり新しいインデックスにデータをコピーしたい - diadia
matchとmatch_phraseクエリの検索結果の違いをメモ
matchとmatch_phraseクエリの検索結果の違いをメモ - diadia
aliasメモ
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>
動的コンポーネントを試してみる
動的コンポーネントは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>
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)すると同じエラーは出なくなる。
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が返る
セッターゲッターについて
まずアクセス修飾子について
アクセス修飾子はクラスを基準としてアクセスできるかどうかを定めるものと考えると良い。
class Person{ public name: string; constructor(name: string){ this.name = name; }; }
let mario = Person("mario"); mario.name = 'maria'; console.log(mario.name); // maria
クラス外でもnameプロパティにアクセスできるのがpublicで、これをクラス外からアクセスできないようにするにはprivateを使う。
class Person{ private name: string; constructor(name: string){ this.name = name; }; }
let mario = Person("mario"); console.log(mario.name); // エラー クラス外からプロパティ値を参照できない mario.name = 'maria'; // エラー クラス外からプロパティを変えられない
privateを使うことで疎結合できるメリットが有るってことらしい。ここはおいておく。 疎結合できることをメリットとすると、クラス外からアクセスする事ができない。
じゃあ特別に参照できるようにするわけで、その時にゲッターを使う。
class Person{ private name: string; constructor(name: string){ this.name = name; }; get name(){ return this.name }; }
let mario = Person("mario"); console.log(mario.name); // mario mario.name = 'maria'; // エラー クラス外からプロパティを変えられない
プロパティの値を変えたいって思うならセッターを使うことになる。ここで疑問。セッターを使えるようにしたら、それはpublicと同じではないか、と。
セッターを使う意義について考えてみたけど、例えばクラス外から参照はできないけどプロパティ属性を変える事ができる場合につかったり、クラス外から属性値をセットする際に不適切値だったらエラーを出すってことしか考えられない。このへんはなにかわかったら書き加えたいところだ。
class Person{ private _name: string; constructor(name: string){ this._name = name; }; get name(){ return this._name }; set name(value:string){ if (!value || value === '') { throw new Error('値が不適切です'); } this._name = value; } }
let mario = Person("mario"); console.log(mario.name); // mario mario.name = 'maria';
あとgetだけでsetがない場合はreadonlyに自動的になるみたい。
typescriptコンパイラの設定について
udemyの超TypeScript入門 完全パック(2021)の内容をメモしている。とてもわかり易いのでおすすめ。
コンパイラの設定方法
- watchモードを使って、保存時に自動的にTSからJSにコンパイルする方法
- tsc --initコマンドでtsconfig.jsonを作り、すべてのファイルを一気にコンパイルする方法
- include,exclude,filesを使ってコンパイルするファイルを選ぶ方法
- targetを指定して、特定のバージョンのjavascriptに変換する方法
- libを指定して、Typescriptが用意している型の定義を追加する
- allowJs, checkJs, jsx, declaration, declarationMapの設定方法
- SourceMapを使用して、ブラウザでTypescriptを操作する方法
- outDirとrootDir,, removeDir, removeComments, noEmit, downlevelLeterationの使い方
- noEmitIbErorオプションを使って、エラーが出た時にコンパイルしない方法
- noImplicitAnyやstrictnullChecksなどのstrictの設定方法
- きれいなコードを書くための設定方法
コンパイラの設定のドキュメント
TypeScript: Documentation - tsc CLI Options
memo
1. watch モードを使って、保存時に自動的に TS から JS にコンパイルする方法
watch モードを使わないでコンパイルする方法は、以下のコマンド使ってコンパイルする。
// tscコマンドを使う tsc index.js // tsc {コンパイルしたいファイル}
watch モードを使う
// -w または --watchオプションを使う tsc -w tsc --watch // 止める際は ctl + C
2. tsc --init コマンドで tsconfig.json を作り、すべてのファイルを一気にコンパイルする方法
tsc コマンドでは、コンパイル対象のファイルを指定する必要があるが、それを予め設定することでファイル指定する手間を省く事ができる。その手間を省くには、tsconfig.json を作っておく必要がある。tsconfig.json は手動で作成するのではなく、以下のコマンドで作成する。
// tsconfig.jsonファイルを作成する tsc --init
djangoのインテリセンスを有効化する
参考: https://kic-yuuki.hatenablog.com/entry/vscode-python-autocomplete
この方の記事が分かりやすかった。 djangoをvscodeで書く際にインテリセンスを働かせる。
visual studio code のルートディレクトリ(プロジェクト)に.vscode/settings.jsonを作成する。そしてsettings.json内にインテリセンスを働かせたいライブラリのパスを追加するとインテリセンスがきくようになる。
.vscode/settings.jsonを作成
mkdir project cd project mkdir .vscode vi settings.json
settings.jsonで書く内容
{ "python.autoComplete.extraPaths": [ "ライブラリが格納されるパス", ] }
ライブラリが格納されるパスは以下で確認することができる。
>> python >>import django >>django.path ['/Users/chiaki/.local/share/virtualenvs/django_test_sample-9pz0Y4Xg/lib/python3.9/site-packages/django'] # ライブラリが格納されるパスはsite-packagesまでなので # '/Users/chiaki/.local/share/virtualenvs/django_test_sample-9pz0Y4Xg/lib/python3.9/site-packages/'
これをsettings.jsonに書くと以下のようになる。
{ "python.autoComplete.extraPaths": [ "/Users/chiaki/.local/share/virtualenvs/django_test_sample-9pz0Y4Xg/lib/python3.9/site-packages/" ] }