docs: def read_by_indexes(table_name, index_name_values=None):
      """Index reader."""
      req = datastore.RunQueryRequest()
  
      query = req.query
      query.kind.add().name = table_name
  
      if not index_name_values:
          index_name_values = []
      for name, val in index_name_values:
          queryFilter = query.filter.property_filter
          queryFilter.property.name = name
          queryFilter.operator = datastore.PropertyFilter.EQUAL
          queryFilter.value.string_value = str(val)
  
      loop_its = 0
      have_more = True
  
      while have_more:
          resp = datastore.run_query(req)
  
          found_something = False
          for found in resp.batch.entity_result:
              yield extract_entity(found)
              found_something = True
  
          if not found_something:
              # This is a guard against bugs or excessive looping - as long we
              # can keep yielding records we'll continue to execute
              loop_its += 1
              if loop_its > 5:
                  raise ValueError("Exceeded the excessive query threshold")
  
          if resp.batch.more_results != datastore.QueryResultBatch.NOT_FINISHED:
              have_more = False
          else:
              have_more = True
              end_cursor = resp.batch.end_cursor
              query.start_cursor.CopyFrom(end_cursor)