Retrieve posts by category ID using ManyToMany in TypeORM with TreeEntity implemented using materialized path structure

Seeking a way to retrieve posts based on category similar to what a CMS does.

For instance, querying posts by Category A should include all posts assigned to Category A as well as those attached to child categories of Category A.

I'm unsure how to construct this query, so any assistance would be greatly appreciated :)

Below are my entities:

@Tree("materialized-path")
export class Category {
  @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;


    @ManyToMany((type) => Post, (post) => post.categories)
    posts: Post[];

    @Expose()
    @TreeChildren()
    children: Category[];

    @Expose()
    @TreeParent()
    parent: Category;
}
export class Post{
   @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @ManyToMany((type) => Category, (category) => category.posts)
    @JoinTable()
    categories: Category[];
}

The following SQL Query accomplishes the task (Example with category id 1):

SELECT * FROM post WHERE id IN (
    SELECT postId FROM post_categories_category as postCat WHERE postCat.categoryId IN (
       SELECT id FROM category WHERE category.mpath LIKE "1.%" OR category.mpath LIKE "%.1.%"
    )
)

Therefore, the question remains - how can this SQL query be converted into a typeORM query?

Answer №1

Here is a quick solution that I have written and tested. It should work perfectly fine. Let me know if you encounter any issues.

@Entity()
@Tree("materialized-path")
export class Category extends BaseEntity {
  @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @ManyToMany((type) => Post, (post) => post.categories)
    posts: Post[];

    @TreeChildren()
    children: Category[];

    @TreeParent()
    parent: Category;

    async getPosts(): Promise<Post[]> {
      const categories = await getConnection().getTreeRepository(Category).findDescendants(this); // retrieves all child categories
      categories.push(this); // includes the parent category

      const ids  = categories.map(cat => cat.id); // creates an array of category IDs

      return await Post.createQueryBuilder('post')
        .distinct(true) // avoids duplicate posts from being fetched
        .innerJoin('post.categories', 'category', 'category.id IN (:...ids)', {ids}) // fetches posts where category belongs to categories array
        .innerJoinAndSelect('post.categories', 'cat') // adds all categories to the selected post 
        .orderBy('post.id')
        .getMany();
    }
}

@Entity()
export class Post extends BaseEntity {
  @PrimaryGeneratedColumn()
   id: number;

   @Column()
   title: string;

   @ManyToMany((type) => Category, (category) => category.posts)
   @JoinTable()
   categories: Category[];
}


This method utilizes query builders. For more information, refer to this link.

It also makes use of the findDescendants function. View more details about it at this link.

I hope this explanation helps :)

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

Prime NG: Filtering options using label in dropdown menu

I'm facing an issue with my angular project that utilizes prime ng. The problem arises when attempting to perform a column search on rows of dropdown values. When searching for 'Jan', the system fails to retrieve any data, but interestingly ...

How can you deduce the type from a different property in Typescript?

I have encountered obstacles in my development process and need assistance overcoming them. Currently, I am trying to configure TObject.props to only accept 'href' or 'download' if the condition TObject.name = 'a' is met, and ...

Steps for positioning grid lines behind the flex container

Currently, I have a flex container with several flex items arranged from top to bottom. My goal is to create light grey color lines behind this flex container to achieve a design similar to this. I attempted to add another flex container on top of this on ...

Manipulate the elements within an array, make changes, and then insert

In the array called newData, I am trying to add one more element with Rank 1. However, the issue is that the Rank value is getting updated for both records. The desired behavior is to have Rank set to 1 for the second record and have the first record' ...

Angular Typescript Filter failing to connect with service injection

I am having trouble accessing the Constant app within a filter in Angular TypeScript. How can I successfully access a service inside a filter? Module App.Filter { import Shared = Core.Shared; export class MilestoneStatusFilter123 { static $inject = ...

Tips for creating TypeScript Google Cloud Functions using webpack

I'm currently facing a challenge while coding a Google Cloud Function using TypeScript. The concept involves having handler functions defined for various Cloud Functions in separate files within the source repository, along with some code that is shar ...

Creating an Object with Quoted Key in JavaScript/Typescript for MongoDB's '$push' Feature

Struggling to format the data in order to add an element into a nested array within MongoDB. Currently attempting this in TypeScript: var data = {$push:{"foo.12.bar":{ prop1: prop1, prop2: prop2, // referenced values above this code snippet ...

Encounter issue with async function in produce using Immer

Having an issue while attempting to create an asynchronous produce with immer. When calling the async function, this error is encountered: Below is my code snippet: import { combineReducers, createStore } from 'redux'; import produce from ' ...

Leveraging ES6 Symbols in Typescript applications

Attempting to execute the following simple line of code: let INJECTION_KEY = Symbol.for('injection') However, I consistently encounter the error: Cannot find name 'Symbol'. Since I am new to TypeScript, I am unsure if there is somet ...

Is it possible to install Shadcn ui for Vite + React using only JavaScript instead of TypeScript?

I'm encountering difficulties when attempting to install and use shadcn components in my Vite + React + Tailwind project. I followed the instructions provided in their documentation here, but it seems like it requires TypeScript to function properly? ...

Unable to send JSON data from server to client following a successful file upload operation

I'm currently working on a project that involves NodeJS, Express, JQuery, and Typescript. The issue I'm facing is related to uploading a file from the front end, which is successful. However, I'm encountering difficulties in returning a JSON ...

What causes the error message "No exported member 'ɵɵFactoryDeclaration' in @angular/core/core" to appear during project compilation?

I am facing an issue where the global Angular CLI version is 13.0.1 and the local version in my project is 10.2.3. Despite making changes to some components (without touching package.json), I encountered an error during the build step of my bitbucket pipel ...

How to define an index signature in Typescript that includes both mandatory and optional keys

I am on a quest to discover a more refined approach for creating a type that permits certain keys of its index signature to be optional. Perhaps this is a scenario where generics would shine, but I have yet to unlock the solution. At present, my construc ...

Vite HMR causes Vue component to exceed the maximum number of recursive updates

After making changes to a nested component in Vue and saving it, I noticed that the Vite HMR kept reloading the component, resulting in a warning from Vue: Maximum recursive updates exceeded... Check out the online demo on stackblitz. Make a change in Chi ...

Unable to include option object in the SHA3 function using typescript

The SHA3 function allows for customizing the output length, as demonstrated in the code snippet below: var hash = CryptoJS.SHA3("Message", { outputLength: 512 }); var hash = CryptoJS.SHA3("Message", { outputLength: 384 }); var hash = CryptoJS.SHA3("Messag ...

Is it possible to utilize the `disableCSSOMInjection` feature with TypeScript?

Looking to implement disableVendorPrefixes and disableCSSOMInjection, new features in TypeScript for v5. Encountering a TypeScript error (TS2769: No overload matches this call.) when trying to use them. Is there a workaround since @types/styled-component ...

Ways to retrieve the returned value from the JS FETCH API outside of its scope

As a beginner in Typescript React and the Ionic framework, I am trying to use the JS FETCH API to fetch data from a third-party source. However, I am struggling to access this fetched data outside of the fetch function. If anyone could provide some guidan ...

Encountering an error when attempting to show user details on a webpage using Angular and Ionic with Promise functionality

On my app's AccountSettingsPage, I am fetching user data from a SQLite DB and displaying it on an Ionic page. However, I encountered the following error: Error: TypeError: Cannot read property 'name' of undefined at Object.eval [as upd ...

Issue encountered while utilizing combineReducers: "Error: The assetsReducer returned an undefined value during initialization."

Issue: The "assetsReducer" has returned an undefined value during initialization. When the state passed to the reducer is undefined, it must explicitly return the initial state, which cannot be undefined. If no value is set for this reducer, consider using ...

What are some effective methods for troubleshooting Vue.js computed properties and templates?

I am facing challenges with debugging in Vue.js, especially when it comes to debugging computed properties or data values in templates. Currently, I am using the IIFE method for debugging as shown in : <h2 dir="auto"> {{(function(){debugger;let ...