Last active
June 6, 2021 17:57
-
-
Save jhewlett/850e52586c30f95739076aa189a0b8f3 to your computer and use it in GitHub Desktop.
Example of interacting with Elasticsearch via REST calls in F#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |} |} | |
] | |
|} | |
|} | |
|} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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