カテゴリー: テクノロジー

iOSでアプリを固定する方法

設定自体は割と簡単で下記の手順で行えます。

アクセシビリティ

手順1.アクセスガイドの設定を有効にする
設定>アクセシビリティ>アクセスガイドを有効にしてパスコードを設定する

手順2.アクセスガイドを起動する
固定したいアプリを起動してアクセスガイドの設定の下に書いてあった「ホームボタン」か「トップボタン」を3回押して何も選択せず開始

アプリの固定は上記の手順2で完了です。解除する場合は同じように3回押してパスコードを入力後終了すれば戻ります。

利用シーンとしては業務用アプリとして他のアプリを起動しないようにして貸し出す時やお子さんなどに対象のアプリ以外起動して欲しくない時などに使える機能かと思います。

元々は電子書籍をiPadで読んでる際に下のバーが気になって気になってしょうがなかったのですがアプリの機能として提供されているものかと思っていたらiOSの標準機能としてあると知り、それをどうにか消せないかと調べた結果知った機能です。起動手順や解除手順が少し面倒なのでホームボタン自体を消す設定を追加して欲しいところですが消す方法がないよりはマシではあります。

電子書籍アプリについては前まではKindle一択でしたがhontoのアプリを知ってからは漫画、小説はhonto一択です。

  • 漫画、小説がKindleと違ってちゃんとシリーズ化される(Kindleは漫画は一部おかしかったり小説はシリーズ化されない)
  • 新刊が出たらシリーズ中に新作表示やお知らせに続刊の表示がされる
  • マイリストには1巻登録すればシリーズ全てが入るのでお気に入りリストが作りやすい(Kindleは選択した巻だけで見づらい)
  • 起動した時はKindleと違ってお知らせではなく書籍一覧のライブラリの画面が表示される
  • 読み終わった際に次の巻があればそのまま続きで読める(Kindleは自分で次の巻を探す必要がある)
  • 再度読み直す際にhontoは最初から、続きからで選べる(Kindleは続きからのみで最初から読み直す際は手動で戻す必要がある)

数ヶ月使ってみて400冊ほど購入してKindleと比べた結果ですがたまにアプリが落ちる時があるのを除いて圧倒的にhontoが一押しです。


[Solr] Cross Collection Join

はじめに

Solr 8.6 以降ではコレクションをまたがった Join クエリを実行することができます。

Cross Collection Join

メインの検索対象を collection1、Join 対象を collection2 とします。Cross Collection Join は、collection2 に対する検索条件にヒットするデータの join key を持つ collection1 側のデータを取得する一種のフィルタクエリとして機能します。注意が必要なのは collection2 側のデータのフィールドは取得できないということです。collection2 は別のサーバで動く Solr 上にあっても構いません。

動作確認

Cross Collection Join の動作を確認するため、2つの Solr プロセスを起動します。

bin/solr start -cloud -p 8983
bin/solr start -cloud -p 7574

8983 の方は solrconfig.xml に以下を記載します。

<queryParser name="join" class="org.apache.solr.search.JoinQParserPlugin">
    <arr name="allowSolrUrls">
      <str>http://localhost:7574/solr</str>
    </arr>
</queryParser>

8983 の collection1 には以下のようなデータが入っています。

$ curl -s 'http://localhost:8983/solr/collection1/select?q.op=OR&q=*:*&rows=5'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*",
      "q.op":"OR",
      "rows":"5",
      "_":"1625974694981"}},
  "response":{"numFound":9238,"start":0,"numFoundExact":true,"docs":[
      {
        "id":"10",
        "type_str":"官公庁",
        "name_str":"軽自動車検査協会大阪主管事務所",
        "_version_":1704977365000519680},
      {
        "id":"11",
        "type_str":"官公庁",
        "name_str":"大阪陸運支局なにわ自動車検査登録事務所",
        "_version_":1704977365035122688},
      {
        "id":"12",
        "type_str":"官公庁",
        "name_str":"住吉税務署",
        "_version_":1704977365036171264},
      {
        "id":"13",
        "type_str":"官公庁",
        "name_str":"玉出年金事務所",
        "_version_":1704977365036171265},
      {
        "id":"14",
        "type_str":"官公庁",
        "name_str":"大阪南労働基準監督署",
        "_version_":1704977365037219840}]
  }}

7574 の collection2 には以下のようなデータが入っています。

$ curl -s 'http://localhost:7574/solr/collection2/select?q.op=OR&q=*:*&rows=5'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*",
      "q.op":"OR",
      "rows":"5",
      "_":"1625993148833"}},
  "response":{"numFound":9238,"start":0,"numFoundExact":true,"docs":[
      {
        "id":"10",
        "area_str":"住之江区",
        "address_str":"住之江区南港東3-4-62",
        "address_p":"34.6164938333333,135.438210722222",
        "_version_":1704977387371888640},
      {
        "id":"11",
        "area_str":"住之江区",
        "address_str":"住之江区南港東3-1-14",
        "address_p":"34.6190439722222,135.442191833333",
        "_version_":1704977387419074560},
      {
        "id":"12",
        "area_str":"住吉区",
        "address_str":"住吉区住吉2丁目17番37号",
        "address_p":"34.6109641111111,135.491388722222",
        "_version_":1704977387420123136},
      {
        "id":"13",
        "area_str":"住之江区",
        "address_str":"住之江区北加賀屋2-3-6",
        "address_p":"34.6231918888888,135.477992138888",
        "_version_":1704977387420123137},
      {
        "id":"14",
        "area_str":"西成区",
        "address_str":"西成区玉出中2丁目13番27号",
        "address_p":"34.6237640555555,135.4924",
        "_version_":1704977387421171712}]
  }}

中央区にある「文化・観光」の施設を検索してみます。施設タイプは collection1、エリア情報は collection2 にあるので Cross Collection Join の出番です。

type_str:文化・観光 {!join method="crossCollection" fromIndex="collection2" from="id" to="id" solrUrl="http://localhost:7574/solr" v="area_str:中央区"}

solrUrl で指定した Solr のコレクション collection2 で area_str:中央区 を検索して collection1 で type_str:文化・観光 を検索した結果と JOIN する、join key はそれぞれの id フィールド、というクエリです。

$ curl -s 'http://localhost:8983/solr/collection1/select?q=type_str%3A%E6%96%87%E5%8C%96%E3%83%BB%E8%A6%B3%E5%85%89%20%7B!join%20method%3D%22crossCollection%22%20fromIndex%3D%22collection2%22%20from%3D%22id%22%20to%3D%22id%22%20solrUrl%3D%22http%3A%2F%2Flocalhost%3A7574%2Fsolr%22%20v%3D%22area_str%3A%E4%B8%AD%E5%A4%AE%E5%8C%BA%22%7D'
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"type_str:文化・観光 {!join method=\"crossCollection\" fromIndex=\"collection2\" from=\"id\" to=\"id\" solrUrl=\"http://localhost:7574/solr\" v=\"area_str:中央区\"}",
      "q.op":"AND",
      "debugQuery":"false",
      "_":"1625975185887"}},
  "response":{"numFound":56,"start":0,"numFoundExact":true,"docs":[
      {
        "id":"3101",
        "type_str":"文化・観光",
        "name_str":"大阪市立島之内図書館",
        "_version_":1704977365352841232},
      {
        "id":"3136",
        "type_str":"文化・観光",
        "name_str":"上方浮世絵館",
        "_version_":1704977365354938370},
      {
        "id":"3137",
        "type_str":"文化・観光",
        "name_str":"府立上方演芸資料館(ワッハ上方)",
        "_version_":1704977365354938371},
      {
        "id":"3138",
        "type_str":"文化・観光",
        "name_str":"国立文楽劇場",
        "_version_":1704977365354938372},
      {
        "id":"3140",
        "type_str":"文化・観光",
        "name_str":"湯木美術館",
        "_version_":1704977365354938374},
      {
        "id":"3143",
        "type_str":"文化・観光",
        "name_str":"適塾",
        "_version_":1704977365354938377},
      {
        "id":"3144",
        "type_str":"文化・観光",
        "name_str":"府立現代美術センター",
        "_version_":1704977365354938378},
      {
        "id":"3145",
        "type_str":"文化・観光",
        "name_str":"大阪歴史博物館",
        "_version_":1704977365354938379},
      {
        "id":"3146",
        "type_str":"文化・観光",
        "name_str":"ピースおおさか:大阪国際平和センター",
        "_version_":1704977365354938380},
      {
        "id":"3147",
        "type_str":"文化・観光",
        "name_str":"大阪城天守閣",
        "_version_":1704977365354938381}]
  }}

[Solr]コレクションのインクリメンタルバックアップ

はじめに

Solr 8.9から SolrCloud におけるコレクションのバックアップの形式が変更されてインクリメンタルバックアップ対応となりました。

バックアップ

インクリメンタルバックアップ対応に伴い、コレクションAPIのBACKUPアクションのパラメータに以下の2つが追加されました。

incremental

true (デフォルト)ならインクリメンタルバックアップ、false なら従来と同様のスナップショットによるバックアップとなります。スナップショットによるバックアップは近いうちに廃止となる予定です。

maxNumBackupPoints

インクリメンタルバックアップではバックアップポイントという形で直前のバックアップとの差分が保存されていきます。
最大で何個をバックアップポイントを保存するのかを maxNumBackupPoints で指定します。

サンプル

サンプルとして大阪の施設情報を利用します。

以下のようなデータを1件ずつ追加して、その都度バックアップします。

os_1.json
[{"id":"10","type":"官公庁","area":"住之江区","name":"軽自動車検査協会大阪主管事務所","address":"住之江区南港東3-4-62","address_p":"34.6164938333333,135.438210722222"}]

os_2.json
[{"id":"11","type":"官公庁","area":"住之江区","name":"大阪陸運支局なにわ自動車検査登録事務所","address":"住之江区南港東3-1-14","address_p":"34.6190439722222,135.442191833333"}]

os_3.json
[{"id":"12","type":"官公庁","area":"住吉区","name":"住吉税務署","address":"住吉区住吉2丁目17番37号","address_p":"34.6109641111111,135.491388722222"}]

os_4.json
[{"id":"13","type":"官公庁","area":"住之江区","name":"玉出年金事務所","address":"住之江区北加賀屋2-3-6","address_p":"34.6231918888888,135.477992138888"}]
$ curl 'http://localhost:8983/solr/backup_test/update?commit=true&indent=true' --data-binary @os_1.json -H 'Content-Type: application/json'

$ curl 'http://localhost:8983/solr/admin/collections?omitHeader=true&action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup&incremental=true&maxNumBackupPoints=3'
{
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":2},
      "response":[
        "startTime","2021-06-28T14:00:11.906389Z",
        "indexFileCount",18,
        "uploadedIndexFileCount",18,
        "indexSizeMB",0.005,
        "uploadedIndexFileMB",0.005,
        "shard","shard1",
        "endTime","2021-06-28T14:00:11.908343Z",
        "shardBackupId","md_shard1_1"]}},
  "response":[
    "collection","backup_test",
    "numShards",1,
    "backupId",1,
    "indexVersion","8.9.0",
    "startTime","2021-06-28T14:00:11.903010Z",
    "indexSizeMB",0.005]}

$ curl 'http://localhost:8983/solr/backup_test/update?commit=true&indent=true' --data-binary @os_2.json -H 'Content-Type: application/json'

$ curl 'http://localhost:8983/solr/admin/collections?omitHeader=true&action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup&incremental=true&maxNumBackupPoints=3'
{
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":2},
      "response":[
        "startTime","2021-06-28T14:00:36.142084Z",
        "indexFileCount",35,
        "uploadedIndexFileCount",18,
        "indexSizeMB",0.01,
        "uploadedIndexFileMB",0.005,
        "shard","shard1",
        "endTime","2021-06-28T14:00:36.144073Z",
        "shardBackupId","md_shard1_2"]}},
  "response":[
    "collection","backup_test",
    "numShards",1,
    "backupId",2,
    "indexVersion","8.9.0",
    "startTime","2021-06-28T14:00:36.137745Z",
    "indexSizeMB",0.01]}

$ curl 'http://localhost:8983/solr/backup_test/update?commit=true&indent=true' --data-binary @os_3.json -H 'Content-Type: application/json'

$ curl 'http://localhost:8983/solr/admin/collections?omitHeader=true&action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup&incremental=true&maxNumBackupPoints=3'
{
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":4},
      "response":[
        "startTime","2021-06-28T14:01:38.399993Z",
        "indexFileCount",52,
        "uploadedIndexFileCount",18,
        "indexSizeMB",0.015,
        "uploadedIndexFileMB",0.005,
        "shard","shard1",
        "endTime","2021-06-28T14:01:38.404263Z",
        "shardBackupId","md_shard1_3"]}},
  "response":[
    "collection","backup_test",
    "numShards",1,
    "backupId",3,
    "indexVersion","8.9.0",
    "startTime","2021-06-28T14:01:38.398621Z",
    "indexSizeMB",0.015],
  "deleted":[[
      "startTime","2021-06-28T13:56:09.437719Z",
      "backupId",0,
      "size",5223,
      "numFiles",18]],
  "collection":"backup_test"}

$ curl 'http://localhost:8983/solr/backup_test/update?commit=true&indent=true' --data-binary @os_4.json -H 'Content-Type: application/json'

$ curl 'http://localhost:8983/solr/admin/collections?omitHeader=true&action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup&incremental=true&maxNumBackupPoints=3'
{
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":3},
      "response":[
        "startTime","2021-06-28T14:02:05.875017Z",
        "indexFileCount",69,
        "uploadedIndexFileCount",18,
        "indexSizeMB",0.019,
        "uploadedIndexFileMB",0.005,
        "shard","shard1",
        "endTime","2021-06-28T14:02:05.878413Z",
        "shardBackupId","md_shard1_4"]}},
  "response":[
    "collection","backup_test",
    "numShards",1,
    "backupId",4,
    "indexVersion","8.9.0",
    "startTime","2021-06-28T14:02:05.873449Z",
    "indexSizeMB",0.019],
  "deleted":[[
      "startTime","2021-06-28T14:00:11.903010Z",
      "backupId",1,
      "size",5223,
      "numFiles",18]],
  "collection":"backup_test"}

各バックアップの応答に backupId が記載されています。リストアの際に backupId を指定することで特定のバックアップポイントに戻すことができます。

4回目のバックアップの応答に deleted という項目があります。maxNumBackupPoints=3 を指定しているので、4回目のバックアップでは最初のバックアップが削除されてバックアップポイントが3個に保たれていることが分かります。

ここでインデックスの状態を確認します。ドキュメントを1件ずつ4回投入したので、全件検索すると4件ヒットします。

$ curl -s 'http://localhost:8983/solr/backup_test/select?omitHeader=true&q.op=OR&q=*%3A*&rows=0'
{
  "response":{"numFound":4,"start":0,"numFoundExact":true,"docs":[]
  }}

リストア

LISTBACKUP アクションで現在利用可能なバックアップポイントを確認できます。

$ curl 'http://localhost:8983/solr/admin/collections?omitHeader=true&action=LISTBACKUP&name=backup1&location=/tmp/solr_backup'
{
  "collection":"backup_test",
  "backups":[{
      "indexFileCount":0,
      "indexSizeMB":0.0,
      "shardBackupIds":{"shard1":"md_shard1_2.json"},
      "collection.configName":"backup_test",
      "backupId":2,
      "collectionAlias":"backup_test",
      "startTime":"2021-06-28T14:00:36.137745Z",
      "indexVersion":"8.9.0"},
    {
      "indexFileCount":0,
      "indexSizeMB":0.0,
      "shardBackupIds":{"shard1":"md_shard1_3.json"},
      "collection.configName":"backup_test",
      "backupId":3,
      "collectionAlias":"backup_test",
      "startTime":"2021-06-28T14:01:38.398621Z",
      "indexVersion":"8.9.0"},
    {
      "indexFileCount":0,
      "indexSizeMB":0.0,
      "shardBackupIds":{"shard1":"md_shard1_4.json"},
      "collection.configName":"backup_test",
      "backupId":4,
      "collectionAlias":"backup_test",
      "startTime":"2021-06-28T14:02:05.873449Z",
      "indexVersion":"8.9.0"}]}

ドキュメント2件のときのバックアップ(backupId=2)に戻します。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=backup_test&location=/tmp/solr_backup&backupId=2'
{
  "responseHeader":{
    "status":0,
    "QTime":599}}
tfukui@deskmini:~/dev/splout_blog/sample_data$ curl -s 'http://localhost:8983/solr/backup_test/select?omitHeader=true&q.op=OR&q=*%3A*&rows=0'
{
  "response":{"numFound":2,"start":0,"numFoundExact":true,"docs":[]
  }}

ドキュメント3件のときのバックアップ(backupId=3)に戻します。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=backup_test&location=/tmp/solr_backup&backupId=3'
{
  "responseHeader":{
    "status":0,
    "QTime":624}}
tfukui@deskmini:~/dev/splout_blog/sample_data$ curl -s 'http://localhost:8983/solr/backup_test/select?omitHeader=true&q.op=OR&q=*%3A*&rows=0'
{
  "response":{"numFound":3,"start":0,"numFoundExact":true,"docs":[]
  }}

既存のコレクションに対するリストアができるようになったことと併せて、コレクションのバックアップ機能がより使いやすくなりました。


[Solr]既存のコレクションに対してバックアップをリストアできるようになりました

はじめに

Solr 8.8 までは、SolrCloud で取得したバックアップをリストアするときには新しいコレクションを指定しなければならないという制限がありました。したがって、クライアントに公開するコレクション名をエイリアスで運用して、バックアップのリストア後にエイリアスを切り替えるという工夫が必要でした。

Solr 8.9 からは既存のコレクションに対してリストアできるようになりました。

バックアップ

curl 'http://localhost:8983/solr/admin/collections?action=BACKUP&name=backup1&collection=backup_test&location=/tmp/solr_backup'

backup_test というコレクションのバックアップを /tmp/solr_backup 以下に作成します。リストア時に参照するときの名前は backup1 です。

リストア(8.8 までの場合)

既存のコレクション名 backup_test を指定するとパラメータエラーとなります。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=backup_test&location=/tmp/solr_backup'
{
  "responseHeader":{
    "status":400,
    "QTime":0},
  "error":{
    "metadata":[
      "error-class","org.apache.solr.common.SolrException",
      "root-error-class","org.apache.solr.common.SolrException"],
    "msg":"Collection 'backup_test' exists, no action taken.",
    "code":400}}

新しいコレクション名 new_backup_test を指定すると、そのコレクションが作られてからリストアされます。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=new_backup_test&location=/tmp/solr_backup'
{
  "responseHeader":{
    "status":0,
    "QTime":612},
  "success":{
    "127.0.1.1:8983_solr":{
      "responseHeader":{
        "status":0,
        "QTime":238},
      "core":"new_backup_test_shard1_replica_n1"}}}

リストア(8.9の場合)

既存のコレクション名 backup_test を指定してリストアできます。

試しに、バックアップ後にインデックスの内容を全部消去します。

$ curl -s 'http://localhost:8983/solr/backup_test/select?omitHeader=true&q.op=OR&q=*%3A*&rows=0'
{
  "response":{"numFound":9238,"start":0,"numFoundExact":true,"docs":[]
  }}

$ curl -s 'http://localhost:8983/solr/backup_test/update?stream.body=*:*&wt=json&commit=true'
{
  "responseHeader":{
    "rf":1,
    "status":0,
    "QTime":2}}

$ curl -s 'http://localhost:8983/solr/backup_test/select?omitHeader=true&q.op=OR&q=*%3A*&rows=0'
{
  "response":{"numFound":0,"start":0,"numFoundExact":true,"docs":[]
  }}

その後、既存のコレクション backup_test を対象にしてリストアします。8.8までとは違ってエラーになりません。

$ curl 'http://localhost:8983/solr/admin/collections?action=RESTORE&name=backup1&collection=backup_test&location=/tmp/solr_backup'
{
  "responseHeader":{
    "status":0,
    "QTime":977}}

全件検索したときの件数が元に戻りました。

$ curl -s 'http://localhost:8983/solr/backup_test/select?omitHeader=true&q.op=OR&q=*%3A*&rows=0'
{
  "response":{"numFound":9238,"start":0,"numFoundExact":true,"docs":[]
  }}

既存のコレクションに対するリストアの仕組み

SOLR-1587 によると、使用中である可能性もある既存のコレクションに対するリストアを実現するために、Solr 8.1 で追加された Read-Only モードが使われているそうです。

  1. 指定されたコレクションを Read-Only モードにセットする
  2. コレクションの各シャードにバックアップをリストアする
  3. コレクションの Read-Only モードを解除する

ChromeでUA-CHへの移行が再開

最近、Chrome の DevTools上に、
navigator.userAgent に関する警告のようなものが表示されるようになりました。

Google Developers のブログによると Chrome のバージョンアップが行われ、
以前、COVID-19 の影響により停止していた UA-CH への移行を再開し、
userAgent の取得に関するサポートを終了するようです。

Google Developers Japan: Chrome の User-Agent 文字列削減に関する最新情報
https://developers-jp.googleblog.com/2021/06/chrome-user-agent.html

UA-CH がどういったものか分かっていなかったので実際に試してみました。

サーバーにヘッダーの設定を追加します。

add_header Accept-CH "Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile";

Chrome からの一度目のアクセスでは、
SEC-CH-UA、SEC-CH-UA-MOBILE のみがブラウザから送信されました。

Chromeから送信されたヘッダー1

二度目移行のアクセスで、残りの Sec-CH-UA ヘッダも送信されるようになりました。

Chromeから送信されたヘッダー2

因みに Edge でも同様でした。

これらのヘッダが送信されるのは https 限定です。
http では送信されません。

ブログには Critical-CH のロールウトも始まったと記述がありましたが、
こちらはどういったものなのかよく分かりませんでした。

必須のクライアントヒントを送信するよう要求できるそうですが、
レスポンスヘッダーに追加しただけでは記述されているような動作にはなりませんでした。

まだ動作しないか、設定方法が全く違うのかもしれません。

しばらく情報を追っていきたいと思います。