packages feed

bloodhound-0.21.0.0: src/Database/Bloodhound/Common/Script.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

module Database.Bloodhound.Common.Script where

import Bloodhound.Import
import Data.Aeson.KeyMap
import Database.Bloodhound.Internal.Newtypes
import GHC.Generics

newtype ScriptFields
  = ScriptFields (KeyMap ScriptFieldValue)
  deriving (Eq, Show)

type ScriptFieldValue = Value

data ScriptSource
  = ScriptId Text
  | ScriptInline Text
  deriving (Eq, Show, Generic)

data Script = Script
  { scriptLanguage :: Maybe ScriptLanguage,
    scriptSource :: ScriptSource,
    scriptParams :: Maybe ScriptParams
  }
  deriving (Eq, Show, Generic)

newtype ScriptLanguage
  = ScriptLanguage Text
  deriving (Eq, Show, Generic, FromJSON, ToJSON)

newtype ScriptParams
  = ScriptParams (KeyMap ScriptParamValue)
  deriving (Eq, Show)

type ScriptParamValue = Value

data BoostMode
  = BoostModeMultiply
  | BoostModeReplace
  | BoostModeSum
  | BoostModeAvg
  | BoostModeMax
  | BoostModeMin
  deriving (Eq, Show, Generic)

data ScoreMode
  = ScoreModeMultiply
  | ScoreModeSum
  | ScoreModeAvg
  | ScoreModeFirst
  | ScoreModeMax
  | ScoreModeMin
  deriving (Eq, Show, Generic)

data FunctionScoreFunction
  = FunctionScoreFunctionScript Script
  | FunctionScoreFunctionRandom Seed
  | FunctionScoreFunctionFieldValueFactor FieldValueFactor
  deriving (Eq, Show, Generic)

newtype Weight
  = Weight Float
  deriving (Eq, Show, Generic, FromJSON, ToJSON)

newtype Seed
  = Seed Float
  deriving (Eq, Show, Generic, FromJSON, ToJSON)

data FieldValueFactor = FieldValueFactor
  { fieldValueFactorField :: FieldName,
    fieldValueFactor :: Maybe Factor,
    fieldValueFactorModifier :: Maybe FactorModifier,
    fieldValueFactorMissing :: Maybe FactorMissingFieldValue
  }
  deriving (Eq, Show, Generic)

newtype Factor
  = Factor Float
  deriving (Eq, Show, Generic, FromJSON, ToJSON)

data FactorModifier
  = FactorModifierNone
  | FactorModifierLog
  | FactorModifierLog1p
  | FactorModifierLog2p
  | FactorModifierLn
  | FactorModifierLn1p
  | FactorModifierLn2p
  | FactorModifierSquare
  | FactorModifierSqrt
  | FactorModifierReciprocal
  deriving (Eq, Show, Generic)

newtype FactorMissingFieldValue
  = FactorMissingFieldValue Float
  deriving (Eq, Show, Generic, FromJSON, ToJSON)

instance ToJSON BoostMode where
  toJSON BoostModeMultiply = "multiply"
  toJSON BoostModeReplace = "replace"
  toJSON BoostModeSum = "sum"
  toJSON BoostModeAvg = "avg"
  toJSON BoostModeMax = "max"
  toJSON BoostModeMin = "min"

instance FromJSON BoostMode where
  parseJSON = withText "BoostMode" parse
    where
      parse "multiply" = pure BoostModeMultiply
      parse "replace" = pure BoostModeReplace
      parse "sum" = pure BoostModeSum
      parse "avg" = pure BoostModeAvg
      parse "max" = pure BoostModeMax
      parse "min" = pure BoostModeMin
      parse bm = fail ("Unexpected BoostMode: " <> show bm)

instance ToJSON ScoreMode where
  toJSON ScoreModeMultiply = "multiply"
  toJSON ScoreModeSum = "sum"
  toJSON ScoreModeFirst = "first"
  toJSON ScoreModeAvg = "avg"
  toJSON ScoreModeMax = "max"
  toJSON ScoreModeMin = "min"

instance FromJSON ScoreMode where
  parseJSON = withText "ScoreMode" parse
    where
      parse "multiply" = pure ScoreModeMultiply
      parse "sum" = pure ScoreModeSum
      parse "first" = pure ScoreModeFirst
      parse "avg" = pure ScoreModeAvg
      parse "max" = pure ScoreModeMax
      parse "min" = pure ScoreModeMin
      parse sm = fail ("Unexpected ScoreMode: " <> show sm)

functionScoreFunctionPair :: FunctionScoreFunction -> (Key, Value)
functionScoreFunctionPair (FunctionScoreFunctionScript functionScoreScript) =
  ("script_score", toJSON functionScoreScript)
functionScoreFunctionPair (FunctionScoreFunctionRandom seed) =
  ("random_score", omitNulls ["seed" .= seed])
functionScoreFunctionPair (FunctionScoreFunctionFieldValueFactor fvf) =
  ("field_value_factor", toJSON fvf)

parseFunctionScoreFunction :: Object -> Parser FunctionScoreFunction
parseFunctionScoreFunction o =
  singleScript
    `taggedWith` "script_score"
    <|> singleRandom
    `taggedWith` "random_score"
    <|> singleFieldValueFactor
    `taggedWith` "field_value_factor"
  where
    taggedWith parser k = parser =<< o .: k
    singleScript = pure . FunctionScoreFunctionScript
    singleRandom o' = FunctionScoreFunctionRandom <$> o' .: "seed"
    singleFieldValueFactor = pure . FunctionScoreFunctionFieldValueFactor

instance ToJSON ScriptFields where
  toJSON (ScriptFields x) = Object x

instance FromJSON ScriptFields where
  parseJSON (Object o) = pure (ScriptFields o)
  parseJSON _ = fail "error parsing ScriptFields"

instance ToJSON Script where
  toJSON script =
    object ["script" .= omitNulls (base script)]
    where
      base (Script lang (ScriptInline source) params) =
        ["lang" .= lang, "source" .= source, "params" .= params]
      base (Script lang (ScriptId id_) params) =
        ["lang" .= lang, "id" .= id_, "params" .= params]

instance FromJSON Script where
  parseJSON = withObject "Script" parse
    where
      parseSource o = do
        inline <- o .:? "source"
        id_ <- o .:? "id"
        return $ case (inline, id_) of
          (Just x, Nothing) -> ScriptInline x
          (Nothing, Just x) -> ScriptId x
          (Nothing, Nothing) -> error "Script has to be either stored or inlined"
          (Just _, Just _) -> error "Script can't both be stored and inlined at the same time"
      parse o =
        o .: "script" >>= \o' ->
          Script
            <$> o' .:? "lang"
            <*> parseSource o'
            <*> o' .:? "params"

instance ToJSON ScriptParams where
  toJSON (ScriptParams x) = Object x

instance FromJSON ScriptParams where
  parseJSON (Object o) = pure (ScriptParams o)
  parseJSON _ = fail "error parsing ScriptParams"

instance ToJSON FieldValueFactor where
  toJSON (FieldValueFactor field factor modifier missing) =
    omitNulls base
    where
      base =
        [ "field" .= field,
          "factor" .= factor,
          "modifier" .= modifier,
          "missing" .= missing
        ]

instance FromJSON FieldValueFactor where
  parseJSON = withObject "FieldValueFactor" parse
    where
      parse o =
        FieldValueFactor
          <$> o .: "field"
          <*> o .:? "factor"
          <*> o .:? "modifier"
          <*> o .:? "missing"

instance ToJSON FactorModifier where
  toJSON FactorModifierNone = "none"
  toJSON FactorModifierLog = "log"
  toJSON FactorModifierLog1p = "log1p"
  toJSON FactorModifierLog2p = "log2p"
  toJSON FactorModifierLn = "ln"
  toJSON FactorModifierLn1p = "ln1p"
  toJSON FactorModifierLn2p = "ln2p"
  toJSON FactorModifierSquare = "square"
  toJSON FactorModifierSqrt = "sqrt"
  toJSON FactorModifierReciprocal = "reciprocal"

instance FromJSON FactorModifier where
  parseJSON = withText "FactorModifier" parse
    where
      parse "none" = pure FactorModifierNone
      parse "log" = pure FactorModifierLog
      parse "log1p" = pure FactorModifierLog1p
      parse "log2p" = pure FactorModifierLog2p
      parse "ln" = pure FactorModifierLn
      parse "ln1p" = pure FactorModifierLn1p
      parse "ln2p" = pure FactorModifierLn2p
      parse "square" = pure FactorModifierSquare
      parse "sqrt" = pure FactorModifierSqrt
      parse "reciprocal" = pure FactorModifierReciprocal
      parse fm = fail ("Unexpected FactorModifier: " <> show fm)