Pagination of search results (#20)

This commit is contained in:
Rok Garbas 2020-05-11 21:56:10 +02:00 committed by GitHub
parent d3c3e2e31b
commit c47f1c0ccb
Failed to generate hash of commit
5 changed files with 240 additions and 57 deletions

View file

@ -7,7 +7,6 @@ module ElasticSearch exposing
, decodeResult
, init
, makeRequest
, showLoadingOnQuery
, update
, view
)
@ -17,23 +16,34 @@ import Browser.Navigation
import Html
exposing
( Html
, a
, button
, div
, em
, form
, h1
, input
, li
, p
, pre
, text
, ul
)
import Html.Attributes
exposing
( class
, classList
, href
, type_
, value
)
import Html.Events
exposing
( onInput
( custom
, onClick
, onInput
, onSubmit
, preventDefaultOn
)
import Http
import Json.Decode
@ -46,6 +56,8 @@ type alias Model a =
{ query : Maybe String
, result : RemoteData.WebData (Result a)
, showDetailsFor : Maybe String
, from : Int
, size : Int
}
@ -78,11 +90,15 @@ type alias ResultItem a =
init :
Maybe String
-> Maybe String
-> Maybe Int
-> Maybe Int
-> ( Model a, Cmd msg )
init query showDetailsFor =
init query showDetailsFor from size =
( { query = query
, result = RemoteData.NotAsked
, showDetailsFor = showDetailsFor
, from = Maybe.withDefault 0 from
, size = Maybe.withDefault 15 size
}
, Cmd.none
)
@ -95,7 +111,8 @@ init query showDetailsFor =
type Msg a
= QueryInput String
= NoOp
| QueryInput String
| QuerySubmit
| QueryResponse (RemoteData.WebData (Result a))
| ShowDetails String
@ -109,6 +126,11 @@ update :
-> ( Model a, Cmd (Msg a) )
update path navKey msg model =
case msg of
NoOp ->
( model
, Cmd.none
)
QueryInput query ->
( { model | query = Just query }
, Cmd.none
@ -116,7 +138,11 @@ update path navKey msg model =
QuerySubmit ->
( model
, createUrl path model.query model.showDetailsFor
, createUrl path
model.query
model.showDetailsFor
0
model.size
|> Browser.Navigation.pushUrl navKey
)
@ -135,27 +161,23 @@ update path navKey msg model =
else
Just selected
)
model.from
model.size
|> Browser.Navigation.pushUrl navKey
)
showLoadingOnQuery : Model a -> Model a
showLoadingOnQuery model =
-- TODO: use this
{ model
| result =
case model.query of
Just query ->
RemoteData.Loading
Nothing ->
RemoteData.NotAsked
}
createUrl : String -> Maybe String -> Maybe String -> String
createUrl path query showDetailsFor =
[]
createUrl :
String
-> Maybe String
-> Maybe String
-> Int
-> Int
-> String
createUrl path query showDetailsFor from size =
[ Url.Builder.int "from" from
, Url.Builder.int "size" size
]
|> List.append
(query
|> Maybe.map
@ -181,14 +203,15 @@ createUrl path query showDetailsFor =
view :
{ title : String }
String
-> String
-> Model a
-> (Maybe String -> Result a -> Html b)
-> (Msg a -> b)
-> Html b
view options model viewSuccess outMsg =
view path title model viewSuccess outMsg =
div [ class "search-page" ]
[ h1 [ class "page-header" ] [ text options.title ]
[ h1 [ class "page-header" ] [ text title ]
, div [ class "search-input" ]
[ form [ onSubmit (outMsg QuerySubmit) ]
[ div [ class "input-append" ]
@ -200,15 +223,6 @@ view options model viewSuccess outMsg =
[]
, div [ class "btn-group" ]
[ button [ class "btn" ] [ text "Search" ]
--TODO: and option to select the right channel+version+evaluation
--, button [ class "btn" ] [ text "Loading ..." ]
--, div [ class "popover bottom" ]
-- [ div [ class "arrow" ] []
-- , div [ class "popover-title" ] [ text "Select options" ]
-- , div [ class "popover-content" ]
-- [ p [] [ text "Sed posuere consectetur est at lobortis. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum." ] ]
-- ]
]
]
]
@ -221,7 +235,30 @@ view options model viewSuccess outMsg =
div [] [ text "Loading" ]
RemoteData.Success result ->
viewSuccess model.showDetailsFor result
div []
[ p []
[ em []
[ text
("Showing results "
++ String.fromInt model.from
++ "-"
++ String.fromInt
(if model.from + model.size > result.hits.total.value then
result.hits.total.value
else
model.from + model.size
)
++ " of "
++ String.fromInt result.hits.total.value
++ "."
)
]
]
, viewPager outMsg model result path
, viewSuccess model.showDetailsFor result
, viewPager outMsg model result path
]
RemoteData.Failure error ->
div []
@ -232,6 +269,97 @@ view options model viewSuccess outMsg =
]
viewPager :
(Msg a -> b)
-> Model a
-> Result a
-> String
-> Html b
viewPager outMsg model result path =
ul [ class "pager" ]
[ li
[ classList
[ ( "disabled", model.from == 0 )
]
]
[ a
[ if model.from == 0 then
href "#disabled"
else
href <|
createUrl
path
model.query
model.showDetailsFor
0
model.size
]
[ text "First" ]
]
, li
[ classList
[ ( "disabled", model.from == 0 )
]
]
[ a
[ href <|
if model.from - model.size < 0 then
"#disabled"
else
createUrl
path
model.query
model.showDetailsFor
(model.from - model.size)
model.size
]
[ text "Previous" ]
]
, li
[ classList
[ ( "disabled", model.from + model.size >= result.hits.total.value )
]
]
[ a
[ href <|
if model.from + model.size >= result.hits.total.value then
"#disabled"
else
createUrl
path
model.query
model.showDetailsFor
(model.from + model.size)
model.size
]
[ text "Next" ]
]
, li
[ classList
[ ( "disabled", model.from + model.size >= result.hits.total.value )
]
]
[ a
[ href <|
if model.from + model.size >= result.hits.total.value then
"#disabled"
else
createUrl
path
model.query
model.showDetailsFor
((result.hits.total.value // model.size) * model.size)
model.size
]
[ text "Last" ]
]
]
-- API
@ -243,8 +371,13 @@ type alias Options =
}
makeRequestBody : String -> String -> Http.Body
makeRequestBody field query =
makeRequestBody :
String
-> String
-> Int
-> Int
-> Http.Body
makeRequestBody field query from size =
let
stringIn name value =
[ ( name, Json.Encode.string value ) ]
@ -254,6 +387,7 @@ makeRequestBody field query =
in
-- Prefix Query
-- {
-- ""
-- "query": {
-- "prefix": {
-- "user": {
@ -285,6 +419,10 @@ makeRequestBody field query =
|> objectIn field
|> objectIn "wildcard"
|> objectIn "query"
|> List.append
[ ( "from", Json.Encode.int from )
, ( "size", Json.Encode.int size )
]
|> Json.Encode.object
|> Http.jsonBody
@ -295,15 +433,17 @@ makeRequest :
-> Json.Decode.Decoder a
-> Options
-> String
-> Int
-> Int
-> Cmd (Msg a)
makeRequest field index decodeResultItemSource options query =
makeRequest field index decodeResultItemSource options query from size =
Http.riskyRequest
{ method = "POST"
, headers =
[ Http.header "Authorization" ("Basic " ++ Base64.encode (options.username ++ ":" ++ options.password))
]
, url = options.url ++ "/" ++ index ++ "/_search"
, body = makeRequestBody field query
, body = makeRequestBody field query from size
, expect =
Http.expectJson
(RemoteData.fromResult >> QueryResponse)

View file

@ -117,6 +117,8 @@ submitQuery old ( new, cmd ) =
, makeRequest
new.elasticsearch
(Maybe.withDefault "" newModel.query)
newModel.from
newModel.size
|> Cmd.map msg
]
)
@ -170,13 +172,13 @@ changeRouteTo model url =
-- on the home page
( newModel, Browser.Navigation.pushUrl newModel.navKey "/packages" )
Just (Route.Packages query showDetailsFor) ->
Page.Packages.init query showDetailsFor
Just (Route.Packages query showDetailsFor from size) ->
Page.Packages.init query showDetailsFor from size
|> updateWith Packages PackagesMsg newModel
|> submitQuery newModel
Just (Route.Options query showDetailsFor) ->
Page.Options.init query showDetailsFor
Just (Route.Options query showDetailsFor from size) ->
Page.Options.init query showDetailsFor from size
|> updateWith Options OptionsMsg newModel
|> submitQuery newModel
@ -188,7 +190,11 @@ update msg model =
case urlRequest of
Browser.Internal url ->
( model
, Browser.Navigation.pushUrl model.navKey <| Url.toString url
, if url.fragment == Just "disabled" then
Cmd.none
else
Browser.Navigation.pushUrl model.navKey <| Url.toString url
)
Browser.External href ->

View file

@ -65,6 +65,8 @@ type alias ResultItemSource =
init :
Maybe String
-> Maybe String
-> Maybe Int
-> Maybe Int
-> ( Model, Cmd Msg )
init =
ElasticSearch.init
@ -96,7 +98,8 @@ update navKey msg model =
view : Model -> Html Msg
view model =
ElasticSearch.view
{ title = "Search NixOS options" }
"options"
"Search NixOS options"
model
viewSuccess
SearchMsg
@ -208,14 +211,18 @@ viewResultItemDetails item =
makeRequest :
ElasticSearch.Options
-> String
-> Int
-> Int
-> Cmd Msg
makeRequest options query =
makeRequest options query from size =
ElasticSearch.makeRequest
"option_name"
"nixos-unstable-options"
decodeResultItemSource
options
query
from
size
|> Cmd.map SearchMsg

View file

@ -74,13 +74,15 @@ type alias ResultPackageLicense =
type alias ResultPackageMaintainer =
{ name : String
, email : String
, github : String
, github : Maybe String
}
init :
Maybe String
-> Maybe String
-> Maybe Int
-> Maybe Int
-> ( Model, Cmd Msg )
init =
ElasticSearch.init
@ -112,7 +114,8 @@ update navKey msg model =
view : Model -> Html Msg
view model =
ElasticSearch.view
{ title = "Search NixOS packages" }
"packages"
"Search NixOS packages"
model
viewSuccess
SearchMsg
@ -238,7 +241,14 @@ viewResultItemDetails item =
showMaintainer maintainer =
li []
[ a
[ href <| "https://github.com/" ++ maintainer.github ]
[ href <|
case maintainer.github of
Just github ->
"https://github.com/" ++ github
Nothing ->
"#"
]
[ text <| maintainer.name ++ " <" ++ maintainer.email ++ ">" ]
]
in
@ -269,14 +279,18 @@ viewResultItemDetails item =
makeRequest :
ElasticSearch.Options
-> String
-> Int
-> Int
-> Cmd Msg
makeRequest options query =
makeRequest options query from size =
ElasticSearch.makeRequest
"attr_name"
"nixos-unstable-packages"
decodeResultItemSource
options
query
from
size
|> Cmd.map SearchMsg
@ -311,4 +325,4 @@ decodeResultPackageMaintainer =
Json.Decode.map3 ResultPackageMaintainer
(Json.Decode.field "name" Json.Decode.string)
(Json.Decode.field "email" Json.Decode.string)
(Json.Decode.field "github" Json.Decode.string)
(Json.Decode.field "github" (Json.Decode.nullable Json.Decode.string))

View file

@ -15,8 +15,8 @@ import Url.Parser.Query
type Route
= NotFound
| Home
| Packages (Maybe String) (Maybe String)
| Options (Maybe String) (Maybe String)
| Packages (Maybe String) (Maybe String) (Maybe Int) (Maybe Int)
| Options (Maybe String) (Maybe String) (Maybe Int) (Maybe Int)
parser : Url.Parser.Parser (Route -> msg) msg
@ -33,12 +33,16 @@ parser =
(Url.Parser.s "packages"
<?> Url.Parser.Query.string "query"
<?> Url.Parser.Query.string "showDetailsFor"
<?> Url.Parser.Query.int "from"
<?> Url.Parser.Query.int "size"
)
, Url.Parser.map
Options
(Url.Parser.s "options"
<?> Url.Parser.Query.string "query"
<?> Url.Parser.Query.string "showDetailsFor"
<?> Url.Parser.Query.int "from"
<?> Url.Parser.Query.int "size"
)
]
@ -88,8 +92,20 @@ routeToPieces page =
NotFound ->
( [ "not-found" ], [] )
Packages query showDetailsFor ->
( [ "packages" ], [ query, showDetailsFor ] )
Packages query showDetailsFor from size ->
( [ "packages" ]
, [ query
, showDetailsFor
, Maybe.map String.fromInt from
, Maybe.map String.fromInt size
]
)
Options query showDetailsFor ->
( [ "options" ], [ query, showDetailsFor ] )
Options query showDetailsFor from size ->
( [ "options" ]
, [ query
, showDetailsFor
, Maybe.map String.fromInt from
, Maybe.map String.fromInt size
]
)