packages feed

pinecone-1.0.0: src/Pinecone/Metadata.hs

-- | Pinecone's filtering query language
module Pinecone.Metadata
    ( Scalar(..)
    , Filter(..)
    ) where

import Pinecone.Prelude

-- | A scalar value used for metadata filters
data Scalar
    = ScalarNumber Scientific
    | ScalarString Text
    | ScalarBoolean Bool
    deriving stock (Eq, Generic, Show)

instance IsString Scalar where
    fromString string = ScalarString (fromString string)

instance Num Scalar where
    fromInteger n = ScalarNumber (fromInteger n)

    ScalarNumber l + ScalarNumber r = ScalarNumber (l + r)
    _ + ScalarNumber r = ScalarNumber r
    l + _ = l

    ScalarNumber l * ScalarNumber r = ScalarNumber (l * r)
    _ * ScalarNumber r = ScalarNumber r
    l * _ = l

    abs (ScalarNumber n) = ScalarNumber (abs n)
    abs n = n

    signum (ScalarNumber n) = ScalarNumber (signum n)
    signum n = n

    negate (ScalarNumber n) = ScalarNumber (negate n)
    negate n = n

instance ToJSON Scalar where
    toJSON (ScalarNumber scientific) = Number scientific
    toJSON (ScalarString text) = String text
    toJSON (ScalarBoolean bool) = Bool bool

instance FromJSON Scalar where
    parseJSON (Number scientific) = pure (ScalarNumber scientific)
    parseJSON (String text) = pure (ScalarString text)
    parseJSON (Bool bool) = pure (ScalarBoolean bool)
    parseJSON object = typeMismatch "Number/String/Boolean" object

-- | Metadata query language
data Filter
    = Equal Text Scalar
    | NotEqual Text Scalar
    | GreaterThan Text Scientific
    | GreaterThanOrEqual Text Scientific
    | LessThan Text Scientific
    | LessThanOrEqual Text Scientific
    | In Text Scalar
    | NotIn Text Scalar
    | Exists Text Bool
    | And (Vector Filter)
    | Or (Vector Filter)
    deriving stock (Eq, Generic, Show)

instance ToJSON Filter where
    toJSON (Equal field literal) =
        toJSON @(Map Text (Map Text Scalar)) [ (field, [ ("$eq", literal) ]) ]
    toJSON (NotEqual field literal) =
        toJSON @(Map Text (Map Text Scalar)) [ (field, [ ("$ne", literal) ]) ]
    toJSON (GreaterThan field literal) =
        toJSON @(Map Text (Map Text Scientific)) [ (field, [ ("$gt", literal) ]) ]
    toJSON (GreaterThanOrEqual field literal) =
        toJSON @(Map Text (Map Text Scientific)) [ (field, [ ("$gte", literal) ]) ]
    toJSON (LessThan field literal) =
        toJSON @(Map Text (Map Text Scientific)) [ (field, [ ("$lt", literal) ]) ]
    toJSON (LessThanOrEqual field literal) =
        toJSON @(Map Text (Map Text Scientific)) [ (field, [ ("$lte", literal) ]) ]
    toJSON (In field literal) =
        toJSON @(Map Text (Map Text Scalar)) [ (field, [ ("$in", literal) ]) ]
    toJSON (NotIn field literal) =
        toJSON @(Map Text (Map Text Scalar)) [ (field, [ ("$nin", literal) ]) ]
    toJSON (Exists field literal) =
        toJSON @(Map Text (Map Text Bool)) [ (field, [ ("$exists", literal) ]) ]
    toJSON (And clauses) =
        toJSON @(Map Text (Vector Filter)) [ ("$and", clauses) ]
    toJSON (Or clauses) =
        toJSON @(Map Text (Vector Filter)) [ ("$or", clauses) ]

instance FromJSON Filter where
    parseJSON v =
            parseEqual v
        <|> parseNotEqual v
        <|> parseGreaterThan v
        <|> parseGreaterThanOrEqual v
        <|> parseLessThan v
        <|> parseLessThanOrEqual v
        <|> parseIn v
        <|> parseNotIn v
        <|> parseAnd v
        <|> parseOr v
      where
        parseEqual value = do
            [ (field, [ ("$eq", literal) ]) ] <- parseJSON @(Map Text (Map Text Scalar)) value
            return (Equal field literal)

        parseNotEqual value = do
            [ (field, [ ("$ne", literal) ]) ] <- parseJSON @(Map Text (Map Text Scalar)) value
            return (NotEqual field literal)

        parseGreaterThan value = do
            [ (field, [ ("$gt", literal) ]) ] <- parseJSON @(Map Text (Map Text Scientific)) value
            return (GreaterThan field literal)

        parseGreaterThanOrEqual value = do
            [ (field, [ ("$gte", literal) ]) ] <- parseJSON @(Map Text (Map Text Scientific)) value
            return (GreaterThanOrEqual field literal)

        parseLessThan value = do
            [ (field, [ ("$lt", literal) ]) ] <- parseJSON @(Map Text (Map Text Scientific)) value
            return (LessThan field literal)

        parseLessThanOrEqual value = do
            [ (field, [ ("$lte", literal) ]) ] <- parseJSON @(Map Text (Map Text Scientific)) value
            return (LessThanOrEqual field literal)

        parseIn value = do
            [ (field, [ ("$in", literal) ]) ] <- parseJSON @(Map Text (Map Text Scalar)) value
            return (In field literal)

        parseNotIn value = do
            [ (field, [ ("$nin", literal) ]) ] <- parseJSON @(Map Text (Map Text Scalar)) value
            return (NotIn field literal)

        parseAnd value = do
            [ ("$and", clauses) ] <- parseJSON @(Map Text (Vector Filter)) value
            return (And clauses)

        parseOr value = do
            [ ("$or", clauses) ] <- parseJSON @(Map Text (Vector Filter)) value
            return (Or clauses)