首页 > 解决方案 > 如果提供了空数组,则忽略 $in

问题描述

我有以下模型:

export type IMangaModel = Document & {
  _id: string;
  title: string;
  description: string;
  demographic: Array<string>;
  genre: Array<string>;
  tags: Array<string>;
  related: Array<IMangaModel>;
  cover_image: {
    extraLarge: string | null;
    large: string | null;
    medium: string | null;
  };
  banner: string | null;
  al_id: number;
  al_url: string;
  likes: Array<IUserModel['_id']>;
};

注意:接口与模型完全匹配,但我将其粘贴为它更短。

我正在使用以下数据过滤此集合:

includeFilters: {
    genres: Array<string>;
    tags: Array<string>;
    demographics: Array<string>;
  };
  excludeFilters: {
    genres: Array<string>;
    tags: Array<string>;
    demographics: Array<string>;
  };

我想要实现的是找到每个文档并检查相应的数组是否具有我通过 includeFilters 发送的数组中的至少一个值。虽然它没有通过 excludeFilters 发送的值。

为此,我使用了以下查询:

const results = await this._manga
      .find(
        {
          tags: {
            $elemMatch: {
              $in: data.includeFilters.tags,
              $nin: data.excludeFilters.tags,
            },
          },
          genre: {
            $elemMatch: {
              $in: data.includeFilters.genres,
              $nin: data.excludeFilters.genres,
            },
          },
          demographic: {
            $elemMatch: {
              $in: data.includeFilters.demographics,
              $nin: data.excludeFilters.demographics,
            },
          },
        },
        { al_id: 1 }
      );

只要includeFilters 数组中的所有数组都至少有一个值,这似乎就可以正常工作。但是,如果提供了一个空数组,那么在 $in 中找不到匹配项,据我所知,$in 需要至少存在一个值,但由于没有提供任何内容,因此什么也找不到。另一方面,$nin工作正常(至少我想这样认为),因为它没有试图排除任何值。

我想要实现的是,如果向 $in 提供了一个空数组,那么它会直接跳过该过滤器并且不查看它。在 mysql 中执行此操作时,如果提供了一个空数组,则将其忽略并返回每条记录作为结果。这就是我想要的 mongodb。

到目前为止,我尝试过的是几件事。

首先,我尝试将所有可能的值添加到空数组中,这不起作用,原因之一是,并非所有文档都有标签、流派和人口统计数据。所以最终将近 90% 的文档没有包含在结果中。

我尝试的第二件事是在连接选项中启用ignoreUndefined选项。然后在创建查询时检查长度是否为 0,如果是,则将 undefined 传递给$in。这没有用。

我尝试的第三件事是遍历所有可能的情况,其中某个数组为空,由于显而易见的原因,这很糟糕,所以我抓了它。

我尝试的第四件事是创建一个函数来构建各种动态查询,由于可能性的数量,这也变得非常混乱。

我尝试的第五件事也是最后一件事是使用$or并认为会以某种方式跳过空结果。

标签: javascriptarraystypescriptmongodbmongoose

解决方案


为了更好地使用 TypeScript,您可以使用 Nenad 的方法和 ES6 方法

const filters = Object.fromEntries(
  Object.entries(body).flatMap(([filterType, filters]) =>
    Object.entries(filters)
      .filter(([, values]) => values.length)
      .map(([key, value]) =>
        filterType === "includeFilters"
          ? [key, { $elemMatch: { $in: [...value] } }]
          : [key, { $elemMatch: { $nin: [...value] } }]
      )
  )
);

推荐阅读