minimongoでバルクインサート用のjsonファイルをインポートしてLIKE検索までやってみた。
minimongo, Node.js
今回の記事のサンプルコードはGitHubにアップロードしてあります。→ovrmrw/minimongo_sample1
前置き
Webアプリ開発してるとDBを扱いたくなりますよね。きっとそうに違いない。
でもいちいちDBをインストールするのもめんどうですよね。きっとみんなそう思ってる。
で、
を読んで、何もインストールしなくてもサンプルコードでDBを動かせるならちょっといいかもと思ってminomongoを試してみました。
README通りに試してみると、おお、確かに動く!DBっぽいものが!
こうなるとテストデータを入手してそこそこのデータ量で試してみたくなりますね。そこで↓なんかいいんじゃないかと。
今回は上記リポジトリにあるnames_part_1000.json.gz
を使ってみます。
ちなみにWindows環境では7zipで解凍できました。
ところがこのファイル、正規のJSONフォーマットではありません。
{"a":"xxx","b":"yyy","z":"zzz"} {"a":"xxx","b":"yyy","z":"zzz"} ...
みたいな感じで、配列にもなっていないし、オブジェクトの区切りがカンマじゃなくて改行なんですね。
前にもバルクインサート用のデータで同じような形式を見たことがあるのですが、もしかしてこれバルクインサートの標準形?
とりあえずこのままではminimongoでインサートできないので、こういう正規のフォーマットに変換してあげないといけません。
[ {"a":"xxx","b":"yyy","z":"zzz"}, {"a":"xxx","b":"yyy","z":"zzz"} ]
そのサンプルコードがこちら↓
var splitter = '\n'; var lines = fs.readFileSync('./names_part_1000.json', 'utf-8').toString().split(splitter); lines = _.filter(lines, function (line) { return line.indexOf('{') > -1 && line.indexOf('}') > -1 }); var json = "[" + splitter + lines.join(',' + splitter) + splitter + "]";
ファイルを読み込んで、改行コードでsplitして、空白行があれば除去して、配列の形に組み直す。
やってみたら意外と簡単でした。
実際にやってみよう
必要なパッケージをnpm installします
npm install minimongo fs lodash --save
バルクインサートするファイルを用意します
jsteemann/BulkInsertBenchmark
のdatasetsフォルダにnames_part_1000.json.gz
があるので、これを7zip等で解凍してnames_part_1000.json
を取り出します。
JSファイルを書きます。
// bulkinsert.js var splitter = '\n'; var minimongo = require("minimongo"); var fs = require('fs'); var _ = require('lodash'); var LocalDb = minimongo.MemoryDb; var db = new LocalDb(); db.addCollection("names"); var lines = fs.readFileSync('./names_part_1000.json', 'utf-8').toString().split(splitter); lines = _.filter(lines, function (line) { return line.indexOf('{') > -1 && line.indexOf('}') > -1 }); var json = "[" + splitter + lines.join(',' + splitter) + splitter + "]"; var obj = JSON.parse(json); db.names.upsert(obj, function () { db.names.find({ 'name.first': 'Johnson' }, {}) .fetch(function (res) { _.forEach(res, function (person) { console.log(person.name); }); }); }); // ついでに変換後のjsonを保存。 fs.writeFileSync('./names_valid.json', JSON.stringify(obj, null, 2));
実行します
node bulkinsert.js
結果はこうなりました↓
{ first: 'Johnson', last: 'Diventura' } { first: 'Johnson', last: 'Wallick' }
ちなみにdb.names.find()
は検索条件{ 'name.first': 'Johnson' }
に一致するオブジェクトを配列で取得する関数ですが、
一件だけ取得するならdb.names.findOne()
という関数があります。
find().fetch()
のfetch
はthen
みたいなものだと思います。(多分)
結論としては…かなりイイね!
追記: LIKE検索
SQLでいうところのLIKE検索をしたいときは、{ 'name.first': 'Johnson' }
のところを{ 'name.first': /john/i }
のようにします。正規表現ですかね。
サンプルコードを一部書き換えてみます。
db.names.upsert(obj, function () { //db.names.find({ 'name.first': 'Johnson' }, {}) db.names.find({ 'name.first': new RegExp('john', 'i') }, {}) .fetch(function (res) { _.forEach(res, function (person) { console.log(person.name); }); }); });
RegExp()
なら文字列から正規表現を作れます。(さっき知った)
結果はこうなりました↓
{ first: 'Johnny', last: 'Kaster' } { first: 'Johnie', last: 'Bugler' } { first: 'Johnnie', last: 'Domine' } { first: 'Johnson', last: 'Diventura' } { first: 'Johnson', last: 'Wallick' }
/john/i
は、文字列john
(大文字小文字を問わない)が含まれるものを検索するという意味になります。
個人的にはLIKE検索ができないDBはDBじゃないのでこれでしばらくいじり倒したいと思います。
以上です、ありがとうございました。
Angular2がalpha.50になってインストールとかRxJSとか色々めんどくさい(beta.0になりました)
Angular2, npm3, RxJS, TypeScript, System.js
【beta.0対応版】
Angular2はalpha.47まではそこまでBreaking Changesが無かったような気がするのですが、ここ最近のアップデートはガンガンBreakingしています。
angular2-polyfills.jsの読み込みが必要になった
alpha.54からはangular2.dev.js
の前にポリフィルの読み込みが必要になりました。
<script src="../node_modules/angular2/bundles/angular2-polyfills.min.js"></script> <script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
これが足りないといくらブラウザをリロードしても真っ白な画面のままです。気を付けましょう。
npm3だとAngular2のインストールがめんどくさい
2.0.0-alpha.50からAngular2のインストール時に依存パッケージが自動インストールされなくなりました。(npm3だけらしい)
なのでこのように依存パッケージを先にインストールするようにnpm installを書きます。
npm i es6-promise@^3.0.2 es6-shim@^0.33.3 --save npm i reflect-metadata@0.1.2 rxjs@5.0.0-beta.0 zone.js@0.5.10 --save --save-exact npm i angular2 --save
RxJSでイベントハンドラを書こうとするとめんどくさい
RxJS公式GitHubの説明通りにts
ファイルの中でこのように書くと、
// hoge.ts import Rx from 'rxjs/Rx'
Rx のところが赤い波線エラーになるかと思います。alpha.53まではちょこちょこいじってなんとかなっていたのですが…
いよいよalpha.54から、この↑書き方をするとAngular2では挙動がおかしくなりました。なのでこう書きます。
// hoge.ts import {Observable} from 'rxjs/Observable' import 'rxjs/add/observable/fromEvent' import 'rxjs/add/observable/timer' import 'rxjs/add/operator/map' import 'rxjs/add/operator/filter' import 'rxjs/add/operator/debounce' import 'rxjs/add/operator/toPromise'
・・・こんな感じで使用するオペレーターを全て個別にimportします。
ぼくだってほんとうはこんなことしたくなかった。
オペレーターに関しては最初に読み込むJSファイル(例えばapp.js
とか)でまとめてimportしておけばルーティングで他のページに遷移しても有効みたいです。それだけが救いですね。
あとHTMLファイルで'Rx.js'を読み込んでおく必要があります。
<script src="../node_modules/rxjs/bundles/Rx.js"></script>
サンプルコード↓(beta.0で動作確認済み)
import {Component} from 'angular2/core' import _ from 'lodash' //import {Observable} from 'angular2/angular2' ← 実行時にオペレーターが見つからないエラー連発 //import {Observable} from 'angular2/core' ← 実行時にオペレーターが見つからないエラー連発 //import {Observable} from '@reactivex/rxjs' ← RxJSのバージョンアップによりフォルダ構成が変わった import {Observable} from 'rxjs/Observable' import 'rxjs/add/observable/fromEvent' import 'rxjs/add/observable/timer' // 必要なら import 'rxjs/add/operator/map' import 'rxjs/add/operator/filter' import 'rxjs/add/operator/debounce' // 必要なら import 'rxjs/add/operator/toPromise' // 必要なら @Component({ selector: 'my-app', template: ` <!-- 省略 --> ` }) export class Foo { initEventObservables(): void { Observable.fromEvent<MouseEvent>(document, 'click') .map(event => event.target.textContent) .filter(text => _.trim(text).length > 0) .subscribe(text => { alert('You clicked ' + text); }); } }