javascript - Mongoose 查詢其中 X 在兩個陣列中,而 Y 只在其中一個陣列中
我正在建立一個過濾系統,但在某個問題上卡住了,請記住這只是示範程式碼,這不是同一個專案,但概念是相同的,我更換了名稱以便讓它盡可能容易理解。
我得到了一個已填充的篩選器對象,它會像這樣被發送 (它由一個包含 id 的陣列組成) - 記住這個:
const filters = {
companies: ["company_a", "company_b"], // this is an example, normally it a mongodb ObjectId("XXXXX") list
states: ["state_1", "state_2"], // this is an example, normally it a mongodb ObjectId("XXXXX") list
}
如果你查看 Company 和 State 這兩個集合,它們會看起來像這樣:
// MongoDB collection: `companies`
[
{
"id": "company_a",
"nationwide": false
},
{
"id": "company_b",
"nationwide": true
}
]
// MongoDB collection: `states`
[
{
"id": "state_1",
"name": "Arizona"
},
{
"id": "state_2",
"name": "Texas"
}
]
還有一個全球集合,將這兩者結合在一起,這就是我要使用的集合:
// MongoDB collection: `country_companies`
[
/* record 1 */
{
"_id": ObjectId("XXXXX"),
"company": {
"_id": "company_a",
"nationwide": false
},
"state": {
"_id": "state_1",
"name": "Arizona"
}
},
/* record 2 */
{
"_id": ObjectId("XXXXX"),
"company": {
"_id": "company_b",
"nationwide": true
},
"state": {
"_id": "state_2",
"name": "Texas"
}
}
]
現在,一家公司可以同時面向全國以及各州(如上方的集合所示)。所以我有一個像這樣的資料庫:
export class CompanyRepository {
private companies: Company[];
public async initialize(): Promise<void> {
if (this.companies.length > 0) throw new Error("Companies have already been initialized!");
this.companies = await CompanyModel.find().exec();
}
public isCompanyNationwide(id: string): boolean {
return this.companies.some(company => company.id === id && company.nationwide === true);
}
}
問題出現在我一旦像這樣執行查詢時,並且將篩選條件放在最上方:
export class CompanyService {
public static async getCompaniesByFilters(filters: CompanyFilters): Promise<Company[]> {
const query: Record<string, unknown> = {};
if (filters.companies.length > 0) query['company._id'] = { $in: filters.companies };
if (filters.states.length > 0) query['state._id'] = { $in: filters.states };
/* this results in a mongodb query:
{
"company._id": { $in: ["company_a", "company_b"] },
"state._id": { $in: ["state_1", "state_2"] }
}
*/
return await CountryCompanyModel.find(query).exec();
}
}
上面的程式碼基本上所做的,是根據你是否選擇了項目來新增它們,最後你會得到一個查詢物件。問題在於,它必須同時存在於兩個陣列中。因此,因為 "company_a" 是全國性的,它不應該在 states 陣列中被搜尋。
為了清楚地理解這一點,這裡有一些系統應該如何運作的例子:
User A selects `["company_a"]`, without any states ->
Receives a list of all company_a records
User B selects `["company_a"]`, with the state `["state_1"]` ->
Receives list of all company_a in state_1 records
User C selects `["company_a", "company_b"]` with the states `["state_1"]` ->
Receives a list of all company_a in state_1, together with all company_b (since company B is nation-wide)
User D selects `["company_b"]` with the states `["state_1", "state_2"]` ->
Receives a list of all company_b, because company_b is nation wide so states filter should be ignored entirely.
我能想到的一個解決方案是這個:
import CompanyRepository from "./company.repository";
const stateWideCompanies = filters.companies.filter(companyId =>
CompanyRepository.isCompanyNationWide(companyId) === false
);
const nationWideCompanies = filters.companies.filter(companyId =>
CompanyRepository.isCompanyNationWide(companyId) === true
);
const countryCompaniesStates = await CountryCompanyModel.find({"company._id": { $in: stateWideCompanies }, "state._id": { $in: filters.states }).exec();
const countryCompaniesNation = await CountryCompanyModel.find({"company._id": { $in: nationWideCompanies }).exec();
const companyList = [...countryCompaniesStates, ...countryCompaniesNation]
這給了我我想要的東西,不過我認為這應該可以由資料庫完成。因為現在我必須做兩個查詢並將它們結合起來,這看起來一點也不乾淨。
我希望我可以在對資料庫的一次查詢中完成這個操作。所以要麼是查詢生成器需要修復,要麼就是查詢本身,我似乎無法讓它正常運作。
答案#1
你所需要做的就是使用布林邏輯建立一個更智慧的查詢,在這種情況下,你要做的只是允許無論選擇了哪些州,都能抓取全國性的公司。
這就是我會做的方法:
const query: Record<string, unknown> = {};
if (filters.companies.length > 0) {
query['company._id'] = { $in: filters.companies };
}
if (filters.states.length > 0) {
query['$or'] = [
{'state._id': { $in: filters.states }},
{ 'company.nationwide': true}
];
}
現在如果選擇了一個州,查詢要麼是 state._id 在所選查詢中,要麼該公司是全國性的。
