Saltar al contenido principal

Funciones

Debe declararlas en el qmapCreator, luego de esto puede utilizar el nombre de la función dentro de la query, debe ser un identificador válido, no es permitido utilizar "" para el nombre de las funciones.

En general

  • El nombre que toma la key en el resultado, es la concatenación de los argumentos.

  • Las funciones se aplican sobre los valores en el JSON de entrada (no sobre los valores incluidos en el resultado).

    const query = `{
    toString(id)
    }`
  • Se pueden componer

    const query = `{
    upperCase(fullname(first_name, last_name)),
    }`
  • Pueden utilizar variables (no es permitido utilizar "" para el nombre de variables) estas se mandan através de qmap o apply

    const query = `{
    take(members, @quantity)
    }`

    qmap(query, {
    variables: {
    quantity: 5
    }
    })
info

Las variables se declaran con un @ al inicio, seguido de un identificador. No puede usar comillas ""

  • Puede utilizar números, strings y booleans utilizando la notación @{*} (únicamente es válido para las funciones).

    const query = `{
    take(members, @{3}),
    parents: filterBy(members, @{"isParent"}, @{true})
    }`

    qmap(query)
  • Pueden usarse dentro de la selección, sin embargo esto limita la información que puede utilizar a la que existe dentro de la clave en el JSON de entrada. También es permitido utilizar selección y acceso como argumentos.

    const query = `{
    product {
    upperCase(provider.name)
    },
    transaction {
    calcTotal(products {
    price
    })
    }
    }`
  • Queda en responsabilidad del desarrollador controlar errores en las funciones, ya que la función apply podría fallar, también se debe tener en cuenta que los argumentos pueden ser null.

  • No se transforma la salida de la función, es decir que puede devolver undefined si lo desea.

  • Las funciones deben ser síncronas, pero podría ser útil posteriormente agregar asíncronas.

Retorno de la función

Se puede modificar el retorno de una función. Puede ser de útilidad cuándo tiene un enfoque de optimización o estético.

caution

Las modificaciones al retorno de la función no afecta el índice.

Optimización

Al ejecutar un select sobre un array este se aplica por cada elemento, por ejemplo si la función tiene el propósito de reducir la cantidad de elementos

function execute(query) {
const { apply } = qmap(query)

apply(input)
}

execute(`{
take(products { name }, @{10})
}`) // 214.7 ms en promedio (300,000 items)

// vs

execute(`{
take(products, @{10}) { name }
}`) // 0.1 ms en promedio (300,000 items)

Esto sucede porque no es lo mismo seleccionar el "name" de 300,000 items y tomar 10 a tomar 10 de 300,000 y seleccionar sólo el "name".

Estético

const query = `{
take(products { name, provider { name } }, @{10})
}`

// vs

const query2 = `{
take(products, @{10}) {
name,
provider {
name
}
}
}`

Forzar modificación del índice

El problema radica en que las operaciones sobre el retorno de la función no afectan el índice (el que le permite saber si se debe incluir la información), por lo que resulta incluyendo todo.

Para poder evitar incluir todo en el índice se puede utilizar index select, lo que permite conservar aún la optimización.

const query = `{
products: take(products #{ name }, @{2}) {
name
}
}`

Se puede volver más legible utilizando on result

const query = `{
producs #{ name },
products: take(%{products}, @{2}) {
name
}
}`

En el ejemplo anterior no fue necesario remover el "extra" ya que al renombrar el resultado de take se está sobreescribiendo, si se agrega información temporal que no será sobreescrita, podría ser una opción utilizar exclude para removerlo.

const query = `{
temp: producs #{ name },
take(%{products}, @{2}) {
name
},
!temp // eliminado después del uso
}`
note

Esto únicamente tiene sentido si se va utilizar el índice de esta selección para realizar una operación condicional, por ejemplo provider podría significar un join con la tabla de Proveedores si es que se usa una RDB

Funciones map

Por defecto las funciones toman el objeto, es decir que se ejecutaría una sola vez y como argumento tendría el array, para ejecutar la función por cada elemento debe llamar su función de esta manera:

const query = `{
upperCase(@[products.name])
}`

const input = {
products: [
{ name: "pencil" },
{ name: "paper" },
],
}

const result = {
products_name: [ 'PENCIL', 'PAPER' ]
}

Al encerrar el argumento que es el array en @[] se indica que se debe aplicar a cada elemento (sólo puede haber uno de estos).

info

Los demás argumentos deben ser valores globales, es decir, que estén en el scope de la clave en el JSON de entrada o bien ser variables. Aunque los demás valores sean arreglos serán tratados como argumentos únicos

  1. Puede utilizar la composición (se pueden componer funciones por elemento y normales):
const query = `{
take(currency(@[ add(@[ids], offset) ]), @quantity)
}`

const input = {
offset: 100,
ids: [1, 2, 3, 4, 5],
}

const result = {
ids_offset_quantity: [ '$101.00', '$102.00', '$103.00' ]
}

Alternativamente se puede encerrar la función en [] para indicar que es una función map.

const query = `{
[ upperCase(products.name) ]
}`

const input = {
products: [
{ name: "pencil" },
{ name: "paper" },
],
}

const result = {
products_name: [ 'PENCIL', 'PAPER' ]
}
info

Con la notación [] se asume que el primer argumento es el array.