API: Implement search resolver
This commit is contained in:
parent
c1f917efb4
commit
8cf92fa220
4 changed files with 84 additions and 6 deletions
6
go.mod
6
go.mod
|
@ -3,20 +3,21 @@ module git.sr.ht/~sircmpwn/searchhut
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
git.sr.ht/~sircmpwn/getopt v1.0.0
|
||||
github.com/99designs/gqlgen v0.17.12
|
||||
github.com/go-chi/chi v1.5.4
|
||||
github.com/go-shiori/go-readability v0.0.0-20220215145315-dd6828d2f09b
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/temoto/robotstxt v1.1.2
|
||||
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec
|
||||
github.com/vektah/gqlparser/v2 v2.4.6
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
||||
)
|
||||
|
||||
require (
|
||||
git.sr.ht/~sircmpwn/getopt v1.0.0 // indirect
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/go-chi/chi v1.5.4 // indirect
|
||||
github.com/go-shiori/dom v0.0.0-20210627111528-4e4722cd0d65 // indirect
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
|
@ -26,7 +27,6 @@ require (
|
|||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.8.1 // indirect
|
||||
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
|
||||
|
|
|
@ -42,7 +42,7 @@ resolver:
|
|||
# gqlgen will search for any type names in the schema in these go packages
|
||||
# if they match it will use them, otherwise it will generate them.
|
||||
autobind:
|
||||
# - "git.sr.ht/~sircmpwn/searchhut/graph/model"
|
||||
- "git.sr.ht/~sircmpwn/searchhut/graph/model"
|
||||
|
||||
# This section declares type mapping between the GraphQL and go type systems
|
||||
#
|
||||
|
|
18
graph/model/page.go
Normal file
18
graph/model/page.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Page struct {
|
||||
ID int `json:"id"`
|
||||
URL string `json:"url"`
|
||||
LastIndexed time.Time `json:"last_indexed"`
|
||||
Title *string `json:"title"`
|
||||
Language *string `json:"language"`
|
||||
Description *string `json:"description"`
|
||||
Author *string `json:"author"`
|
||||
Excerpt *string `json:"excerpt"`
|
||||
|
||||
DomainID int
|
||||
}
|
|
@ -5,18 +5,78 @@ package graph
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"git.sr.ht/~sircmpwn/searchhut/database"
|
||||
"git.sr.ht/~sircmpwn/searchhut/graph/generated"
|
||||
"git.sr.ht/~sircmpwn/searchhut/graph/model"
|
||||
)
|
||||
|
||||
// Search is the resolver for the search field.
|
||||
func (r *queryResolver) Search(ctx context.Context, query string) ([]*model.Result, error) {
|
||||
// Domain is the resolver for the domain field.
|
||||
func (r *pageResolver) Domain(ctx context.Context, obj *model.Page) (*model.Domain, error) {
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
}
|
||||
|
||||
// Search is the resolver for the search field.
|
||||
func (r *queryResolver) Search(ctx context.Context, query string) ([]*model.Result, error) {
|
||||
var results []*model.Result
|
||||
|
||||
if err := database.WithTx(ctx, &sql.TxOptions{
|
||||
Isolation: 0,
|
||||
ReadOnly: true,
|
||||
}, func(tx *sql.Tx) error {
|
||||
rows, err := tx.QueryContext(ctx, `
|
||||
SELECT
|
||||
id,
|
||||
domain_id,
|
||||
url,
|
||||
last_index_date,
|
||||
title,
|
||||
language,
|
||||
description,
|
||||
author,
|
||||
excerpt,
|
||||
ts_rank_cd(fts_vector, websearch_to_tsquery('english', $1), 32) AS rank
|
||||
FROM page
|
||||
WHERE websearch_to_tsquery('english', $1) @@ fts_vector
|
||||
ORDER BY rank DESC
|
||||
LIMIT 10;
|
||||
`, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
rank float32
|
||||
page model.Page
|
||||
)
|
||||
if err := rows.Scan(&page.ID, &page.DomainID, &page.URL,
|
||||
&page.LastIndexed, &page.Title, &page.Language,
|
||||
&page.Description, &page.Author, &page.Excerpt,
|
||||
&rank); err != nil {
|
||||
return err
|
||||
}
|
||||
results = append(results, &model.Result{
|
||||
Page: &page,
|
||||
Context: nil, // TODO
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// Page returns generated.PageResolver implementation.
|
||||
func (r *Resolver) Page() generated.PageResolver { return &pageResolver{r} }
|
||||
|
||||
// Query returns generated.QueryResolver implementation.
|
||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type pageResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
|
|
Loading…
Reference in a new issue