如何在 JavaScript 中实现 "index join" 数据结构?

How to implement an "index join" data structure in JavaScript?

我正在学习 join algorithms 有关关系数据查询处理的知识。简单的情况是嵌套循环连接:

function nestedJoin(R, S, compare) {
  const out = []
  
  for (const r of R) {
    for (const s of S) {
      if (compare(r, s)) {
        out.push([ r, s ])
      }
    }
  }

  return out
}

其中 compare 将比较连接属性。

我想知道的情况是索引连接。从作弊 sheet 复制到 JS 之类的,我们有:

function indexJoin(R, S) {
  const out = []
  
  for (const r of R) {
    const X = findInIndex(S.C, r.c)
    for (const s of X) {
      out.push([ r, s ])
    }
  }

  return out
}

但是那是什么 findInIndex(S.C, r.c)?传递给它的是什么 (S.C)?它是如何工作的?

join indices paper 是这样说的:

With no indices, two basic algorithms based on sorting [4] and hashing [5, lo] avoid the prohibitive cost of the nested loop method.

A join index is a binary relation. It only contains pairs of surrogates which makes ( it small. However, for generality, we assume that it does not always fit in RAM. Therefore, a join index must be clustered. Since we may need fast access to JI tuples via either r values or s values depending on whether there are selects on relations R or S, a JI should be clustered on (r, s). A simple and uniform solution is to maintain two copies of the JI, one clustered on r and the other clustered on s. Each copy is implemented by a W-tree an efficient variation of the versatile B-tree [l, 71.

那么,如果它是一个 B+树,那么在每个 B+树中将使用什么键和值,您将如何使用键和值(您以什么顺序插入键和获取值)?另外,如果“连接索引”在 JavaScript 中,就不能像这样实现吗?

const joinIndex = {}

function join(r, s) {
  const rest = joinIndex[r.c] = joinIndex[r.c] ?? {}
  rest[s.c] = true
}

function findInIndex(leftKey) {
  return Object.keys(joinIndex[leftKey])
}

请说明如何使用我的方法或 B+ 树方法实现连接算法。如果是B+tree方法,你不需要实现B+tree,只需要解释一下你将如何插入2个B+tree的东西in/out来更清楚地解释算法。

首先,论文中所说的join index,最好可以想象成table实现了两个之间的many-to-many关系table秒。此连接索引 table 中的一条记录由两个外键组成:一个引用 R table 中的主键,另一个引用 S table.[=16= 中的主键]

我没有得到作弊 sheet 中使用的 S.C 符号。但很明显,您需要以某种方式指定要使用的 which 连接索引,更具体地说,您要在其上使用哪个 B+Tree(集群)(以防其中两个已定义),最后,您要在其中找到哪个值(r.c,r 的键)。

B+树的作用是提供一个有序的散列table,即你可以高效地搜索一个键,并且可以很容易地从那个点按顺序走到后面的条目。在 join index 的这种特殊用途中,这允许您有效地找到给定 r[=35 的所有对 (r1, s) =]1。通过从 B+ 树的根向下钻取到具有 r1 的第一个叶子,可以找到第一个。然后向前穿过B+树的底层会找到所有其他带有r1的元组,直到遇到一个不再有这个r1的元组 值。

请注意,您仍然需要原始 table 的索引,以便找到给定键的完整记录。实际上,这也可以用 B+Tree 来完成,但在 JavaScript 中,一个简单的字典(普通对象)就足够了。

所以在 JavaScript 语法中我们可以想象这样的事情:

// Arguments:
//  - joinIndexTree: a B+Tree having (rKey, sKey) tuples, keyed and ordered by rKey.
//  - rKey: the key to search all matches for
function findInIndex(joinIndexTree, rKey) {
  let result = []; // This will collect all the sKey for
                   // which thee is a (rKey, sKey)
  // Find left-most location in B+Tree where rKey should occur (if present)
  let btreeCursor = joinIndexTree.find(rKey);
  if (btreeCursor.EOF()) return result; // At the far-right end of the B+Tree
  let tuple = btreeCursor.get();  // Read the tuple found at this location
  while (tuple[0] == rKey) { // First member of tuple matches rKey
     result.push(tuple[1]); // Collect the corresponding s-value
     btreeCursor.next(); // Walk to the next tuple
     if (btreeCursor.EOF()) break; // At the end of the B+Tree
     tuple = btreeCursor.get();  // Read the tuple found at this location
  }
  return result;
}

主程序是:

const joinIndexTree = ;// B+Tree with (rKey, sKey) pairs, ordered by rKey
const sIndex = Object.fromEntries(S.map(s => [s.id, s])); // dictionary

function indexJoin(joinIndexTree, R, S, sIndex) {
  const out = []
  
  for (const r of R) {
    const sids = findInIndex(joinIndexTree, r.id)
    for (const s_id of sids) {
      const s = sIndex[s_id]; // Look up by primary key index
      out.push([ r, s ])
    }
  }

  return out
}

当你只需要对table(查询)进行read-only操作时,你可以创建一个数组字典,而不是B+树,你可以在其中查找joinIndex[r.id] 并获得 s.id 值的数组。这当然很容易设置和使用,但是当 table 不是 read-only.

时保持更新很痛苦

作为 B+Tree 的替代,你也可以使用其他平衡搜索树,例如 AVL 和 red-black 树,但根据我的经验,B+Tree 具有更好的性能。