Find a Mongoose query that retrieves items where X exists in both arrays, while Y only exists in one array

I am currently constructing a filtering mechanism and have run into a roadblock. It's worth noting that this code is for demonstration purposes only and does not belong to the actual project, but the core idea remains consistent. I have altered the names to ensure clarity.

I have a populated filter object sent in this format (comprising an array of IDs) - keep this structure in mind:

const filters = {
  companies: ["company_a", "company_b"], // example, typically it would be a list of mongodb ObjectId("XXXXX")
  states: ["state_1", "state_2"], // example, normally it would be a list of mongodb ObjectId("XXXXX")
}

If we inspect both the Company and State collections, they would resemble something like this:

// 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"
  }
]

Additionally, there is a combined global collection which includes elements from both of these collections, and this is the collection that will be utilized:

// 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"
    }
  }
]

A company can either operate on a nationwide or state-specific basis (as observed in the aforementioned collection). Hence, I have designed a repository like so:

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);
  }
}

The issue arises when I execute the query with the specified filters at the beginning:

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 };
    /* generates a mongodb query:
      {
        "company._id": { $in: ["company_a", "company_b"] },
        "state._id": { $in: ["state_1", "state_2"] }  
      }
    */
    return await CountryCompanyModel.find(query).exec();
  }
}

The above code essentially adds items based on user selections, culminating in a query object. The challenge lies in requiring inclusion in BOTH arrays. For instance, since "company_a" is nationwide, it should not be included in the states array search.


To clarify how the system should function, here are some examples:

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"]` ->
  Gets a list of all company_a records in state_1

User C selects `["company_a", "company_b"]` with the states `["state_1"]` ->
  Obtains a list of all company_a records in state_1, alongside all company_b records (since company B is nation-wide)

User D chooses `["company_b"]` with the states `["state_1", "state_2"]` ->
  Gets a list of all company_b records, as company_b is nationwide and the state filter should be disregarded.

A possible solution could be this approach:

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]

This implementation achieves the desired outcome, yet I believe it should be feasible to accomplish within a single database query. Performing two queries and combining them does not seem efficient.

I aspire to conduct this task in ONE database query. Consequently, either the query builder needs tweaking or the query itself must be adjusted, as I'm unable to achieve the desired functionality.

Answer №1

To achieve the desired results, it is crucial to enhance your query using boolean logic. Specifically, when dealing with a scenario where you need to retrieve nationwide companies regardless of the selected states.

Below outlines my approach:

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}
        ];
}

With this adjustment, the query will prioritize either the state._id being in the selected range or the company having a nationwide presence.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Join a subscription and remain subscribed in sequential order

Within the code below, there is a nested subscribe function. It takes a schedule_id and retrieves questions based on that schedule_id. The functionality works correctly, but the order in which getQuestion() is executed is not guaranteed. Schedule IDs: 111, ...

What is the best way to organize my node.js, express, and mongodb application?

I've been thinking about how different developers organize their Node.js apps. My usual approach involves creating models, views, and controllers. But since I'm new to the Node.js world, I'm eager to understand more about the community&apos ...

Ways to prevent false activation of functions due to changes and clicks

I have a text box and a clear button. When the user inputs something in the text box and then clicks out of it, a function called 'validate()' is triggered to perform an action. However, when I click the clear button instead and trigger another f ...

Avoid allowing users to accidentally double click on JavaScript buttons

I am working with two buttons in a Javascript carousel and need to prevent users from double-clicking on the buttons. Below is the code I am using: var onRightArrow = function(e) { if (unitCtr<=unitTotal) { unitCtr++; TweenLite.to(p ...

What could be causing my overloaded controller methods to not be receiving requests correctly?

Within my view, I have a page called PrintPatientConsent.aspx that I need to call for two different types. However, only the default action method is being called by default, even when passing parameters. Here is the code snippet for reference: [Accept ...

A guide to implementing typescript with Next.js getStaticProps

I have Next.js set up with the TypeScript feature enabled Currently, I am attempting to utilize the getStaticProps function following the guidelines outlined here: https://nextjs.org/docs/basic-features/typescript Utilizing the GetStaticProps type export ...

Requiring a condition for each element in an array which is part of an object

Let's discuss a scenario where I have to decide whether to display a block based on a certain condition. Here is an example array structure: const data = [ { name: "item1" , values : [0,0,0,0,0]}, { name: "item2" , values : [0,0,0,0,0]}, { nam ...

"Utilize an HTML file open dialog box to gain access to the server's

Is it feasible to implement a file open dialog box that displays files and directories from the server's file system? In my web application, there is a need for users to select a file that resides on the server. Are there any plugins available for th ...

Assigning array materials in ThreeJS allows you to create complex

When I assign framemat to createScene(ID, geometry, 1, framemat), everything works fine. But when I try createScene( ID, geometry, 1, materials[ID] ), it doesn't cooperate. var jsonLoader = new THREE.JSONLoader(), paths = [ "obj/jgd/ ...

Expanding TypeScript Definitions

I've been experimenting with TypeScript and Express. After importing type declarations from Typings, I found the following code: // Imported from typings // Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f2 ...

MongoDB embodies intricate aggregates

I have two schemas that need to be joined and certain calculations need to be performed. a { academic_year: { type:String} } b { b1: {type:Number, default:0 }, b2: {type:Number, default:0 }, b3: [{ b3_1: {type:Number, d ...

How can I achieve a fade-in effect whenever the flag image is clicked?

A unique "international" quotes script has been created, showcasing Dutch, English, German, and French quotes. The script displays different quotes every day, with a draft-result visible in the upper right corner of this page ... The code used for this sc ...

Is there a way to update the value of an <input> element dynamically?

There is an input in a datalist that I am working with. <input type="text" value="1" id="txtcount"> I am trying to obtain the new value of the input when the text changes. I attempted to use the following code, but it did not work as expected. &l ...

Looping through an array of nested objects using Vue

I have encountered a challenge with accessing specific data within an array that I am iterating over. The array is structured as follows, using Vue.js: companies: [ name: "company1" id: 1 type: "finance" additionalData: "{& ...

Developing a constrained variable limited to specific values

Recently delving into Typescript - I am interested in creating a variable type that is restricted to specific values. For instance, I have an element where I want to adjust the width based on a "zoom" variable. I would like to define a variable type call ...

invoke a specified function at runtime

I recently came across a useful library called https://github.com/ivanhofer/typesafe-i18n This library has the capability to generate strongly typed translation data and functions, as illustrated below. (the examples provided are simplified for clarity) e ...

Tips for utilizing a Three.js curve to guide the movement of a mesh along a specified path

Developing an animation, currently at this stage: http://jsfiddle.net/CoderX99/66b3j9wa/1/ Please avoid delving into the code, it's written in CoffeeScript and may not be beneficial for your mental well-being. Imagine a "nordic" landscape with ship ...

Error: The @IsEmpty property decorator in ES2017 Nest JS is encountering issues when used as an expression. It seems that the signature of the decorator cannot be resolved, resulting in

Hey everyone, I'm fairly new to Nest JS and I'm currently working on adding a DTO validator. However, when I attempt to use functions like isNotEmpty or Max, the compiler throws an error at me: Unable to resolve signature of property decorator ...

Chaining updateMany() calls in MongoDB while ensuring synchronous response handling

I have encountered an issue while attempting to send 3 separate updateMany requests within a get request, each using a different query. While the first two requests work perfectly, the third updateMany request only functions as expected after refreshing th ...

Unpack an array with entries and an iterator

I am working with an array of objects, each containing the same properties. My goal is to create a function that will return an array of arrays, where each inner array holds values based on the property names of the objects. Here is an example input: inp ...