Further improve counting of media uses

This commit is contained in:
Jan-Lukas Else 2021-07-12 19:29:32 +02:00
parent d98ae73af0
commit 529d8b616b
6 changed files with 83 additions and 27 deletions

View File

@ -185,6 +185,10 @@ func (db *database) exec(query string, args ...interface{}) (sql.Result, error)
} }
func (db *database) query(query string, args ...interface{}) (*sql.Rows, error) { func (db *database) query(query string, args ...interface{}) (*sql.Rows, error) {
// Check if prepared cache should be skipped
if len(args) > 0 && args[0] == dbNoCache {
return db.db.Query(query, args[1:]...)
}
// Use prepared statement // Use prepared statement
st, _ := db.prepare(query) st, _ := db.prepare(query)
if st != nil { if st != nil {
@ -195,6 +199,10 @@ func (db *database) query(query string, args ...interface{}) (*sql.Rows, error)
} }
func (db *database) queryRow(query string, args ...interface{}) (*sql.Row, error) { func (db *database) queryRow(query string, args ...interface{}) (*sql.Row, error) {
// Check if prepared cache should be skipped
if len(args) > 0 && args[0] == dbNoCache {
return db.db.QueryRow(query, args[1:]...), nil
}
// Use prepared statement // Use prepared statement
st, _ := db.prepare(query) st, _ := db.prepare(query)
if st != nil { if st != nil {

View File

@ -3,6 +3,8 @@ package main
import ( import (
"net/http" "net/http"
"sort" "sort"
"github.com/thoas/go-funk"
) )
func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
@ -15,12 +17,26 @@ func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) {
sort.Slice(files, func(i, j int) bool { sort.Slice(files, func(i, j int) bool {
return files[i].Time.After(files[j].Time) return files[i].Time.After(files[j].Time)
}) })
// Find uses
fileNames, ok := funk.Map(files, func(f *mediaFile) string {
return f.Name
}).([]string)
if !ok {
a.serveError(w, r, "Failed to get file names", http.StatusInternalServerError)
return
}
uses, err := a.db.usesOfMediaFile(fileNames...)
if err != nil {
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
// Serve HTML // Serve HTML
blog := r.Context().Value(blogContextKey).(string) blog := r.Context().Value(blogContextKey).(string)
a.render(w, r, templateEditorFiles, &renderData{ a.render(w, r, templateEditorFiles, &renderData{
BlogString: blog, BlogString: blog,
Data: map[string]interface{}{ Data: map[string]interface{}{
"Files": files, "Files": files,
"Uses": uses,
}, },
}) })
} }

View File

@ -471,15 +471,44 @@ func (d *database) allPublishedDates(blog string) (dates []publishedDate, err er
return return
} }
func (db *database) usesOfMediaFile(name string) (count int, err error) { const mediaUseSql = `
query := "select count(distinct path) from (select path from posts_fts where content match @fts_name union all select path from post_parameters where instr(value, @name) > 0)" with mediafiles (name) as (values %s)
row, err := db.queryRow(query, sql.Named("fts_name", fmt.Sprintf("\"%s\"", name)), sql.Named("name", name)) select name, count(path) as count from (
if err != nil { select distinct m.name, p.path
return 0, err from mediafiles m, post_parameters p
where instr(p.value, m.name) > 0
union
select distinct m.name, p.path
from mediafiles m, posts_fts p
where p.content match '"' || m.name || '"'
)
group by name;
`
func (db *database) usesOfMediaFile(names ...string) (counts map[string]int, err error) {
sqlArgs := []interface{}{dbNoCache}
nameValues := ""
for i, n := range names {
if i > 0 {
nameValues += ", "
}
named := fmt.Sprintf("name%v", i)
nameValues += fmt.Sprintf("(@%s)", named)
sqlArgs = append(sqlArgs, sql.Named(named, n))
} }
err = row.Scan(&count) rows, err := db.query(fmt.Sprintf(mediaUseSql, nameValues), sqlArgs...)
if err != nil { if err != nil {
return 0, err return nil, err
} }
return count, nil counts = map[string]int{}
var name string
var count int
for rows.Next() {
err = rows.Scan(&name, &count)
if err != nil {
return nil, err
}
counts[name] = count
}
return counts, nil
} }

View File

@ -338,7 +338,10 @@ func Test_usesOfMediaFile(t *testing.T) {
}, &postCreationOptions{new: true}) }, &postCreationOptions{new: true})
require.NoError(t, err) require.NoError(t, err)
count, err := app.db.usesOfMediaFile("test.jpg") counts, err := app.db.usesOfMediaFile("test.jpg")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 2, count) assert.Len(t, counts, 1)
if assert.NotNil(t, counts["test.jpg"]) {
assert.Equal(t, 2, counts["test.jpg"])
}
} }

View File

@ -56,22 +56,21 @@ func (a *goBlog) initRendering() error {
"translations": a.postTranslations, "translations": a.postTranslations,
"shorturl": a.shortPostURL, "shorturl": a.shortPostURL,
// Others // Others
"dateformat": dateFormat, "dateformat": dateFormat,
"isodate": isoDateFormat, "isodate": isoDateFormat,
"unixtodate": unixToLocalDateString, "unixtodate": unixToLocalDateString,
"now": localNowString, "now": localNowString,
"asset": a.assetFileName, "asset": a.assetFileName,
"string": a.ts.GetTemplateStringVariantFunc(), "string": a.ts.GetTemplateStringVariantFunc(),
"include": a.includeRenderedTemplate, "include": a.includeRenderedTemplate,
"urlize": urlize, "urlize": urlize,
"sort": sortedStrings, "sort": sortedStrings,
"absolute": a.getFullAddress, "absolute": a.getFullAddress,
"mentions": a.db.getWebmentionsByAddress, "mentions": a.db.getWebmentionsByAddress,
"geotitle": a.geoTitle, "geotitle": a.geoTitle,
"geolink": geoOSMLink, "geolink": geoOSMLink,
"opensearch": openSearchUrl, "opensearch": openSearchUrl,
"mbytes": mBytesString, "mbytes": mBytesString,
"mediafileuses": a.db.usesOfMediaFile,
} }
baseTemplate, err := template.New("base").Funcs(templateFunctions).ParseFiles(path.Join(templatesDir, templateBase+templatesExt)) baseTemplate, err := template.New("base").Funcs(templateFunctions).ParseFiles(path.Join(templatesDir, templateBase+templatesExt))
if err != nil { if err != nil {

View File

@ -9,8 +9,9 @@
{{ if .Data.Files }} {{ if .Data.Files }}
<form class="fw-form p" method="post"> <form class="fw-form p" method="post">
<select name="filename"> <select name="filename">
{{ $uses := .Data.Uses }}
{{ range $i, $file := .Data.Files }} {{ range $i, $file := .Data.Files }}
<option value="{{ $file.Name }}">{{ $file.Name }} ({{ isodate $file.Time.String }}, {{ mbytes $file.Size }}, ~{{ mediafileuses $file.Name }} {{ string $blog.Lang "fileuses" }})</option> <option value="{{ $file.Name }}">{{ $file.Name }} ({{ isodate $file.Time.String }}, {{ mbytes $file.Size }}, ~{{ index $uses $file.Name }} {{ string $blog.Lang "fileuses" }})</option>
{{ end }} {{ end }}
</select> </select>
<input type="submit" formaction="{{ .Blog.RelativePath "/editor/files/view" }}" value="{{ string .Blog.Lang "view" }}"> <input type="submit" formaction="{{ .Blog.RelativePath "/editor/files/view" }}" value="{{ string .Blog.Lang "view" }}">