veer66

veer66

ผมจะเลือกจากข้อมูลตามหน้านี้ของ Wikipedia โดยที่เลือกสิ่งที่สนใจตามแบบผมเอง

Wikipedia's table

ข้อแรกผมต้องการแอปที่มีหลากหลาย platform ที่สุด

Android ได้ Linux ได้ Web ได้ iOS ได้ Windows ได้ macOS ได้ เพื่อที่ว่าจะได้มีคนถูกกีดกันออกไปน้อยที่สุด แต่ถ้าอยากเน้นกีดกันก็เลือก Messages ของ Apple เลย ใครไม่ชอบ Apple ไม่ใช้ Apple ก็ไม่ต้องมาคุย

มีเลือกแบบนี้พวก Whatsapp, Facebook Messenger, Line, Signal ก็ตกรอบไป

แอปที่ผ่านเข้ารอบมีตามนี้ Discord Gadu-Gadu ICQ Jitsi Mattermost Movim Element Skype Slack Telegram Trillian Wire

ข้อสองสองตัดพวกใช้เบอร์โทรออก

เพราะถ้าคิดว่าจะถูกคุกตาม นี่เบอร์โทรลงทะเบียนบัตรประชาชนไว้ น่าจะหาตัวได้

ดังนั้นก็จะเหลือ Discord Gadu-Gadu Jitsi Mattermost Movim Element Skype Slack Trillian Wire

ข้อสามเพื่อที่ว่าจะเชื่อถือได้โปรแกรมมันก็ควรจะ open source เวลาเข้ารหัสก็เปิดดูได้ว่าเข้าใจไหม ทำอย่างไร

ก็จะเหลือ Jitsi Mattermost Movim Element Wire

ข้อที่สี่ต้องเข้ารหัสจากผู้ส่งถึงผู้รับโดยตรงก็เหลือ

Element กับ Wire

ข้อที่ห้าต้องเข้ารหัสตลอดเวลาก็เหลือ

Wire ตัวเดียว

  • sudo apt-get install fonts-noto fonts-noto-extra fonts-noto-core fonts-noto-mono fonts-noto-ui-core fonts-noto-ui-extra fonts-noto-cjk fonts-noto-unhinted fonts-noto-hinted fonts-noto-cjk-extra fonts-noto-color-emoji
  • mkdir -p $HOME/.config/fontconfig
  • cat > $HOME/.config/fontconfig/fonts.conf

แปะอันนี้ใส่ไป

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match>
    <test compare="contains" name="lang">
      <string>th</string>
    </test>
    <edit mode="prepend" name="family">
      <string>Noto Sans Thai</string>
    </edit>
  </match>
  <alias>
    <family>serif</family>
    <prefer>
      <family>Noto Serif</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>monospace</family>
    <prefer>
      <family>Noto Mono</family>
    </prefer>    
  </alias>
</fontconfig>
  • กด control D

เสร็จ

บางทีเปลี่ยน font แล้วลง พวก Noto เรียนร้อยแล้วก็ยังไม่ได้อย่างใจ บางทีเป็นเพราะว่าระบบจะเลือก font ให้เราเอง ซึ่งไปแก้ได้ในไฟล์

ผมชึ้นหัวว่า MX Linux แต่จริง ๆ จะใช้ Void Linux, Ubuntu กระทั่งพวก NetBSD ก็น่าจะเหมือนกันหมด

$HOME/.config/fontconfig/fonts.conf

ซึ่งอันนี้ผมแก้ตาม https://jichu4n.com/posts/how-to-set-default-fonts-and-font-aliases-on-linux/ และ https://wiki.archlinux.org/index.php/Font_configuration/Examples

สมมุติว่าในกรณีที่ลง Noto แต่ไม่มี font ของ TLWG นะครับ ใส่ไปแบบนี้

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match>
    <test compare="contains" name="lang">
      <string>th</string>
    </test>
    <edit mode="prepend" name="family">
      <string>Noto Sans Thai</string>
    </edit>
  </match>
  <alias>
    <family>serif</family>
    <prefer>
      <family>Noto Serif</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>monospace</family>
    <prefer>
      <family>Noto Mono</family>
    </prefer>    
  </alias>
</fontconfig>

แต่ทำไปทำมาก็รู้สึกว่าไม่ค่อยโดยเท่าไหร่ ผมก็ลง Loma แล้วเปลี่ยน Noto Sans Thai เป็น Loma อยู่ดี

ชีวิตผมจะทำอะไรอีก ก็มีประมาณนี้ล่ะ

(defn iter>seq [it]
  []
  (when (.hasNext it)
    (let [p (.next it)]
      (lazy-seq (cons [(read-val* (.key p))
                       (read-val (.val p))]
                      (iter>seq it))))))

###########
# # # # # # # #
###########

ใน Python ตาม code ข้างล่าง append เป็น method ของ object a แต่พอมานับจำนวนสมาชิกใช้ len(a) ซึ่ง len เป็น built-in function แทน ถ้าอยากให้มีรูปแบบเดียวกันเปลี่ยนจาก len(a) ไปเป็น a.__len__() ก็ได้ แต่ก็จะมี ____ เกินมาอีก ใน Python เองก็คงจะมีหลักเกณฑ์ว่าเมื่อไหร่อะไรจะเป็น function อะไรจะเป็น method ซึ่งก็อาจจะต้องเรียนรู้หรือจำไว้

a = [1, 2, 3, 4, 5]
a.append(10)
print(len(a))

ใน JavaScript ก็มีรูปแบบที่ต่างกันคือ push เป็น method แต่ length เป็น property สังเกตได้จากว่าตอนเรียกไม่มีวงเล็บ ใน JavaScript ก็คงมีหลักการว่าเมื่อไหร่อะไรจะเป็น method หรือเป็น property ซึ่งก็ต้องเรียนรู้หรือจำไว้

a = [1, 2, 3, 4, 5]
a.push(10)
console.log(a.length)

ใน Ruby ทีนี้เหมือนกันแล้วคือ push ก็เป็น method และ length ก็เป็น method

a = [1, 2, 3, 4, 5]
a.push(10)
p(a.length)

แต่ก็ยังมีประเด็นต่อไปอีกว่าทำไมใน Ruby เขียนแบบนี้

10 + 20 + 30 + 40 + 50

ไม่เป็น method และเรียกเหมือนอย่างอื่นบ้างล่ะ

10.+(20).+(30).+(40).+(50)

(Emacs) Lisp จะต่างกับอันอื่นหน่อย ตรงที่ push มันไปต่อหน้าแทนที่จะต่อหลัง

(setq a '(1 2 3 4 5))
(push 10 a)
(print (length a))
(+ 10 20 30 40 50)

เขียน Lisp นี่ทุกอย่างก็จะเรียงเหมือนกันหมดเลย ได้แก่ (push ...) (length ...) (+ ...) ไม่เดี๋ยวต่อหน้า len(...) หรือเดี๋ยวต่อหลังเป็น a.length หรือว่าเดี๋ยวมีหรือไม่มีวงเล็บ เช่น a.length กับ a.length

แต่ปัญหาที่มันจะเจอคือหลายท่านรับไม่ได้ที่วงเล็บไปอยู่ข้างหน้าชื่อ function ถึงกับว่ามันวงเล็บย้ายที่แล้วเขียนไม่ได้เลย

แต่ Julia ก็เขียนแบบนี้ได้ append! ก็เป็น length ก็เป็น function ทั้งคู่ แถมตอนบวกก็เขียนแบบ function ได้ด้วย

a = [1,2,3,4,5]
append!(a,10)
print(length(a))
+(10,20,30,40,50) # จะเขียนแบบ 10 + 20 + 30 + 40 + 50 ก็ได้

แต่ก็อาจจะคิดได้ว่าทำเวลาสร้าง array ไม่เป็น function บ้าง ก็อาจจะเปลี่ยน [1,2,3,4,5] เป็น push!([],1,2,3,4,5) แทน ทีนี้ก็จะเหลือแต่ = แล้วที่ดูไม่เหมือนอย่างอื่น ซึ่งอันนี้ผมก็นึกวิธีแก้ให้มันเหมือนกันไม่ออก

from google.colab import drive
drive.mount('/content/drive')
import os
os.listdir('/content/drive/My Drive')

โพสต์นี้คนอ่านที่เป็นเป้าหมายหลักก็คือผมเอง

อย่างแรกเลยสมมุติว่า ElasticSearch ที่รันอยู่แล้ว

สร้าง dir ที่จะเก็บ snapshot และ chown ให้เป็นของ user เดียวกับที่ daemon ของ elasicsearch รันอยู่

แก้ elasticsearch.yml ที่ path.repo ให้ชี้ไปที่ dir ที่สร้างไว้ในข้อ 1 เช่น [“/bak/b1”] ไรงี้

backup/snapshot

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/bak/b1"
  }
}
'

restore

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty"

ตอนนี้ก็คือจะเสร็จแล้ว สิ่งที่ผมทำคือ rsync /bak/b1 ไปเครื่องอื่นแล้วลอง restore เพื่อไม่ให้น่าเบื่อเครื่องอื่นที่ว่าก็จะใช้ docker ด้วย

พอ rsync มาแล้ว ก็ chown -R 1000:1000 $(pwd)/bak/b1

สร้าง elasticsearch.yml

cluster.name: "docker-cluster"
network.host: 0.0.0.0
path.repo: ["/usr/share/elasticsearch/bak"]

รัน ElasticSearch ผ่าน Docker

docker run \
       --name=es6 \
       --net=host \
       -e "discovery.type=single-node" \
       -v $(pwd)/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
       -v $(pwd)/bak/b1:/usr/share/elasticsearch/bak \
       docker.elastic.co/elasticsearch/elasticsearch:6.8.4

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/bak"
  }
}
'

สั่ง restore

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/bak"
  }
}
'

เสร็จแล้ว

คำสั่งต่าง ๆ ก็มาจาก guide ครับ

ผมทำตามตัวอย่างที่ https://tantivy-search.github.io/examples/basic_search.html แต่โมพวก field แล้วก็แก้ให้เป็นสองไฟล์แบบที่อยากใช้งาน

Cargo.toml

[package]
name = "v_search"
version = "0.1.0"
authors = ["Vee Satayamas <vee.sa@protonmail.com>"]
edition = "2018"

[dependencies]
tantivy = "0.10.2"

[[bin]]
name = "make_index"
path = "src/make_index.rs"

[[bin]]
name = "search"
path = "src/search.rs"

make_index.rs

ส่วนที่ผมงงแรก ๆ เลยคือ STORED อ่านมาบอกว่าสามารถอ่านข้อมูลคืนมาด้วย id เข้าใจว่าถ้า field ไหนไม่ STORED คือทำ index อย่างเดียวจริง ๆ แต่ข้อมูลต้นฉบับหาย แต่ผมก็ยังไม่ลองว่าถ้าไม่ใส่มันจะไม่ได้เชียวหรือ ?

use tantivy::doc;
use tantivy::schema::*;
use tantivy::Index;

fn main() -> tantivy::Result<()> {
    let index_path = "idx"; 
    let mut schema_builder = Schema::builder();
    schema_builder.add_text_field("textunit", TEXT | STORED);
    let schema = schema_builder.build();
    let textunit = schema.get_field("textunit").unwrap();
    let index = Index::create_in_dir(&index_path, schema.clone())?;
    let mut index_writer = index.writer(50_000_000)?;

    index_writer.add_document(doc!(
        textunit => "A",
    ));

    index_writer.commit()?;
    Ok(())
}

search.rs

use tantivy::collector::TopDocs;
use tantivy::query::QueryParser;
use tantivy::schema::*;
use tantivy::Index;
use tantivy::ReloadPolicy;

fn main() -> tantivy::Result<()> {
    let index_path = "idx"; 
    let mut schema_builder = Schema::builder();
    schema_builder.add_text_field("textunit", TEXT | STORED);
    let schema = schema_builder.build();
    let textunit = schema.get_field("textunit").unwrap();
    let index = Index::open_in_dir(index_path)?;
    let reader = index
        .reader_builder()
        .reload_policy(ReloadPolicy::OnCommit)
        .try_into()?;
    let searcher = reader.searcher();

    let query_parser = QueryParser::for_index(&index, vec![textunit]);
    let query = query_parser.parse_query("A")?;
    let top_docs = searcher.search(&query, &TopDocs::with_limit(10))?;
    for (_score, doc_address) in top_docs {
        let retrieved_doc = searcher.doc(doc_address)?;
        println!("{}", schema.to_json(&retrieved_doc));
    }
    Ok(())
}

อันนี้ผมก็คือทำ index ไว้ใน directory ชื่อ idx แล้ว แล้วอีกไฟล์ก็มา search แค่นี้เลย

code แค่นี้เลย

fn cnt_links(s: usize, e: usize, tu: &Textunit, en_sent: &RTok) -> usize {
    tu.links.iter()
        .filter(|link| link.target > s && link.target < e)
        .filter(|link| link.source >= en_sent.s && link.source < en_sent.e)
        .count()
}

พอเขียน test แบบยาว ก็เจอจุดพัง

#[test]                                                                                                                                                
    fn cnt_links_simple() {
        let tu = Textunit {
            bi_text: BiText { source: String::from(""),
                              target: String::from(""),
            },
            bi_rtoks: BiRToks {
                source: vec![RTok {s:100, e:101, text: String::from(" ")},
                             RTok {s:102, e:103, text: String::from(" ")},],
                target: vec![RTok {s:300, e:301, text: String::from(" ")},
                             RTok {s:302, e:303, text: String::from(" ")},],
            },
            links: vec![Link {source: 0, target: 1},
                        Link {source: 1, target: 0}],

        };
        let en_sent = RTok {s: 302, e:303, text: String::from(" ")};
        assert_eq!(cnt_links(0, 1, &tu, &en_sent), 1);
    }

ระบบ type ช่วยอะไรไม่ค่อยได้เพราะมันเป็น integer หมด ยกเว้นจะใช้ type แบบที่มีหน่วยได้ แต่แบบนั้นก็ไม่รู้ว่าโปรแกรมจะยาวกว่านี้อีกเปล่า

รู้สึกว่าปัญหาของโปรแกรมนี้หลัก ๆ เลยคือ test มันยาวเกิน