TypeORM + MongoDB においてクエリ結果を生で取得する

2019/12/09 13:32:222019/12/09 20:23:06

追記: 最初に断っておくと、主題の解説というよりはバグの解説になってしまった。取得方法だけ見たい場合は 取る までスクロールされたい。

最近は TypeORM + MongoDB という実験的バリバリの構成で開発していることがある。当然 TypeORM の MongoDriver のサポートは非常に実験的かつコミュニティサポートということがあり、バグはめちゃめちゃ見つかるのだが、手元でコンパイルが成功しないライブラリであることや(おま環?)うまく PR を出せたとしても他 PR の様子を見るになっかなかマージされないというのがあり、泣き寝入りするか手元で解決するかになる。
今回、定義としてはカラムに存在する object や array の値が返り値から綺麗さっぱり消滅している問題(typeorm@0.2.21)に対処するため、一部それらのデータが必要的な部分に対して RDB でいう生クエリ実行のような手段を用いて解決を試みたので、それの概要を記する。

今回の問題
今回の問題はこうである。
user.ts
type Computer = Object 
 
@Entity("users") 
@Index(["user_id"], { unique: true }) 
export class User { 
    @ObjectIdColumn() 
    _id: ObjectID 
 
    @Column() 
    user_id: string 
 
    @Column() 
    name: string 
 
    @Column(type => Card) 
    favorite_computer: Computer 
}
こういう感じで、ユーザーのデータに対して好きなパソコンのデータというのがあるとする。
Computer はまあ Object であり、名前やメーカーやその他情報がはいっている感じである。
これを取得すると、
findOneUserResult.ts
User { 
  _id: 5dedd3980a55fdc51114a3ed, 
  user_id: '39333d52-8174-4654-bc69-0ebb24600a45', 
  name: 'おれ' 
}
こうなる。これ今更ながら再現性確認したほうが良い気がしてきたが、わざわざデータを作って試す気がしない…。いや流石にやります 再現性確認してきます なかったらここで記事終わります
再現性ありました 続きを書きます

こういう風に、そのデータに付随している Object / Array がエンティティ定義に存在するにも関わらず取得できない。
これは非常に困るので、できるだけ手を抜いて直接データを取得するアプローチを試みたい。

取る
皆さん御存知の通り、MongoDriver は他の RDB のドライバとは内部実装がまったくもって全然異なっており、query どころか queryBuilder が完全に無効化されている。
  • これに関しては実装を共通化しましょうみたいな流れの Issue があったはずで、マイルストーンとしては存在している覚えがあったが探しても見つからなかった、気のせいかもしれない。
実際の Repository->Manager はデータを cursor を使って取得しているので、そこを叩く。
  • convertFindManyOptionsOrConditionsToMongodbQuery とかいうのが FindOptions を Query に変換していて、これにならって叩いてやる。
  • 後から思ったが、cursor は内部的に MongoQueryRunner を用いていたはずで、これは多分 DB 接続済みの Mongo インスタンスがそのまま降ってきてる気がするので 、そこを直接叩いても良いかもしれない。
findByCursor.ts
const result: User[] = await repository.createCursor({ user_id: "39333d52-8174-4654-bc69-0ebb24600a45" }).toArray()
こんな感じになる。
  • manager.createCursor(Producer, {query}) でも同値が取れる。
  • repository.createEntityCursor({query}) だと find と同じような欠損が発生するので、そこらへんの処理にバグが隠れている気がする。
  • というか find 系統の関数においては createEntityCursor が用いられている
  • めっちゃ怪しいな…
  • ただ、さっきから {query} としている部分には where の中身しか渡せてない
  • { where: {}, take: 1 } とかにすると結果が無になります
  • 多分そのとおりのデータを探そうとする
  • 当然ながら返り値に型はつかないので付けておくこと

まとめ
とりあえず createCursor 系統の関数で生データが取れるというお話でした。
typeorm 自体 0.2.21 と 1.0.0 出るのいつだよという感じで Breaking changes し放題なので、普通にプロダクションユースは不可能ではないかと思った。今作っているのはただの趣味なので使い続けますが…。

余談
現時点での @next である 0.3.0-alpha.24 、なにか find 系の関数全てに Breaking changes があって、入れた時点でまっかっかになっちゃったのもあるんですが、Entity 定義関連のバグとして、Index の expireAfterSeconds の受け渡しが壊れていて、無指定で undefined になるはずが null が渡っちゃうのでシンクロナイズが失敗して落ちるというのがあった。


著者の画像

ci7lus

@ci7lus

Caramelize - Made withCaramelizeand / Privacy