feat: negative tag search

This commit is contained in:
Damillora 2022-04-16 19:23:19 +07:00
parent a0868e15a1
commit a1da324f56
12 changed files with 739 additions and 841 deletions

View File

@ -70,7 +70,10 @@ func tagGetRelated(c *gin.Context) {
}
func tagAutocomplete(c *gin.Context) {
tags := services.GetTagAutocomplete()
tagParam := c.Query("tag")
positiveParam := c.Query("positive")
tags := services.GetTagAutocomplete(tagParam, positiveParam != "")
c.JSON(http.StatusOK, tags)
}

View File

@ -2,6 +2,7 @@ package services
import (
"fmt"
"strings"
"github.com/Damillora/Shioriko/pkg/database"
"github.com/Damillora/Shioriko/pkg/models"
@ -17,36 +18,74 @@ func GetPostAll(page int) []database.Post {
}
func GetPostTags(page int, tagSyntax []string) []database.Post {
tags, err := ParseReadTags(tagSyntax)
positiveTagSyntax := []string{}
negativeTagSyntax := []string{}
for _, tag := range tagSyntax {
if strings.HasPrefix(tag, "-") {
negativeTagSyntax = append(negativeTagSyntax, strings.TrimPrefix(tag, "-"))
} else {
positiveTagSyntax = append(positiveTagSyntax, tag)
}
}
positiveTags, err := ParseReadTags(positiveTagSyntax)
if err != nil {
return []database.Post{}
}
negativeTags, err := ParseReadTags(negativeTagSyntax)
if err != nil {
return []database.Post{}
}
var tagIds []string
for _, tag := range tags {
tagIds = append(tagIds, tag.ID)
var positiveTagIds []string
for _, tag := range positiveTags {
positiveTagIds = append(positiveTagIds, tag.ID)
}
fmt.Printf("%v", tagIds)
var postIds []string
var negativeTagIds []string
for _, tag := range negativeTags {
negativeTagIds = append(negativeTagIds, tag.ID)
}
var positivePostIds []string
database.DB.
Model(&tags).
Model(&positiveTags).
Joins("join post_tags on post_tags.tag_id = tags.id").
Select("post_tags.post_id").
Where("post_tags.tag_id IN ?", tagIds).
Where("post_tags.tag_id IN ?", positiveTagIds).
Group("post_tags.post_id").
Having("count(*) = ?", len(tagIds)).
Having("count(*) = ?", len(positiveTagIds)).
Distinct().
Find(&postIds)
Find(&positivePostIds)
var negativePostIds []string
database.DB.
Model(&positiveTags).
Joins("join post_tags on post_tags.tag_id = tags.id").
Select("post_tags.post_id").
Where("post_tags.tag_id IN ?", negativeTagIds).
Group("post_tags.post_id").
Having("count(*) = ?", len(negativeTagIds)).
Distinct().
Find(&negativePostIds)
var posts []database.Post
database.DB.
query := database.DB.
Joins("Blob").
Preload("Tags").
Preload("Tags.TagType").
Where("posts.id IN ?", postIds).
Order("created_at desc").
Preload("Tags.TagType")
if len(positivePostIds) > 0 && len(negativePostIds) > 0 {
query = query.
Where("posts.id IN ? AND posts.id NOT IN ?", positivePostIds, negativePostIds)
} else if len(positivePostIds) > 0 {
query = query.
Where("posts.id IN ?", positivePostIds)
} else if len(negativePostIds) > 0 {
query = query.
Where("posts.id NOT IN ?", negativePostIds)
}
query.Order("created_at desc").
Offset((page - 1) * perPage).
Limit(20).
Find(&posts)
@ -118,28 +157,73 @@ func CountPostPages() int {
return int(count)
}
func CountPostPagesTag(tagSyntax []string) int {
tags, err := ParseReadTags(tagSyntax)
positiveTagSyntax := []string{}
negativeTagSyntax := []string{}
for _, tag := range tagSyntax {
if strings.HasPrefix(tag, "-") {
negativeTagSyntax = append(negativeTagSyntax, strings.TrimPrefix(tag, "-"))
} else {
positiveTagSyntax = append(positiveTagSyntax, tag)
}
}
positiveTags, err := ParseReadTags(positiveTagSyntax)
if err != nil {
return 0
}
negativeTags, err := ParseReadTags(negativeTagSyntax)
if err != nil {
return 0
}
var tagIds []string
for _, tag := range tags {
tagIds = append(tagIds, tag.ID)
var positiveTagIds []string
for _, tag := range positiveTags {
positiveTagIds = append(positiveTagIds, tag.ID)
}
fmt.Printf("%v", tagIds)
var count int64
var negativeTagIds []string
for _, tag := range negativeTags {
negativeTagIds = append(negativeTagIds, tag.ID)
}
var positivePostIds []string
database.DB.
Model(&tags).
Distinct().
Model(&positiveTags).
Joins("join post_tags on post_tags.tag_id = tags.id").
Select("post_tags.post_id").
Where("post_tags.tag_id IN ?", tagIds).
Where("post_tags.tag_id IN ?", positiveTagIds).
Group("post_tags.post_id").
Having("count(*) = ?", len(tagIds)).
Count(&count)
Having("count(*) = ?", len(positiveTagIds)).
Distinct().
Find(&positivePostIds)
var negativePostIds []string
database.DB.
Model(&positiveTags).
Joins("join post_tags on post_tags.tag_id = tags.id").
Select("post_tags.post_id").
Where("post_tags.tag_id IN ?", negativeTagIds).
Group("post_tags.post_id").
Having("count(*) = ?", len(negativeTagIds)).
Distinct().
Find(&negativePostIds)
var count int64
query := database.DB.
Model(&database.Post{})
if len(positivePostIds) > 0 && len(negativePostIds) > 0 {
query = query.
Where("posts.id IN ? AND posts.id NOT IN ?", positivePostIds, negativePostIds)
} else if len(positivePostIds) > 0 {
query = query.
Where("posts.id IN ?", positivePostIds)
} else if len(negativePostIds) > 0 {
query = query.
Where("posts.id NOT IN ?", negativePostIds)
}
query.
Count(&count)
fmt.Println(count)
return int(count)
}

View File

@ -61,12 +61,56 @@ func GetTag(tagString string) (*models.TagReadModel, error) {
return &tagModel, nil
}
func GetTagAutocomplete() []string {
func GetTagAutocomplete(searchValue string, forcePositive bool) []string {
if forcePositive {
return getPositiveTagAutocomplete(strings.TrimPrefix(searchValue, "-"))
}
if strings.HasPrefix(searchValue, "-") {
return getNegativeTagAutocomplete(strings.TrimPrefix(searchValue, "-"))
} else {
return getPositiveTagAutocomplete(searchValue)
}
}
func getPositiveTagAutocomplete(searchValue string) []string {
var tags []string
result := database.DB.Model(&database.Tag{}).
query := database.DB.Model(&database.Tag{}).
Joins("join tag_types on tag_types.id = tags.tag_type_id").
Select("concat(tag_types.name,':',tags.name) as name").
Find(&tags)
Select("concat(tag_types.name,':',tags.name) as name")
tagFields := strings.Split(searchValue, ":")
if len(tagFields) == 2 {
query = query.
Where("tags.name LIKE ?", "%"+tagFields[1]+"%").
Where("tag_types.name = ?", tagFields[0])
} else if len(tagFields) == 1 {
query = query.
Where("tags.name LIKE ?", "%"+tagFields[0]+"%")
}
result := query.Find(&tags)
if result.Error != nil {
return []string{}
}
return tags
}
func getNegativeTagAutocomplete(searchValue string) []string {
var tags []string
query := database.DB.Model(&database.Tag{}).
Joins("join tag_types on tag_types.id = tags.tag_type_id").
Select("concat('-',tag_types.name,':',tags.name) as name")
tagFields := strings.Split(searchValue, ":")
if len(tagFields) == 2 {
query = query.
Where("tags.name LIKE ?", "%"+tagFields[1]+"%").
Where("tag_types.name = ?", tagFields[0])
} else if len(tagFields) == 1 {
query = query.
Where("tags.name LIKE ?", "%"+tagFields[0]+"%")
}
result := query.Find(&tags)
if result.Error != nil {
return []string{}
}

View File

@ -18,16 +18,13 @@
"svelte": "^3.0.0"
},
"dependencies": {
"@svelte-parts/editor": "^0.0.26",
"@tiptap/core": "^2.0.0-beta.174",
"@tiptap/starter-kit": "^2.0.0-beta.183",
"@rollup/plugin-json": "^4.1.0",
"axios": "^0.21.1",
"bulma": "^0.9.2",
"node-sass": "^6.0.0",
"postcss": "^8.2.14",
"query-string": "^7.0.0",
"sirv-cli": "^1.0.0",
"svelte-infinite-scroll": "^1.5.2",
"svelte-preprocess": "^4.7.3",
"svelte-routing": "^1.6.0",
"svelte-tags-input": "^2.7.1"

View File

@ -1,3 +1,4 @@
import json from '@rollup/plugin-json';
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
@ -40,6 +41,7 @@ export default {
entryFileNames: 'bundle.js'
},
plugins: [
json(),
svelte({
compilerOptions: {
// enable run-time checks when not in production

View File

@ -25,8 +25,8 @@
form.tags = value.detail.tags;
};
const onAutocomplete = async () => {
const list = await getTagAutocomplete();
const onAutocomplete = async (tag) => {
const list = await getTagAutocomplete({ tag, positive: true });
return list;
};
@ -97,6 +97,7 @@
addKeys={[9, 32]}
on:tags={onTagChange}
autoComplete={onAutocomplete}
autoCompleteFilter={false}
/>
</div>
</div>

View File

@ -53,8 +53,11 @@ export async function getRelatedTags({ tag }) {
const response = await axios.get(endpoint);
return response.data;
}
export async function getTagAutocomplete() {
const endpoint = url + "/api/tag-autocomplete";
export async function getTagAutocomplete({ tag, positive }) {
let endpoint = url + "/api/tag-autocomplete?tag=" + tag;
if (positive) {
endpoint = endpoint + "&positive=true";
}
const response = await axios.get(endpoint);
return response.data;
}

View File

@ -21,9 +21,3 @@
</div>
</AuthCheck>
</div>
<style>
.pre-line {
white-space: pre-line;
}
</style>

View File

@ -9,10 +9,10 @@
searchTerms = value.detail.tags;
};
const onAutocomplete = async () => {
const list = await getTagAutocomplete();
return list;
};
const onAutocomplete = async (tag) => {
const list = await getTagAutocomplete({ tag });
return list;
};
const onSearch = (i) => {
if (searchTerms.length > 0) {
@ -41,6 +41,7 @@
addKeys={[9, 32]}
on:tags={onTagChange}
autoComplete={onAutocomplete}
autoCompleteFilter={false}
/>
</div>
</div>

View File

@ -42,7 +42,7 @@
pagination = paginate(page, totalPages);
}
if (searchTerms.length == 1) {
if (searchTerms.filter(x => !x.startsWith("-")).length == 1) {
tagInfo = await getTag({ tag: searchTerms[0] });
}
};
@ -52,8 +52,8 @@
searchTerms = value.detail.tags;
};
const onAutocomplete = async () => {
const list = await getTagAutocomplete();
const onAutocomplete = async (tag) => {
const list = await getTagAutocomplete({ tag });
return list;
};
@ -100,6 +100,7 @@
addKeys={[9, 32]}
on:tags={onTagChange}
autoComplete={onAutocomplete}
autoCompleteFilter={false}
/>
</div>
</div>
@ -167,7 +168,6 @@
<div class="column is-full">
<nav
class="pagination is-centered"
role="navigation"
aria-label="pagination"
>
<a

View File

@ -38,8 +38,8 @@
form.tags = value.detail.tags;
};
const onAutocomplete = async () => {
const list = await getTagAutocomplete();
const onAutocomplete = async (tag) => {
const list = await getTagAutocomplete({ tag, positive: true });
return list;
};
@ -107,7 +107,7 @@
<div class="field">
<label for="tags" class="label">Tags</label>
<div class="control" id="tags">
<Tags addKeys={[9, 32]} on:tags={onTagChange} autoComplete={onAutocomplete} />
<Tags addKeys={[9, 32]} on:tags={onTagChange} autoComplete={onAutocomplete} autoCompleteFilter={false}/>
</div>
</div>
<div class="control">

File diff suppressed because it is too large Load Diff