Как изменить опубликованные наборы полей в Meteor/mongo

Кто-нибудь знает, как изменить наборы полей в публикации (Meteor.publish или Meteor.publishComposite) на основе определенных характеристик/значений полей записи по отношению к вошедшему в систему пользователю?

Я использовал publishComposite с курсорами на одном уровне, например:

Meteor.publishComposite("games", [
  {
    find: userRecord,
    children: [
      {
        find: gamesWeArePlaying
      }
    ]
  },
  {
    find: userRecord,
    children: [
      {
        find: gamesWeOwn
      }
    ]
  },
  {
    find: userRecord,
    children: [
      {
        find: examineWithAnalysis
      }
    ]
  },
  {
    find: userRecord,
    children: [
      {
        find: examineWithoutAnalysis
      }
    ]
  },
  {
    find: userRecord,
    children: [
      {
        find: allGames
      }
    ]
  }
]);

Каждая функция поиска возвращает курсор с соответствующими записями и набором полей, разрешенным для этого пользователя в этом состоянии. Просто имейте в виду, что в каждом из дочерних элементов он найдет набор уникальных записей, но поля, которые он возвращает, относятся к этому курсору. Наиболее распространенными являются переходы от gamesWeArePlaying к экзамену с анализом, а в случае учителя/ученика экзамен с анализом — к экзамену без анализа.

Но происходит следующее: когда игра (то есть отдельная запись) переходит от одного курсора к другому, базовая структура не может отправить клиенту правильные измененные поля. Записи minimongo выходят из строя и даже не соответствуют тому, что на самом деле находится в базе данных серверов.

Итак, мой вопрос: каков наилучший способ реактивно вернуть набор записей, где каждая отдельная запись имеет определенный набор полей, возвращаемый на основе характеристик указанной записи?


person David Logan    schedule 22.10.2020    source источник
comment
Не могли бы вы предоставить фактический код, а также пример данных и ожидаемых результатов? Мы можем теоретизировать целыми днями, но всегда полезно иметь надежные примеры для работы.   -  person Harry Adel    schedule 25.10.2020
comment
И то и другое сложно. Вы можете найти актуальный код по адресу github.com/djlogan2/iccserver. /blob/ если вы действительно хотите знать. Ожидаемые результаты еще более сложны. В основном сервер НЕ отправляет обновления minimongo, которые соответствуют добавленным/удаленным полям. Вот почему я задал вопрос, как я сделал. Легче задать теоретический вопрос: если у меня есть запись {f1:1, f2:a, f3:b и т. д.}, как я могу опубликовать ту же запись с разными полями на основе значения f1? Это не совсем то, что я делаю, но это близко.   -  person David Logan    schedule 26.10.2020
comment
Извините за запоздалый ответ, я полагаю, что лучший способ справиться с вашим делом — разделить публикацию на более мелкие, чтобы избежать повторных запусков, пытающихся изменить одно и то же пространство имен публикации. ``` Meteor.publishComposite(gamesWeArePlaying, [ { find: userRecord, children: [ { find: gamesWeArePlaying } ] }) Meteor.publishComposite(examineWithAnalysis, [ { find: userRecord, children: [ { find: examWithAnalysis } ] }) / / и т. д. ```   -  person Harry Adel    schedule 31.10.2020
comment
Да, на данный момент это, кажется, единственный способ, которым я тоже вижу. Пока мы разговариваем, я работаю над созданием своей низкоуровневой публикации, используя this.added, this.changed и this.removed, и изменяю поля вручную. publishComposite даже отправляет клиенту поля, которые не загружает НИ ОДИН из моих курсоров! Вздох.   -  person David Logan    schedule 02.11.2020


Ответы (2)


Я прочитал ваш код github, и кажется, что ваше истинное намерение состоит в том, чтобы опубликовать все игры, к которым у этого пользователя должен быть доступ, и вы хотите, чтобы у пользователя были разные разрешения для каждой из игр. Это скорее mongodb решение, вы хотите иметь обычную публикацию (не составную, они медленные и плохо работают), где вы сначала получаете идентификатор пользователя. Затем просто запросите все игры (поскольку вы уже являетесь составным), как это

{
  $or : [
    { isolation_group : user.isolation_group }, // part of the user's isolation group
    { owner           : user._id }              // games we own
  ]
}

Это должно получить все игры, к которым пользователь должен быть причастен, в зависимости от их группы изоляции или их владения. Я бы предложил добавить isolation_group даже к играм, которыми вы владеете, чтобы упростить запрос, но это необязательно. После того, как вы извлечете все эти игры, они будут у вас в массиве, и здесь нам на помощь приходит такая библиотека, как lodash — вы можете сделать серию таких _.map() вызовов

let gamesWeArePlaying = _.compact(_.map(ArrayOfAllGames, (game, key) => {
  if (game.status !== "playing" || 
        (game.white.id !== user._id && game.black.id !== user._id)) {
    return '';
  }

  return _.pick(game, [
    "black", "clocks", "fen", "lag", "observers", "pending",
    "rated", "rating_type", "startTime", "status", "tomove",
    "variations", "white", "wild"
  ]);
}));

Вот где это становится немного сложнее, если вы публикуете их, как вы упомянули с помощью this.added/changed/removed, в тот же client side collection, вы обязаны получить ту же игру, но вы хотите показать ее с определенными полями на основе результатов нескольких _.map() вызовов. . Если вы добавите одну и ту же игру несколько раз, Meteor будет публиковать одну и ту же игру несколько раз с разными разрешениями, а это не то, что вам нужно. Поэтому измените этот вызов карты, чтобы он фактически проверял все разрешения сразу (например, gamesWeArePlaying, gamesWeOwn, examWithAnalysis,...) и измените _.pick() список полей на основе этого. Это даст вам единый набор игр с соответствующими полями для публикации, которые будут обновляться в ответ.

person Danail Gabenski    schedule 02.11.2020

Хорошо, ребята, я понял это, и это была глупая ошибка программиста с моей стороны. Во-первых, позвольте мне заявить, что publishComposite и изменение полей (предполагаемые проблемы с производительностью в сторону) действительно работают! Что-то вроде этого работает:

publishComposite("publication", {
   find() {return collection1.find()},
   children: [
      {find(c1_record) {return collection2.find({s1},{fields: {f1: 1}})}}
      {find(c1_record) {return collection2.find({s2},{fields: {f1: 1, f2: 1}})}}
      {find(c1_record) {return collection2.find({s3},{fields: {f2: 1}})}}
   ]
});

Никаких проблем с этим ни с Mongo, ни с Meteor, ни с DDP, ни с клиентом, ни с minimongo. Это прекрасно работает.

Моя проблема в том, что вы должны быть ОЧЕНЬ ОЧЕНЬ осторожны, когда используете publishComposite, когда находки более высокого уровня не предназначены для публикации, а только для подквалифицирующих записей. Возьмем мой пример. Выше я публиковал записи игр и публиковал определенные поля на основе определенных состояний (если вы играли, могли ли вы видеть компьютерный анализ и т. д.)

Но в другом месте я должен публиковать записи чата. Чтобы правильно публиковать записи чата, мне нужно было выяснить, в какие игры играет или наблюдает пользователь. Играли ли они или могли видеть компьютерный анализ, не имело значения. Мне просто нужно было знать, в какие игры они играют. Итак, я сделал это:

publishComposite("chat", {
find() {
  return Meteor.users.find({_id:...})
},
children[
  {
    find() {return gamescollection.find({..all games user is involved with...})}, 
    children: [{
      find(){
        chat.find({...chats for game...})}
    }]
  }
]
});

Видите проблему?

Поиск верхнего уровня публикует запись пользователя, все поля во всей красе (возможная проблема №1!)

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

Я изменил эту последнюю публикацию, чтобы возвращать только идентификатор игровых записей, с которыми связан пользователь (то есть gamescollection.find({...},{fields: {_id: 1}}), и просто так все заработало.

person David Logan    schedule 03.11.2020
comment
Рад, что у вас все заработало, я все же предупрежу вас, что это может привести к проблемам с производительностью в будущем, как я испытал всего лишь с сотнями тысяч записей. - person Danail Gabenski; 06.11.2020