class_docstr: Stores data in-memory. It's ephemeral and cannot be saved to disk.
docstr: Retrieves documents that are most relevant to the query using BM25 algorithm.
  
  :param query: The query string.
  :param filters: A dictionary with filters to narrow down the search space.
  :param top_k: The number of top documents to retrieve. Default is 10.
  :param scale_score: Whether to scale the scores of the retrieved documents. Default is False.
  :return: A list of the top_k documents most relevant to the query.
docstr_tok: ["Retrieves","documents","that","are","most","relevant","to","the","query","using","BM25","algorithm",".",":","param","query",":","The","query","string",".",":","param","filters",":","A","dictionary","with","filters","to","narrow","down","the","search","space",".",":","param","top_k",":","The","number","of","top","documents","to","retrieve",".","Default","is","10.",":","param","scale_score",":","Whether","to","scale","the","scores","of","the","retrieved","documents",".","Default","is","False",".",":","return",":","A","list","of","the","top_k","documents","most","relevant","to","the","query","."]
code: def bm25_retrieval(
      self,
      query: str,
      filters: Optional[Dict[str, Any]] = None,
      top_k: int = 10,
      scale_score: bool = False,
  ) -> List[Document]:
      """
      Retrieves documents that are most relevant to the query using BM25 algorithm.
  
      :param query: The query string.
      :param filters: A dictionary with filters to narrow down the search space.
      :param top_k: The number of top documents to retrieve. Default is 10.
      :param scale_score: Whether to scale the scores of the retrieved documents. Default is False.
      :return: A list of the top_k documents most relevant to the query.
      """
      if not query:
          raise ValueError("Query should be a non-empty string")
  
      content_type_filter = {
          "operator": "OR",
          "conditions": [
              {"field": "content", "operator": "!=", "value": None},
              {"field": "dataframe", "operator": "!=", "value": None},
          ],
      }
      if filters:
          if "operator" not in filters:
              filters = convert(filters)
          filters = {
              "operator": "AND",
              "conditions": [content_type_filter, filters],
          }
      else:
          filters = content_type_filter
      all_documents = self.filter_documents(filters=filters)
  
      # Lowercase all documents
      lower_case_documents = []
      for doc in all_documents:
          if doc.content is None and doc.dataframe is None:
              logger.info(
                  "Document '%s' has no text or dataframe content. Skipping it.",
                  doc.id,
              )
          else:
              if doc.content is not None:
                  lower_case_documents.append(doc.content.lower())
                  if doc.dataframe is not None:
                      logger.warning(
                          "Document '%s' has both text and dataframe content. "
                          "Using text content and skipping dataframe content.",
                          doc.id,
                      )
                      continue
              if doc.dataframe is not None:
                  str_content = doc.dataframe.astype(str)
                  csv_content = str_content.to_csv(index=False)
                  lower_case_documents.append(csv_content.lower())
  
      # Tokenize the entire content of the DocumentStore
      tokenized_corpus = [
          self.tokenizer(doc)
          for doc in tqdm(
              lower_case_documents,
              unit=" docs",
              desc="Ranking by BM25...",
          )
      ]
      if len(tokenized_corpus) == 0:
          logger.info(
              "No documents found for BM25 retrieval. Returning empty list."
          )
          return []
  
      # initialize BM25
      bm25_scorer = self.bm25_algorithm(
          tokenized_corpus, **self.bm25_parameters
      )
      # tokenize query
      tokenized_query = self.tokenizer(query.lower())
      # get scores for the query against the corpus
      docs_scores = bm25_scorer.get_scores(tokenized_query)
      if scale_score:
          docs_scores = [
              expit(float(score / BM25_SCALING_FACTOR))
              for score in docs_scores
          ]
      # get the last top_k indexes and reverse them
      top_docs_positions = np.argsort(docs_scores)[-top_k:][::-1]
  
      # Create documents with the BM25 score to return them
      return_documents = []
      for i in top_docs_positions:
          doc = all_documents[i]
          score = docs_scores[i]
          if score <= 0.0:
              continue
          doc_fields = doc.to_dict()
          doc_fields["score"] = score
          return_document = Document.from_dict(doc_fields)
          return_documents.append(return_document)
      return return_documents
  
imports: import re
  from typing import Literal, Any, Dict, List, Optional, Iterable
  import logging
  import numpy
  from haystack_bm25 import rank_bm25
  from tqdm.auto import tqdm
  from haystack import default_from_dict, default_to_dict
  from haystack.dataclasses import Document
  from haystack.document_stores.types import DuplicatePolicy
  from haystack.utils.filters import document_matches_filter, convert
  from haystack.document_stores.errors import DuplicateDocumentError, DocumentStoreError
  from haystack.utils import expit
label_desc: Use image node_id 8 for calling the InMemoryDocumentStore obj's underlying member method code with example usage: obj.bm25_retrieval(query, filters, top_k, scale_score) and returns: return_documents, list