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
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.sr.ht/~sircmpwn/getopt v1.0.0
|
||||||
github.com/99designs/gqlgen v0.17.12
|
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/go-shiori/go-readability v0.0.0-20220215145315-dd6828d2f09b
|
||||||
github.com/lib/pq v1.10.6
|
github.com/lib/pq v1.10.6
|
||||||
github.com/temoto/robotstxt v1.1.2
|
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
|
github.com/vektah/gqlparser/v2 v2.4.6
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.sr.ht/~sircmpwn/getopt v1.0.0 // indirect
|
|
||||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // 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/go-shiori/dom v0.0.0-20210627111528-4e4722cd0d65 // indirect
|
||||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 // indirect
|
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // 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/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
github.com/urfave/cli/v2 v2.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
|
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/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // 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
|
# 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.
|
# if they match it will use them, otherwise it will generate them.
|
||||||
autobind:
|
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
|
# 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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.sr.ht/~sircmpwn/searchhut/database"
|
||||||
"git.sr.ht/~sircmpwn/searchhut/graph/generated"
|
"git.sr.ht/~sircmpwn/searchhut/graph/generated"
|
||||||
"git.sr.ht/~sircmpwn/searchhut/graph/model"
|
"git.sr.ht/~sircmpwn/searchhut/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Search is the resolver for the search field.
|
// Domain is the resolver for the domain field.
|
||||||
func (r *queryResolver) Search(ctx context.Context, query string) ([]*model.Result, error) {
|
func (r *pageResolver) Domain(ctx context.Context, obj *model.Page) (*model.Domain, error) {
|
||||||
panic(fmt.Errorf("not implemented"))
|
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.
|
// Query returns generated.QueryResolver implementation.
|
||||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||||
|
|
||||||
|
type pageResolver struct{ *Resolver }
|
||||||
type queryResolver struct{ *Resolver }
|
type queryResolver struct{ *Resolver }
|
||||||
|
|
Loading…
Reference in a new issue