目次

recently_commented_onが正しく機能しない

PostgreSQLをバックエンドにして、「最近のコメント」を作成しようとすると、おかしなエントリが表示されてしまいます。(MySQLの場合やSQLiteの場合はわかりません。ファイルをバックエンドにしている場合は大丈夫です。)

これは、DISTINCTの使い方が正しくないためです。大まかに言うと、MTはコメントの一覧を作成して、これを作成された日時で降順に(descend)ソートします。そしてその結果から、コメントされているエントリの一覧を作成します。このとき、ひとつのエントリに複数のコメントがあれば、当然、エントリが重複して抽出されますから、これを解消するために、DISTINCTを指定しています。ところが、DISTINCTは重複行は取り除くものの、その結果得られるエントリ一覧の順序は保証されないのです。従って、折角ソートして作った一覧を、DISTINCTが壊してしまっているのです。

とりあえず、以下のパッチをあてることで、この問題は解決します。但し、この修正を行なった後に、バックエンドをPostgreSQL以外に変更すると、おかしなことになりますので、その場合は lib/MT/Template/ContextHandlers.pm(MT-3.1xの場合はlib/MT/Template/Context.pm)をオリジナルのものに戻すようにしてください。なお、パッチはMT-3.2以降と、MT-3.1xとで微妙に違います。

余談ですが、「最近のコメント」を作るために、sort_order=“ascend”を指定するようにかいてある記事を見かけますが、このオプションは recently_commented_onには無効ですし、更にいうと、昇順(ascend)にしたら、それは最近のではなく最古のになってしまいますので、あらゆる意味で間違いです

パッチ(MT-3.2)

パッチファイル

*** MT-3.2-b1-ja.orig/lib/MT/ObjectDriver/DBI/postgres.pm       2005-09-02 16:13:00.000000000 +0900
--- MT-3.2-b1-ja/lib/MT/ObjectDriver/DBI/postgres.pm    2005-09-13 17:14:18.000000000 +0900
***************
*** 86,91 ****
--- 86,106 ----
              $sql = $s_sql . $sql;
              $w_sql .= ") t\n";
          }
+       elsif ($j_args->{'aggregate_sort'}) {
+           ## sorting with aggregate function,
+           my $as_args = $j_args->{'aggregate_sort'};
+           my $cols = $class->column_names;
+           my $s_sql = "from (select " .
+                         join(', ', map "${tbl}_$_", @$cols) .
+                       ", $as_args->{'function'}(${j_tbl}_$as_args->{'column'}) as ${tbl}_${j_tbl}_$as_args->{'column'}\n";
+           $sql = $s_sql . $sql;
+           $w_sql .= "group by " .
+                       join(',', map "${tbl}_$_", @$cols) . "\n";
+           $w_sql .= ") t\n";
+           my $dir = $as_args->{'direction'} &&
+                       $as_args->{'direction'} eq 'descend' ? 'desc' : 'asc';
+                       $w_sql .= "order by ${tbl}_${j_tbl}_$as_args->{'column'} $dir\n";
+       }
  
          if (my $n = $j_args->{limit}) {
              $n =~ s/\D//g;   ## Get rid of any non-numerics.
*** MT-3.2-b1-ja.orig/lib/MT/Template/ContextHandlers.pm        2005-09-02 16:13:00.000000000 +0900
--- MT-3.2-b1-ja/lib/MT/Template/ContextHandlers.pm     2005-09-13 17:17:15.000000000 +0900
***************
*** 866,875 ****
              require MT::Comment;
              $args{'join'} = [ 'MT::Comment', 'entry_id',
                  { blog_id => $blog_id, visible => 1 },
!                 { 'sort' => 'created_on',
!                   direction => 'descend',
!                   unique => 1,
!                   limit => $n } ];
              $no_resort = 1;
          }
          @entries = MT::Entry->load(\%terms, \%args);
--- 866,875 ----
              require MT::Comment;
              $args{'join'} = [ 'MT::Comment', 'entry_id',
                  { blog_id => $blog_id, visible => 1 },
!               { 'aggregate_sort' => { 'column' => 'created_on',
!                                         'function' => 'max',
!                                         'direction' => 'descend' },
!                    limit => $n } ];
              $no_resort = 1;
          }
          @entries = MT::Entry->load(\%terms, \%args);

パッチ(MT-3.1x)

パッチファイル

*** MT-3.121-full-ja.orig/lib/MT/ObjectDriver/DBI/postgres.pm	2004-10-01 15:18:37.000000000 +0900
--- MT-3.121-full-ja/lib/MT/ObjectDriver/DBI/postgres.pm	2005-01-18 09:54:25.000000000 +0900
***************
*** 78,83 ****
--- 78,98 ----
              $sql = $s_sql . $sql;
              $w_sql .= ") t\n";
          }
+ 	elsif ($j_args->{'aggregate_sort'}) {
+ 	    ## sorting with aggregate function,
+ 	    my $as_args = $j_args->{'aggregate_sort'};
+ 	    my $cols = $class->column_names;
+ 	    my $s_sql = "from (select " .
+                         join(', ', map "${tbl}_$_", @$cols) .
+ 			", $as_args->{'function'}(${j_tbl}_$as_args->{'column'}) as ${tbl}_${j_tbl}_$as_args->{'column'}\n";
+ 	    $sql = $s_sql . $sql;
+ 	    $w_sql .= "group by " .
+ 	    		join(',', map "${tbl}_$_", @$cols) . "\n";
+ 	    $w_sql .= ") t\n";
+ 	    my $dir = $as_args->{'direction'} &&
+ 	    		$as_args->{'direction'} eq 'descend' ? 'desc' : 'asc';
+ 			$w_sql .= "order by ${tbl}_${j_tbl}_$as_args->{'column'} $dir\n";
+ 	}
  
          if (my $n = $j_args->{limit}) {
              $n =~ s/\D//g;   ## Get rid of any non-numerics.
*** MT-3.121-full-ja.orig/lib/MT/Template/Context.pm	2004-10-26 17:27:53.000000000 +0900
--- MT-3.121-full-ja/lib/MT/Template/Context.pm	2005-01-18 09:52:01.000000000 +0900
***************
*** 741,749 ****
          } elsif (my $n = $args->{recently_commented_on}) {
              $args{'join'} = [ 'MT::Comment', 'entry_id',
                  { blog_id => $blog_id, visible => 1 },
!                 { 'sort' => 'created_on',
!                   direction => 'descend',
!                   unique => 1,
                    limit => $n } ];
              $no_resort = 1;
          }
--- 741,749 ----
          } elsif (my $n = $args->{recently_commented_on}) {
              $args{'join'} = [ 'MT::Comment', 'entry_id',
                  { blog_id => $blog_id, visible => 1 },
!                 { 'aggregate_sort' => { 'column' => 'created_on',
! 					'function' => 'max',
! 					'direction' => 'descend' },
                    limit => $n } ];
              $no_resort = 1;
          }

MovableTypeに関してへ戻る。