From 6edd0b4ef0f888074ea58c3c5314d5e49eb2994c Mon Sep 17 00:00:00 2001
From: Dustin Thomas <stdio@cptlobster.dev>
Date: Fri, 1 Mar 2024 20:37:02 -0600
Subject: [PATCH 1/3] fix map datatype

---
 read_datasheet.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/read_datasheet.py b/read_datasheet.py
index f19d30f..d22a51c 100755
--- a/read_datasheet.py
+++ b/read_datasheet.py
@@ -236,7 +236,7 @@ def flatten(tables):
 
             # if the item has at least two commas in it, split it
             if tables[table][key].count(',') >= 2:
-                out[fullkeyname] = map(lambda x: x.strip(), tables[table][key].split(","))
+                out[fullkeyname] = list(map(lambda x: x.strip(), tables[table][key].split(",")))
                 print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",")
 
 

From 4561b1c1a3a98ff6c3384694cbc33ee8cc30308a Mon Sep 17 00:00:00 2001
From: Dustin Thomas <stdio@cptlobster.dev>
Date: Fri, 1 Mar 2024 20:37:22 -0600
Subject: [PATCH 2/3] fix error when index does not exist

---
 search.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/search.py b/search.py
index b45934a..c962b86 100644
--- a/search.py
+++ b/search.py
@@ -1,6 +1,7 @@
 """Interactions with the Meilisearch API for adding and searching cables."""
 from meilisearch import Client
 from meilisearch.task import TaskInfo
+from meilisearch.errors import MeilisearchApiError
 import json
 
 DEFAULT_URL = "http://localhost:7700"
@@ -24,7 +25,9 @@ class JukeboxSearch:
         self.index = index or DEFAULT_INDEX
         self.client = Client(url, api_key)
         # create the index if it does not exist already
-        if self.client.get_index(self.index) is None:
+        try:
+            self.client.get_index(self.index)
+        except MeilisearchApiError as _:
             self.client.create_index(self.index)
 
     def add_document(self, document: dict) -> TaskInfo:

From aadb6ba24d7964cc996bfb5046e2917575cbad6d Mon Sep 17 00:00:00 2001
From: Dustin Thomas <stdio@cptlobster.dev>
Date: Fri, 1 Mar 2024 21:24:37 -0600
Subject: [PATCH 3/3] add search functions to JukeboxSearch

---
 read_datasheet.py |  2 +-
 search.py         | 60 ++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 55 insertions(+), 7 deletions(-)

diff --git a/read_datasheet.py b/read_datasheet.py
index d22a51c..0517608 100755
--- a/read_datasheet.py
+++ b/read_datasheet.py
@@ -235,7 +235,7 @@ def flatten(tables):
                 print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",")
 
             # if the item has at least two commas in it, split it
-            if tables[table][key].count(',') >= 2:
+            if tables[table][key].count(',') > 0:
                 out[fullkeyname] = list(map(lambda x: x.strip(), tables[table][key].split(",")))
                 print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",")
 
diff --git a/search.py b/search.py
index c962b86..88aef38 100644
--- a/search.py
+++ b/search.py
@@ -7,21 +7,28 @@ import json
 DEFAULT_URL = "http://localhost:7700"
 DEFAULT_APIKEY = "fluffybunnyrabbit" # I WOULD RECOMMEND SOMETHING MORE SECURE
 DEFAULT_INDEX = "cables"
+DEFAULT_FILTERABLE_ATTRS = ["partnum", "uuid", "position"] # default filterable attributes
 
 
 class JukeboxSearch:
     """Class for interacting with the Meilisearch API."""
-    def __init__(self, url: str = None, api_key: str = None, index: str = None):
+    def __init__(self,
+                 url: str = None,
+                 api_key: str = None,
+                 index: str = None,
+                 filterable_attrs: list = None):
         """Connect to Meilisearch and perform first-run tasks as necessary.
 
         :param url: Address of the Meilisearch server. Defaults to ``http://localhost:7700`` if unspecified.
         :param api_key: API key used to authenticate with Meilisearch. It is highly recommended to set this as something
         secure if you can access this endpoint publicly, but you can ignore this and set Meilisearch's default API key
         to ``fluffybunnyrabbit``.
-        :param index: The name of the index to configure. Defaults to ``cables`` if unspecified."""
+        :param index: The name of the index to configure. Defaults to ``cables`` if unspecified.
+        :param filterable_attrs: List of all the attributes we want to filter by."""
         # connect to Meilisearch
         url = url or DEFAULT_URL
         api_key = api_key or DEFAULT_APIKEY
+        filterable_attrs = filterable_attrs or DEFAULT_FILTERABLE_ATTRS
         self.index = index or DEFAULT_INDEX
         self.client = Client(url, api_key)
         # create the index if it does not exist already
@@ -29,13 +36,17 @@ class JukeboxSearch:
             self.client.get_index(self.index)
         except MeilisearchApiError as _:
             self.client.create_index(self.index)
+        # make a variable to easily reference the index
+        self.idxref = self.client.index(self.index)
+
+        self.idxref.update_filterable_attributes(filterable_attrs)
 
     def add_document(self, document: dict) -> TaskInfo:
         """Add a cable to the Meilisearch index.
 
         :param document: Dictionary containing all the cable data.
         :returns: A TaskInfo object for the addition of the new document."""
-        return self.client.index(self.index).add_documents(document)
+        return self.idxref.add_documents(document)
 
     def add_documents(self, documents: list):
         """Add a list of cables to the Meilisearch index.
@@ -47,6 +58,43 @@ class JukeboxSearch:
             taskinfo = self.add_document(i)
         return taskinfo
 
-    def query(self):
-        """Execute a search query on the Meilisearch index."""
-        pass
+    def search(self, query: str, filters: str = None):
+        """Execute a search query on the Meilisearch index.
+        
+        :param query: Seach query
+        :param filters: A meilisearch compatible filter statement.
+        :returns: The search results dict. Actual results are in a list under "hits", but there are other nice values that are useful in the root element."""
+        if filters:
+            q = self.idxref.search(query, {"filter": filters})
+        else:
+            q = self.idxref.search(query)
+        return q
+    
+    def _filter_one(self, filter: str):
+        """Get the first item to match a filter.
+        
+        :param filter: A meilisearch compatible filter statement.
+        :returns: A dict containing the results; If no results found, an empty dict."""
+        q = self.search("", filter)
+        if q["estimatedTotalHits"] != 0:
+            return ["hits"][0]
+        else:
+            return dict()
+
+    def get_position(self, position: str):
+        """Get a part by position.
+        
+        :param partnum: The position to search for."""
+        return self._filter_one(f"position = {position}")
+    
+    def get_uuid(self, uuid: str):
+        """Get a specific UUID.
+        
+        :param uuid: The UUID to search for."""
+        return self._filter_one(f"uuid = {uuid}")
+
+    def get_partnum(self, partnum: str):
+        """Get a specific part number.
+        
+        :param partnum: The part number to search for."""
+        return self._filter_one(f"partnum = {partnum}")