summaryrefslogtreecommitdiff
path: root/logical_search.py
diff options
context:
space:
mode:
Diffstat (limited to 'logical_search.py')
-rw-r--r--logical_search.py105
1 files changed, 80 insertions, 25 deletions
diff --git a/logical_search.py b/logical_search.py
index 2f79db0..e710d0b 100644
--- a/logical_search.py
+++ b/logical_search.py
@@ -3,9 +3,7 @@
# © Copyright 2021-2022, Scott Gasch
"""This is a module concerned with the creation of and searching of a
-corpus of documents. The corpus is held in memory for fast
-searching.
-
+corpus of documents. The corpus and index are held in memory.
"""
from __future__ import annotations
@@ -28,18 +26,27 @@ class ParseError(Exception):
class Document:
"""A class representing a searchable document."""
- # A unique identifier for each document.
docid: str = ''
+ """A unique identifier for each document -- must be provided
+ by the caller. See :meth:`python_modules.id_generator.get` or
+ :meth:`python_modules.string_utils.generate_uuid` for potential
+ sources."""
- # A set of tag strings for this document. May be empty.
tags: Set[str] = field(default_factory=set)
+ """A set of tag strings for this document. May be empty. Tags
+ are simply text labels that are associated with a document and
+ may be used to search for it later.
+ """
- # A list of key->value strings for this document. May be empty.
properties: List[Tuple[str, str]] = field(default_factory=list)
+ """A list of key->value strings for this document. May be empty.
+ Properties are more flexible tags that have both a label and a
+ value. e.g. "category:mystery" or "author:smith"."""
- # An optional reference to something else; interpreted only by
- # caller code, ignored here.
reference: Optional[Any] = None
+ """An optional reference to something else for convenience;
+ interpreted only by caller code, ignored here.
+ """
class Operation(enum.Enum):
@@ -69,7 +76,11 @@ class Operation(enum.Enum):
class Corpus(object):
- """A collection of searchable documents.
+ """A collection of searchable documents. The caller can
+ add documents to it (or edit existing docs) via :meth:`add_doc`,
+ retrieve a document given its docid via :meth:`get_doc`, and
+ perform various lookups of documents. The most interesting
+ lookup is implemented in :meth:`query`.
>>> c = Corpus()
>>> c.add_doc(Document(
@@ -123,11 +134,14 @@ class Corpus(object):
distinct docid that will serve as its primary identifier. If
the same Document is added multiple times, only the most
recent addition is indexed. If two distinct documents with
- the same docid are added, the latter klobbers the former in the
- indexes.
+ the same docid are added, the latter klobbers the former in
+ the indexes. See :meth:`python_modules.id_generator.get` or
+ :meth:`python_modules.string_utils.generate_uuid` for potential
+ sources of docids.
Each Document may have an optional set of tags which can be
- used later in expressions to the query method.
+ used later in expressions to the query method. These are simple
+ text labels.
Each Document may have an optional list of key->value tuples
which can be used later in expressions to the query method.
@@ -136,6 +150,9 @@ class Corpus(object):
never interpreted by this module. This is meant to allow easy
mapping between Documents in this corpus and external objects
they may represent.
+
+ Args:
+ doc: the document to add or edit
"""
if doc.docid in self.documents_by_docid:
@@ -161,12 +178,27 @@ class Corpus(object):
self.docids_with_property[key].add(doc.docid)
def get_docids_by_exact_tag(self, tag: str) -> Set[str]:
- """Return the set of docids that have a particular tag."""
+ """Return the set of docids that have a particular tag.
+
+ Args:
+ tag: the tag for which to search
+
+ Returns:
+ A set containing docids with the provided tag which
+ may be empty."""
return self.docids_by_tag[tag]
def get_docids_by_searching_tags(self, tag: str) -> Set[str]:
- """Return the set of docids with a tag that contains a str"""
+ """Return the set of docids with a tag that contains a str.
+
+ Args:
+ tag: the tag pattern for which to search
+ Returns:
+ A set containing docids with tags that match the pattern
+ provided. e.g., if the arg was "foo" tags "football", "foobar",
+ and "food" all match.
+ """
ret = set()
for search_tag in self.docids_by_tag:
if tag in search_tag:
@@ -178,42 +210,65 @@ class Corpus(object):
"""Return the set of docids that have a particular property no matter
what that property's value.
+ Args:
+ key: the key value to search for.
+
+ Returns:
+ A set of docids that contain the key (no matter what value)
+ which may be empty.
"""
return self.docids_with_property[key]
def get_docids_by_property(self, key: str, value: str) -> Set[str]:
"""Return the set of docids that have a particular property with a
- particular value..
+ particular value.
+ Args:
+ key: the key to search for
+ value: the value that key must have in order to match a doc.
+
+ Returns:
+ A set of docids that contain key with value which may be empty.
"""
return self.docids_by_property[(key, value)]
def invert_docid_set(self, original: Set[str]) -> Set[str]:
"""Invert a set of docids."""
-
return {docid for docid in self.documents_by_docid if docid not in original}
def get_doc(self, docid: str) -> Optional[Document]:
- """Given a docid, retrieve the previously added Document."""
+ """Given a docid, retrieve the previously added Document.
+ Args:
+ docid: the docid to retrieve
+
+ Returns:
+ The Document with docid or None to indicate no match.
+ """
return self.documents_by_docid.get(docid, None)
def query(self, query: str) -> Optional[Set[str]]:
"""Query the corpus for documents that match a logical expression.
- Returns a (potentially empty) set of docids for the matching
- (previously added) documents or None on error.
- e.g.
+ Args:
+ query: the logical query expressed using a simple language
+ that understands conjunction (and operator), disjunction
+ (or operator) and inversion (not operator) as well as
+ parenthesis. Here are some legal sample queries::
+
+ tag1 and tag2 and not tag3
- tag1 and tag2 and not tag3
+ (tag1 or tag2) and (tag3 or tag4)
- (tag1 or tag2) and (tag3 or tag4)
+ (tag1 and key2:value2) or (tag2 and key1:value1)
- (tag1 and key2:value2) or (tag2 and key1:value1)
+ key:*
- key:*
+ tag1 and key:*
- tag1 and key:*
+ Returns:
+ A (potentially empty) set of docids for the matching
+ (previously added) documents or None on error.
"""
try: