私は私が把握しようとしているページ上で一連のソートの問題を抱えていますが、これまでのところ役に立ちません。
関連するカスタム分類法用語の下にリストされているCPT投稿のリストをソートする
私は この質問 への答えとしてこのコードを見つけました。これはすべてのカスタム分類法の用語のリストをそれぞれの下の関連するカスタム投稿タイプのリストと共に返すために完璧に機能します。
$custom_terms = get_terms('custom_taxonomy');
foreach($custom_terms as $custom_term) {
wp_reset_query();
$args = array('post_type' => 'custom_post_type',
'tax_query' => array(
array(
'taxonomy' => 'custom_taxonomy',
'field' => 'slug',
'terms' => $custom_term->slug,
),
),
);
$loop = new WP_Query($args);
if($loop->have_posts()) {
echo '<h2>'.$custom_term->name.'</h2>';
while($loop->have_posts()) : $loop->the_post();
echo '<a href="'.get_permalink().'">'.get_the_title().'</a>';
endwhile;
}
}
しかし、各分類用語の下に表示されるカスタム投稿タイプのリストはソートされておらず(または投稿日順にソートされている可能性もあります)、アルファベット順にソートします( に表示されるカスタムソートで)スレッド そしてこの下の質問).
通常、私は単にミックスのどこかに'orderby' => 'title'
を追加しますが、これはここではうまくいかないか、どこに追加すべきかわからないようです。
誰かがこれらを分類する方法を知っていますか?
更新
'orderby' => 'title'
の'post_type' => 'custom_post_type'
の後に$args
を追加しようとしましたが、効果はありませんでした。
返されたカスタム分類法用語の最初の記事「The」を無視しています
このブログ記事からのいくつかのコードの助けを借りて および これらの修正および改善の提案 私はカスタム投稿タイプの下にあるすべての投稿のリストを取得し、投稿タイトルに。
可能であれば、カスタム分類用語のリストを同様の方法で返します。
これは、最初の「The」を無視して、投稿をアルファベット順にソートするために使用されるコードです。
function wpcf_create_temp_column($fields) {
global $wpdb;
$matches = 'The';
$has_the = " CASE
WHEN $wpdb->posts.post_title regexp( '^($matches)[[:space:]]' )
THEN trim(substr($wpdb->posts.post_title from 4))
ELSE $wpdb->posts.post_title
END AS title2";
if ($has_the) {
$fields .= ( preg_match( '/^(\s+)?,/', $has_the ) ) ? $has_the : ", $has_the";
}
return $fields;
}
function wpcf_sort_by_temp_column ($orderby) {
$custom_orderby = " UPPER(title2) ASC";
if ($custom_orderby) {
$orderby = $custom_orderby;
}
return $orderby;
}
これにより、_custom
orderbyパラメータを使用してこのフィルタを使用できます。
add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
// Do nothing
if( '_custom' !== $q->get( 'orderby' ) )
return $orderby;
global $wpdb;
$matches = 'The'; // REGEXP is not case sensitive here
// Custom ordering (SQL)
return sprintf(
"
CASE
WHEN {$wpdb->posts}.post_title REGEXP( '^($matches)[[:space:]]+' )
THEN TRIM( SUBSTR( {$wpdb->posts}.post_title FROM %d ))
ELSE {$wpdb->posts}.post_title
END %s
",
strlen( $matches ) + 1,
'ASC' === strtoupper( $q->get( 'order' ) ) ? 'ASC' : 'DESC'
);
}, 10, 2 );
上記のコードを使って、投稿ではなく分類法の用語に適用できる関数を作成できるかどうか試してみましたが、まったく新しいことではなく、そのように機能するものを作成できませんでした。 。
ポストの代わりにリストの分類用語に適用するためにコードを書き直すことは可能ですか?
返された分類用語+関連する投稿のリストを特定の文字で始まるものに限定する
現時点では、返される分類用語と関連する投稿のリストは非常に長いです。リストをブラウズするときにそれをもう少し扱いやすいようにするために、私はそれをアルファベット順にA-E、F-Jなどから表示する5または6ページにそれを切り刻みたいです。
それが実行できない場合は、それをA、B、C、Dなどに完全に分割しても構いません。
私はこれに関する以前の質問を拾い読みしようとしました、しかし、私は遠くに私のために働くように思えた何かを見つけませんでした。
更新
この記事 これは私が望むことをしているようですが、私のサイトでは動作しません。これは私がリンクされた記事に基づいて修正を加えて追加したコードです。それがすることは、完全に空のリストを返すことです。
$game_titles = get_terms('game');
$count = count($game_titles);
$letters = 'A';
if($count > 0){
foreach($game_titles as $game_title) {
wp_reset_query();
$args = array('post_type' => 'release',
'tax_query' => array(
array(
'taxonomy' => 'game',
'field' => 'slug',
'terms' => $game_title->slug,
),
),
);
unset($game_titles);
if (preg_match("/^$letters/i", $term->name)){
$loop = new WP_Query($args);
if($loop->have_posts()) {
echo '<h2>' . $game_title->name . '</h2>';
echo '<ul>';
unset($game_title);
unset($args);
while($loop->have_posts()) : $loop->the_post();
/* code */
endwhile;
echo '</ul>';
}
}
}
}
UPDATE - @ PieterGoosenのコードを追加
以下の@ PieterGoosenの回答のコードをfunctions.php(コードは回答のものと同じ)と私のページテンプレートファイルに追加しました( 追加したループの内容を含むコードはここで確認できます )。
私が得ていることは部分的には正しいですが、それでも正しくないことがいくつかあります。新しいコードで、私はrelease
CPTのすべての投稿のリストを、関連する1つのgame
用語でソートして取得しています。選択された用語はアルファベットで最も古い発生文字で始まる用語です - 小惑星のようにパックマンよりも選択されますが、パックマンはスペースインベーダーよりも選択されます。
表示されるのは、すべてのgame
用語のリストと、各用語の下にあるすべての関連するrelease
投稿のリストです。さらに、それぞれのrelease
の投稿は一度だけ表示されますが、複数の投稿が関連付けられている場合は、それぞれのgame
の下にそれらを再表示させることをお勧めします。
ただし、ソート機能はうまく機能しているようです。この新しいコードはすべての投稿タイトルで 'the'を無視し、現在のソートに基づいて用語名でも無視されているように見えます(用語名が表示された場合はより明確になります)。
UPDATE 2 - @ PieterGoosenの新しいコードをテストする
私は更新されたコードで私のコードを更新しました、そしてそれは魅力のように働きます。私がする必要があるのは、いくつかのスタイルとループコンテンツを適用することだけです。
参考までに、この解決策は私の古いコードと比較して私のページ上のデータベースクエリの数を大幅に改善しました(get_num_queries()
を使ったチェック - 私の古いコードは3166のデータベースクエリを持っていました。代わりに33で(+今私は実際に表示したいすべてのデータを取得しています+ページの読み込みが速くなります)。
これは非常に興味深い質問であり、非常に高価でリソースを大量に消費するプロセスになり、ページを大幅に遅くする可能性があります
この質問の私のコードはすべて次の投稿タイプと分類に基づいているため、コードを実行する前に、投稿タイプと分類を自分の要件に合わせて調整してください
カスタム投稿タイプ->release
カスタム分類->game
投稿に割り当てられた最初の用語を並べ替えて含めるだけでなく、すべての用語とこれらの用語に属する投稿を含める必要があるため、別のソリューションを完全に検討する必要がありますが、パフォーマンスに留意してください。
新しいソリューションは、 @ birgireによるソリューション を 前の質問 に完全に適合させ、返された投稿ではなく、クエリ自体を並べ替えます。
あなたが尋ねたのと同じ質問に対する私の最初の解決策 この問題を解決するためにもはや必要ないので、あなたがループを作成してソートするかどうかはあなた次第です非表示のカスタムフィールド。ただし、カスタムフィールドを使用する私のソリューションは、まだ完全に有効です。この質問に対する私の元の答えはカスタムフィールドソリューションを含んでいたので、私はそれを使い続けますが、カスタムフィールドソリューションではなく@birgireのソリューションを使用することに決めた場合に行うことができる変更を表示しようとします
用語を照会してからeach用語に対してカスタムクエリを実行して、用語に割り当てられた投稿を取得するコードの最初のブロックは非常に高価です。あなたが言うように、あなたは大量の用語を持っています。どんな量の用語でも、あなたは本当にdbを本当に本当に激しく叩いています。
これを確認するには、コードの後にecho get_num_queries()
を実行します。これにより、特定のページの読み込みで実際に実行しているdbクエリの量が表示されます。これを行う前に座ることをお勧めします;-)
(注:最初にベンチマークを取得します。すべてのコードを削除し、echo get_num_queries()
を実行します。メモを取り、コードを置き換えてecho get_num_queries()
を再度実行し、2つを減算して正確なカウントを取得します)
何かを始める前に、禁止用語を用語や投稿名から削除するヘルパー関数を見てみましょう。この関数は重要であり、コードを保持するためにどこでも使用されますDRY
/**
* Function get_name_banned_removed()
*
* A helper function to handle removing banned words
*
* @param string $tring String to remove banned words from
* @param array $banned Array of banned words to remove
* @return string $string
*/
function get_name_banned_removed( $string = '', $banned = [] )
{
// Make sure we have a $string to handle
if ( !$string )
return $string;
// Sanitize the string
$string = filter_var( $string, FILTER_SANITIZE_STRING );
// Make sure we have an array of banned words
if ( !$banned
|| !is_array( $banned )
)
return $string;
// Make sure that all banned words is lowercase
$banned = array_map( 'strtolower', $banned );
// Trim the string and explode into an array, remove banned words and implode
$text = trim( $string );
$text = strtolower( $text );
$text_exploded = explode( ' ', $text );
if ( in_array( $text_exploded[0], $banned ) )
unset( $text_exploded[0] );
$text_as_string = implode( ' ', $text_exploded );
return $string = $text_as_string;
}
このコードはfunctions.php
またはカスタムプラグイン内に配置する必要があります(preferably)
これでカバーできたので、次の部分を見てみましょう
注:@ birgireの方法を使用して禁止された単語を削除し、その変更された投稿でソートする場合タイトルの場合は、この部分を完全にスキップできます。非表示のカスタムフィールドを設定するため、このフィールドを並べ替えます)
繰り返しになりますが、このリンク(これはプラグインまたはfunctions.php
に移動する必要があります)からのソリューションです。すべての投稿に_custom_sort_post_title
というフィールド。これにより、禁止されている主要なWordが削除された投稿タイトルが保存されます
(大きな注意:リンクの回答から元のコードのバージョンを大幅に編集しました =)
add_action( 'wp', function ()
{
add_filter( 'posts_fields', function ( $fields, \WP_Query $q )
{
global $wpdb;
remove_filter( current_filter(), __FUNCTION__ );
// Only target a query where the new custom_query parameter is set with a value of custom_meta_1
if ( 'custom_meta_1' === $q->get( 'custom_query' ) ) {
// Only get the ID and post title fields to reduce server load
$fields = "$wpdb->posts.ID, $wpdb->posts.post_title";
}
return $fields;
}, 10, 2);
$args = [
'post_type' => 'release', // Set according to needs
'posts_per_page' => -1, // Set to execute smaller chucks per page load if necessary
'suppress_filters' => false, // Allow the posts_fields filter
'custom_query' => 'custom_meta_1', // New parameter to allow that our filter only target this query
'meta_query' => [
[
'key' => '_custom_sort_post_title', // Make it a hidden custom field
'compare' => 'NOT EXISTS'
]
]
];
$q = get_posts( $args );
// Make sure we have posts before we continue, if not, bail
if ( !$q )
return;
foreach ( $q as $p ) {
$new_post_title = strtolower( $p->post_title );
if ( function_exists( 'get_name_banned_removed' ) )
$new_post_title = get_name_banned_removed( $new_post_title, ['the'] );
// Set our custom field value
add_post_meta(
$p->ID, // Post ID
'_custom_sort_post_title', // Custom field name
$new_post_title // Custom field value
);
} //endforeach $q
});
このコードを削除すると、functions.php
またはプラグインに必要なコードは次のようになります。(NOTE:このアクションにPART TWO)でさらに作業を追加します
add_action( 'transition_post_status', function ( $new_status, $old_status, $post )
{
// Make sure we only run this for the release post type
if ( 'release' !== $post->post_type )
return;
$text = strtolower( $post->post_title );
if ( function_exists( 'get_name_banned_removed' ) )
$text = get_name_banned_removed( $text, ['the'] );
// Set our custom field value
update_post_meta(
$post->ID, // Post ID
'_custom_sort_post_title', // Custom field name
$text // Custom field value
);
}, 10, 3 );
このコードは、_custom_sort_post_title
カスタムフィールドがそれに応じて設定されていることを確認します。
これは本当に高価になる部分です。すべての用語を取得してそれらをループし、それに応じて投稿をクエリすることは、ではなくのオプションです。
ここで必要なことは、ここで1つのクエリを実行してすべての投稿IDを取得し、用語キャッシュを更新し、get_object_term_cache()
を使用して投稿用語を取得し、所属する用語に従ってIDの配列をソートすることです結果を一時的に安全にします。大量の投稿データを乱雑なシリアル化された配列に詰め込みたくないので、一時的に安全な投稿IDのみを使用します。
get_name_banned_removed()
関数を使用して用語名を作成し、禁止された単語を削除してタイトルを投稿します。
いくつかの注意事項
/**
* Function get_sorted_post_ids_terms_and_fields()
*
* Return a sorted array of post ids. These ID's are sorted according to
* - Post title with the banned words removed before sorting
* - Post terms with the banned words removed before sorting
*
* @param string $post_type Post type to get posts from Default 'release'
* @param string $taxonomy Taxonomy to get posts from Default 'game'
* @return array $ids
*/
function get_sorted_post_ids_terms_and_fields( $post_type = 'release', $taxonomy = 'game' )
{
$array_combine = [];
// Sanitize our post type and taxonomy names
if ( 'release' !== $post_type )
$post_type = filter_var( $post_type, FILTER_SANITIZE_STRING );
if ( 'game' !== $taxonomy )
$taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
// Make sure that the taxonomy exist to avoid bugs later on
if ( !taxonomy_exists( $taxonomy ) )
return $array_combine;
// Our taxonomy exist, let's continue
// Create a unique transient name
$transient_name = 'spbtaf_' . md5( $taxonomy.$post_type );
if ( false === ( $array_combine = get_transient ( $transient_name ) ) ) {
// Set our query arguments. Note, we will not do any sorting here
$args = [
'fields' => 'ids', // Only get post ID's
'post_type' => $post_type,
'posts_per_page' => -1,
'meta_key' => '_custom_sort_post_title', // Remove if you use @birgire's solution
'orderby' => 'meta_value', // Change to '_custom' if you use @birgire's solution
'order' => 'ASC',
'tax_query' => [
[
'taxonomy' => $taxonomy,
'operator' => 'EXISTS'
]
],
];
$ids = get_posts( $args );
// Make sure we have posts
if ( $ids ) {
// Update the object term cache, saves plenty db time and calls
update_object_term_cache( $ids, $post_type );
$term_post_ids_array = [];
$term_names = [];
// Loop through the posts and save in an array
foreach ( $ids as $id ) {
// Get the post terms from our post term cache
$terms = get_object_term_cache( $id, $taxonomy );
// Loop through the terms. We definitely have terms
foreach ( $terms as $term ) {
// Remove the banned words from the term name
$term_name = strtolower( $term->name );
if ( function_exists( 'get_name_banned_removed' ) )
$term_name = get_name_banned_removed( $term_name, ['the'] );
// Save the term name and post ids in an array
$term_post_ids_array[$term_name][] = $id;
// Save the real term names in an array
$term_names[$term_name] = $term->name;
} //endforeach $terms
unset( $term );
} //endforeach $ids
unset( $id );
// Sort the array according to our modified term ids
ksort( $term_post_ids_array );
ksort( $term_names );
// Lets replace the modified term names with their proper names
$array_combine = array_combine( $term_names, $term_post_ids_array );
} // endif $ids
// Set the transient
set_transient( $transient_name, $array_combine, 30*DAY_IN_SECONDS );
} // endif get_transient
return $array_combine;
}
この関数は、投稿IDの並べ替えられた配列を返します。
禁止されている先頭の単語(like the
)が削除された、所属する用語
禁止された主要なWordを削除した投稿タイトル
配列も用語に従ってソートされます
トランジェントは30日間に設定されています。必要に応じて調整できます。
新しい投稿が公開されたとき、または投稿が更新、削除、または削除解除されたときに、一時的なものを削除してリセットする必要があります。このために、transition_post_status
アクションフックを使用します。 (すべてをきちんとまとめておくために、PART ONE POINT TWOからのアクションをこのアクションと組み合わせてみましょう。 @birgireのカスタムフィルターで並べ替えている場合は削除できます)
add_action( 'transition_post_status', function ( $new_status, $old_status, $post )
{
/* ----------START DELETE IF YOU USE @birgire's SORTING FILTER------------*/
// Make sure we only run this for the release post type
if ( 'release' !== $post->post_type )
return;
$text = strtolower( $post->post_title );
if ( function_exists( 'get_name_banned_removed' ) )
$text = get_name_banned_removed( $text, ['the'] );
// Set our custom field value
update_post_meta(
$post->ID, // Post ID
'_custom_sort_post_title', // Custom field name
$text // Custom field value
);
/* -------------END DELETE IF YOU USE @birgire's SORTING FILTER------------*/
global $wpdb;
// Delete the transients
$wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient%_spbtaf_%')" );
$wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient_timeout%_spbtaf_%')" );
// Reset the transient
if ( function_exists( 'get_sorted_post_ids_terms_and_fields' ) )
get_sorted_post_ids_terms_and_fields(); //REMEMBER TO SET POST TYPE AND TAXONOMY
}, 10, 3 );
ご覧のとおり、transition_post_status
アクションでトランジェントをリセットします。これにより、フロントエンドから多くの負荷がかかります
ループは少しトリッキーです。 get_sorted_post_ids_terms_and_fields()
関数からの用語名と投稿IDの多次元配列があります。ここでは、db呼び出しなどを可能な限り低く抑えるために、ここで賢くする必要があります。トリッキーな部分は、それぞれのIDから完全な投稿オブジェクトを取得することです。また、投稿は複数の用語に属しているため、IDも重複しています。
計画
カスタムクエリを使用して、すべての投稿を取得します。 WP_Query
は重複した投稿を返さないため、ここでわずかな問題があります。これがトリックの出番です。WP_Query
が返す投稿をキャッシュに追加します。投稿がキャッシュに保存されると、db呼び出しを行わずに、get_post()
を使用して何度もクエリを実行できます。これは、すべてのコードの賢い部分です。
最初に、WP_Query
に渡す前にすべてのpost_idを取得するために多次元配列をフラット化する方法も必要です。
/**
* Function flatten_array()
*
* Function to flatten an array and get all array values
* Special thanks to zdenko
* @link https://Gist.github.com/kohnmd/11197713
*
* @param array $array The multidimensional array to flatten
* @return array $array The flattened array
*/
function flatten_array( $array )
{
// Make sure $array is an array, if not return $array as an array
if ( !is_array( $array ) )
return [$array];
return array_reduce( $array, function ( $a, $b ) {
return array_merge( $a, flatten_array( $b ) );
}, [] );
}
次に、投稿のキャッシュに保存するために、IDのフラット化された配列から投稿をクエリします。このため、次の理由からカスタムSQLクエリを使用します。
超速い
このクエリを変更するためのフィルターやアクションは必要ありません
すべてのハードワークは、get_sorted_post_ids_terms_and_fields()
ですでに行われています。ここで必要なのは、その関数からIDを取得し、完全な投稿オブジェクトを取得することです。
投稿がすべて揃ったら、update_post_cache
を使用して投稿を投稿キャッシュに追加できます
/**
* Function set_posts_to_cache()
*
* Function to query all the full post objects who's ID's is in the
* get_sorted_post_ids_terms_and_fields() function and then to add these
* post objects into the post cache so we can query them over and over again
* with get_post()
*
* @param string $post_type Post type to get posts from Default 'release'
* @param string $taxonomy Taxonomy to get posts from Default 'game'
*/
function set_posts_to_cache( $post_type = 'release', $taxonomy = 'game' )
{
global $wpdb;
// Check if the taxonomy exists
if ( !taxonomy_exists( $taxonomy ) )
return false;
// Sanitize the taxonomy name
$taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
// Sanitize the post type
if ( 'release' !== $post_type )
$post_type = filter_var( $post_type, FILTER_SANITIZE_STRING );
// Get our post ID's
if ( function_exists( 'get_sorted_post_ids_terms_and_fields' ) ) {
$combined_array = get_sorted_post_ids_terms_and_fields( $post_type, $taxonomy );
if ( $combined_array ) {
if ( function_exists( 'flatten_array' ) ) {
// Flatten our array in order to pass it to WP_Query
$flatten_array = flatten_array( $combined_array );
$unique_ids = array_unique( $flatten_array );
$string_ids = implode( ', ', array_map( 'absint', $unique_ids ) );
/**
* Run our custom SQL query and add our posts in cache
*
* We only need to get the posts by ID and post type. Remember, the function
* get_sorted_post_ids_terms_and_fields() has already taken care of all the hard
* work. All this query needs to do is to retrieve the posts which ID's are returned
* by get_sorted_post_ids_terms_and_fields() to add the posts in cache
*/
$posts_to_cache = $wpdb->get_results(
$wpdb->prepare(
"SELECT $wpdb->posts.*
FROM $wpdb->posts
WHERE 1=1
AND $wpdb->posts.ID IN ($string_ids)
AND $wpdb->posts.post_type = %s
ORDER BY $wpdb->posts.post_date DESC
LIMIT 0, %d",
$post_type,
count( $unique_ids )
)
);
// Update the post cache
update_post_caches( $posts_to_cache );
} // endif function_exists( 'flatten_array' )
} // endif if ( $combined_array )
} // endif ( function_exists( 'get_sorted_post_ids_terms_and_fields' ) )
}
ループを見てみましょう
if ( function_exists( 'get_sorted_post_ids_terms_and_fields' ) ) {
$combined_array = get_sorted_post_ids_terms_and_fields();
// Make sure we have a valid array
if ( $combined_array ) {
if ( function_exists( 'set_posts_to_cache' ) ) {
// Set all our posts into the post cache. remember to pass the correct post type and taxonomy
set_posts_to_cache( 'release', 'game');
// Set a variable to hold the first letter of the term name
$first_letter = '';
foreach ( $combined_array as $term_name=>$post_ids ) {
// Display the first letter from the terms
$term_name_modified = strtolower( $term_name );
if ( function_exists( 'get_name_banned_removed' ) )
$term_name_modified = get_name_banned_removed( $term_name_modified, ['the'] );
$starting_letter = strtoupper( mb_substr( $term_name_modified, 0, 1 ) );
if ( $first_letter !== $starting_letter )
echo '<p>' . $starting_letter . '</p>';
// Update the $first_letter variable
$first_letter = $starting_letter;
// Display the term name above the posts
echo $term_name . '</br>';
// Apply the get_post() function to all post ids to get full posts objects
$posts_array = array_map( 'get_post', $post_ids );
// Now that we have full posts, lets display them in our loop
foreach ( $posts_array as $post ) {
setup_postdata( $post );
// APPLY YOUR LOOP AS PER NORMAL AS PER YOUR LINK.
echo '<li>' . get_the_title() . '</li>';
} // endforeach $posts_array
wp_reset_postdata(); // VERY VERY IMPORTANT
} // endforeach $combined_array
} // endif function_exists flatten_array
} // endif $combined_array
} // endif function_exists get_sorted_post_ids_terms_and_fields
投稿は次のように表示されます。
すべての投稿は、最初の並べ替えプロセスで禁止されたWordが削除された投稿名に基づいて、所属する用語でアルファベット順に並べ替えられます
すべての用語はアルファベット順にソートされ、禁止された単語は最初のソートから削除されます。すべての用語の下に投稿が並べ替えられています
すべての用語は、名前が最初のソートで削除された禁止語で始まる文字でソートされます
独自のカスタムスタイリングを適用し、ループにマークアップするだけです。
この巨大なマラソンの後、数字を計算しましょう。 24の投稿で、投稿のタイトルと用語名を削除するために膨大な量の作業が行われたため、最終的には
約0.16秒で6db呼び出し(
約0.14秒で5dbコール
そして、私たちは一時的なものを乱用しませんでした。このような巨大な仕事にはかなり印象的です、笑。
興味を引くために、最初のコードブロックからループをコピーし、次の結果を得ました
私のポイントを証明した大きな違い;-)
用語の最初の文字に従ってクエリをページングする場合、ここで追加の作業が必要になります。このセクションは、これに対するproper解決策が得られるまで作成中のままです。
どのような解決策を考え出しても、答えのパート1と2には影響しません。パート3は間違いなく影響を受け、何らかの書き換えが必要になります