@@ -220,7 +220,7 @@ data FieldDefinition =
220220 FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
221221
222222data MappingField =
223− MappingField { fieldName :: Text
223+ MappingField { mappingFieldName :: FieldName
224224 , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
225225
226226data Mapping = Mapping { typeName :: Text
@@ -308,79 +308,177 @@ searchByType (Server server) indexName mappingName search = dispatchSearch url s
308308 url = joinPath [server, indexName, mappingName, "_search"]
309309
310310data Search = Search { queryBody :: Maybe Query
311− , filterBody :: Maybe Filter } deriving (Eq, Show)
311+ , filterBody :: Maybe Filter
312+ , sortBody :: Maybe Sort
313+ -- default False
314+ , trackSortScores :: Bool
315+ , from :: Int
316+ , size :: Int} deriving (Eq, Show)
317+
318+pageSearch :: Int -> Int -> Search -> Search
319+pageSearch from size search = search { from = from, size = size }
320+
321+-- defaultFieldMaybeJSON :: Text -> Maybe a ->
322+maybeJson field (Just value) = [field .= toJSON value]
323+maybeJson _ _ = []
324+maybeJsonF field (Just value) = [field .= fmap toJSON value]
325+maybeJsonF _ _ = []
312326
313327instance ToJSON Search where
314− toJSON (Search query filters) = object [--"query" .= fmap toJSON query,
315− "filter" .= fmap toJSON filters]
328+ toJSON (Search query filter sort trackSortScores from size) =
329+ object merged where
330+ lQuery = maybeJson "query" query
331+ lFilter = maybeJson "filter" filter
332+ lSort = maybeJsonF "sort" sort
333+ merged = mconcat [[ "from" .= from
334+ , "size" .= size
335+ , "track_scores" .= trackSortScores]
336+ , lQuery
337+ , lFilter
338+ , lSort]
339+
340+type Sort = [SortSpec]
341+
342+data SortSpec = DefaultSortSpec DefaultSort
343+ | GeoDistanceSortSpec SortOrder GeoPoint deriving (Eq, Show)
344+
345+instance ToJSON SortSpec where
346+ toJSON (DefaultSortSpec
347+ (DefaultSort (FieldName sortFieldName) sortOrder ignoreUnmapped
348+ sortMode missingSort nestedFilter)) =
349+ object [sortFieldName .= object merged] where
350+ base = ["order" .= toJSON sortOrder
351+ , "ignore_unmapped" .= ignoreUnmapped]
352+ lSortMode = maybeJson "mode" sortMode
353+ lMissingSort = maybeJson "missing" missingSort
354+ lNestedFilter = maybeJson "nested_filter" nestedFilter
355+ merged = mconcat [base, lSortMode, lMissingSort, lNestedFilter]
356+
357+instance ToJSON SortOrder where
358+ toJSON Ascending = String "asc"
359+ toJSON Descending = String "desc"
360+
361+instance ToJSON SortMode where
362+ toJSON SortMin = String "min"
363+ toJSON SortMax = String "max"
364+ toJSON SortSum = String "sum"
365+ toJSON SortAvg = String "avg"
366+
367+instance ToJSON Missing where
368+ toJSON LastMissing = String "_last"
369+ toJSON FirstMissing = String "_first"
370+ toJSON (CustomMissing txt) = String txt
371+
372+data DefaultSort =
373+ DefaultSort { sortFieldName :: FieldName
374+ , sortOrder :: SortOrder
375+ -- default False
376+ , ignoreUnmapped :: Bool
377+ , sortMode :: Maybe SortMode
378+ , missingSort :: Maybe Missing
379+ , nestedFilter :: Maybe Filter } deriving (Eq, Show)
380+
381+data SortOrder = Ascending
382+ | Descending deriving (Eq, Show)
383+
384+data Missing = LastMissing
385+ | FirstMissing
386+ | CustomMissing Text deriving (Eq, Show)
387+
388+data SortMode = SortMin
389+ | SortMax
390+ | SortSum
391+ | SortAvg deriving (Eq, Show)
316392
317−type QueryString = Text
318393-- status:active
319394-- author:"John Smith"
320395-- for book.title and book.date containing quick OR brown, book.\*:(quick brown)
321396-- field title has no value or doesn't exist, _missing_:title
322397-- field title has any non-null value, _exists:title
323398
324−data BooleanOperator = AND | OR deriving (Eq, Show)
325−
326−type DisMax = Bool -- "use_dis_max"
327−newtype TieBreaker = TieBreaker Int deriving (Eq, Show)
328−data QueryField = DefaultField Text
329− | Fields [Text] DisMax TieBreaker deriving (Eq, Show)
330−
331−-- this is will go away later
332−type Query = QueryStringQuery
333−
334−data QueryStringQuery =
335− QueryStringQuery { query :: QueryString
336− -- default _all
337− , field :: Maybe QueryField
338− -- default OR
339− , defaultOperator :: Maybe BooleanOperator
340− -- analyzer name
341− , analyzer :: Maybe Text
342− -- default true
343− , allowLeadingWildcard :: Maybe Bool
344− -- default true
345− , lowercaseExpandedTerms :: Maybe Bool
346− -- default true
347− , enablePositionIncrements :: Maybe Bool
348− -- default 50
349− , fuzzyMaxExpansions :: Maybe Int
350− -- Fuzziness -- default AUTO, add type later
351− , fuzziness :: Maybe Text
352− , fuzzyPrefixLength :: Maybe Int -- default 0
353− -- default 0, 0 means exact phrase matches
354− , phraseSlop :: Maybe Int
355− -- default 1.0
356− , boost :: Maybe Double
357− -- default false, true forces wildcard analysis
358− , analyzeWildcard :: Maybe Bool
359− -- default false
360− , autoGeneratePhraseQueries :: Maybe Bool
361− -- # "should" clauses in the boolean query should match
362− , minimumShouldMatch :: Maybe Text
363− -- Text to handle weird % and other cases. Needs type
364− -- default false, true shuts off format based failures
365− , lenient :: Maybe Bool
366− -- default ROOT, locale used for string conversions
367− , locale :: Maybe Text
368− } deriving (Eq, Show)
369−
370−emptyQueryStringQuery = QueryStringQuery "" Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
371−queryStringQuery query = emptyQueryStringQuery { query = query }
372−
373−type FieldName = Text
399+-- type DisMax = Bool -- "use_dis_max"
400+-- newtype TieBreaker = TieBreaker Int deriving (Eq, Show)
401+-- data QueryField = DefaultField Text
402+-- | Fields [Text] DisMax TieBreaker deriving (Eq, Show)
403+
404+-- -- this is will go away later
405+
406+-- data QueryStringQuery =
407+-- QueryStringQuery { query :: Text
408+-- -- default _all
409+-- , field :: Maybe QueryField
410+-- -- default OR
411+-- , defaultOperator :: Maybe BooleanOperator
412+-- -- analyzer name
413+-- , analyzer :: Maybe Text
414+-- -- default true
415+-- , allowLeadingWildcard :: Maybe Bool
416+-- -- default true
417+-- , lowercaseExpandedTerms :: Maybe Bool
418+-- -- default true
419+-- , enablePositionIncrements :: Maybe Bool
420+-- -- default 50
421+-- , fuzzyMaxExpansions :: Maybe Int
422+-- -- Fuzziness -- default AUTO, add type later
423+-- , fuzziness :: Maybe Text
424+-- , fuzzyPrefixLength :: Maybe Int -- default 0
425+-- -- default 0, 0 means exact phrase matches
426+-- , phraseSlop :: Maybe Int
427+-- -- default 1.0
428+-- , boost :: Maybe Double
429+-- -- default false, true forces wildcard analysis
430+-- , analyzeWildcard :: Maybe Bool
431+-- -- default false
432+-- , autoGeneratePhraseQueries :: Maybe Bool
433+-- -- # "should" clauses in the boolean query should match
434+-- , minimumShouldMatch :: Maybe Text
435+-- -- Text to handle weird % and other cases. Needs type
436+-- -- default false, true shuts off format based failures
437+-- , lenient :: Maybe Bool
438+-- -- default ROOT, locale used for string conversions
439+-- , locale :: Maybe Text
440+-- } deriving (Eq, Show)
441+
442+-- emptyQueryStringQuery = QueryStringQuery "" Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
443+-- queryStringQuery query = emptyQueryStringQuery { query = query }
444+
445+-- type FieldName = Text
374446
375447type Cache = Bool -- caching on/off
376−type CacheName = Text
377−type CacheKey = Text
378448defaultCache = False
379449
380−type Existence = Bool
381−type NullValue = Bool
382450type PrefixValue = Text
383451
452+data BooleanOperator = And | Or deriving (Eq, Show)
453+
454+newtype QueryString = QueryString Text deriving (Eq, Show)
455+newtype FieldName = FieldName Text deriving (Eq, Show)
456+newtype CacheName = CacheName Text deriving (Eq, Show)
457+newtype CacheKey = CacheKey Text deriving (Eq, Show)
458+newtype Existence = Existence Bool deriving (Eq, Show)
459+newtype NullValue = NullValue Bool deriving (Eq, Show)
460+
461+-- data QueryType = BooleanQueryType
462+-- | PhraseQueryType
463+-- | PhrasePrefixQueryType deriving (Eq, Show)
464+type Boost = Double
465+
466+data Query = TermQuery Term (Maybe Boost)
467+ | ConstantScoreFilter Filter Boost
468+ | ConstantScoreQuery Query Boost
469+ deriving (Eq, Show)
470+
471+instance ToJSON Query where
472+ toJSON (TermQuery (Term termField termValue) boost) =
473+ object ["term" .=
474+ object [termField .= object merged]]
475+ where
476+ base = ["value" .= termValue]
477+ boosted = case boost of
478+ (Just boostValue) -> ["boost" .= boostValue]
479+ Nothing -> []
480+ merged = mappend base boosted
481+
384482data Filter = AndFilter [Filter] Cache
385483 | OrFilter [Filter] Cache
386484 | NotFilter Filter Cache
@@ -432,7 +530,7 @@ instance ToJSON Filter where
432530 toJSON (IdentityFilter) =
433531 object ["match_all" .= object []]
434532
435− toJSON (ExistsFilter fieldName) =
533+ toJSON (ExistsFilter (FieldName fieldName)) =
436534 object ["exists" .= object
437535 ["field" .= fieldName]]
438536
@@ -443,7 +541,7 @@ instance ToJSON Filter where
443541 object ["geo_bounding_box" .= toJSON bbConstraint
444542 , "type" .= toJSON filterType]
445543
446− toJSON (GeoDistanceFilter (GeoPoint geoField latLon)
544+ toJSON (GeoDistanceFilter (GeoPoint (FieldName geoField) latLon)
447545 distance distanceType optimizeBbox cache) =
448546 object ["geo_distance" .=
449547 object ["distance" .= toJSON distance
@@ -452,14 +550,14 @@ instance ToJSON Filter where
452550 , geoField .= toJSON latLon
453551 , "_cache" .= cache]]
454552
455− toJSON (GeoDistanceRangeFilter (GeoPoint geoField latLon)
553+ toJSON (GeoDistanceRangeFilter (GeoPoint (FieldName geoField) latLon)
456554 (DistanceRange distanceFrom distanceTo)) =
457555 object ["geo_distance_range" .=
458556 object ["from" .= toJSON distanceFrom
459557 , "to" .= toJSON distanceTo
460558 , geoField .= toJSON latLon]]
461559
462− toJSON (GeoPolygonFilter geoField latLons) =
560+ toJSON (GeoPolygonFilter (FieldName geoField) latLons) =
463561 object ["geo_polygon" .=
464562 object [geoField .=
465563 object ["points" .= fmap toJSON latLons]]]
@@ -472,18 +570,18 @@ instance ToJSON Filter where
472570 toJSON (LimitFilter limit) =
473571 object ["limit" .= object ["value" .= limit]]
474572
475− toJSON (MissingFilter fieldName existence nullValue) =
573+ toJSON (MissingFilter (FieldName fieldName) (Existence existence) (NullValue nullValue)) =
476574 object ["missing" .=
477575 object ["field" .= fieldName
478576 , "existence" .= existence
479577 , "null_value" .= nullValue]]
480578
481− toJSON (PrefixFilter fieldName fieldValue cache) =
579+ toJSON (PrefixFilter (FieldName fieldName) fieldValue cache) =
482580 object ["prefix" .=
483581 object [fieldName .= fieldValue
484582 , "_cache" .= cache]]
485583
486− toJSON (RangeFilter fieldName (Left halfRange) rangeExecution cache) =
584+ toJSON (RangeFilter (FieldName fieldName) (Left halfRange) rangeExecution cache) =
487585 object ["range" .=
488586 object [fieldName .=
489587 object [key .= val]
@@ -492,7 +590,7 @@ instance ToJSON Filter where
492590 where
493591 (key, val) = halfRangeToKV halfRange
494592
495− toJSON (RangeFilter fieldName (Right range) rangeExecution cache) =
593+ toJSON (RangeFilter (FieldName fieldName) (Right range) rangeExecution cache) =
496594 object ["range" .=
497595 object [fieldName .=
498596 object [lessKey .= lessVal
@@ -502,7 +600,8 @@ instance ToJSON Filter where
502600 where
503601 (lessKey, lessVal, greaterKey, greaterVal) = rangeToKV range
504602
505− toJSON (RegexpFilter fieldName (Regexp regexText) flags cacheName cache cacheKey) =
603+ toJSON (RegexpFilter (FieldName fieldName)
604+ (Regexp regexText) flags (CacheName cacheName) cache (CacheKey cacheKey)) =
506605 object ["regexp" .=
507606 object [fieldName .=
508607 object ["value" .= regexText
@@ -512,7 +611,7 @@ instance ToJSON Filter where
512611 , "_cache_key" .= cacheKey]]
513612
514613instance ToJSON GeoPoint where
515− toJSON (GeoPoint geoField latLon) =
614+ toJSON (GeoPoint (FieldName geoField) latLon) =
516615 object [geoField .= toJSON latLon]
517616
518617showText :: Show a => a -> Text
@@ -546,7 +645,7 @@ instance ToJSON OptimizeBbox where
546645 toJSON (OptimizeGeoFilterType gft) = toJSON gft
547646
548647instance ToJSON GeoBoundingBoxConstraint where
549− toJSON (GeoBoundingBoxConstraint geoBBField constraintBox cache) =
648+ toJSON (GeoBoundingBoxConstraint (FieldName geoBBField) constraintBox cache) =
550649 object [geoBBField .= toJSON constraintBox
551650 , "_cache" .= cache]
552651
@@ -721,16 +820,16 @@ data ShardResult =
721820
722821instance (FromJSON a, ToJSON a) => FromJSON (SearchResult a) where
723822 parseJSON (Object v) = SearchResult <$>
724− v .: "took" <*>
823+ v .: "took" <*>
725824 v .: "timed_out" <*>
726− v .: "_shards" <*>
825+ v .: "_shards" <*>
727826 v .: "hits"
728827 parseJSON _ = empty
729828
730829instance (FromJSON a, ToJSON a) => FromJSON (SearchHits a) where
731830 parseJSON (Object v) = SearchHits <$>
732− v .: "total" <*>
733− v .: "max_score" <*>
831+ v .: "total" <*>
832+ v .: "max_score" <*>
734833 v .: "hits"
735834 parseJSON _ = empty
736835
@@ -738,7 +837,7 @@ instance (FromJSON a, ToJSON a) => FromJSON (Hit a) where
738837 parseJSON (Object v) = Hit <$>
739838 v .: "_index" <*>
740839 v .: "_type" <*>
741− v .: "_id" <*>
840+ v .: "_id" <*>
742841 v .: "_score" <*>
743842 v .: "_source"
744843 parseJSON _ = empty