Is there anyway to write a switch statement in FQL for UDF?

I want to query the database differently based on the users role. the result would look the same on the client but the way the filtering is done will be pretty different for the different roles. Different enough to require a different FQL query not just changing the Role variable. I was wondering if I could write a switch in FQL inside my UDF? I could not find anything that jumped out at me in the docs. Maybe perhaps a nested If(If(If())) is the only way? Something like below? Thanks!

*** THIS IS NOT VALID FQL ***

Query(
  Lambda(
    ["input"],
    Let(
      {
        role: Select(["data", "role"], Get(Identity()))
      },
      Switch(
        Var("role"),
        ["ADMIN"] : Some(Fql(Var("input"))),
        ["EMPLOYEE", "CUSTOMER"] : SomeDifferent(Fql(Var("input"))),
        [] : null
      )
    )
  )
)

No we do not have a switch at this point, that’s probably a good function to add.
I have a hunch though that you could implement it by using a combination an object

{
  ADMIN: Some(Fql(Var("input")))
  EMPLOYEE: SomeDifferent(Fql(Var("input")))
  CUSTOMER: SomeDifferent(Fql(Var("input")))
}

And then Just using Select on that object with your ‘Var(“role”)’ as parameter to get the FQL function you need (and pass in the default to Select as a third argument)
You can probably implement your switch statement easily yourself.

Interesting! I will explore this. I do feel like it will run all of the FQL statements though instead of allowing me to select one. This would be more like running every query and then selecting the result of the FQL correct? Thanks for your help!

You might still want to put guards around each case, want to avoid query failure for bad data, or otherwise do not want to run every case.

Let can accept an array of bindings, a useful trick to reuse variables in subsequent expressions.

Here is a switch, put together with some helper methods.

const matchCase = (testValue, expr) =>
  q.If(
    q.IsNull(q.Var('_case')),
    q.If(q.Equals(testValue, q.Var('_switch')), expr, null),
    q.Var('_case')
  );

const defaultCase = (expr) =>
  q.If(q.IsNull(q.Var('_case')), expr, q.Var('_case'));

const body = q.Query(
  q.Lambda(
    ['input'],
    q.Let(
      {
        type: q.Select('type', q.Var('input')),
        value: q.Select('value', q.Var('input')),

        result: q.Let(
          [
            { _switch: q.Var('type') },
            { _case: null },
            { _case: matchCase('string', q.Multiply(q.ToNumber(q.Var('value')), 10)) },
            { _case: matchCase('number', q.Multiply(q.Var('value'), 10)) },
            { _case: defaultCase(0) },
          ],
          q.Var('_case')
        ),
      },

      q.Var('result')
    )
  )
);

Be careful with Let(<array>) as it compiles to Let(<obj>) in a UDF a thus you loose a part of your logic.

You are indeed correct that it will execute all of these which is of course bad for reads and would not work at all for mutations. Sorry about that, I didn’t think that through.

You can probably delay the execution but I’m not 100% certain how, whether Lambda would delay it or whether you need Query as well and how to execute it if your use Query. I’ve asked internally how those behave to be certain before I answer further.

I think it goes back to being able to store a Lambda and reuse it later. @n400 answered me recently stating that it might come in a later version but nothing possible yet.

In Switch cases I have, where there is heavy processing done within the cases, I ended up going back to writing If’s statements. It goes deep and it’s not pretty but it is the only way to do it when stored in a UDF (as far as I know).

P.S: Please prove me wrong :pray:

1 Like

The ToString method does in fact show it combined into an object, so it appears that only the last instance in the array is left. But they are all there. The UDF I shared works for all cases, so I know that it works.

But I was very surprised (and concerned at first) when I saw most of my UDF missing when reported back to me in the dashboard!

Of course you may have examples of one that didn’t work, so I will definitely listen, so I avoid making a bad mistake, or advising others to do so as well.

Dang, it made me changed quite a bit of code. I’ll use it again then!

A quick example to validate that it does work:

Query(
  Lambda('x', 
    Let([
        { case: null },
        { case: If(Equals(Var('x'), 0), 0, Var('case')) }, 
        { case: If(Equals(Var('x'), 1), 1, Var('case')) }, 
        { case: If(Equals(Var('x'), 2), 2, Var('case')) }
      ], 
      Var('case')
    )
  )
)

Still needed to provide an update on delayed execution.
At this point that is not possible, although the Query function seems to delay the execution according to our docs, there is no function (yet) that accepts a Query. In short, Lambdas are not first class citizen (yet), so you cannot run them later but we are working on changing that. Which would make a custom implementation of switch-like behaviour much more elegant of course :slight_smile:

2 Likes