我是怎麼把筆記導入ElasticSearch的

儘管我最近開始用VNote作讀書或讀在線文檔的筆記,但更多的時候,我把經驗型知識都記錄在一個名爲my_note的Git倉庫中。這個倉庫中有許多.org文件:javascript

  1. TeX.org,記錄與LaTeX相關的問題和解決方法;
  2. asm.org,記錄的是與編寫彙編語言程序相關的問題和解決辦法;
  3. cl.org,記錄的是與編寫Common Lisp代碼相關的問題和解決辦法;

這些內容被我稱爲FAQ。儘管不一樣的文件記載着不一樣方面的內容,但它們的格式是一致的:java

  1. 每一個文件都以org-mode的語法書寫;
  2. 文件中只有一級條目,沒有嵌套;
  3. 每個條目的標題就是一個問題的表述,下方的文字則是這個問題的答案。

A picture is worth a thousand wordsgit

這些問題都比較常見(否則怎麼叫FAQ呢——也許上圖的不算常見吧),回過頭來查找的機率很高。顯然,在紛繁複雜的文字中憑肉眼尋找關鍵字是低效的,即便是祭出grep,用正則表達式這樣的大殺器來查找也不是特別稱手——由於並不知道怎樣的正則表達式能夠匹配到尋找的內容——也許多寫了關鍵詞,也許少寫了,也許順序不對。github

對於搜索這類非結構的文字資料來講,全文檢索是一個更好的選擇,所以,我是把這些內容丟進ElasticSearch裏再查找的。正則表達式

解析並導入到ElasticSearch

FAQ中的每個條目,都對應ElasticSearch中的一個文檔,它們都存儲在索引faq中。一個文檔有以下的字段:json

  1. answer,即問題的答案;
  2. path,文件絕對路徑,表示文檔來自於哪個文件中的條目;
  3. question,即問題的描述;
  4. questionLineNum,即問題存在於文件的第幾行。

解析這些文件的邏輯也很簡單:每當讀入行首爲星號的一行後(這一行即爲問題),便繼續讀入後續的每一行直到再次遇到行首爲星號的行爲止,這些後續讀入的行組成了這個問題的答案。有了問題和答案,即可以導入到ElasticSearch中。最終的腳本以下async

const request = require('co-request');

const fs = require('fs');

function parseFaqOrg(path) {
  const content = fs.readFileSync(path).toString('utf-8');
  const lines = content.split('\n');
  const qas = [];
  let answer = [];
  let lineNum = 0;
  let mode;
  let question;
  let questionLineNum;
  for (const line of lines) {
    lineNum += 1;
    if (line.startsWith('*')) {
      if (mode === 'answer') {
        // 在遇到星號的時候模式已經處於answer中,說明在此以前還有未處理的QA
        qas.push({
          answer: answer.join('\n'),
          path,
          question,
          questionLineNum
        });
        answer = [];
        question = null;
      }
      mode = 'question';
    } else {
      mode = 'answer';
    }
    if (mode === 'answer') {
      answer.push(line);
    } else {
      question = line;
      questionLineNum = lineNum;
    }
  }
  if (question) {
    qas.push({
      answer: answer.join('\n'),
      question
    });
  }
  // console.log(JSON.stringify(qas, null, 2));
  return qas;
}

async function dropFaq() {
  await request({
    method: 'delete',
    url: 'http://localhost:9200/faq'
  });
}

/**
 * 重建faq索引並寫入全量的筆記數據
 */
async function main() {
  console.log(new Date().toLocaleString());
  await dropFaq();
  const dir = '/Users/liutos/Documents/Projects/my_note/faq/';
  const basenames = fs.readdirSync(dir);
  for (const basename of basenames) {
    const path = dir + basename;
    const type = basename.match(/(.*)\.org/)[1];
    const qas = parseFaqOrg(path);
    for (const qa of qas) {
      await request({
        body: qa,
        json: true,
        method: 'post',
        url: 'http://localhost:9200/faq/_doc'
      });
    }
    console.log(`文件${path}處理完畢`);
  }
  console.log(new Date().toLocaleString());
}

main();

後記

若是想知道我是如何在Emacs中查詢這些FAQ的,能夠參見《在Emacs中搭建筆記查閱系統的嘗試》這篇文章。post

閱讀原文ui

相關文章
相關標籤/搜索