Skip to content

Dynamic nested where #518

@kylefarris

Description

@kylefarris

I've made a function that allows one to just pass an object in order to filter results.

It looks like this:

/**
 * Generates a dynamic filter function for ElectroDB entities based on provided filter criteria.
 * The returned function can be used to create filter expressions for querying the database.
 *
 * @param filters - An object where keys are attribute names and values are the values to filter by.
 * @template T - The type of the entity being filtered.
 * @returns A function that takes an attribute accessor and operations object, returning a filter expression string.
 * @example
 * MyEntity.query({ id: 'e51685dc-f8e6-4c05-b7cf-1d6c22d16bfd' })
 *  .where(dynamicFilter<MyEntity>({ someAttr: 'a value', isActive: true }))
 *  .go();
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function dynamicFilter<T>(filters: Record<string, unknown>): (attr: any, ops: any) => string {
    return (attr, { eq }) => {
        return Object.entries(filters)
            .map(([key, value]) => {
                if (/\./.test(key)) {
                    // Use Function constructor to safely access nested property on attr object
                    const accessor = (0, eval)(`(attr) => attr.${key}`)(attr);
                    return eq(accessor, value);
                }
                return eq(attr[key as keyof T], value);
            })
            .join(' AND ');
    };
}

As you can see from the example in the jsdoc, you can run use it something like this:

MyEntity.query({ id: 'e51685dc-f8e6-4c05-b7cf-1d6c22d16bfd' })
    .where(dynamicFilter<MyEntity>({ someAttr: 'a value', isActive: true }))
    .go();

It actually works exactly as advertised, but, of course, it's really only useful for eq operations at the moment which is fine for my uses anyway.

I have 2 questions:

  1. Is this even necessary? Is there a built-in way to do something like where({ foo: 'bar', 'deep.nested.attr': true })?
  2. I'm still having trouble filtering on nested attributes though I think what I'm doing should "just work" since I'm effectively doing an eval on 'deep.nested.attr' to convert it to attr.deep.nested.attr. I get an error saying "cannot read properties of undefined (reading 'type')" and I can't quite figure out why. I have checked just directly trying eq(attr.deep.nested.attr, value) and that also yields the same error so it doesn't seem to be an issue with the indirect eval.

If the answer to my first question is "no", is there a way to formerly request such a feature? One that does, in fact, support deeply-nested filtering. You're welcome to use any bit of the code above to achieve it, of course.

Amazing library, by the way!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions