Full text profile search for the member directory

This commit is contained in:
Noah Petherbridge 2024-06-19 14:12:25 -07:00
parent 6ac121b345
commit 616f6ae76b
5 changed files with 68 additions and 14 deletions

View File

@ -68,6 +68,11 @@ var (
"music_movies",
"hide_age",
}
EssayProfileFields = []string{
"about_me",
"interests",
"music_movies",
}
// Site preference names (stored in ProfileField table)
SitePreferenceFields = []string{

View File

@ -32,7 +32,8 @@ func Search() http.HandlerFunc {
// Search filters.
var (
isCertified = r.FormValue("certified")
username = r.FormValue("name") // username search
username = r.FormValue("name") // username search
searchTerm = r.FormValue("search") // profile text search
gender = r.FormValue("gender")
orientation = r.FormValue("orientation")
maritalStatus = r.FormValue("marital_status")
@ -48,6 +49,8 @@ func Search() http.HandlerFunc {
ageMin, ageMax = ageMax, ageMin
}
search := models.ParseSearchString(searchTerm)
// Get current user.
currentUser, err := session.CurrentUser(r)
if err != nil {
@ -91,6 +94,7 @@ func Search() http.HandlerFunc {
Orientation: orientation,
MaritalStatus: maritalStatus,
HereFor: hereFor,
ProfileText: search,
Certified: isCertified == "true",
NotCertified: isCertified == "false",
InnerCircle: isCertified == "circle",
@ -127,6 +131,7 @@ func Search() http.HandlerFunc {
"MaritalStatus": maritalStatus,
"HereFor": hereFor,
"EmailOrUsername": username,
"Search": searchTerm,
"AgeMin": ageMin,
"AgeMax": ageMax,
"FriendSearch": friendSearch,

View File

@ -258,6 +258,7 @@ type UserSearch struct {
Orientation string
MaritalStatus string
HereFor string
ProfileText *Search
Certified bool
NotCertified bool
InnerCircle bool
@ -364,6 +365,30 @@ func SearchUsers(user *User, search *UserSearch, pager *Pagination) ([]*User, er
placeholders = append(placeholders, "here_for", "%"+search.HereFor+"%")
}
// Profile text search.
if terms := search.ProfileText; terms != nil {
for _, term := range terms.Includes {
var ilike = "%" + strings.ToLower(term) + "%"
wheres = append(wheres, `
EXISTS (
SELECT 1 FROM profile_fields
WHERE user_id = users.id AND name IN ? AND value ILIKE ?
)
`)
placeholders = append(placeholders, config.EssayProfileFields, ilike)
}
for _, term := range terms.Excludes {
var ilike = "%" + strings.ToLower(term) + "%"
wheres = append(wheres, `
NOT EXISTS (
SELECT 1 FROM profile_fields
WHERE user_id = users.id AND name IN ? AND value ILIKE ?
)
`)
placeholders = append(placeholders, config.EssayProfileFields, ilike)
}
}
// Only admin user can show disabled/banned users.
var statuses = []string{}
if user.HasAdminScope(config.ScopeUserBan) {

View File

@ -6,6 +6,7 @@ import (
"strings"
"time"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/log"
)
@ -55,7 +56,7 @@ func CountNotesAboutUser(currentUser *User, user *User) int64 {
count int64
)
if currentUser.IsAdmin {
if currentUser.HasAdminScope(config.ScopeUserNotes) {
wheres = append(wheres, "about_user_id = ?")
placeholders = append(placeholders, user.ID)
} else {

View File

@ -87,9 +87,9 @@
<div class="column pr-1">
<div class="field">
<label class="label">Status:</label>
<label class="label" for="certified">Status:</label>
<div class="select is-fullwidth">
<select id="certified" name="certified">
<select id="certified" name="certified" id="certified">
<optgroup label="Certification Status">
<option value="true">Only certified users</option>
<option value="false"{{if eq $Root.Certified "false"}} selected{{end}}>Non-certified only</option>
@ -115,15 +115,33 @@
<div class="column px-1">
<div class="field">
<label class="label">Name or username:</label>
<label class="label" for="name">Name or username:</label>
<input type="text" class="input"
name="name"
name="name" id="name"
autocomplete="off"
value="{{$Root.EmailOrUsername}}">
</div>
</div>
<div class="column px-1">
<div class="column is-half pl-1">
<div class="field">
<label class="label" for="search">Profile text:</label>
<input type="text" class="input"
name="search" id="search"
autocomplete="off"
value="{{$Root.Search}}">
<p class="help">
Tip: you can <span class="has-text-success">"quote exact phrases"</span> and
<span class="has-text-success">-exclude</span> words (or
<span class="has-text-success">-"exclude phrases"</span>) from your search.
</p>
</div>
</div>
</div>
<div class="columns is-centered">
<div class="column pr-1">
<div class="field">
<label class="label">Age:</label>
<div class="columns is-mobile is-gapless">
@ -151,7 +169,7 @@
</div>
</div>
<div class="column pl-1">
<div class="column px-1">
<div class="field">
<label class="label" for="gender">Gender:</label>
<div class="select is-fullwidth">
@ -165,10 +183,7 @@
</div>
</div>
</div>
<div class="columns is-centered">
<div class="column pr-1">
<div class="column px-1">
<div class="field">
<label class="label" for="orientation">Orientation:</label>
<div class="select is-fullwidth">
@ -182,7 +197,7 @@
</div>
</div>
<div class="column px-1">
<div class="column pl-1">
<div class="field">
<label class="label" for="marital_status">Relationship:</label>
<div class="select is-fullwidth">
@ -196,7 +211,10 @@
</div>
</div>
<div class="column px-1">
</div>
<div class="columns is-centered">
<div class="column pr-1">
<div class="field">
<label class="label" for="here_for">Here for:</label>
<div class="select is-fullwidth">