Pagination of search results (#20)
This commit is contained in:
parent
d3c3e2e31b
commit
c47f1c0ccb
|
@ -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)
|
||||
|
|
16
src/Main.elm
16
src/Main.elm
|
@ -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 ->
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
]
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue