Skip to content

Instantly share code, notes, and snippets.

@jhewlett
Last active June 6, 2021 17:57
Show Gist options
  • Save jhewlett/850e52586c30f95739076aa189a0b8f3 to your computer and use it in GitHub Desktop.
Save jhewlett/850e52586c30f95739076aa189a0b8f3 to your computer and use it in GitHub Desktop.
Example of interacting with Elasticsearch via REST calls in F#
// Domain-specific module that uses the lower-level Elastic module
namespace RawElastic
open Thoth.Json.Net
open System.Net.Http
open System
module String =
let split (sep : string) (target : string) =
target.Split(sep, StringSplitOptions.RemoveEmptyEntries)
type CourseInput = {
Id : string
Title : string
Authors : string
Tags : string
Description : string
}
type CourseOutput = {
Id : string
Title : string
Authors : string list
Tags : string list
Description : string
}
module CourseOutput =
let decoder =
Decode.object
(fun get ->
{
Id = get.Required.Field "id" Decode.string
Title = get.Required.Field "title" Decode.string
Authors = get.Required.Field "authors" (Decode.list Decode.string)
Tags = get.Required.Field "tags" (Decode.list Decode.string)
Description = get.Required.Field "description" Decode.string
} : CourseOutput
)
module Courses =
let createIndex (httpClientFactory : IHttpClientFactory) : unit =
Elastic.createIndex
httpClientFactory
"courses"
{|
mappings =
{|
properties =
{|
id = {| ``type`` = "keyword" |}
title = {| ``type`` = "text" |}
authors = {| ``type`` = "text" |}
tags = {| ``type`` = "text" |}
description = {| ``type`` = "text" |}
|}
|}
|}
let upsertCourse (httpClientFactory : IHttpClientFactory) (course : CourseInput) : unit =
Elastic.upsertDoc
httpClientFactory
"courses"
course.Id
{
Id = course.Id
Title = course.Title
Authors = course.Authors |> String.split ", " |> List.ofArray
Tags = course.Tags |> String.split ", " |> List.ofArray
Description = course.Description
}
let search (httpClientFactory : IHttpClientFactory) (searchTerm : string) : CourseOutput array =
Elastic.search
httpClientFactory
CourseOutput.decoder
"courses"
{|
query =
{|
bool =
{|
must = [
{| ``match`` = box {| title = searchTerm |} |} //need to box due to heterogeneous shape of list contents
{| ``match`` = box {| description = searchTerm |} |}
]
|}
|}
|}
module RawElastic.Elastic
open System.Net.Http
open Newtonsoft.Json
open Newtonsoft.Json.Serialization
open Thoth.Json.Net
module String =
let contains (substr : string) (target : string) =
target.Contains(substr)
module Json =
let options =
JsonSerializerSettings(
ContractResolver = DefaultContractResolver(
NamingStrategy = CamelCaseNamingStrategy()
)
)
let serialize obj =
JsonConvert.SerializeObject(obj, options)
// See example Http module here: https://gist.github.com/jhewlett/865c11442ab383a5ada5d0c6cad174d1
let createIndex (httpClientFactory : IHttpClientFactory) (indexName : string) (body : obj) : unit =
let request =
Http.createRequest (sprintf "http://localhost:9200/%s" indexName) Put
|> withBody (RequestBody.Json (Json.serialize body))
try
request
|> Http.executeAsync httpClientFactory
|> Async.RunSynchronously
|> ignore
with
| :? HttpRequestException as ex when ex.Message |> String.contains "resource_already_exists_exception" ->
() // a true upsert
let upsertDoc (httpClientFactory : IHttpClientFactory) (indexName : string) (id : string) (doc : obj) : unit =
let request =
Http.createRequest (sprintf "http://localhost:9200/%s/_doc/%s" indexName id) Put
|> withBody (RequestBody.Json (Json.serialize doc))
request
|> Http.executeAsync httpClientFactory
|> Async.RunSynchronously
|> ignore
let search<'doc> (httpClientFactory : IHttpClientFactory) (decoder : Decoder<'doc>) (indexName : string) (query : obj) : 'doc array =
let request =
Http.createRequest (sprintf "http://localhost:9200/%s/_search" indexName) Post
|> withBody (RequestBody.Json (Json.serialize query))
let response =
request
|> Http.executeAsync httpClientFactory
|> Async.RunSynchronously
response.Body
|> Decode.unsafeFromString
(Decode.at
["hits"; "hits"]
(Decode.array
(Decode.field "_source" decoder)
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment