Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add expression based cursor #60

Closed

Conversation

nikolay-slavov
Copy link

@nikolay-slavov nikolay-slavov commented Mar 1, 2019

Hi, this merge request adds an abilty to build expression cursor.
When we need to paginate records and order them by value generated on the fly,
we will need that value in the both order by and where clauses.

Use case:

If we need to paginate records with cursor_fields: [:rank_value, :id], where
rank_value is calculated on the fly we can do the following:

  query =
    from(
      f in Post,
      select_merge: %{
        rank_value:
          fragment("ts_rank(document, plainto_tsquery('simple', ?)) AS rank_value", ^q)
      },
      where: fragment("document @@ plainto_tsquery('simple', ?)", ^q),
      order_by: [
        desc: fragment("rank_value"),
        desc: f.id
      ]
    )

    query
    |> Repo.paginate(
      limit: 30,
      fetch_cursor_value_fun: fn
        # Here we build the rank_value for each returned row
        schema, {:rank_value, _} ->
          {:ok, %{rows: [[rank_value]]}} =
            Repo.query("SELECT ts_rank($1, plainto_tsquery('simple', $2))", [
              schema.document,
              q
            ])

          rank_value

        schema, field ->
          Paginator.default_fetch_cursor_value(schema, field)
      end,
      cursor_fields: [
        {:rank_value, # Here we build the rank_value that will be used in the where clause
         fn ->
           dynamic(
             [x],
             fragment("ts_rank(document, plainto_tsquery('simple', ?))", ^q)
           )
         end},
        :id
      ]
    )

From example above we can see that the cursor_fields now accepts and tuples.
The first element of the tuple is the name of the cursor field and the second one is a
function responsible for how the value should be generated.

Base automatically changed from master to main January 26, 2021 17:52
@quexpl
Copy link

quexpl commented Nov 26, 2021

Thanks @nikolay-slavov great job! I'm sorting by date field which is inside json map, at this point I'm not using embedded_schema so your PR was a bless for me. Unfortunately I had few issues after pulling it to most recent version from main branch.
After changes in #68 cursor is now a map rather than list which impacts your changes.
Now cursor is a map for example: %{rank_value: customer_3.rank_value, id: customer_3.id}. When an expression-based cursor is used, the entire tuple of the cursor is used as the field name, which caused decoding errors:

** (ArgumentError) cannot deserialize #Function<20.33583418/0 in Webapp.Machines.page_machines/3>, the term is not safe for deserialization

@telphan
Copy link
Contributor

telphan commented Jul 28, 2023

Closing in favour of #166

@telphan telphan closed this Jul 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants