dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

Elasticsearch で nested フィールドを検索

2024-02-24 21:21:58 | elasticsearch
Elasticsearch で nested フィールドに対する検索のメモ。
■インデックス
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "id": {"type": "keyword", "store": "true"},
      "tags": {
        "type": "nested",
        "properties": {
          "tag": {"type": "keyword", "store": "true"}
        }
      },
      "status": {"type": "integer", "store": "true"}
      }
    }
  }
}

■bulk でデータ登録
{"index": {"_id": "doc_1"}}
{"id": "doc_1", "tags": [{"tag": "abc"}, {"tag": "def"}], "status": 1}

{"index": {"_id": "doc_2"}}
{"id": "doc_1", "tags": [{"tag": "def"}, {"tag": "ghi"}], "status": 1}

{"index": {"_id": "doc_3"}}
{"id": "doc_1", "tags": [{"tag": "ghi"}, {"tag": "jkl"}], "status": 1}

■検索クエリ
{
  "query": {
    "bool": {
      "must": [
        {"term": {"status": 1}},
        {"nested": {
            "path": "tags",
          "query": {"term": {"tags.tag": "def"}}
        }}
      ]
    }
  }
}

■検索結果
{
  ...,
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 2.0296195,
    "hits" : [
      {
        "_index" : "test_nested_01",
        "_id" : "doc_1",
        "_score" : 2.0296195,
        "_source" : {
          "id" : "doc_1",
          "tags" : [
            {
              "tag" : "abc"
            },
            {
              "tag" : "def"
            }
          ],
          "status" : 1
        }
      },
      {
        "_index" : "test_nested_01",
        "_id" : "doc_2",
        "_score" : 2.0296195,
        "_source" : {
          "id" : "doc_1",
          "tags" : [
            {
              "tag" : "def"
            },
            {
              "tag" : "ghi"
            }
          ],
          "status" : 1
        }
      }
    ]
  }
}


python で画像をリサイズ

2024-02-24 13:36:44 | python
python で画像をリサイズする方法のメモ。
import sys
from PIL import Image

def main():
    in_file = sys.argv[1]
    out_file = sys.argv[2]
    rate = float(sys.argv[3])

    img = Image.open(in_file)
    out_width = int(img.width * rate)
    out_height = int(img.height * rate)

    out_img = img.resize((out_width, out_height))
    out_img.save(out_file)

    return 0

exit(main())


python で svg を png に変換

2024-02-24 13:21:43 | SVG
python で svg ファイルを png ファイルに変換する方法のメモ。

■ライブラリなどのインストール
pip install svglib
pip install reportlab

sudo yum install cairo cairo-devel
pip install rlpycairo

■プログラム
import sys
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM

def main():
    in_file = sys.argv[1]
    out_file = sys.argv[2]

    img = svg2rlg(in_file)
    renderPM.drawToFile(img, out_file, fmt='png')

    return 0

exit(main())


TypeScript で TTL つきキャッシュを実装

2024-02-23 15:02:11 | Node.js
TypeScript での TTL つきキャッシュの実装例です。
※2024/02/26 にバグを修正

オブジェクトの登録時刻がキャッシュ作成時に指定した TTL を過ぎると、キャッシュを参照した際に undefined を返却します。

キャッシュの実装で利用している list の実装は以下にあります。
Typescript での双方向連結リスト
■キャッシュの実装
/**
 *
 * Cache with TTL
 *
 */

import { Node, List } from '../list/lib/list';
import { SimpleCache } from 'SimpleCache';


export class TtlCache {
  private _max_size: number;
  private _list: List = new List();
  private _hash: any = {};
  private _ttl_msec: number;
  
  public constructor(max_size: number, ttl_msec: number) {
    if (max_size < 1) max_size = 1;
    this._max_size = max_size;
    this._ttl_msec = ttl_msec;
  }
  
  public get(key: string) {
    super.get(key);
    // obj = [key, obj, time];
    const now = (new Date()).getTime();
    const node = this._hash[key];
    if (node === undefined) return undefined;
    
    if (node.obj[2] + this._ttl_msec < now) {
      // timeout
      this._list.remove_node(node);
      delete this._hash[key];
      return undefined;
    }

    this._list.remove(node);
    this._list.push(node);
    return node.obj[1];
  }
  

  public set(key: string, obj: any) {
    // obj = [key, obj, time];
    // update obj if exists
    const now = (new Date()).getTime();
    let node = this._hash[key];
    if (node === undefined) {
      const node = new Node([key, obj, now]);
      this.push(node);
      
      if (this._list.length > this._max_size) {
	this._list.shift_node();
	delete this._hash[key];
      }
    }
    else {
      node.obj = [key, obj, now];
      this.remove_node(node);
      this.push_node(node);
    }
    return obj;
  }
}

■実行例
import { setTimeout } from 'timers/promises';
import { TtlCache } from './TtlCache';


async function test_set() {
  const cache = new TtlCache(8, 1000);
  
  const num = 10;
  for (let i = 0; i < num; i++) {
    const key = `key_${i}`;
    const value = `value_${i}`;
    cache.set(key, value);
    const time = (new Date()).getTime();
    console.log(`${time} ${key}: ${value}`);
  }

  console.log('');
  await setTimeout(500);

  for (let i = 0; i < num; i++) {
    await setTimeout(100);
    const key = `key_${i}`;
    const value = cache.get(key);
    const time = (new Date()).getTime();
    console.log(`${time} ${key}: ${value}`);
  }
  
  return 0;
}

async function main() {
  let errs = 0;
  errs += await test_set();
  
  return 0;
}

main();

■実行結果
1708956725261 key_0: value_0
1708956725262 key_1: value_1
1708956725262 key_2: value_2
1708956725262 key_3: value_3
1708956725262 key_4: value_4
1708956725262 key_5: value_5
1708956725262 key_6: value_6
1708956725262 key_7: value_7
1708956725262 key_8: value_8
1708956725262 key_9: value_9

1708956725870 key_0: undefined
1708956725973 key_1: undefined
1708956726076 key_2: value_2
1708956726179 key_3: value_3
1708956726280 key_4: undefined
1708956726384 key_5: undefined
1708956726492 key_6: undefined
1708956726595 key_7: undefined
1708956726699 key_8: undefined
1708956726806 key_9: undefined


TypeScript でシンプルなキャッシュの実装

2024-02-23 14:45:46 | Node.js
TypeScript で最大オブジェクト数までオブジェクトを格納するシンプルなキャッシュの実装例です。
※2024/02/25 バグを修正しました

キー:オブジェクト をリストで管理し、直近でアクセスされた キー:オブジェクト をリストの末尾に移動させます。
格納する キー:オブジェクト が最大数に達すると、リストの先頭の キー:オブジェクト を削除して、新しい キー:オブジェクト を登録します。
オブジェクトを取得する際、リストの末尾に該当の キー:オブジェクト を移動させ、アクセス頻度が高いキーがキャッシュに残りやすくしています。

list の実装は以下にあります。
Typescript での双方向連結リスト
■キャッシュ
/**
 *
 * Simple Cache
 *
 */

import { Node, List } from '../list/lib/list';


export class SimpleCache {
  private _size: number = 0;
  private _max_size: number;
  private _list: List = new List();
  private _hash: any = {};
  
  public constructor(max_size: number) {
    if (max_size < 1) max_size = 1;
    this._max_size = max_size;
  }
  
  public get(key: string) {
    const node = this._hash[key];
    if (node === undefined) return undefined;
    
    this._list.remove_node(node);
    this._list.push(node);
    return node.obj;
  }
  
  public set(key: string, obj: any) {
    let node = this._hash[key];
    if (node === undefined) {
      // update obj if exists
      node = new Node([key, obj]);
      this._hash[key] = node;
      this._list.push_node(node);
      this._size += 1;
    }
    else {
      // set (key, obj)
      this._list.remove_node(node);
      this._list.push_node(node);
    }
      
    // remove head if size is max
    if (this._size > this._max_size) {
      const [old_key, old_obj] = this._list.shift();
      delete this._hash[old_key];
      this._size -= 1;
    }
    
    return obj;
  }
}

■実行例
import { SimpleCache } from './SimpleCache';

function test_set() {
  const cache = new SimpleCache(3);
  const num = 10;
  
  console.log('set');
  for (let i = 0; i < num; i++) {
    const key = `key_${i}`;
    const value = `value_${i}`;
    cache.set(key, value);
    console.log(`${key}: ${value}`);
  }

  console.log('get');
  for (let i = 0; i < num; i++) {
    const key = `key_${i}`;
    const value = cache.get(key);
    console.log(`${key}: ${value}`);
  }

  return 0;
}


function main() {
  let errs = 0;
  errs += test_set();
  
  return 0;
}

main();

■実行結果
set
key_0: value_0
key_1: value_1
key_2: value_2
key_3: value_3
key_4: value_4
key_5: value_5
key_6: value_6
key_7: value_7
key_8: value_8
key_9: value_9
get
key_0: undefined
key_1: undefined
key_2: undefined
key_3: undefined
key_4: undefined
key_5: undefined
key_6: undefined
key_7: key_7,value_7
key_8: key_8,value_8
key_9: key_9,value_9


BigQuery で "{属性名}: {属性値}, ..." の属性値を取得するユーザ関数

2024-02-18 00:05:14 | BigQuery
BigQuery で "{属性名}: {属性値}, ..." の形式の文字列から指定の属性名の属性値を取得する関数のメモ。
以下のような文字列から指定の属性名の属性値を取得するユーザ関数を作成します。
price: positive, function: negative, quality: neutral

関数定義は以下の通り。
create or replace function
  dataset.get_attribute_value(text string, name string)
returns
  string
as (
    regexp_extract(
      text
      , concat('(?:^|,[ ]*)', name, ':[ ]*([^, ]+)')
    )
);

以下で属性値を取得する。
select
  dataset.get_attribute_value('price: neutral, quality: positive', 'price') as attr1
  , dataset.get_attribute_value('price: neutral, quality: positive', 'function') as attr2;

実行結果は以下の通り。
[{
  "attr1": "neutral",
  "attr2": null
}]


BigQuery の ML.GENERATE_TEXT() で生成AIを利用

2024-02-17 23:55:22 | BigQuery
BigQuery の ML.GENERATE_TEXT() で生成AIを利用する方法のメモ。

まず以下のテーブルを作成する。
create table dataset.test (
  id    integer,
  text  string,
);

insert into dataset.test (id, text) values (1, '今日はいい気分です。');
insert into dataset.test (id, text) values (2, '今日は普通の気分です。');
insert into dataset.test (id, text) values (3, '今日は気分が悪い。');


上記のテーブルの text フィールドに対して、生成AIで回答を生成する。
select
  *
from
  ml.generate_text(
    model `dataset.ai_model`
    , (select id, text AS prompt from dataset.test)
    , struct(true as flatten_json_output)
  )
order by
  id asc
;

実行結果は以下の通り。
[{
  "ml_generate_text_llm_result": "それは素晴らしいですね!...",
  "ml_generate_text_rai_result": null,
  "ml_generate_text_status": "",
  "id": 1,
  "prompt": "今日はいい気分です。"
}, {
  "ml_generate_text_llm_result": "今日は普通の気分とのことですね。...",
  "ml_generate_text_rai_result": null,
  "ml_generate_text_status": "",
  "id": 2,
  "prompt": "今日は普通の気分です。"
}, {
  "ml_generate_text_llm_result": "気分が悪いとのこと、お察しします。...",
  "ml_generate_text_rai_result": null,
  "ml_generate_text_status": "",
  "id": 3,
  "prompt": "今日は気分が悪い。"
}]