class_docstr: Processes source nodes before other nodes are joined.
  
  Generally, the source nodes will be combined with other dataflow plan nodes to produce a new set of nodes to realize
  a condition of the query. For example, to realize a time range constraint, a ConstrainTimeRangeNode will be added
  to the source nodes.
  
  e.g.
  
  <SomeDataflowPlanNode/>
  
  ->
  
  <ConstrainTimeRangeNode>
      <SomeDataflowPlanNode/>
  </ConstrainTimeRangeNode>
docstr: Assemble nodes representing all possible one-hop joins.
docstr_tok: ["Assemble","nodes","representing","all","possible","one-hop","joins","."]
code: def add_multi_hop_joins(
      self,
      desired_linkable_specs: Sequence[LinkableInstanceSpec],
      nodes: Sequence[BaseOutput],
      join_type: SqlJoinType,
  ) -> Sequence[BaseOutput]:
      """Assemble nodes representing all possible one-hop joins."""
      all_multi_hop_join_candidates: List[MultiHopJoinCandidate] = []
      lineage_for_all_multi_hop_join_candidates: Set[
          MultiHopJoinCandidateLineage
      ] = set()
  
      for desired_linkable_spec in desired_linkable_specs:
          for (
              multi_hop_join_candidate
          ) in self._get_candidates_nodes_for_multi_hop(
              desired_linkable_spec=desired_linkable_spec,
              nodes=nodes,
              join_type=join_type,
          ):
              # Dedupe candidates that are the same join.
              if (
                  multi_hop_join_candidate.lineage
                  not in lineage_for_all_multi_hop_join_candidates
              ):
                  all_multi_hop_join_candidates.append(
                      multi_hop_join_candidate
                  )
                  lineage_for_all_multi_hop_join_candidates.add(
                      multi_hop_join_candidate.lineage
                  )
  
      return list(
          x.node_with_multi_hop_elements
          for x in all_multi_hop_join_candidates
      ) + list(nodes)
  
imports: from __future__ import annotations
  import logging
  from dataclasses import dataclass
  from typing import List, Optional, Sequence, Set
  from dbt_semantic_interfaces.pretty_print import pformat_big_objects
  from dbt_semantic_interfaces.references import EntityReference, TimeDimensionReference
  from metricflow.dataflow.builder.node_data_set import DataflowPlanNodeOutputDataSetResolver
  from metricflow.dataflow.builder.partitions import PartitionJoinResolver
  from metricflow.dataflow.dataflow_plan import BaseOutput, ConstrainTimeRangeNode, FilterElementsNode, JoinDescription, JoinToBaseOutputNode
  from metricflow.filters.time_constraint import TimeRangeConstraint
  from metricflow.model.semantics.semantic_model_join_evaluator import MAX_JOIN_HOPS, SemanticModelJoinEvaluator
  from metricflow.protocols.semantics import SemanticModelAccessor
  from metricflow.specs.spec_set_transforms import ToElementNameSet
  from metricflow.specs.specs import InstanceSpecSet, LinkableInstanceSpec, LinklessEntitySpec
  from metricflow.sql.sql_plan import SqlJoinType
label_desc: Use image node_id 5 for calling the PreJoinNodeProcessor obj's underlying member method code with example usage: obj.add_multi_hop_joins(desired_linkable_specs, nodes, join_type) and returns: unknown