DBIx-Class-Schema-GraphQL
view release on metacpan or search on metacpan
lib/DBIx/Class/Schema/GraphQL.pm view on Meta::CPAN
push @and_parts, { -or => [ map { _filter_to_dbic($_, $source) } @$or ] };
}
# Column conditions
for my $key (keys %$filter) {
next if $key eq 'AND' || $key eq 'OR';
next unless defined $filter->{$key};
if ($key =~ /^(.+)_(not|like|gt|gte|lt|lte)$/) {
my ($col, $op) = ($1, $2);
my $dbic_op = { not => '!=' , like => 'like',
gt => '>' , gte => '>=',
lt => '<' , lte => '<=' }->{$op};
push @and_parts, { $col => { $dbic_op => $filter->{$key} } };
}
else {
# exact match
push @and_parts, { $key => $filter->{$key} };
}
}
return @and_parts == 1 ? $and_parts[0]
: @and_parts > 1 ? { -and => \@and_parts }
: {};
}
# _apply_pagination($rs, $args, $source)
#
# Applies ordering, then either offset (page) or cursor (cursor) pagination.
# Returns ($paged_rs, $next_cursor, $has_next_page).
#
# Precedence: cursor pagination wins if both are supplied.
sub _apply_pagination {
my ($rs, $args, $source) = @_;
my @pk_cols = $source->primary_columns;
# Ordering
my $order_by = $args->{orderBy};
if ($order_by && $order_by->{field}) {
my $field = $order_by->{field};
if ( $source->has_column($field) ) {
my $dir = lc($order_by->{direction} // 'ASC');
$rs = $rs->search(undef, { order_by => { "-$dir" => $field } });
}
else {
die "Invalid field '$field' provided for orderBy.\n";
}
}
else {
# Default: stable order by PK
$rs = $rs->search(undef, { order_by => [ map { { -asc => $_ } } @pk_cols ] });
}
# Cursor pagination
if (my $cursor_args = $args->{cursor}) {
my $first = $cursor_args->{first} // 10;
my $after = $cursor_args->{after};
if ($after) {
my @decoded = _decode_cursor($after);
if (@pk_cols == 1) {
# Simple: WHERE pk > cursor_val (works for ordered-by-PK default)
$rs = $rs->search({ $pk_cols[0] => { '>' => $decoded[0] } });
}
else {
# Composite PK: fetch everything after decoded position
# by re-applying a compound condition
my %after_cond = map { $pk_cols[$_] => { '>' => $decoded[$_] } }
0 .. $#pk_cols;
$rs = $rs->search(\%after_cond);
}
}
# Fetch one extra to detect hasNextPage
my @rows = $rs->search(undef, { rows => $first + 1 })->all;
my $has_next = @rows > $first;
@rows = @rows[0 .. $first - 1] if $has_next;
my $next_cursor;
if ($has_next && @rows) {
my $last_row = $rows[-1];
$next_cursor = _encode_cursor(map { $last_row->get_column($_) } @pk_cols);
}
return (
[ map { { $_->get_columns } } @rows ],
$next_cursor,
$has_next ? 1 : 0,
);
}
# Offset pagination
if (my $page_args = $args->{page}) {
my $skip = $page_args->{skip} // 0;
my $take = $page_args->{take} // 10;
my @rows = $rs->search(undef, { rows => $take, offset => $skip })->all;
return (
[ map { { $_->get_columns } } @rows ],
undef, # no cursor for offset pagination
0,
);
}
# No pagination: return everything
my @rows = $rs->all;
return (
[ map { { $_->get_columns } } @rows ],
undef,
0,
);
}
sub _build_mutation_fields {
my ($source, $moniker, $gql_type) = @_;
my %fields;
my @pk_cols = $source->primary_columns;
my %is_pk = map { $_ => 1 } @pk_cols;
my @all_cols = $source->columns;
my @data_cols = grep { !$is_pk{$_} } @all_cols;
# createX
my %create_args;
for my $col (@all_cols) {
my $scalar = _scalar_for_column($source, $col);
$create_args{$col} = {
type => _col_is_required($source, $col)
? GraphQL::Type::NonNull->new(of => $scalar)
( run in 1.021 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )