docs: def select(self, meta_predicate=None, region_predicate=None,
                 semiJoinDataset=None,  semiJoinMeta=None):
          """ *Wrapper of* ``SELECT``
  
          Selection operation. Enables to filter datasets on the basis of region features or metadata attributes. In
          addition it is possibile to perform a selection based on the existence of certain metadata
          :attr:`~.semiJoinMeta` attributes and the matching of their values with those associated with at
          least one sample in an external dataset :attr:`~.semiJoinDataset`.
  
          Therefore, the selection can be based on:
  
              * *Metadata predicates*: selection based on the existence and values of certain
                metadata attributes in each sample. In predicates, attribute-value conditions
                can be composed using logical predicates & (and), | (or) and ~ (not)
              * *Region predicates*: selection based on region attributes. Conditions
                can be composed using logical predicates & (and), | (or) and ~ (not)
              * *SemiJoin clauses*: selection based on the existence of certain metadata :attr:`~.semiJoinMeta`
                attributes and the matching of their values with those associated with at
                least one sample in an external dataset :attr:`~.semiJoinDataset`
  
  
          In the following example we select all the samples from Example_Dataset_1 regarding antibody CTCF.
          From these samples we select only the regions on chromosome 6. Finally we select only the samples which have
          a matching antibody_targetClass in Example_Dataset_2::
  
              import gmql as gl
              d1 = gl.get_example_dataset("Example_Dataset_1")
              d2 = gl.get_example_dataset("Example_Dataset_2")
  
              d_select = d.select(meta_predicate = d['antibody'] == "CTCF",
                                  region_predicate = d.chr == "chr6",
                                  semiJoinDataset=d2, semiJoinMeta=["antibody_targetClass"])
  
          :param meta_predicate: logical predicate on the metadata <attribute, value> pairs
          :param region_predicate: logical predicate on the region feature values
          :param semiJoinDataset: an other GMQLDataset
          :param semiJoinMeta: a list of metadata attributes (strings)
          :return: a new GMQLDataset
          """
  
          semiJoinDataset_exists = False
          if isinstance(meta_predicate, MetaField):
              meta_condition = Some(meta_predicate.getMetaCondition())
          elif meta_predicate is None:
              meta_condition = none()
          else:
              raise TypeError("meta_predicate must be a MetaField or None."
                              " {} was provided".format(type(meta_predicate)))
  
          if isinstance(region_predicate, RegField):
              region_condition = Some(region_predicate.getRegionCondition())
          elif region_predicate is None:
              region_condition = none()
          else:
              raise TypeError("region_predicate must be a RegField or None."
                              " {} was provided".format(type(region_predicate)))
  
          new_location = self.location
          new_local_sources, new_remote_sources = self._local_sources, self._remote_sources
  
          if isinstance(semiJoinDataset, GMQLDataset):
              other_dataset = Some(semiJoinDataset.__index)
              semiJoinDataset_exists = True
          elif semiJoinDataset is None:
              other_dataset = none()
          else:
              raise TypeError("semiJoinDataset must be a GMQLDataset or None."
                              " {} was provided".format(type(semiJoinDataset)))
  
          if isinstance(semiJoinMeta, list) and \
                  all([isinstance(x, str) for x in semiJoinMeta]):
              if semiJoinDataset_exists:
                  semi_join = Some(self.opmng.getMetaJoinCondition(semiJoinMeta))
  
                  new_local_sources, new_remote_sources = self.__combine_sources(self, semiJoinDataset)
                  new_location = self.__combine_locations(self, semiJoinDataset)
              else:
                  raise ValueError("semiJoinDataset and semiJoinMeta must be present at the "
                                   "same time or totally absent")
          elif semiJoinMeta is None:
              semi_join = none()
          else:
              raise TypeError("semiJoinMeta must be a list of strings or None."
                              " {} was provided".format(type(semiJoinMeta)))
  
          new_index = self.opmng.select(self.__index, other_dataset,
                                        semi_join, meta_condition, region_condition)
  
          return GMQLDataset(index=new_index, location=new_location, local_sources=new_local_sources,
                             remote_sources=new_remote_sources, meta_profile=self.meta_profile)