(defpackage #:search (:use #:cl #:app-constants #:cl-json #:local-time #:utils) (:import-from #:dexador #:request) (:export #:build-keywords-string #:build-payload #:delete-entry #:find-entry #:get-id #:get-keywords #:submit-entry #:set-filter-attributes #:delete-all-entries #:create-dump)) (in-package #:search) ;; Explains the "~{~A~^,~}" in the format call below. ;; https://stackoverflow.com/questions/8830888/whats-the-canonical-way-to-join-strings-in-a-list (defun build-keywords-string (id) "Gets the keywords for `ID' in meilisearch DB and formats into a string. The string should look something like: 'art,blog post,testing,whatever'. One thing to note is meilisearch uses the comma to separate and create tokens of the string. So, the user can have spaces in their keywords but they cannot separate the keywords with a space. 'art blog post' is classed as three tokens and 'art,blog post' is classes as two." (format nil "~{~A~^,~}" (search:get-keywords (search:find-entry id)))) (defun build-payload (id-value title-value relative-path-value thumbnail-path-value created-at-value keywords-value) "Creates a JSON object which reflects the schema in the meilsearch database. Note: The JSON object to encoded as a string." (cl-json:encode-json-to-string `((("id" . ,id-value) ("title" . ,title-value) ("relative-path" . ,relative-path-value) ("thumbnail-path" . ,thumbnail-path-value) ("year" . ,(local-time:timestamp-year created-at-value)) ("month" . ,(utils:month-number-to-name (local-time:timestamp-month created-at-value))) ("day" . ,(local-time:timestamp-day created-at-value)) ("keywords" . ,(cl-ppcre:split "," keywords-value)))))) (defun build-search-url (path) "Constructs the URL to connect to the meilisearch instance (beta or prod.) The function will check to see which environment the current instance of this site is running in and use the beta or prod. URL's to connect to meilisearch. `PATH' is the relative path which this function will concatenate onto the end of the base URL." (if (ritherdon-archive.config:developmentp) (concatenate 'string "http://localhost:7700" path) (concatenate 'string "https://www.nera.com" path))) (defun delete-entry (id) "Deletes and entry from the meilisearch database based on its `ID'. This does not affect this website's (nera) database -- only the meilisearch one." (dexador:request (build-search-url (format nil "/indexes/nera/documents/~a" id)); (get-id (find-entry id)))) :method :delete :use-connection-pool nil :headers `(("Authorization" . ,(meilisearch-api-key))) :verbose nil)) (defun documents-total () "Gets the total number of documents in the meilisearch database." (rest (second (second (assoc :indexes (cl-json:decode-json-from-string (dexador:request (build-search-url "/stats") :method :get :use-connection-pool nil :headers `(("Content-Type" . "application/json") ("Authorization" . ,(meilisearch-api-key))) :verbose nil))))))) (defun find-entry (title) "Finds the entry in the meilisearch database by its `TITLE'." (cl-json:decode-json-from-string (dexador:request (build-search-url "/indexes/nera/search") :method :post :use-connection-pool nil :headers `(("Content-Type" . "application/json") ("Authorization" . ,(meilisearch-api-key))) :content (format nil "{ \"q\": \"~a\", \"limit\": 1 }" title) :verbose nil))) (defun get-id (payload) "Gets the id from the JSON `PAYLOAD', make sure limit is set to 1." (rest (third (second (first payload))))) (defun get-keywords (payload) "Get the keywords from the JSON `PAYLOAD'." (rest (first (last (last (second (first payload))))))) (defun meilisearch-api-key () "Returns either the beta or prod. API key for meilisearch. The API key is determined based on which environment this website is currently running in." (if (ritherdon-archive.config:developmentp) "Bearer meilisearch-beta-key" "Bearer meilisearch-production-key-nera")) (defun submit-entry (payload) "Adds a new article to the meilisearch database." (dexador:request ;;"http://127.0.0.1:7700/indexes/nera/documents" (build-search-url "/indexes/nera/documents") :method :post :use-connection-pool nil :headers `(("Content-Type" . "application/json") ("Authorization" . ,(meilisearch-api-key))) :content payload :verbose nil)) (defun set-filter-attributes () "Sets the filter attributes in the Meilisearch database. These values are hard-coded into the system because they are based on what Nic has requested. She would like the filtering to consist on years and months." (utils:run-bash-command (format nil "curl -X PATCH \'~a\' -H \'Authorization: ~a\' -H \'Content-Type: application/json\' --data-binary \'{ \"filterableAttributes\": [ \"year\", \"month\", \"keywords\" ]}\'" (build-search-url "/indexes/nera/settings") (meilisearch-api-key)))) (defun delete-all-entries () "Deletes all the archive entries in the Meilisearch database -- not the DB." (utils:run-bash-command (format nil "curl -X DELETE \'~a\'" (build-search-url "/indexes/nera/documents")))) (defun create-dump () "Creates a dump of the Meilisearch database." (utils:run-bash-command (format nil "curl -X POST \'~a\'" (build-search-url "/dumps"))))