Answers.Body: <p>@James Smith Your note and some searching around yielded some results. Here's my working solution. I realized that all I needed to do was combine all tags with an "AND" operator, and that my search was actually not an "AND" + "OR" search at all.</p>
  
  <p><strong>Form:</strong></p>
  
  <pre><code>{% set query = craft.request.getQuery() %}
  {% set selectedSkills = query.skills ?? [] %}
  {% set selectedSchool = query.school ?? [] %}
  {% set nameSearch = query.nameSearch ?? "" %}
  
  &lt;form action="{{ url('search') }}"&gt;
      &lt;input type="search" name="nameSearch" placeholder="Name" value="{{nameSearch}}"&gt;
  
      &lt;label for="skills"&gt;Skills&lt;/label&gt;
      &lt;select multiple id="skills" name="skills[]"&gt;
          {% for tag in craft.tags.group('profileSkills').all() %}
              {% set selectedAttr = tag.id in selectedSkills ? 'selected' : '' %}
              &lt;option value="{{ tag.id }}" {{selectedAttr}} &gt;{{tag.title}}&lt;/option&gt;
          {% endfor %}
      &lt;/select&gt;
  
      &lt;label for="school"&gt;School&lt;/label&gt;
      &lt;select id="school" name="school"&gt;
          &lt;option value=""&gt;All&lt;/option&gt;
          {% for tag in craft.tags.group('schoolPrograms').all() %}
              {% set selectedAttr = tag.id in selectedSchool ? 'selected' : '' %}
              &lt;option value="{{ tag.id }}" {{selectedAttr}} &gt;{{tag.title}}&lt;/option&gt;
          {% endfor %}
      &lt;/select&gt;
  
      &lt;input type="submit" value="Search"&gt;
      &lt;a href="/search"&gt;&lt;button type="button"&gt;Reset&lt;/button&gt;&lt;/a&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p><strong>Results:</strong></p>
  
  <pre><code>&lt;div class="results"&gt;
  
      {# Set initial search operator to AND #}
      {% set relationParam = ['and'] %}
      {% set searchParams = "" %}
  
      {% if query.nameSearch is defined and query.nameSearch is not empty %}
          {% set searchParams = 'fullName:*' ~ query.nameSearch ~'*' %}
      {% endif %}
  
      {# This is a multi-tag select #}
      {% if query.skills is defined and query.skills is not empty %}
          {% for skill in query.skills %}
              {% set relationParam = relationParam|merge([{targetElement:skill }]) %}
          {% endfor %}
      {% endif %}
  
      {# This is a single tag select #}
      {% if query.school is defined and query.school is not empty %}
              {% set relationParam = relationParam|merge([{targetElement:query.school }]) %}
      {% endif %}
  
      {# Get users of group "member" with any keywords provided #}
      {% set users = craft.users.group('members').search(searchParams) %}
  
      {# Check to see if we have any tags selected#}
      {% if relationParam|length &gt; 1 %}
          {# Filter users based on all selected tags #}
          {% set users = users.relatedTo(relationParam) %}
      {% endif %}
  
      {%- if users|length %}
          &lt;p&gt;{{ users|length }} results:&lt;/p&gt;
          &lt;ul&gt;
              {% for entry in users %}
                  &lt;li&gt;&lt;a href="{{ entry.url }}"&gt;{{ entry.fullName ? entry.fullName : entry.name }}&lt;/a&gt;&lt;/li&gt;
              {% endfor %}
          &lt;/ul&gt;
      {% else %}
          &lt;p&gt;Your search didn’t return any results.&lt;/p&gt;
      {% endif -%}        
  
  &lt;/div&gt;
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-05-13T14:09:44.467Z
Answers.LastActivityDate: 2020-05-13T14:09:44.467Z
Answers.UserDisplayName: Adrian
Answers.Body: <p>In order to get at the tag relationships, I think you'll need to use the <code>relatedTo</code> parameter instead of <code>search</code>. You'll still use <code>search</code> for the full-name search. This should solve both your problems.</p>
  
  <p>The tricky part is fetching the appropriate elements to pipe into <code>relatedTo()</code> - you can't just send through the raw title string from the post data - you'll need to fetch the actual element ids.</p>
  
  <p>Here's a very rough example. You'll need to replace 'facetFieldHandle_n` with the field handle names of your tag selector fields held against the users.</p>
  
  <pre><code>{% set facet1 = craft.tags.title(searchSkills).ids() %}
  {% set facet2 = craft.tags.title(searchSchool).ids() %}
  
  {% set params = ['and',
      { targetElement: facet1, field: 'facetFieldHandle_1' },
      { targetElement: facet2, field: 'facetFieldHandle_2' }
  ] %}
  
  {% set users = craft.users.relatedTo(params).search(searchName).orderBy('score').all() %}
  
  &lt;ul&gt;
      {% for user in users %}
          &lt;li&gt;{{ user.fullName }}&lt;/li&gt;
      {% endfor %}
  &lt;/ul&gt;
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-05-13T12:23:42.467Z
Answers.Text: Thanks James — I guess I'm trying to filter by 3 parameters in order:  First by School, then from those who are tagged with School, then by Skill(s), and then by keywords.
Answers.UserDisplayName: Adrian
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-05-13T08:59:48.660Z
Answers.LastActivityDate: 2020-05-13T08:59:48.660Z
Answers.UserDisplayName: James Smith
Body: <p><strong>Context:</strong>
  I'm trying to build a search form that will filter users based on <code>Name</code>, <code>Skills</code> and <code>School</code>. Users who have any of the search criteria should appear in my results.</p>
  
  <p><code>Name</code> Is a fuzzy keyword search of <code>user.fullName</code></p>
  
  <p><code>Skills</code> Is a multiple select of tags, where any should match (if multiple are selected)</p>
  
  <p><code>Schools</code> Is a single select of a tag.</p>
  
  <p><strong>Problem 1:</strong>
  When I search just by tags, leaving the Name field blanks, only the last tag is used to return results. So if two users were tagged with multiple tags, only the one relating to the last search term tag will be returned.</p>
  
  <p><strong>Problem 2:</strong><br>
  It seems I can't combine the query so that I am searching not only by 'who has these tags', but also 'who's name partially matches this search' as well.</p>
  
  <p>Thanks for any suggestions!</p>
  
  <p><strong>Code: Form</strong></p>
  
  <pre><code>&lt;form action="{{ url('search') }}"&gt;
      &lt;input type="search" name="qName" placeholder="Name" value="{{craft.app.request.getParam('qName')}}"&gt;
  
      &lt;label for="qSkills"&gt;Skills&lt;/label&gt;
      &lt;select multiple id="qSkills" name="qSkills[]"&gt;
          {% for item in craft.tags.group('profileSkills').all() %}
          &lt;option value="{{ item.title }}"&gt;{{item.title}}&lt;/option&gt;
          {% endfor %}
      &lt;/select&gt;
  
      &lt;label for="qSchool"&gt;School&lt;/label&gt;
      &lt;select id="qSchool" name="qSchool"&gt;
          &lt;option value=""&gt;All&lt;/option&gt;
          {% for item in craft.tags.group('schoolPrograms').all() %}
          &lt;option value="{{ item.id }}"&gt;{{item.title}}&lt;/option&gt;
          {% endfor %}
      &lt;/select&gt;
  
      &lt;input type="submit" value="Search"&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p><strong>Code: Results (on same page)</strong></p>
  
  <pre><code>&lt;div class="results"&gt;
      {% set searchName = craft.app.request.getParam('qName') %}
      {% set searchSkills = craft.app.request.getParam('qSkills') %}
      {% set searchSchool = craft.app.request.getParam('qSchool') %}
      {% set queryParams = {} %}
  
      {% if searchName is defined and searchName is not empty %}
          {% set queryParams = queryParams|merge({
              search: {
                  query:'fullName:*' ~ searchName ~'*',
              },
          }) %}
      {% endif %}
  
      {% if searchSkills is defined and searchSkills is not empty %}
          {% set skillList = "" %}
          {% for skill in searchSkills %}
              {% set skillList = skillList ~ 'profileSkills:' ~  skill ~ " OR " %}
          {% endfor %}
          {% set queryParams = queryParams|merge({
              search: {
                  query:  skillList,
              },
          }) %}
  
      {% endif %}
  
      {% if searchSchool is defined and searchSchool is not empty %}
          {% set queryParams = queryParams|merge({
              search: {
                  query: 'profileSchoolProgram:' ~ searchSchool,
              },
          }) %}
      {% endif %}
  
  
      {% set entries = craft.users(queryParams).group('members')
          .orderBy('score')
          .all() 
      %}
  
      {% if entries|length %}
          &lt;p&gt;{{ entries|length }} results:&lt;/p&gt;
          &lt;ul&gt;
              {% for entry in entries %}
                  &lt;li&gt;&lt;a href="{{ entry.url }}"&gt;{{ entry.fullName }}&lt;/a&gt;&lt;/li&gt;
              {% endfor %}
          &lt;/ul&gt;
      {% else %}
          &lt;p&gt;Your search didn’t return any results.&lt;/p&gt;
      {% endif %}
  &lt;/div&gt;
  
  </code></pre>
  
Tags: <craft3><users><search><tags><frontend-form>
Title: Frontend Form Search Users based on Tags
ThreadText: # Frontend Form Search Users based on Tags
  
  (Asked by: username_0 on 2020-05-11)
  
  **Context:**
  I'm trying to build a search form that will filter users based on `Name`, `Skills` and `School`. Users who have any of the search criteria should appear in my results.
  
  `Name` Is a fuzzy keyword search of `user.fullName`
  
  `Skills` Is a multiple select of tags, where any should match (if multiple are selected)
  
  `Schools` Is a single select of a tag.
  
  **Problem 1:**
  When I search just by tags, leaving the Name field blanks, only the last tag is used to return results. So if two users were tagged with multiple tags, only the one relating to the last search term tag will be returned.
  
  **Problem 2:**  
  It seems I can't combine the query so that I am searching not only by 'who has these tags', but also 'who's name partially matches this search' as well.
  
  Thanks for any suggestions!
  
  **Code: Form**
  
  ```
  <form action="{{ url('search') }}">
      <input type="search" name="qName" placeholder="Name" value="{{craft.app.request.getParam('qName')}}">
  
      <label for="qSkills">Skills</label>
      <select multiple id="qSkills" name="qSkills[]">
          {% for item in craft.tags.group('profileSkills').all() %}
          <option value="{{ item.title }}">{{item.title}}</option>
          {% endfor %}
      </select>
  
      <label for="qSchool">School</label>
      <select id="qSchool" name="qSchool">
          <option value="">All</option>
          {% for item in craft.tags.group('schoolPrograms').all() %}
          <option value="{{ item.id }}">{{item.title}}</option>
          {% endfor %}
      </select>
  
      <input type="submit" value="Search">
  </form>
  
  ```
  
  **Code: Results (on same page)**
  
  ```
  <div class="results">
      {% set searchName = craft.app.request.getParam('qName') %}
      {% set searchSkills = craft.app.request.getParam('qSkills') %}
      {% set searchSchool = craft.app.request.getParam('qSchool') %}
      {% set queryParams = {} %}
  
      {% if searchName is defined and searchName is not empty %}
          {% set queryParams = queryParams|merge({
              search: {
                  query:'fullName:*' ~ searchName ~'*',
              },
          }) %}
      {% endif %}
  
      {% if searchSkills is defined and searchSkills is not empty %}
          {% set skillList = "" %}
          {% for skill in searchSkills %}
              {% set skillList = skillList ~ 'profileSkills:' ~  skill ~ " OR " %}
          {% endfor %}
          {% set queryParams = queryParams|merge({
              search: {
                  query:  skillList,
              },
          }) %}
  
      {% endif %}
  
      {% if searchSchool is defined and searchSchool is not empty %}
          {% set queryParams = queryParams|merge({
              search: {
                  query: 'profileSchoolProgram:' ~ searchSchool,
              },
          }) %}
      {% endif %}
  
      {% set entries = craft.users(queryParams).group('members')
          .orderBy('score')
          .all() 
      %}
  
      {% if entries|length %}
          <p>{{ entries|length }} results:</p>
          <ul>
              {% for entry in entries %}
                  <li><a href="{{ entry.url }}">{{ entry.fullName }}</a></li>
              {% endfor %}
          </ul>
      {% else %}
          <p>Your search didn’t return any results.</p>
      {% endif %}
  </div>
  
  ```
  
  **Tags:** craft3, users, search, tags, frontend-form
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2020-05-13. Score: 1 | Accepted answer
  
  @username_1 Your note and some searching around yielded some results. Here's my working solution. I realized that all I needed to do was combine all tags with an "AND" operator, and that my search was actually not an "AND" + "OR" search at all.
  
  **Form:**
  
  ```
  {% set query = craft.request.getQuery() %}
  {% set selectedSkills = query.skills ?? [] %}
  {% set selectedSchool = query.school ?? [] %}
  {% set nameSearch = query.nameSearch ?? "" %}
  
  <form action="{{ url('search') }}">
      <input type="search" name="nameSearch" placeholder="Name" value="{{nameSearch}}">
  
      <label for="skills">Skills</label>
      <select multiple id="skills" name="skills[]">
          {% for tag in craft.tags.group('profileSkills').all() %}
              {% set selectedAttr = tag.id in selectedSkills ? 'selected' : '' %}
              <option value="{{ tag.id }}" {{selectedAttr}} >{{tag.title}}</option>
          {% endfor %}
      </select>
  
      <label for="school">School</label>
      <select id="school" name="school">
          <option value="">All</option>
          {% for tag in craft.tags.group('schoolPrograms').all() %}
              {% set selectedAttr = tag.id in selectedSchool ? 'selected' : '' %}
              <option value="{{ tag.id }}" {{selectedAttr}} >{{tag.title}}</option>
          {% endfor %}
      </select>
  
      <input type="submit" value="Search">
      <a href="/search"><button type="button">Reset</button></a>
  </form>
  
  ```
  
  **Results:**
  
  ```
  <div class="results">
  
      {# Set initial search operator to AND #}
      {% set relationParam = ['and'] %}
      {% set searchParams = "" %}
  
      {% if query.nameSearch is defined and query.nameSearch is not empty %}
          {% set searchParams = 'fullName:*' ~ query.nameSearch ~'*' %}
      {% endif %}
  
      {# This is a multi-tag select #}
      {% if query.skills is defined and query.skills is not empty %}
          {% for skill in query.skills %}
              {% set relationParam = relationParam|merge([{targetElement:skill }]) %}
          {% endfor %}
      {% endif %}
  
      {# This is a single tag select #}
      {% if query.school is defined and query.school is not empty %}
              {% set relationParam = relationParam|merge([{targetElement:query.school }]) %}
      {% endif %}
  
      {# Get users of group "member" with any keywords provided #}
      {% set users = craft.users.group('members').search(searchParams) %}
  
      {# Check to see if we have any tags selected#}
      {% if relationParam|length > 1 %}
          {# Filter users based on all selected tags #}
          {% set users = users.relatedTo(relationParam) %}
      {% endif %}
  
      {%- if users|length %}
          <p>{{ users|length }} results:</p>
          <ul>
              {% for entry in users %}
                  <li><a href="{{ entry.url }}">{{ entry.fullName ? entry.fullName : entry.name }}</a></li>
              {% endfor %}
          </ul>
      {% else %}
          <p>Your search didn’t return any results.</p>
      {% endif -%}        
  
  </div>
  
  ```
  
  
  ---
  
  ### Answer by username_1 on 2020-05-13. Score: 1
  
  In order to get at the tag relationships, I think you'll need to use the `relatedTo` parameter instead of `search`. You'll still use `search` for the full-name search. This should solve both your problems.
  
  The tricky part is fetching the appropriate elements to pipe into `relatedTo()` - you can't just send through the raw title string from the post data - you'll need to fetch the actual element ids.
  
  Here's a very rough example. You'll need to replace 'facetFieldHandle\_n` with the field handle names of your tag selector fields held against the users.
  
  ```
  {% set facet1 = craft.tags.title(searchSkills).ids() %}
  {% set facet2 = craft.tags.title(searchSchool).ids() %}
  
  {% set params = ['and',
      { targetElement: facet1, field: 'facetFieldHandle_1' },
      { targetElement: facet2, field: 'facetFieldHandle_2' }
  ] %}
  
  {% set users = craft.users.relatedTo(params).search(searchName).orderBy('score').all() %}
  
  <ul>
      {% for user in users %}
          <li>{{ user.fullName }}</li>
      {% endfor %}
  </ul>
  
  ```
  
  > **Comments:**
  > **username_0**: Thanks username_1 — I guess I'm trying to filter by 3 parameters in order:  First by School, then from those who are tagged with School, then by Skill(s), and then by keywords.  
  
  ---
  
  