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) {
// Check if prepared cache should be skipped
if len(args) > 0 && args[0] == dbNoCache {
return db.db.Query(query, args[1:]...)
}
// Use prepared statement
st, _ := db.prepare(query)
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) {
// 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
st, _ := db.prepare(query)
if st != nil {

View File

@ -3,6 +3,8 @@ package main
import (
"net/http"
"sort"
"github.com/thoas/go-funk"
)
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 {
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
blog := r.Context().Value(blogContextKey).(string)
a.render(w, r, templateEditorFiles, &renderData{
BlogString: blog,
Data: map[string]interface{}{
"Files": files,
"Uses": uses,
},
})
}

View File

@ -471,15 +471,44 @@ func (d *database) allPublishedDates(blog string) (dates []publishedDate, err er
return
}
func (db *database) usesOfMediaFile(name string) (count int, err error) {
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)"
row, err := db.queryRow(query, sql.Named("fts_name", fmt.Sprintf("\"%s\"", name)), sql.Named("name", name))
if err != nil {
return 0, err
const mediaUseSql = `
with mediafiles (name) as (values %s)
select name, count(path) as count from (
select distinct m.name, p.path
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 {
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})
require.NoError(t, err)
count, err := app.db.usesOfMediaFile("test.jpg")
counts, err := app.db.usesOfMediaFile("test.jpg")
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,
"shorturl": a.shortPostURL,
// Others
"dateformat": dateFormat,
"isodate": isoDateFormat,
"unixtodate": unixToLocalDateString,
"now": localNowString,
"asset": a.assetFileName,
"string": a.ts.GetTemplateStringVariantFunc(),
"include": a.includeRenderedTemplate,
"urlize": urlize,
"sort": sortedStrings,
"absolute": a.getFullAddress,
"mentions": a.db.getWebmentionsByAddress,
"geotitle": a.geoTitle,
"geolink": geoOSMLink,
"opensearch": openSearchUrl,
"mbytes": mBytesString,
"mediafileuses": a.db.usesOfMediaFile,
"dateformat": dateFormat,
"isodate": isoDateFormat,
"unixtodate": unixToLocalDateString,
"now": localNowString,
"asset": a.assetFileName,
"string": a.ts.GetTemplateStringVariantFunc(),
"include": a.includeRenderedTemplate,
"urlize": urlize,
"sort": sortedStrings,
"absolute": a.getFullAddress,
"mentions": a.db.getWebmentionsByAddress,
"geotitle": a.geoTitle,
"geolink": geoOSMLink,
"opensearch": openSearchUrl,
"mbytes": mBytesString,
}
baseTemplate, err := template.New("base").Funcs(templateFunctions).ParseFiles(path.Join(templatesDir, templateBase+templatesExt))
if err != nil {

View File

@ -9,8 +9,9 @@
{{ if .Data.Files }}
<form class="fw-form p" method="post">
<select name="filename">
{{ $uses := .Data.Uses }}
{{ 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 }}
</select>
<input type="submit" formaction="{{ .Blog.RelativePath "/editor/files/view" }}" value="{{ string .Blog.Lang "view" }}">