edgedb-js icon indicating copy to clipboard operation
edgedb-js copied to clipboard

Query-builder cardinality inference error

Open vonkoff opened this issue 2 years ago • 4 comments

I'm getting an error over address and image when trying to filter them through an ID. Seems like a cardinality inference error which I don't think it should be the case since I am filter by uuid.

  const query_employer = e.insert(e.Employer, {
    name: EMPLOYER,
    address: e.select(e.Address, address => ({
      filter: e.op(address.id, '=', e.cast(e.uuid, result_address.id)),
    })),
    guild: e.select(e.Guild, guild => ({
      filter: e.op(guild.name, '=', GUILD),
    })),
    image: e.select(e.Employer_image, image => ({
      filter: e.op(image.id, '=', e.cast(e.uuid, result_image.id)),
    })),
  });

  const result_employer = await query_employer.run(client);
  console.log(result_employer);

The typescript error:

Type '$expr_Select<{ __element__: ObjectType<"default::Employer_image", { id: PropertyDesc<$uuid, Cardinality.One, true, false, true, true>; __type__: LinkDesc<$Type, Cardinality.One, ... 4 more ..., false>; ... 4 more ...; "<image": LinkDesc<...>; }, Omit<...>>; __cardinality__: Cardinality.Many; }>' is not assignable to type 'TypeSet<ObjectType<string, { id: PropertyDesc<$uuid, Cardinality.One, true, false, true, true>; __type__: LinkDesc<$Type, Cardinality.One, {}, false, false, true, false>; ... 4 more ...; "<image": LinkDesc<...>; }, any>, Cardinality.AtMostOne | ... 1 more ... | Cardinality.Empty>'.
  Types of property '__cardinality__' are incompatible.
    Type 'Cardinality.Many' is not assignable to type 'Cardinality.AtMostOne | Cardinality.One | Cardinality.Empty'.ts(2322)
typeutil.d.ts(5, 32): The expected type comes from property 'image' which is declared here on type '{ [x: `@${string}`]: TypeSet<BaseType, Cardinality> | scalarLiterals; image: TypeSet<ObjectType<string, { id: PropertyDesc<$uuid, Cardinality.One, true, false, true, true>; ... 5 more ...; "<image": LinkDesc<...>; }, any>, Cardinality.AtMostOne | ... 1 more ... | Cardinality.Empty>; guild: TypeSet<...>; address: Typ...'

Relevant schema:

  type Employer {
    required property name -> str;
    required link address -> Address;
    required link guild -> Guild;
    required link image -> Employer_image;

    constraint exclusive on ((.name, .address))
  }
  type Guild extending Named {}
  type Employer_image {
    required property name -> str;
    required property orig_link -> str;
    required property s3_link -> str;
  }
  type Address {
    property street -> str;
    required link city -> City;
    required link state -> State;
    required link country -> Country;

    constraint exclusive on ((.city, .state, .country))
  }
  type City extending Named {}
  type State extending Named {}
  type Country extending Named {}

For now I'm fixing it up by wrapping image and address with e.assert_single

vonkoff avatar Jun 06 '22 01:06 vonkoff

Seems this is a problem with inferring cardinality from e.cast. Either of these approaches will work:

e.cast(e.uuid, e.str("c900312b-2b8d-4d8d-b491-0761ecbb72e1")); // works
e.uuid("c900312b-2b8d-4d8d-b491-0761ecbb72e1"); // works
e.cast(e.uuid, "c900312b-2b8d-4d8d-b491-0761ecbb72e1"); // doesn't work

Use one of the working forms for now. I'll look into fixing this soon.

colinhacks avatar Jun 07 '22 00:06 colinhacks

Using the 2nd approach was my go to! Thanks.

vonkoff avatar Jun 09 '22 21:06 vonkoff

For folks googling this, assert_single might be a good alternative where you're not actually filtering on exclusive but limiting or otherwise sure it's Cardinality.AtMostOne | Cardinality.One | Cardinality.Empty.

jackfischer avatar Sep 02 '22 14:09 jackfischer

btw, inferring the cardinality based on the filter was removed in https://github.com/edgedb/edgedb-js/pull/460 ostensibly due to TypeScript recursion depth issues on the type that inferred the cardinality from the filter itself.

We'll revisit here and see if we can make it work well.

scotttrinh avatar Oct 26 '23 18:10 scotttrinh