Distinct with custom field

Consider the following data:

[
  [
    "2021-06-01T14:17:45.032122Z",
    "51342145",
    Ref(Collection("chatMessages"), "1241454")
  ],
  [
    "2021-06-01T14:17:40.338061Z",
    "51342145",
    Ref(Collection("chatMessages"), "245634545")
  ]
]

And the following indexes:

CreateIndex({
  name: 'chatMessagesBySenderId',
  source: Collection('chatMessages'),
  terms: [
    { field: ['data', 'senderId'] }
  ],
  values: [
    { field: ['data', 'createdAt'], reverse: true },
    { field: ['data', 'recipientId'] },
    { field: ['ref'] }
  ]
})
CreateIndex({
  name: 'chatMessagesByRecipientId',
  source: Collection('chatMessages'),
  terms: [
    { field: ['data', 'recipientId'] }
  ],
  values: [
    { field: ['data', 'createdAt'], reverse: true },
    { field: ['data', 'senderId'] },
    { field: ['ref'] }
  ]
})

I wanted to do:

Paginate(
  Distinct(
    Union(
      Match(Index("chatMessagesByRecipientId"), authUser.id),
      Match(Index("chatMessagesBySenderId"), authUser.id)
    )
  )
);

and I wanted it to return:

{
  data: [
    [
      "2021-06-01T14:17:45.032122Z",
      "51342145",
      Ref(Collection("chatMessages"), "1241454")
    ]
  ]
}

In this case, I wanted to use the data[1] which is the 51342145 for uniqueness.

I’m unsure if this can be achieved in another way, but the best solution I can think of is to accept a second argument for Distinct which is a callback that will return data that will be used for uniqueness:

Paginate(
  Distinct(
    Union(
      Match(Index("chatMessagesByRecipientId"), authUser.id),
      Match(Index("chatMessagesBySenderId"), authUser.id)
    ),
    Lambda(
      ['createdAt', 'id', 'ref'],
      Var('id')
    )
  )
);

In the case above, Distinct will call the callback which will return the Var('id') which it will use to check for uniqueness.

You could probably replace Distinct with Reduce and construct an object that recorded the first distinct id discovered.

I have tried it that way but I could not make it work.

This appears to do what you want:

Reduce(
  Lambda(
    ["acc", "value"],
    Merge(
      Var("acc"),
      ToObject(
        [[
          ToString(Select(1, Var("value"))),
          Var("value")
        ]]
      )
    )
  ),
  {},
  Paginate(
    Union(
      Match(Index("chatMessagesByRecipientId"), 123),
      Match(Index("chatMessagesBySenderId"), 123)
    )
  )
)

I used a static id based on the documents I created to test the query: replace 123 with authUser.id.

The important bit in the reducer is calling Merge to compose a new accumulator object for each iteration that includes each individual page item. Composing dynamic objects can’t be done with { Var("field"): Var("value") }, which is the approach I tried first, but ToObject makes it work.