# Lab 6: Full Text Search

We now have a lot of users in our system. We want to enable our users to search through the list of users.

{% hint style="info" %}
If you did Lab 5 previously you can remove the `Thread.sleep(3000)` from the `UserService.findAll()` method
{% endhint %}

### Search Users

In this lab, we will stream through the list of users and filter it using the Java streams API. Create a new method `searchUser` in the `UserService`.

<pre class="language-java" data-title="UserService.java"><code class="lang-java"><strong>public List&#x3C;EasyUser> searchUser(String searchString) {
</strong>  return easyUserList.stream().filter(
      it -> it.uuid.toString().contains(searchString)
            || it.username.contains(searchString)
            || it.password.contains(
          searchString)
  ).toList();
}
</code></pre>

### ListComponent

Create a new package `de.tschuehly.easy.spring.auth.web.list` and then create a `ListComponent.java` ViewComponent.

<pre class="language-java" data-title="ListComponent.java"><code class="lang-java">package de.tschuehly.easy.spring.auth.web.list;

import de.tschuehly.spring.viewcomponent.core.component.ViewComponent;
import de.tschuehly.spring.viewcomponent.jte.ViewContext;
import java.util.List;

<strong>@ViewComponent
</strong>public class ListComponent {

  public ViewContext render(List&#x3C;ViewContext> viewContextList){ // (1)
    return new ListContext(viewContextList);
  }

  public record ListContext(List&#x3C;ViewContext> viewContextList) 
    implements ViewContext {}
}
</code></pre>

(1): The render method has a `List<ViewContext>` parameter and pass it to the `ListContext`

We then create a `ListComponent.jte` template and loop through the ViewContext List with the `@for` syntax and then render each ViewContext.

{% code title="ListComponent.jte" %}

```java
@import de.tschuehly.easy.spring.auth.web.list.ListComponent.ListContext
@param ListContext listContext

@for(var context: listContext.viewContextList())
    ${context}
@endfor
```

{% endcode %}

Next, we will autowire the `listComponent` we just created in the `UserTableComponent` and create a `renderSearch` method.

{% code title="UserTableComponent.java" %}

```java
public ViewContext renderSearch(String searchQuery) {
  List<ViewContext> userRowList = userService.searchUser(searchQuery) // (1)
      .stream().map(userRowComponent::render) // (2)
      .toList();
  return listComponent.render(userRowList); // (3)
}
```

{% endcode %}

**(1):** We will pass the `searchQuery` parameter to the `userService.searchUser` method&#x20;

**(2):** We stream through the list of users and then render each user with the `userRowComponent::render` method.

**(3):** We then pass the `userRowList` to the `listComponent.render` method and return the result.

### UserController

In the `UserController` we first need to autowire the `UserTableComponent`  .

Then we create a `GET_SEARCH_USER` and `SEARCH_PARAM`constant and a search user endpoint:

{% code title="UserController.java" %}

```java
public static final String GET_SEARCH_USER = "/search-user";
public static final String SEARCH_PARAM = "searchQuery";

@HxRequest
@GetMapping(GET_SEARCH_USER)
public ViewContext searchUser(
    @RequestParam(SEARCH_PARAM) String searchQuery // (1)
) {
  return userTableComponent.renderSearch(searchQuery); // (2)
}
```

{% endcode %}

**(1):** The endpoint has a `@RequestParam` that has `SEARCH_PARAM` as value

(2): In the method we call the `userTableComponent.renderSearch` method and return the result.

In the `UserTableComponent.jte` template we first need to add two imports:

```
@import static de.tschuehly.easy.spring.auth.user.UserController.*
@import de.tschuehly.easy.spring.auth.htmx.HtmxUtil
```

Then we will create a new `<tr>` element as the first child of the `<thead>`:

```clike
<tr>
  <th colspan="4">
    <label>
      Search Users
      <input type="text"
        name="${SEARCH_PARAM}" <%-- (1) --%>
        hx-get="${GET_SEARCH_USER}" <%-- (2) --%>
        hx-trigger="input changed delay:500ms, search" <%-- (3) --%>
        hx-target="${HtmxUtil.idSelector(USER_TABLE_BODY_ID)}"> <%-- (4) --%>
    </label>
  </th>
</tr>
```

**(1):** The `<input>` field has the `SEARCH_PARAM` constant as `name` attribute, this will map to the  `@RequestParam` we defined in the `UserController.searchUser` method.

**(2):** We then add a `hx-get` attribute and set it to `GET_SEARCH_USER` .

**(3):** We tell htmx to trigger the request when an `input` event is detected and the value has `changed`. Htmx then starts a `delay` timer of `500ms` . If the event is seen again in the 500ms it will reset the delay.

**(4):** We target the body of  the table with the `USER_TABLE_BODY_ID` and swap the innerHTML of it.

### Success!

If we restart the application and navigate to [http://localhost:8080](http://localhost:8080/) we can see that the search is working:

<figure><img src="/files/Z9tnaeyvHogQ8ikmpJQM" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Lab-6 Checkpoint 1

If you are stuck you can resume at this checkpoint with:

`git checkout tags/lab-6-checkpoint-1 -b lab-6-c1`
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tschuehly.gitbook.io/server-side-spring-htmx-workshop/lab-6-full-text-search.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
