web-dev-qa-db-ja.com

複数のタグを検索するときにすべてのタグを持つユーザーを照会する方法は?

一部のタグに基づいてすべての連絡先を表示するこのカスタムモジュールがあります。モジュールのフィールドに1つ以上のタグを追加できます

私が欲しいのは、モジュールに表示するオプションを追加することです:

  1. モジュールで指定されたすべてのタグを持つすべての連絡先

または

  1. モジュールで指定された1つ以上のタグを持つすべての連絡先

私が持っているコードは後者を実行するだけです。オプション1の可能性を追加するにはどうすればよいですか?

私が持っているコードは:

public static function getPeopleByTags($params)
{
  //echo "<pre>";
  $tags = $params->get("tags");

  $model = JModelLegacy::getInstance('Field', 'FieldsModel', array('ignore_request' => true)); //load fields model

  //get people info from database
  $db = JFactory::getDbo();
  $query = $db->getQuery(true);
  $query->select('id,catid,name,alias,image,email_to,published,user_id,tags.tag_id');
  //$query->select('*');

  $query->from($db->quoteName('#__contact_details').'AS contacts');
  $query->join('right','#__contentitem_tag_map AS tags ON `tags`.`content_item_id` = `contacts`.`id`');
  $query->where($db->quoteName("published") ." = 1  AND `tags`.`type_id` = 2 AND `tags`.`tag_id` IN (".implode(',',$tags).") ");
  $query->group('`contacts`.`id`');
  $db->setQuery($query);
  $people = $db->loadObjectList();

  return $people;

}

Joomla 3.9.0

2

Tags - SimilarモジュールはHAVING句を使用してこれを実現します。

$query->having('COUNT(' . $db->quoteName('tags.tag_id') . ') = ' . count($tags));

しかし、これが最善の解決策かどうかはわかりません。 WHERE INは、任意のタグに一致するすべての行を選択し、HAVINGは、行が選択された後にのみフィルタリングします。

3
Sharky

時間をかけて自分のテストデータをセットアップしたところ、HAVING句の実装に関してSharkyは正しい(+1〜Sharky)でした。

_$tag_ids = $params->get("tags");
$db = JFactory::getDbo();
$query = $db->getQuery(true)
    ->select("id, catid, name, alias, image, email_to, published, user_id")
    ->select(" GROUP_CONCAT(tags.tag_id) AS tag_ids")
    ->from("#__contact_details AS contacts")
    ->innerJoin("#__contentitem_tag_map AS tags ON tags.content_item_id = contacts.id")
    ->where("published = 1")
    ->where("type_id = 2")
    ->where("tag_id IN (" . implode(',', $tag_ids) . ")")
    ->group("contacts.id")
    ->having("COUNT(*) = " . sizeof($tag_ids));

echo $query->dump();
$db->setQuery($query);
echo "<pre>";
var_export($db->loadObjectList());
_

出力:

_SELECT id, catid, name, alias, image, email_to, published, user_id, GROUP_CONCAT(tags.tag_id) AS tag_ids
FROM zyxwv_contact_details AS contacts
INNER JOIN zyxwv_contentitem_tag_map AS tags ON tags.content_item_id = contacts.id
WHERE published = 1 AND type_id = 2 AND tag_id IN (9,10)
GROUP BY contacts.id
HAVING COUNT(*) = 2
_
_array (
  0 => 
  stdClass::__set_state(array(
     'id' => '12',
     'catid' => '21',
     'name' => 'Sharky',
     'alias' => 'sharky',
     'image' => 'images/sharky.jpg',
     'email_to' => '',
     'published' => '1',
     'user_id' => '0',
     'tag_ids' => '9,10',
  )),
  1 => 
  stdClass::__set_state(array(
     'id' => '82',
     'catid' => '21',
     'name' => 'mickmackusa',
     'alias' => 'mickmackusa',
     'image' => '',
     'email_to' => '',
     'published' => '1',
     'user_id' => '0',
     'tag_ids' => '9,10',
  )),
)
_
  • 結果セットのすべてのtag_idを表示するには、SELECT句でGROUP_CONCAT()を使用します。
  • INNER JOINとRIGHT JOINを使用するロジックを好みますが、パフォーマンスの点でどのように比較するのかわかりません。
  • HAVING句はCOUNT(*)を使用して、_tag_id_列をフィードするのと同じ結果を得ることができます。

COUNT()に続く番号を変更すると、連絡先/ユーザーが結果セットに含まれるために必要な修飾タグの数が決まります。たとえば、3つの異なるタグを検索したが2つしか必要ない場合、連絡先/ユーザーが3つの修飾タグのうち2つを持っているすべての行を取得します。

1
mickmackusa