prisma: Simple MongoDB queries are much slower compared to Mongoose

Bug description

I’m in the middle of refactoring from mongoose to prisma using MongoDB database hosted on MongoDB Atlas. I found out that simple queries are much slower compared to mongoose. We are talking 10-20x slower.

I tried to measure it with console.time and these are the rough results

prisma: 456.724ms
mongoose: 40.727ms

prisma: 473.63ms
mongoose: 28.241ms

How to reproduce

  1. Use a hosted MongoDB (I use MongoDB Atlas)
  2. Define a simple schema
  3. Install both Prisma and Mongoose (to try my code, even Typegoose)
  4. Try to perform the same queries with both libraries.

The queries I used are like this

Mongoose query

NewsFeed.find(
    { type: NewsFeedTypes.RELEASE_NOTE },
    {
      title: 1,
      text: 1,
      date: {
        $dateToString: {
          date: '$publicDate',
          format: '%d.%m.%Y',
        },
      },
    }
  )
    .lean()
    .exec();

Mongoose (Typegoose) schema

@modelOptions({
  schemaOptions: {
    collection: 'NewsFeed',
    timestamps: {
      createdAt: 'created',
      updatedAt: 'modified',
    },
  },
})
export class NewsFeedSchema {
  @prop({ ref: () => GridFsFileSchema })
  public image!: Ref<GridFsFileSchema> | null;

  @prop({ type: () => Date })
  public publicDate?: Date;

  @prop({ type: () => String, required: true })
  public text!: string;

  @prop({ type: () => String, required: true })
  public title!: string;

  @prop({ type: () => String, enum: NewsFeedTypes, required: true })
  public type!: NewsFeedType;

  @prop({ type: () => Boolean, required: true, default: false })
  public visibleToBrands!: boolean;

  @prop({ type: () => Boolean, required: true, default: false })
  public visibleToCreative!: boolean;

  public created!: Date;

  public modified!: Date;
}

export const NewsFeed = getModelForClass(NewsFeedSchema);

Expected behavior

It should be roughly the same speed, not 10x-20x slower.

Prisma information

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["filteredRelationCount", "extendedWhereUnique"]
}

datasource db {
  provider = "mongodb"
  url      = env("MONGODB_URI")
}

model NewsFeed {
  id                 String       @id @default(auto()) @map("_id") @db.ObjectId
  image              String?      @db.ObjectId
  publicDate         DateTime?
  text               String
  title              String
  type               NewsFeedType
  visibleToBrands    Boolean      @default(false)
  visibleToDesigners Boolean      @default(false)
  createdAt          DateTime     @default(now()) @map("created")
  updatedAt          DateTime     @updatedAt @map("modified")
}
prisma.newsFeed.findMany({
    where: {
      type: NewsFeedType.RELEASE_NOTE,
      publicDate: {
        lte: new Date(),
      },
    },
  });

Environment & setup

  • OS: MacOS
  • Database: MongoDB
  • Node.js version: 16.17.0

Prisma Version

prisma                  : 4.7.1
@prisma/client          : 4.7.1
Current platform        : darwin-arm64
Query Engine (Node-API) : libquery-engine 272861e07ab64f234d3ffc4094e32bd61775599c (at node_modules/.pnpm/@prisma+engines@4.7.1/node_modules/@prisma/engines/libquery_engine-darwin-arm64.dylib.node)
Migration Engine        : migration-engine-cli 272861e07ab64f234d3ffc4094e32bd61775599c (at node_modules/.pnpm/@prisma+engines@4.7.1/node_modules/@prisma/engines/migration-engine-darwin-arm64)
Introspection Engine    : introspection-core 272861e07ab64f234d3ffc4094e32bd61775599c (at node_modules/.pnpm/@prisma+engines@4.7.1/node_modules/@prisma/engines/introspection-engine-darwin-arm64)
Format Binary           : prisma-fmt 272861e07ab64f234d3ffc4094e32bd61775599c (at node_modules/.pnpm/@prisma+engines@4.7.1/node_modules/@prisma/engines/prisma-fmt-darwin-arm64)
Format Wasm             : @prisma/prisma-fmt-wasm 4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c
Default Engines Hash    : 272861e07ab64f234d3ffc4094e32bd61775599c
Studio                  : 0.477.0
Preview Features        : filteredRelationCount, extendedWhereUnique

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 7
  • Comments: 17 (7 by maintainers)

Most upvoted comments

We are experiencing the same issue here, it’s examining way too many documents with the query Prisma generated compared to the native approach and it’s blowing up the MongoDB query Query Targeting monitor and making our apps slower!! Please address this.

When we have capacity, we will reproduce, understand and then hopefully fix this. @rjzheng’s investigation looks like a good lead. Prisma is open source, so if someone opens a PR with a fix before we would of course also take that into consideration and review and potentially merge it.

I just updated my first comment here with an example of how it could be resolved from the application code level

But as an addition here are more ideas that I got

application code

// the application – creating new record in db
prismaDb.collectionName.create({
    ... // data
  }, 
  // new 2nd argument, options object:
  {
    omitTransactions: true // for MongoDB, the driver will omit creating a transaction 
                           // and will relate on the internal mongoDb queue and atomicity features
  }
);

OR

in .schema file:

// per datasource

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
  useTransactions = false    // ««« turn off the default all-is-transaction Prisma's driver feature
                             // for all models by default
}
// per model

model Record {
  id          String   @id @default(auto()) @map("_id") @db.ObjectId
  name        String   @unique
  updatedAt   DateTime @updatedAt()
  createdAt   DateTime @default(now())

  @@useTransactions(false)  // ««« new attribute
}

We experienced the same that Prisma queries are much slower. Our investigation shows that the converted Mongo aggregation queries don’t use set indexes. This is why they are +10x slower. It gets worse with an extensive collection.

We have the same issue on our side, is there any update on this issue ?

I tried digging more. I setup logging in Prisma to log queries that Prisma generates. I wanted to compare the execution time between the query that prisma generated and the simplest query that can be written. I measured the execution time in DataGrip (JetBrains’s DB IDE). For sure it’s not super reliable but… It’s a number.

Prisma’s query

db.NewsFeed.aggregate([ { $match: { $expr: { $and: [ { $and: [ { $eq: [ "$type", { $literal: "RELEASE_NOTE", }, ], }, { $ne: [ "$type", "$$REMOVE", ], }, ], }, { $and: [ { $lte: [ "$publicDate", { $literal: DateTime("2022-12-20 6:43:24.725 +00:00:00"), }, ], }, { $ne: [ "$publicDate", "$$REMOVE", ], }, ], }, ], }, }, }, { $project: { _id: 1, image: 1, publicDate: 1, text: 1, title: 1, type: 1, visibleToBrands: 1, visibleToDesigners: 1, created: 1, modified: 1, }, }, ])

Execution time: ~150ms

Simplest manual query

db.NewsFeed.find({ type: "RELEASE_NOTE", publicDate: { $lte: new Date() } })

Execution time: ~130ms

So it looks like that the query that prisma generated is not slower by much so… The performance kick might be caused by data parsing? I don’t know