[英]Ignore $in if empty array is provided
我有以下 model:
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']>;
};
注意:接口与 model 完全匹配,但我将其粘贴为它更短。
我正在使用以下数据过滤此集合:
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 数组中的所有 arrays 至少有一个值,这似乎就可以正常工作。 但是,如果提供了一个空数组,那么在 $in 中找不到匹配项,据我所知,$in 要求至少存在一个值,但由于没有提供任何内容,因此什么也找不到。 另一方面, $nin工作正常(至少我想这样认为),因为它没有试图排除任何值。
我想要实现的是,如果向 $in 提供了一个空数组,那么它会直接跳过该过滤器并且不查看它。 在 mysql 中执行此操作时,如果提供了一个空数组,则其将被忽略并返回每条记录作为结果。 这就是我想要的 mongodb。
到目前为止,我尝试过的是几件事。
首先,我尝试将所有可能的值添加到空数组中,这不起作用,原因之一是,并非所有文档都有标签、流派和人口统计数据。 所以最终将近 90% 的文档没有包含在结果中。
我尝试的第二件事是在连接选项中启用ignoreUndefined选项。 然后在创建查询时检查长度是否为 0,如果是,则将 undefined 传递给$in 。 这没有用。
我尝试的第三件事是遍历所有可能的情况,其中某个数组为空,由于显而易见的原因,这很糟糕,所以我抓了它。
我尝试的第四件事是制作一个 function 来构建各种动态查询,由于可能性的数量,这也变得非常混乱。
我尝试的第五件事也是最后一件事是使用$or并认为会以某种方式跳过空结果。
最简单的解决方案是动态创建整个过滤器。 它还将简化您的查询。 你可以这样做:
let elem_match = {
tags: {},
genres: {},
demographics: {}
}
for (let key in data.includeFilters){
if(data.includeFilters[key].length) elem_match[key]["$in"] = data.includeFilters[key];
}
for (let key in data.excludeFilters){
if(data.excludeFilters[key].length) elem_match[key]["$nin"] = data.excludeFilters[key];
}
let filter = {};
for (let key in elem_match){
if(Object.keys(elem_match[key]).length !== 0) filter[key] = { $elemMatch: elem_match[key]}
}
const results = await this._manga.find(filter, { al_id: 1 });
这是一个用于测试的片段:
let includeFilters = { genres: ['1', '2'], tags: ['1', '2'], demographics: ['1', '2'] }; let excludeFilters = { genres: ['1', '2'], tags: ['1', '2'], demographics: ['1', '2'] }; let elem_match = { tags: {}, genres: {}, demographics: {} } for (let key in includeFilters) { if(includeFilters[key].length) elem_match[key]["$in"] = includeFilters[key]; } for (let key in excludeFilters) { if(excludeFilters[key].length) elem_match[key]["$nin"] = excludeFilters[key]; } let filter = {}; for (let key in elem_match) { if(Object.keys(elem_match[key]).length:== 0) filter[key] = { $elemMatch. elem_match[key]} } console:log('Filter, ', filter)
为了使 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] } }]
)
)
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.