diff --git a/civicpy/civic.py b/civicpy/civic.py
index 203f39f..f6cf475 100644
--- a/civicpy/civic.py
+++ b/civicpy/civic.py
@@ -850,8 +850,8 @@ def bulk_search_variants_by_coordinates(sorted_queries, search_mode='any'):
 
     :param search_mode: ['any', 'query_encompassing', 'variant_encompassing', 'exact']
                         any: any overlap between a query and a variant is a match
-                        query_encompassing: variants must fit within the coordinates of the query
-                        variant_encompassing: variants must encompass the coordinates of the query
+                        query_encompassing: CIViC variant records must fit within the coordinates of the query
+                        record_encompassing: CIViC variant records must encompass the coordinates of the query
                         exact: variants must match coordinates precisely, as well as alternate
                                allele, if provided
                         search_mode is 'exact' by default
@@ -881,6 +881,10 @@ def bulk_search_variants_by_coordinates(sorted_queries, search_mode='any'):
     ct = MODULE.COORDINATE_TABLE
     matches = defaultdict(list)
     Match = namedtuple('Match', ct.columns)
+
+    def append_match(matches_list, query, ct_row):
+        matches_list[query].append(Match(**ct_row.to_dict()))
+
     while query_pointer < len(sorted_queries) and ct_pointer < len(ct):
         if last_query_pointer != query_pointer:
             q = sorted_queries[query_pointer]
@@ -908,16 +912,16 @@ def bulk_search_variants_by_coordinates(sorted_queries, search_mode='any'):
             query_pointer += 1
             continue
         if search_mode == 'any':
-            matches[q].append(c.to_dict())
+            append_match(matches, q, c)
         elif search_mode == 'exact' and q_start == c_start and q_stop == c_stop:
             q_alt = q.alt
             c_alt = c.alt
             if not (q_alt and c_alt and q_alt != c_alt):
-                matches[q].append(Match(**c.to_dict()))
-        elif search_mode == 'query_encompassing':
-            raise NotImplementedError
-        elif search_mode == 'variant_encompassing':
-            raise NotImplementedError
+                append_match(matches, q, c)
+        elif search_mode == 'query_encompassing' and q_start <= c_start and q_stop >= c_stop:
+            append_match(matches, q, c)
+        elif search_mode == 'record_encompassing' and c_start <= q_start and c_stop >= q_stop:
+            append_match(matches, q, c)
         if match_start is None:
             match_start = ct_pointer
         ct_pointer += 1

implement intermediate search strategies
Query encompassing and record encompassing search strategies need to be implemented for bulk coordinate search. See Figure 2D from manuscript for details.
diff --git a/civicpy/tests/test_civic.py b/civicpy/tests/test_civic.py
index dc6e337..bbc6bb7 100644
--- a/civicpy/tests/test_civic.py
+++ b/civicpy/tests/test_civic.py
@@ -133,11 +133,38 @@ class TestCoordinateSearch(object):
         assertion_ids = [x.id for x in assertions]
         assert set(assertion_ids) >= set(v600e_assertion_ids)
 
-    def test_bulk_search_variants(self):
+    def test_bulk_any_search_variants(self):
         sorted_queries = [
             CoordinateQuery('7', 140453136, 140453136, 'T'),
             CoordinateQuery('7', 140453136, 140453137, 'TT')
         ]
-        search_results = civic.bulk_search_variants_by_coordinates(sorted_queries)
-        assert len(search_results[sorted_queries[0]]) >= 12
-        assert len(search_results[sorted_queries[1]]) >= len(search_results[sorted_queries[0]])
+        search_results = civic.bulk_search_variants_by_coordinates(sorted_queries, search_mode='any')
+        assert len(search_results[sorted_queries[0]]) == 19
+        assert len(search_results[sorted_queries[1]]) >= 19
+
+    def test_bulk_exact_search_variants(self):
+        sorted_queries = [
+            CoordinateQuery('7', 140453136, 140453136, 'T'),
+            CoordinateQuery('7', 140453136, 140453137, 'TT')
+        ]
+        search_results = civic.bulk_search_variants_by_coordinates(sorted_queries, search_mode='exact')
+        assert len(search_results[sorted_queries[0]]) == 1
+        assert len(search_results[sorted_queries[1]]) == 2
+
+    def test_bulk_qe_search_variants(self):
+        sorted_queries = [
+            CoordinateQuery('7', 140453136, 140453136),
+            CoordinateQuery('7', 140453136, 140453137)
+        ]
+        search_results = civic.bulk_search_variants_by_coordinates(sorted_queries, search_mode='query_encompassing')
+        assert len(search_results[sorted_queries[0]]) == 1
+        assert len(search_results[sorted_queries[1]]) == 4
+
+    def test_bulk_re_search_variants(self):
+        sorted_queries = [
+            CoordinateQuery('7', 140453136, 140453136),
+            CoordinateQuery('7', 140453136, 140453137)
+        ]
+        search_results = civic.bulk_search_variants_by_coordinates(sorted_queries, search_mode='record_encompassing')
+        assert len(search_results[sorted_queries[0]]) == 19
+        assert len(search_results[sorted_queries[1]]) == 16

pip install -e .
parse_log_pytest
requirements.txt
pytest
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
-e git+https://github.com/griffithlab/civicpy.git@37a2f8b3cb75dc46b70ffa5884d69ffc4d29cf04#egg=civicpy
Click==7.0
coverage==7.2.7
exceptiongroup==1.2.2
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
networkx==2.6.3
numpy==1.21.6
obonet==0.2.3
packaging==24.0
pandas==0.24.1
pluggy==1.2.0
pytest==7.4.4
pytest-cov==4.1.0
python-coveralls==2.9.3
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.1
requests==2.31.0
six==1.17.0
tomli==2.0.1
typing_extensions==4.7.1
urllib3==2.0.7
zipp==3.15.0

name: civicpy
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - charset-normalizer==3.4.1
      - click==7.0
      - coverage==7.2.7
      - exceptiongroup==1.2.2
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - networkx==2.6.3
      - numpy==1.21.6
      - obonet==0.2.3
      - packaging==24.0
      - pandas==0.24.1
      - pluggy==1.2.0
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - python-coveralls==2.9.3
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.1
      - requests==2.31.0
      - six==1.17.0
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - zipp==3.15.0
prefix: /opt/conda/envs/civicpy

civicpy/tests/test_civic.py::TestCoordinateSearch::test_bulk_qe_search_variants civicpy/tests/test_civic.py::TestCoordinateSearch::test_bulk_re_search_variants
civicpy/tests/test_civic.py::TestGetFunctions::test_get_assertions civicpy/tests/test_civic.py::TestCivicRecord::test_module civicpy/tests/test_civic.py::TestEvidence::test_get_source_ids civicpy/tests/test_civic.py::TestEvidence::test_get_all civicpy/tests/test_civic.py::TestEvidence::test_get_non_rejected civicpy/tests/test_civic.py::TestEvidence::test_get_accepted_only civicpy/tests/test_civic.py::TestVariants::test_get_all civicpy/tests/test_civic.py::TestVariants::test_get_non_rejected civicpy/tests/test_civic.py::TestVariants::test_get_accepted_only civicpy/tests/test_civic.py::TestAssertions::test_get_all civicpy/tests/test_civic.py::TestAssertions::test_get_non_rejected civicpy/tests/test_civic.py::TestAssertions::test_get_accepted_only civicpy/tests/test_civic.py::TestGenes::test_get_all civicpy/tests/test_civic.py::TestGenes::test_get_non_rejected civicpy/tests/test_civic.py::TestGenes::test_get_accepted_only civicpy/tests/test_civic.py::TestCoordinateSearch::test_search_assertions civicpy/tests/test_civic.py::TestCoordinateSearch::test_bulk_any_search_variants civicpy/tests/test_civic.py::TestCoordinateSearch::test_bulk_exact_search_variants
civicpy/civic.py
civicpy/civic.py