Blog multi-tag query: whitelist and blacklist
* Can query blog posts by multiple tags now. * e.g. /tagged/blog,updates,-photos would query all posts that have tags "blog" OR "updates" but NOT show any post with tag "photos"
This commit is contained in:
parent
bf86ceb585
commit
b642562792
|
@ -141,14 +141,16 @@ func (m postMan) GetPostsByTag(tag, privacy string, page, perPage int) (PagedPos
|
|||
pp.PerPage = 20
|
||||
}
|
||||
|
||||
// Get the distinct post IDs for this tag.
|
||||
var tags []TaggedPost
|
||||
var postIDs []uint
|
||||
r := DB.Where("tag = ?", tag).Find(&tags)
|
||||
for _, taggedPost := range tags {
|
||||
postIDs = append(postIDs, taggedPost.PostID)
|
||||
// Multi-tag query.
|
||||
whitelist, blacklist, err := ParseMultitag(tag)
|
||||
if err != nil {
|
||||
return pp, err
|
||||
}
|
||||
|
||||
// Query the whitelist of post IDs which match the whitelist tags.
|
||||
postIDs := getPostIDsByTag("tag IN (?)", whitelist)
|
||||
notPostIDs := getPostIDsByTag("tag IN (?)", blacklist)
|
||||
postIDs = narrowWhitelistByBlacklist(postIDs, notPostIDs)
|
||||
if len(postIDs) == 0 {
|
||||
return pp, errors.New("no posts found")
|
||||
}
|
||||
|
@ -162,7 +164,7 @@ func (m postMan) GetPostsByTag(tag, privacy string, page, perPage int) (PagedPos
|
|||
query.Model(&Post{}).Count(&pp.Total)
|
||||
|
||||
// Query the paginated slice of results.
|
||||
r = query.
|
||||
r := query.
|
||||
Offset((page - 1) * perPage).
|
||||
Limit(perPage).
|
||||
Find(&pp.Posts)
|
||||
|
@ -182,6 +184,43 @@ func (m postMan) GetPostsByTag(tag, privacy string, page, perPage int) (PagedPos
|
|||
return pp, r.Error
|
||||
}
|
||||
|
||||
// getPostIDsByTag helper function returns the post IDs that either whitelist,
|
||||
// or blacklist, a set of tags.
|
||||
func getPostIDsByTag(query string, value []string) []int {
|
||||
var tags []TaggedPost
|
||||
var result []int
|
||||
|
||||
if len(value) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
DB.Where(query, value).Find(&tags)
|
||||
for _, tag := range tags {
|
||||
result = append(result, tag.PostID)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// narrowWhitelistByBlacklist removes IDs in whitelist that appear in blacklist.
|
||||
func narrowWhitelistByBlacklist(wl []int, bl []int) []int {
|
||||
// Map the blacklist into a hash map.
|
||||
var blacklist = map[int]interface{}{}
|
||||
for _, id := range bl {
|
||||
blacklist[id] = nil
|
||||
}
|
||||
|
||||
// Limit the whitelist by the blacklist.
|
||||
var result []int
|
||||
for _, id := range wl {
|
||||
if _, ok := blacklist[id]; !ok {
|
||||
result = append(result, id)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetArchive queries the archive view of the blog.
|
||||
// Set private=true to return private posts, false returns public only.
|
||||
func (m postMan) GetArchive(private bool) ([]*PostArchive, error) {
|
||||
|
@ -271,6 +310,32 @@ func (m postMan) CountComments(posts ...Post) (map[int]int, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// ParseMultitag parses a tag string to return arrays for IN and NOT IN queries from DB.
|
||||
//
|
||||
// Example input: "blog,updates,-photo,-ask"
|
||||
// Returns: ["blog", "updates"], ["photo", "ask"]
|
||||
func ParseMultitag(tagline string) (whitelist, blacklist []string, err error) {
|
||||
words := strings.Split(tagline, ",")
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
if len(word) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Negation
|
||||
if strings.HasPrefix(word, "-") {
|
||||
blacklist = append(blacklist, strings.TrimPrefix(word, "-"))
|
||||
} else {
|
||||
whitelist = append(whitelist, word)
|
||||
}
|
||||
}
|
||||
|
||||
if len(whitelist) == 0 && len(blacklist) == 0 {
|
||||
err = errors.New("parsing error")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CountComments on the posts in a PagedPosts list.
|
||||
func (pp *PagedPosts) CountComments() error {
|
||||
counts, err := Posts.CountComments(pp.Posts...)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
type TaggedPost struct {
|
||||
ID int `gorm:"primary_key"`
|
||||
Tag string
|
||||
PostID uint // foreign key to Post
|
||||
PostID int // foreign key to Post
|
||||
}
|
||||
|
||||
// SummarizeTags returns the list of all tags ordered by frequency used.
|
||||
|
|
Loading…
Reference in New Issue
Block a user