{-# LANGUAGE DataKinds                   #-}
{-# LANGUAGE DeriveGeneric               #-}
{-# LANGUAGE FlexibleInstances           #-}
{-# LANGUAGE GeneralizedNewtypeDeriving  #-}
{-# LANGUAGE LambdaCase                  #-}
{-# LANGUAGE NoImplicitPrelude           #-}
{-# LANGUAGE OverloadedStrings           #-}
{-# LANGUAGE RecordWildCards             #-}
{-# LANGUAGE TypeFamilies                #-}

{-# OPTIONS_GHC -fno-warn-unused-imports #-}

-- Module      : Network.AWS.CloudSearchDomains.Search
-- Copyright   : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
-- License     : This Source Code Form is subject to the terms of
--               the Mozilla Public License, v. 2.0.
--               A copy of the MPL can be found in the LICENSE file or
--               you can obtain it at http://mozilla.org/MPL/2.0/.
-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
--
-- Derived from AWS service descriptions, licensed under Apache 2.0.

-- | Retrieves a list of documents that match the specified search criteria. How
-- you specify the search criteria depends on which query parser you use. Amazon
-- CloudSearch supports four query parsers:
--
-- 'simple': search all 'text' and 'text-array' fields for the specified string.
-- Search for phrases, individual terms, and prefixes.   'structured': search
-- specific fields, construct compound queries using Boolean operators, and use
-- advanced features such as term boosting and proximity searching.  'lucene':
-- specify search criteria using the Apache Lucene query parser syntax.  'dismax':
-- specify search criteria using the simplified subset of the Apache Lucene
-- query parser syntax defined by the DisMax query parser.  For more
-- information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/searching.html Searching Your Data> in the /Amazon CloudSearch Developer Guide/
-- .
--
-- The endpoint for submitting 'Search' requests is domain-specific. You submit
-- search requests to a domain's search endpoint. To get the search endpoint for
-- your domain, use the Amazon CloudSearch configuration service 'DescribeDomains'
-- action. A domain's endpoints are also displayed on the domain dashboard in
-- the Amazon CloudSearch console.
--
-- <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/API_Search.html>
module Network.AWS.CloudSearchDomains.Search
    (
    -- * Request
      Search
    -- ** Request constructor
    , search
    -- ** Request lenses
    , s1Cursor
    , s1Expr
    , s1Facet
    , s1FilterQuery
    , s1Highlight
    , s1Partial
    , s1Query
    , s1QueryOptions
    , s1QueryParser
    , s1Return
    , s1Size
    , s1Sort
    , s1Start

    -- * Response
    , SearchResponse
    -- ** Response constructor
    , searchResponse
    -- ** Response lenses
    , sr1Facets
    , sr1Hits
    , sr1Status
    ) where

import Network.AWS.Prelude
import Network.AWS.Request.RestJSON
import Network.AWS.CloudSearchDomains.Types
import qualified GHC.Exts

data Search = Search
    { _s1Cursor       :: Maybe Text
    , _s1Expr         :: Maybe Text
    , _s1Facet        :: Maybe Text
    , _s1FilterQuery  :: Maybe Text
    , _s1Highlight    :: Maybe Text
    , _s1Partial      :: Maybe Bool
    , _s1Query        :: Text
    , _s1QueryOptions :: Maybe Text
    , _s1QueryParser  :: Maybe QueryParser
    , _s1Return       :: Maybe Text
    , _s1Size         :: Maybe Integer
    , _s1Sort         :: Maybe Text
    , _s1Start        :: Maybe Integer
    } deriving (Eq, Read, Show)

-- | 'Search' constructor.
--
-- The fields accessible through corresponding lenses are:
--
-- * 's1Cursor' @::@ 'Maybe' 'Text'
--
-- * 's1Expr' @::@ 'Maybe' 'Text'
--
-- * 's1Facet' @::@ 'Maybe' 'Text'
--
-- * 's1FilterQuery' @::@ 'Maybe' 'Text'
--
-- * 's1Highlight' @::@ 'Maybe' 'Text'
--
-- * 's1Partial' @::@ 'Maybe' 'Bool'
--
-- * 's1Query' @::@ 'Text'
--
-- * 's1QueryOptions' @::@ 'Maybe' 'Text'
--
-- * 's1QueryParser' @::@ 'Maybe' 'QueryParser'
--
-- * 's1Return' @::@ 'Maybe' 'Text'
--
-- * 's1Size' @::@ 'Maybe' 'Integer'
--
-- * 's1Sort' @::@ 'Maybe' 'Text'
--
-- * 's1Start' @::@ 'Maybe' 'Integer'
--
search :: Text -- ^ 's1Query'
       -> Search
search p1 = Search
    { _s1Query        = p1
    , _s1Cursor       = Nothing
    , _s1Expr         = Nothing
    , _s1Facet        = Nothing
    , _s1FilterQuery  = Nothing
    , _s1Highlight    = Nothing
    , _s1Partial      = Nothing
    , _s1QueryOptions = Nothing
    , _s1QueryParser  = Nothing
    , _s1Return       = Nothing
    , _s1Size         = Nothing
    , _s1Sort         = Nothing
    , _s1Start        = Nothing
    }

-- | Retrieves a cursor value you can use to page through large result sets. Use
-- the 'size' parameter to control the number of hits to include in each response.
-- You can specify either the 'cursor' or 'start' parameter in a request; they are
-- mutually exclusive. To get the first cursor, set the cursor value to 'initial'.
-- In subsequent requests, specify the cursor value returned in the hits section
-- of the response.
--
-- For more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/paginating-results.html Paginating Results> in the /Amazon CloudSearchDeveloper Guide/.
s1Cursor :: Lens' Search (Maybe Text)
s1Cursor = lens _s1Cursor (\s a -> s { _s1Cursor = a })

-- | Defines one or more numeric expressions that can be used to sort results or
-- specify search or filter criteria. You can also specify expressions as return
-- fields.
--
-- For more information about defining and using expressions, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/configuring-expressions.html ConfiguringExpressions> in the /Amazon CloudSearch Developer Guide/.
s1Expr :: Lens' Search (Maybe Text)
s1Expr = lens _s1Expr (\s a -> s { _s1Expr = a })

-- | Specifies one or more fields for which to get facet information, and options
-- that control how the facet information is returned. Each specified field must
-- be facet-enabled in the domain configuration. The fields and options are
-- specified in JSON using the form '{"FIELD":{"OPTION":VALUE,"OPTION:"STRING"},"FIELD":{"OPTION":VALUE,"OPTION":"STRING"}}'.
--
-- You can specify the following faceting options:
--
-- 'buckets' specifies an array of the facet values or ranges to count. Ranges
-- are specified using the same syntax that you use to search for a range of
-- values. For more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/searching-ranges.html  Searching for a Range of Values> in the /Amazon CloudSearch Developer Guide/. Buckets are returned in the order they are specified in the request. The 'sort' and 'size' options are not valid if you specify 'buckets'.
--
-- 'size' specifies the maximum number of facets to include in the results. By
-- default, Amazon CloudSearch returns counts for the top 10. The 'size' parameter
-- is only valid when you specify the 'sort' option; it cannot be used in
-- conjunction with 'buckets'.
--
-- 'sort' specifies how you want to sort the facets in the results: 'bucket' or 'count'. Specify 'bucket' to sort alphabetically or numerically by facet value (in
-- ascending order). Specify 'count' to sort by the facet counts computed for each
-- facet value (in descending order). To retrieve facet counts for particular
-- values or ranges of values, use the 'buckets' option instead of 'sort'.
--
-- If no facet options are specified, facet counts are computed for all field
-- values, the facets are sorted by facet count, and the top 10 facets are
-- returned in the results.
--
-- For more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/faceting.html Getting and Using Facet Information> in the /AmazonCloudSearch Developer Guide/.
s1Facet :: Lens' Search (Maybe Text)
s1Facet = lens _s1Facet (\s a -> s { _s1Facet = a })

-- | Specifies a structured query that filters the results of a search without
-- affecting how the results are scored and sorted. You use 'filterQuery' in
-- conjunction with the 'query' parameter to filter the documents that match the
-- constraints specified in the 'query' parameter. Specifying a filter controls
-- only which matching documents are included in the results, it has no effect
-- on how they are scored and sorted. The 'filterQuery' parameter supports the
-- full structured query syntax.
--
-- For more information about using filters, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/filtering-results.html Filtering Matching Documents>
-- in the /Amazon CloudSearch Developer Guide/.
s1FilterQuery :: Lens' Search (Maybe Text)
s1FilterQuery = lens _s1FilterQuery (\s a -> s { _s1FilterQuery = a })

-- | Retrieves highlights for matches in the specified 'text' or 'text-array' fields.
-- Each specified field must be highlight enabled in the domain configuration.
-- The fields and options are specified in JSON using the form '{"FIELD":{"OPTION":VALUE,"OPTION:"STRING"},"FIELD":{"OPTION":VALUE,"OPTION":"STRING"}}'.
--
-- You can specify the following highlight options:
--
-- 'format': specifies the format of the data in the text field: 'text' or 'html'.
-- When data is returned as HTML, all non-alphanumeric characters are encoded.
-- The default is 'html'.   'max_phrases': specifies the maximum number of
-- occurrences of the search term(s) you want to highlight. By default, the
-- first occurrence is highlighted.   'pre_tag': specifies the string to prepend
-- to an occurrence of a search term. The default for HTML highlights is '&lt;em&gt;'. The default for text highlights is '*'.   'post_tag': specifies the string to
-- append to an occurrence of a search term. The default for HTML highlights is '&lt;/em&gt;'. The default for text highlights is '*'.   If no highlight options are
-- specified for a field, the returned field text is treated as HTML and the
-- first match is highlighted with emphasis tags: '&lt;em>search-term&lt;/em&gt;'.
s1Highlight :: Lens' Search (Maybe Text)
s1Highlight = lens _s1Highlight (\s a -> s { _s1Highlight = a })

-- | Enables partial results to be returned if one or more index partitions are
-- unavailable. When your search index is partitioned across multiple search
-- instances, by default Amazon CloudSearch only returns results if every
-- partition can be queried. This means that the failure of a single search
-- instance can result in 5xx (internal server) errors. When you enable partial
-- results, Amazon CloudSearch returns whatever results are available and
-- includes the percentage of documents searched in the search results
-- (percent-searched). This enables you to more gracefully degrade your users'
-- search experience. For example, rather than displaying no results, you could
-- display the partial results and a message indicating that the results might
-- be incomplete due to a temporary system outage.
s1Partial :: Lens' Search (Maybe Bool)
s1Partial = lens _s1Partial (\s a -> s { _s1Partial = a })

-- | Specifies the search criteria for the request. How you specify the search
-- criteria depends on the query parser used for the request and the parser
-- options specified in the 'queryOptions' parameter. By default, the 'simple' query
-- parser is used to process requests. To use the 'structured', 'lucene', or 'dismax'
-- query parser, you must also specify the 'queryParser' parameter.
--
-- For more information about specifying search criteria, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/searching.html Searching YourData> in the /Amazon CloudSearch Developer Guide/.
s1Query :: Lens' Search Text
s1Query = lens _s1Query (\s a -> s { _s1Query = a })

-- | Configures options for the query parser specified in the 'queryParser'
-- parameter.
--
-- The options you can configure vary according to which parser you use:
--
-- 'defaultOperator': The default operator used to combine individual terms in
-- the search string. For example: 'defaultOperator: 'or''. For the 'dismax' parser,
-- you specify a percentage that represents the percentage of terms in the
-- search string (rounded down) that must match, rather than a default operator.
-- A value of '0%' is the equivalent to OR, and a value of '100%' is equivalent to
-- AND. The percentage must be specified as a value in the range 0-100 followed
-- by the percent (%) symbol. For example, 'defaultOperator: 50%'. Valid values: 'and', 'or', a percentage in the range 0%-100% ('dismax'). Default: 'and' ('simple', 'structured', 'lucene') or '100' ('dismax'). Valid for: 'simple', 'structured', 'lucene', and 'dismax'.
-- 'fields': An array of the fields to search when no fields are specified in a
-- search. If no fields are specified in a search and this option is not
-- specified, all text and text-array fields are searched. You can specify a
-- weight for each field to control the relative importance of each field when
-- Amazon CloudSearch calculates relevance scores. To specify a field weight,
-- append a caret ('^') symbol and the weight to the field name. For example, to
-- boost the importance of the 'title' field over the 'description' field you could
-- specify: '"fields":["title^5","description"]'. Valid values: The name of any
-- configured field and an optional numeric value greater than zero. Default:
-- All 'text' and 'text-array' fields. Valid for: 'simple', 'structured', 'lucene', and 'dismax'.  'operators': An array of the operators or special characters you want to
-- disable for the simple query parser. If you disable the 'and', 'or', or 'not'
-- operators, the corresponding operators ('+', '|', '-') have no special meaning and
-- are dropped from the search string. Similarly, disabling 'prefix' disables the
-- wildcard operator ('*') and disabling 'phrase' disables the ability to search for
-- phrases by enclosing phrases in double quotes. Disabling precedence disables
-- the ability to control order of precedence using parentheses. Disabling 'near'
-- disables the ability to use the ~ operator to perform a sloppy phrase search.
-- Disabling the 'fuzzy' operator disables the ability to use the ~ operator to
-- perform a fuzzy search. 'escape' disables the ability to use a backslash ('\') to
-- escape special characters within the search string. Disabling whitespace is
-- an advanced option that prevents the parser from tokenizing on whitespace,
-- which can be useful for Vietnamese. (It prevents Vietnamese words from being
-- split incorrectly.) For example, you could disable all operators other than
-- the phrase operator to support just simple term and phrase queries: '"operators":["and","not","or", "prefix"]'. Valid values: 'and', 'escape', 'fuzzy', 'near', 'not', 'or', 'phrase', 'precedence', 'prefix',
-- 'whitespace'. Default: All operators and special characters are enabled. Valid
-- for: 'simple'.  'phraseFields': An array of the 'text' or 'text-array' fields you
-- want to use for phrase searches. When the terms in the search string appear
-- in close proximity within a field, the field scores higher. You can specify a
-- weight for each field to boost that score. The 'phraseSlop' option controls how
-- much the matches can deviate from the search string and still be boosted. To
-- specify a field weight, append a caret ('^') symbol and the weight to the field
-- name. For example, to boost phrase matches in the 'title' field over the 'abstract' field, you could specify: '"phraseFields":["title^3", "plot"]' Valid values:
-- The name of any 'text' or 'text-array' field and an optional numeric value
-- greater than zero. Default: No fields. If you don't specify any fields with 'phraseFields', proximity scoring is disabled even if 'phraseSlop' is specified. Valid for: 'dismax'.  'phraseSlop': An integer value that specifies how much matches can deviate
-- from the search phrase and still be boosted according to the weights
-- specified in the 'phraseFields' option; for example, 'phraseSlop: 2'. You must
-- also specify 'phraseFields' to enable proximity scoring. Valid values: positive
-- integers. Default: 0. Valid for: 'dismax'.  'explicitPhraseSlop': An integer
-- value that specifies how much a match can deviate from the search phrase when
-- the phrase is enclosed in double quotes in the search string. (Phrases that
-- exceed this proximity distance are not considered a match.) For example, to
-- specify a slop of three for dismax phrase queries, you would specify '"explicitPhraseSlop":3'. Valid values: positive integers. Default: 0. Valid for: 'dismax'.  'tieBreaker': When a term in the search string is found in a document's field, a score is calculated for that field based on how common the word is in that field compared to other documents. If the term occurs in multiple fields within a document, by default only the highest scoring field contributes to the document's overall score. You can specify a
-- 'tieBreaker' value to enable the matches in lower-scoring fields to contribute
-- to the document's score. That way, if two documents have the same max field
-- score for a particular term, the score for the document that has matches in
-- more fields will be higher. The formula for calculating the score with a
-- tieBreaker is '(max field score) + (tieBreaker) * (sum of the scores for therest of the matching fields)'. Set 'tieBreaker' to 0 to disregard all but the
-- highest scoring field (pure max): '"tieBreaker":0'. Set to 1 to sum the scores
-- from all fields (pure sum): '"tieBreaker":1'. Valid values: 0.0 to 1.0.
-- Default: 0.0. Valid for: 'dismax'.
s1QueryOptions :: Lens' Search (Maybe Text)
s1QueryOptions = lens _s1QueryOptions (\s a -> s { _s1QueryOptions = a })

-- | Specifies which query parser to use to process the request. If 'queryParser' is
-- not specified, Amazon CloudSearch uses the 'simple' query parser.
--
-- Amazon CloudSearch supports four query parsers:
--
-- 'simple': perform simple searches of 'text' and 'text-array' fields. By default,
-- the 'simple' query parser searches all 'text' and 'text-array' fields. You can
-- specify which fields to search by with the 'queryOptions' parameter. If you
-- prefix a search term with a plus sign (+) documents must contain the term to
-- be considered a match. (This is the default, unless you configure the default
-- operator with the 'queryOptions' parameter.) You can use the '-' (NOT), '|' (OR),
-- and '*' (wildcard) operators to exclude particular terms, find results that
-- match any of the specified terms, or search for a prefix. To search for a
-- phrase rather than individual terms, enclose the phrase in double quotes. For
-- more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/searching-text.html Searching for Text> in the /Amazon CloudSearch DeveloperGuide/.   'structured': perform advanced searches by combining multiple
-- expressions to define the search criteria. You can also search within
-- particular fields, search for values and ranges of values, and use advanced
-- options such as term boosting, 'matchall', and 'near'. For more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/searching-compound-queries.html Constructing Compound Queries> in the /Amazon CloudSearch Developer Guide/.   'lucene': search using the Apache
-- Lucene query parser syntax. For more information, see <http://lucene.apache.org/core/4_6_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package_description Apache Lucene QueryParser Syntax>.   'dismax': search using the simplified subset of the Apache
-- Lucene query parser syntax defined by the DisMax query parser. For more
-- information, see <http://wiki.apache.org/solr/DisMaxQParserPlugin#Query_Syntax DisMax Query Parser Syntax>.
s1QueryParser :: Lens' Search (Maybe QueryParser)
s1QueryParser = lens _s1QueryParser (\s a -> s { _s1QueryParser = a })

-- | Specifies the field and expression values to include in the response.
-- Multiple fields or expressions are specified as a comma-separated list. By
-- default, a search response includes all return enabled fields ('_all_fields').
-- To return only the document IDs for the matching documents, specify '_no_fields'
-- . To retrieve the relevance score calculated for each document, specify '_score'
-- .
s1Return :: Lens' Search (Maybe Text)
s1Return = lens _s1Return (\s a -> s { _s1Return = a })

-- | Specifies the maximum number of search hits to include in the response.
s1Size :: Lens' Search (Maybe Integer)
s1Size = lens _s1Size (\s a -> s { _s1Size = a })

-- | Specifies the fields or custom expressions to use to sort the search results.
-- Multiple fields or expressions are specified as a comma-separated list. You
-- must specify the sort direction ('asc' or 'desc') for each field; for example, 'year desc,title asc'. To use a field to sort results, the field must be sort-enabled in the
-- domain configuration. Array type fields cannot be used for sorting. If no 'sort'
-- parameter is specified, results are sorted by their default relevance scores
-- in descending order: '_score desc'. You can also sort by document ID ('_id asc')
-- and version ('_version desc').
--
-- For more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/sorting-results.html Sorting Results> in the /Amazon CloudSearchDeveloper Guide/.
s1Sort :: Lens' Search (Maybe Text)
s1Sort = lens _s1Sort (\s a -> s { _s1Sort = a })

-- | Specifies the offset of the first search hit you want to return. Note that
-- the result set is zero-based; the first result is at index 0. You can specify
-- either the 'start' or 'cursor' parameter in a request, they are mutually
-- exclusive.
--
-- For more information, see <http://docs.aws.amazon.com/cloudsearch/latest/developerguide/paginating-results.html Paginating Results> in the /Amazon CloudSearchDeveloper Guide/.
s1Start :: Lens' Search (Maybe Integer)
s1Start = lens _s1Start (\s a -> s { _s1Start = a })

data SearchResponse = SearchResponse
    { _sr1Facets :: Map Text BucketInfo
    , _sr1Hits   :: Maybe Hits
    , _sr1Status :: Maybe SearchStatus
    } deriving (Eq, Read, Show)

-- | 'SearchResponse' constructor.
--
-- The fields accessible through corresponding lenses are:
--
-- * 'sr1Facets' @::@ 'HashMap' 'Text' 'BucketInfo'
--
-- * 'sr1Hits' @::@ 'Maybe' 'Hits'
--
-- * 'sr1Status' @::@ 'Maybe' 'SearchStatus'
--
searchResponse :: SearchResponse
searchResponse = SearchResponse
    { _sr1Status = Nothing
    , _sr1Hits   = Nothing
    , _sr1Facets = mempty
    }

-- | The requested facet information.
sr1Facets :: Lens' SearchResponse (HashMap Text BucketInfo)
sr1Facets = lens _sr1Facets (\s a -> s { _sr1Facets = a }) . _Map

-- | The documents that match the search criteria.
sr1Hits :: Lens' SearchResponse (Maybe Hits)
sr1Hits = lens _sr1Hits (\s a -> s { _sr1Hits = a })

-- | The status information returned for the search request.
sr1Status :: Lens' SearchResponse (Maybe SearchStatus)
sr1Status = lens _sr1Status (\s a -> s { _sr1Status = a })

instance ToPath Search where
    toPath = const "/2013-01-01/search"

instance ToQuery Search where
    toQuery Search{..} = mconcat
        [ "cursor"    =? _s1Cursor
        , "expr"      =? _s1Expr
        , "facet"     =? _s1Facet
        , "fq"        =? _s1FilterQuery
        , "highlight" =? _s1Highlight
        , "partial"   =? _s1Partial
        , "q"         =? _s1Query
        , "q.options" =? _s1QueryOptions
        , "q.parser"  =? _s1QueryParser
        , "return"    =? _s1Return
        , "size"      =? _s1Size
        , "sort"      =? _s1Sort
        , "start"     =? _s1Start
        ]

instance ToHeaders Search

instance ToJSON Search where
    toJSON = const (toJSON Empty)

instance AWSRequest Search where
    type Sv Search = CloudSearchDomains
    type Rs Search = SearchResponse

    request  = get
    response = jsonResponse

instance FromJSON SearchResponse where
    parseJSON = withObject "SearchResponse" $ \o -> SearchResponse
        <$> o .:? "facets" .!= mempty
        <*> o .:? "hits"
        <*> o .:? "status"