mirror of
https://github.com/Damillora/Shioriko.git
synced 2024-12-22 16:03:45 +00:00
feat: negative tag search
This commit is contained in:
parent
a0868e15a1
commit
a1da324f56
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -21,9 +21,3 @@
|
||||
</div>
|
||||
</AuthCheck>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.pre-line {
|
||||
white-space: pre-line;
|
||||
}
|
||||
</style>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
1341
web/app/yarn.lock
1341
web/app/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user