mongoose#queryでsortのする

調べてたら引っかかったし、ブコメでも同じようにハマっている人がいるようなので回答を。

Word.find().sort([['created','ascending']]).each(function(doc){
  console.log(doc);
});

ちゃんと指定しているはずなんだけどなぁ。。この辺の原因分かる方は教えてくれると助かります。

http://mongoosejs.com/docs/api.html#query_Query-sort

この書き方だと引数が違いますし、Model.find()で返答されるものはMogoose#Queryです。なので、eachメソッドも存在しません。

ドキュメントは以下。

// sort by "field" ascending and "test" descending
query.sort({ field: 'asc', test: -1 });

// equivalent
query.sort('field -test');

http://mongoosejs.com/docs/api.html#query_Query-sort

なので、この形で記載するなら以下のようになります。

// 連想配列
Word.find().sort({'created','ascending'}).exec(function(err, doc){
  console.log(doc);
});
// 文字列
Word.find().sort('created').exec(function(err, doc){
  console.log(doc);
});

バージョンが違うのかもしれませんけど、3.8.0だと上記のようにすれば動くと思います。

と、書いてみたものの伝わるのかなー。

APIのテストにはSuperTestが便利

node.js + expressでのテスト環境を探していて見つかったんだけど、これは便利

概要

色々なhttpリクエスト投げて、結果をMochaで表示できる。

リンク先

https://github.com/visionmedia/supertest

http://visionmedia.github.io/mocha/

導入

両方共グローバルにしてもいいかも。SuperTestはグローバルにする必要もないかと思ってしてない。

npm install supertest npm install -g mocha

使い方

以下のテストコードを走らせるとhttp://localhost:3000につないでrequest.get()などで接続したホストに対してリクエストを投げて、レスポンスをテストすることができる。

/**
 * テスト雛形
 * hostにつないで、各メソッド事のテストを書く
 * @author sato_shinichiro
 */ 
var request = require('supertest');
var host = 'http://localhost:3000';

describe('テスト種別1', function () {

	before(function() {
		console.log('テスト前に動く');
	});

	after(function() {
		console.log('テスト後に動く');
	});

	it('テストする内容(ステータスエラー)', function (done) {
		var status = 300;
		var res = {
			meta: {
				code: 200,
				message: "ok"
			},
			data: null
		};
		request(host)
			.get('/test')
			.expect(status, res, function(err) {
				console.log('テスト1終了');
				done(err);
			});
	});
	it('テストする内容(レスポンス内容エラー)', function (done) {
		var status = 200;
		var res = {
			meta: {
				code: 300,
				message: "ok"
			},
			data: null
		};
		request(host)
			.get('/test')
			.expect(status, res, function(err) {
				console.log('テスト2終了');
				done(err);
			});
	});
});

describe('テスト種別2', function () {
	it('テストする内容(成功)', function (done) {
		var status = 200;
		var res = {
			meta: {
				code: 200,
				message: "ok"
			},
			data: null
		};
		request(host)
			.get('/test')
			.expect(status, res, function(err) {
				console.log('テスト3終了');
				done(err);
			});
	});
});

テストを走らせるには以下のコマンド。./testにテスト用のjsがいる。mochaはフォルダを指定すると配下のjsファイルを実行してくれる。

mocha test

テスト結果はこんな感じ。

 テスト前に動く
テスト1終了
․テスト2終了
․テスト後に動く
テスト3終了
․

  ✖ 2 of 3 tests failed:

  1) テスト種別1 テストする内容(ステータスエラー):
     Error: expected 300 "Multiple Choices", got 200 "OK"
      stacktrace...

  2) テスト種別1 テストする内容(レスポンス内容エラー):
      
      actual expected
      
      1 | {
      2 |   "meta": {
      3 |     "code": 300200,
      4 |     "message": "ok"
      5 |   },
      6 |   "data": null
      7 | }

      stacktrace...

stacktraceって書いてる部分はパスが書いてあるから削った。本来はちゃんとstacktraceが出る。

もし、サーバを立てずにexpressだけで動かしたかったら以下の様なコードを書いてexportしたappをhostの代わりに指定してあげれば動く

var express = require('express');
var app = express.createServer();
exports.module = app;

まとめ

最初Express用のテストフレームワークかと思ったけど、一般的なサーバのAPIならこれで全て叩いてテストできるんじゃなかろうか。SuperTestは便利。

mongooseで配列を連想配列でネストした時につく_idを消したい

悩んだので覚書

ユーザが書いたコメントを参照として引っ張ってきて欲しくて、それと同時に記載した時間も取りたいっていう場合を想定。

単純にスキーマを書くとこうなる。

var userSchema = {
	name: String,
	comments : [{
		comment: {type: Number, ref: 'comment'},
		updated: {type: Date, default: Date.now}
	}]
};

そうするとなぜか、commentsの要素たちに_idが勝手に入ってしまう。

非常に_idが邪魔

var userSchema = {
	name: String,
	comments : [Schema.Types.Mixed]
};

こう書くとと、commentsに_idは入らないのでmongooseが勝手に挿入してるっぽい。

調べるとどうも連想配列を新しいスキーマとして認識してるから_idが勝手に入るよう。

なので、スキーマとして_idを消すような処理を入れる

var userSchema = {
	name: String,
	comments : [new Schema({
		comment: {type: Schema.Types.ObjectId, ref: 'comment'},
		updated: {type: Date, default: Date.now}
	}, {_id: false})]
};

こうすると、_idが消える。めでたしめでたし。