@ -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 ... )