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"master
parent
bf86ceb585
commit
b642562792
|
@ -141,14 +141,16 @@ func (m postMan) GetPostsByTag(tag, privacy string, page, perPage int) (PagedPos
|
||||||
pp.PerPage = 20
|
pp.PerPage = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the distinct post IDs for this tag.
|
// Multi-tag query.
|
||||||
var tags []TaggedPost
|
whitelist, blacklist, err := ParseMultitag(tag)
|
||||||
var postIDs []uint
|
if err != nil {
|
||||||
r := DB.Where("tag = ?", tag).Find(&tags)
|
return pp, err
|
||||||
for _, taggedPost := range tags {
|
|
||||||
postIDs = append(postIDs, taggedPost.PostID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
if len(postIDs) == 0 {
|
||||||
return pp, errors.New("no posts found")
|
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.Model(&Post{}).Count(&pp.Total)
|
||||||
|
|
||||||
// Query the paginated slice of results.
|
// Query the paginated slice of results.
|
||||||
r = query.
|
r := query.
|
||||||
Offset((page - 1) * perPage).
|
Offset((page - 1) * perPage).
|
||||||
Limit(perPage).
|
Limit(perPage).
|
||||||
Find(&pp.Posts)
|
Find(&pp.Posts)
|
||||||
|
@ -182,6 +184,43 @@ func (m postMan) GetPostsByTag(tag, privacy string, page, perPage int) (PagedPos
|
||||||
return pp, r.Error
|
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.
|
// GetArchive queries the archive view of the blog.
|
||||||
// Set private=true to return private posts, false returns public only.
|
// Set private=true to return private posts, false returns public only.
|
||||||
func (m postMan) GetArchive(private bool) ([]*PostArchive, error) {
|
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
|
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.
|
// CountComments on the posts in a PagedPosts list.
|
||||||
func (pp *PagedPosts) CountComments() error {
|
func (pp *PagedPosts) CountComments() error {
|
||||||
counts, err := Posts.CountComments(pp.Posts...)
|
counts, err := Posts.CountComments(pp.Posts...)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
type TaggedPost struct {
|
type TaggedPost struct {
|
||||||
ID int `gorm:"primary_key"`
|
ID int `gorm:"primary_key"`
|
||||||
Tag string
|
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.
|
// SummarizeTags returns the list of all tags ordered by frequency used.
|
||||||
|
|
Loading…
Reference in New Issue