0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Pi × Fess で全文検索環境構築(3)Fess 構成

0
Last updated at Posted at 2026-09-26

はじめに

NAS内のファイル数が数十万規模になると、ファイル名検索では目的のデータを見つけにくくなり、ディレクトリー構造の複雑化で検索効率も低下します。
このような環境では全文検索が有効です。
本シリーズでは、NAS内のファイルを高速かつ容易に検索するため、オープンソースの 全文検索サーバー Fess を用いて検索サーバーを構築する手順を紹介します。
検索サーバーのホストとしてRaspberry Pi 5を採用し、低消費電力で常時稼働可能な検索ノードの設置を目標としました。

本記事は全3回のうち 第3回 Fess 構成 です。

シリーズ一覧

前提

  • Ollamaが使用可能であること
  • Fess、OpenSearch、Nginxが構成済みであること

Fess構成

管理者パスワードの変更

Webブラウザーから https://<FESS>/admin にアクセスし、ユーザー名 admin、パスワード admin でログインします。
image.png

パスワードを更新します。
image.png

戻る で前の画面に戻ります。
image.png

全般の設定

管理画面から システム > 全般 に移動します。

Raspberry Pi 5のCPUコア数 4 にあわせてクロール時の総スレッド数を 4 にします。そのため、同時クローラー数を 1、クロール設定(後述)内のスレッド数を 4 に設定します。

カテゴリー 項目 設定値
システム サムネイルの表示 有効
クローラー 同時クローラー設定 1

プラグインの設定

システム > プラグインに移動して インストール をクリックします。
image.png

fess-llm-ollama-15.8.0 を選択して インストール をクリックします。
image.png

管理画面からログアウトします。

Fessを再起動します。

$ sudo systemctl restart fess

クロール設定の作成

クロール設定

Webブラウザーから https://<FESS>/admin にアクセスし、ユーザー admin でログインします。

クローラー > ファイルシステムに移動して 新規作成 をクリックします。
image.png

クロールに必要な値を入力して設定を保存します。

設定項目の説明は以下にあります。
ファイルクロール (ファイルサーバー検索)
設定項目のうち、スレッド数を 4 に変更するとクロール時のスレッド数が 4 になります。

認証設定

NASへのアクセスにユーザー名、パスワードが必要な場合ファイル認証設定を追加します。
クローラー > ファイル認証に移動して 新規作成 をクリックします。
image.png

認証に必要な情報を入力して設定を保存します。

設定項目の説明は以下にあります。
ファイル認証

クロールの実行

クロールを手動開始して動作を確認します。

システム > スケジューラ で Default Crawler を開きます。
image.png

今すぐ開始 をクリックします。
image.png

サムネイル生成のための追加設定

クロール対象を smb:// で指定の場合、サムネイル作成に失敗します。これは generate-thumbnail スクリプトが file:// で始まるファイルのサムネイル作成を前提としていることによります。
また、unoconvコマンドを前提の generate-thumbnail コマンドが、unoconv を使用できないRaspberry Piでは動作しない問題があります。

smb:// で始まるファイルのサムネイル作成可能化

generate-thumbnail スクリプトに file:// でファイルのパスを指定可能にするため、クロール対象のNASフォルダーをマウントします。

マウント・ポイントを作成します。

$ sudo mkdir -p /mnt/nas/<SHARED_FOLDER>

NAS接続ユーザー名とパスワードを設定ファイルに記述します。

$ sudo nano /etc/nas-credentials
nas-credentials
username=<USERNAME>
password=<PASSWORD>

設定ファイルの権限を変更します。

$ sudo chown root:root /etc/nas-credentials
$ sudo chmod 600 /etc/nas-credentials

Raspberry Pi起動時にNASを自動マウントするように設定を追加します。

$ sudo nano /etc/fstab

fstab への追記例は以下のとおりです。

fstab
<NAS_SHARED_FOLDER_PATH>    /mnt/nas/<SHARED_FOLDER>    cifs credentials=/etc/nas-credentials,iocharset=utf8,uid=fess,gid=fess,ro,file_mode=0555,dir_mode=0555,noauto,x-systemd.automount,x-systemd.mount-timeout=10 0 0

マウント設定を反映します。

$ sudo systemctl daemon-reload
$ sudo systemctl restart remote-fs.target
$ sudo mount -a

generate-thuubnail の変更

Fess提供の generate-thumbnail に以下の変更を追加します。

  • サムネイル作成対象のファイル名が smb:// で始まる場合、 file:// に変換
  • unoconv 使用の処理を soffice または libreoffice に変更

変更前にバックアップを作成し、generate-thumbnail を編集します。

$ sudo cp -p /usr/share/fess/bin/generate-thumbnail /usr/share/fess/bin/generate-thumbnail.default
$ sudo nano /usr/share/fess/bin/generate-thumbnail
変更後の /usr/share/fess/bin/generate-thumbnail 全量を表示するにはクリック 9行目、10行目の XXXXXXXX は環境にあわせて変更します。
generate-thumbnail
#!/bin/bash

cmd_type=$1
url=$2
output_file=$3
mimetype=${4:-}
image_size=100x100

if [[ "$url" =~ ^smb://XXXXXXXX ]]; then
    target_file=$(echo "$url" | sed -E 's|smb://XXXXXXXX[^/]+/|/mnt/nas/|')
    target_file=$(python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))" "$target_file")
else
    target_file=$(echo "$url" | sed -e "s#^file:/*#/#g")
fi

check_command() {
  cmd=$1
  cmd_path=$(command -v "${cmd}")
  if [[ ! -e "${cmd_path}" ]] ; then
    echo "${cmd} does not exist."
    exit 1
  fi
}

get_imagemagick_cmd() {
  if command -v magick >/dev/null 2>&1; then
    echo "magick"
  elif command -v convert >/dev/null 2>&1; then
    echo "convert"
  else
    echo ""
  fi
}

if [[ x"$HOME" = "x/root" ]] || [[ x"$HOME" = "x/var/root" ]] ; then
  HOME=/var/lib/fess
fi

if [[ x"${cmd_type}" = "xmsoffice" ]] ; then
  im_cmd=$(get_imagemagick_cmd)
  if [[ -z "${im_cmd}" ]] ; then
    echo "ImageMagick (convert or magick) does not exist."
    exit 1
  fi
  check_command pdftoppm

  if command -v soffice >/dev/null 2>&1; then
    lo_cmd="soffice"
  elif command -v libreoffice >/dev/null 2>&1; then
    lo_cmd="libreoffice"
  elif command -v unoconv >/dev/null 2>&1; then
    lo_cmd="unoconv"
  else
    echo "Neither LibreOffice nor unoconv exists."
    exit 1
  fi

  tmp_dir="/tmp/fess_msoffice_$$"
  mkdir -p "${tmp_dir}"
  tmp_pdf_file="${tmp_dir}/converted.pdf"

  if [[ "${lo_cmd}" = "unoconv" ]]; then
    unoconv -e PageRange=1-1 -o "${tmp_pdf_file}" -f pdf "${target_file}"
  else
    "${lo_cmd}" --headless --convert-to pdf --outdir "${tmp_dir}" "${target_file}" >/dev/null 2>&1
    generated_pdf=$(ls "${tmp_dir}"/*.pdf 2>/dev/null | head -n 1)
    if [[ -n "${generated_pdf}" ]]; then
      mv "${generated_pdf}" "${tmp_pdf_file}"
    fi
  fi

  if [[ ! -f "${tmp_pdf_file}" ]] ; then
    rm -rf "${tmp_dir}"
    echo "Office to PDF conversion failed."
    exit 1
  fi

  tmp_png_prefix="${tmp_dir}/thumb"
  pdftoppm -png -singlefile -r 150 "${tmp_pdf_file}" "${tmp_png_prefix}"
  tmp_png_file="${tmp_png_prefix}.png"

  if [[ ! -f "${tmp_png_file}" ]] ; then
    rm -rf "${tmp_dir}"
    echo "pdftoppm does not work."
    exit 1
  fi

  ${im_cmd} "${tmp_png_file}" -thumbnail ${image_size} "${output_file}"
  rm -rf "${tmp_dir}"

elif [[ x"${cmd_type}" = "xpdf" ]] ; then
  im_cmd=$(get_imagemagick_cmd)
  if [[ -z "${im_cmd}" ]] ; then
    echo "ImageMagick (convert or magick) does not exist."
    exit 1
  fi
  check_command pdftoppm

  tmp_png_prefix="/tmp/fess_thumb_$$"
  pdftoppm -png -singlefile -r 150 "${target_file}" "${tmp_png_prefix}"
  tmp_png_file="${tmp_png_prefix}.png"

  if [[ ! -f "${tmp_png_file}" ]] ; then
    echo "pdftoppm does not work."
    exit 1
  fi

  ${im_cmd} "${tmp_png_file}" -thumbnail ${image_size} "${output_file}"
  rm -f "${tmp_png_prefix}"*png

elif [[ x"${cmd_type}" = "xps" ]] ; then
  im_cmd=$(get_imagemagick_cmd)
  if [[ -z "${im_cmd}" ]] ; then
    echo "ImageMagick (convert or magick) does not exist."
    exit 1
  fi
  check_command pdftoppm
  check_command ps2pdf

  tmp_pdf_file=/tmp/thumbnail.pdf.$$
  ps2pdf "${target_file}" ${tmp_pdf_file}
  if [[ ! -f ${tmp_pdf_file} ]] ; then
    echo "ps2pdf does not work."
    exit 1
  fi

  tmp_png_prefix=/tmp/thumbnail.png.$$
  pdftoppm -png -singlefile ${tmp_pdf_file} ${tmp_png_prefix}
  tmp_png_file="${tmp_png_prefix}.png"
  rm -f ${tmp_pdf_file}

  if [[ ! -f ${tmp_png_file} ]] ; then
    echo "pdftoppm does not work."
    exit 1
  fi

  ${im_cmd} "${tmp_png_file}" -thumbnail ${image_size} "${output_file}"
  rm -f ${tmp_png_prefix}*png

elif [[ x"${cmd_type}" = "xsvg" ]] ; then
  check_command rsvg-convert
  rsvg-convert -w 100 -h 100 --keep-aspect-ratio "${target_file}" -o "${output_file}"

elif [[ x"${cmd_type}" = "ximage" ]] ; then
  im_cmd=$(get_imagemagick_cmd)
  if [[ -z "${im_cmd}" ]] ; then
    echo "ImageMagick (convert or magick) does not exist."
    exit 1
  fi

  format_hint=""
  case "${mimetype}" in
    "image/gif")  format_hint="gif:" ;;
    "image/tiff") format_hint="tiff:" ;;
    "image/png")  format_hint="png:" ;;
    "image/jpeg") format_hint="jpeg:" ;;
    "image/bmp"|"image/x-windows-bmp"|"image/x-ms-bmp") format_hint="bmp:" ;;
    "image/vnd.adobe.photoshop"|"image/photoshop"|"application/x-photoshop"|"application/photoshop") format_hint="psd:" ;;
  esac

  ${im_cmd} "${format_hint}${target_file}" -thumbnail ${image_size} "${output_file}"

elif [[ x"${cmd_type}" = "x" ]] ; then
  echo "No filetype."
  exit 1
else
  echo "Unsupported type: ${cmd_type}"
  exit 1
fi

if [[ ! -f ${output_file} ]] ; then
  echo "Thumbnail is not created."
  exit 1
fi

検索結果

通常の検索、AI検索結果の例を以下に示します。

  • 通常の検索
    キーワードにヒットしたファイルがサムネイルやファイル内容の一部をあわせて結果表示されます。
    image.png

  • AI検索
    NAS内ファイルの情報をもとにキーワードに対する回答と関連するファイルが表示されます。
    image.png

結論

NAS内のファイルに対する索引作成により、検索にかかる時間は従来のファイル名検索と比べて大幅に短縮されました。
また、全文検索によりファイル内容まで検索可能になり、目的の情報へ迅速に到達できるようになりました。
さらにLLMと組み合わせたAI検索は自然言語での検索が可能となり情報検索の質が向上しました。
Raspberry Piを使用した低消費電力の検索サーバーは常時稼働に適しており、NAS内の情報資産を効率的に活用できる環境が整いました。

一方でNAS全体の差分検出には長時間を要しており、現在は全量クロールがボトルネックとなっています。差分ファイルのみの効率よくクロールできる仕組みづくりが今後の課題です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?