Агрегация MongoDB $ graphLookup - найти общие "соединения" в коллекциях следующих отношений - PullRequest
0 голосов
/ 01 декабря 2018

У меня есть коллекция тех, кто следит за кем (например, Instagram):

db.users.insertMany([
  { _id: 1, name: "Arnold Schwarzenegger" },
  { _id: 2, name: "James Earl Jones" },
  { _id: 3, name: "Harrison Ford" },
  { _id: 4, name: "Jennifer Lawrence" }
]);

db.follows.insertMany([
  { _id: 12, follower: 1, following: 2 },
  { _id: 13, follower: 1, following: 3 },
  { _id: 24, follower: 2, following: 4 },
  { _id: 23, follower: 2, following: 3 }
]);

Я пытаюсь предложить другим пользователям, что один пользователь может подписаться.то есть за какими людьми они могли следовать;рекомендуемые последователи, упорядоченные по количеству существующих общих связей.

В этом примере:

+--------+--------------+----------+
|   A    | is following |    B     |
+--------+--------------+----------+
| Arnold | ->           | James    |
| Arnold | ->           | Harrison |
| James  | ->           | Jennifer |
| James  | ->           | Harrison |
+--------+--------------+----------+

Кто может следовать за Арнольдом и Джеймсом? (исключая существующиесоединения)

The answer should be: Jennifer

Это неудачная попытка:

db.users.aggregate([
  {
    $match: { _id: 1 } // Arnold
  },
  {
    $graphLookup: {
      from: "follows",
      startWith: "$_id",
      connectFromField: "following",
      connectToField: "follower",
      maxDepth: 1,
      as: "connections",
    }
  }
]);

Что приводит к:

  {
    "_id": 1,
    "name": "Arnold Schwarzenegger",
    "connections": [
      {
        "_id": 24,
        "follower": 2,
        "following": 4
      },
      {
        "_id": 13,
        "follower": 1,
        "following": 3
      },
      {
        "_id": 23,
        "follower": 2,
        "following": 3
      },
      {
        "_id": 12,
        "follower": 1,
        "following": 2
      }
    ]
  }

Я считаю, что мне нужно сделать немного раскручивания,но я застрял сейчас

1 Ответ

0 голосов
/ 02 декабря 2018

Вот два возможных подхода.(Я не тестировал большие наборы данных, поэтому ваш пробег может отличаться!)

Первый основан на вашем $graphLookup этапе:

db.users.aggregate([
  { $match: { _id: 1 }},
  { $graphLookup: {
    from: 'follows',
    startWith: '$_id',
    connectFromField: 'following',
    connectToField: 'follower',
    maxDepth: 1,
    as: 'connections'
  }},
  { $unwind: { path: '$connections' }},
  { $group: {
    _id: '$connections.follower',
    follows: {
      $addToSet: '$connections.following'
    }
  }},
  { $unwind: { path: '$follows' }},
  { $group: {
    _id: '$follows',
    isFollowedBy: {
      $addToSet: '$_id'
    }
  }},
  { $match: { isFollowedBy: { $not: { $in: [1] }} }},
  { $group: {
    _id: null,
    newConnections: {
      $addToSet: '$_id'
    }
  }},
  { $project: { _id: 0 }}
])

Обратите внимание, что этот конвейер в конечном итоге строитотношения с другой коллекцией на полпути, так что другой подход - начать с другой коллекции следующим образом:

db.follows.aggregate([
  { $lookup: {
    from: 'follows',
    localField: 'following',
    foreignField: 'follower',
    as: 'potentialSet'
  }},
  { $unwind: {
    path: "$potentialSet",
    preserveNullAndEmptyArrays: true
  }},
  { $group: {
    _id: "$follower",
    "alreadyFollowing": {
      $addToSet: "$following"
    },
    "potentialConnections": {
      "$addToSet": "$potentialSet.following"
    }
  }},
  { $project: {
    newConnections: { $setDifference: [ "$potentialConnections", "$alreadyFollowing" ] }
  }},
  { $match: { _id: 1 }},
  { $project: { _id: 0 }}
])

Если это поможет, я использовал MongoDB Compass Community Edition , чтобы помочь построить этитрубопроводы.Это довольно круто, так как позволяет вам быстро выполнять итерации и видеть результат каждого этапа, который действительно помогает, когда вы пытаетесь отладить конвейер.

...