---
title: 'استعلامات Drizzle ORM العلاقاتية: دليل db.query لعام (2026)'
description: 'استعلم عن العلاقات المتداخلة في Drizzle ORM باستخدام db.query ومعامل with: تصفية وتحديد عدد الصفوف التابعة، تشغيل ربط many-to-many، بالإضافة إلى التغييرات بين v1 و v2.'
url: https://nerdleveltech.com/ar/drizzle-orm-relational-queries-nested-with-tutorial
language: ar
date: '2026-06-24'
category: backend
tags:
  - drizzle orm
  - relational queries
  - postgres
  - typescript
  - node.js
  - orm
  - backend
markdown_url: https://nerdleveltech.com/ar/drizzle-orm-relational-queries-nested-with-tutorial.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/ar/drizzle-orm-relational-queries-nested-with-tutorial'
terms: https://nerdleveltech.com/llms.txt
---

# استعلامات Drizzle ORM العلاقاتية: دليل db.query لعام (2026)

تسمح لك **استعلامات العلاقات في Drizzle ORM** بقراءة البيانات المتداخلة والمرتبطة في استدعاء واحد آمن النوع (type-safe) بدلاً من كتابة عمليات الربط (joins) اليدوية وإعادة تشكيل الصفوف. يمكنك تحديد العلاقات باستخدام `relations()`، وتمرير مخططك (schema) الكامل إلى `drizzle()`، ثم استدعاء `db.query.<table>.findMany({ with: { ... } })`. يقوم Drizzle بإصدار جملة SQL واحدة ويعيد كائنات متداخلة بشكل صحيح.[^rqb]

## ملخص

في الإصدار المستقر الحالي (`drizzle-orm@0.45.2`)، توجد استعلامات العلاقات على `db.query`. قم بالإعلان عن `relations()` لكل جدول، وقم بتهيئة العميل باستخدام `drizzle(client, { schema })`، واستخدم معامل `with` لسحب الصفوف المرتبطة — بعمق تداخل كما تحتاج. يمكنك تصفية وفرز وتقييد الصفوف **التابعة** (child)، ولكن في الإصدار v1 لا يمكنك تصفية الصفوف **الأصلية** (parent) بناءً على عمود في الصف التابع (هذه الميزة ستتوفر في v2). تم تشغيل كل نمط استعلام في هذا الدليل على `drizzle-orm@0.45.2` مقابل مثيل Postgres حقيقي (PGlite) وتم التحقق من النوع باستخدام `tsc --noEmit`.[^versions]

## ما ستتعلمه

- كيفية تحديد العلاقات وربط `db.query` بشكل صحيح
- كيفية الاستعلام عن العلاقات المتداخلة باستخدام معامل `with` (علاقة واحد إلى متعدد، التداخل العميق)
- لماذا يكون `db.query` غير معرف (`undefined`) — والحل المكون من سطر واحد
- كيفية اختيار أعمدة جزئية في الجداول الأصلية والتابعة
- كيفية تصفية وترتيب وتقييد الصفوف التابعة (وقيود الإصدار v1 على تصفية الجداول الأصلية)
- كيفية تشغيل استعلام "متعدد إلى متعدد" من خلال جدول ربط (junction table)
- متى تستخدم استعلامات العلاقات مقابل استخدام `select` العادي مع الربط (join)
- ما الذي يتغير في استعلامات العلاقات v2 (`defineRelations`)، ولماذا تظهر الوثائق المباشرة `db._query`

## الإعداد: تحديد العلاقات وتمرير المخطط

استعلامات العلاقات هي امتداد لمنشئ الاستعلامات في Drizzle، لذا فهي تحتاج إلى شيئين: إعلان `relations()` لكل جدول، وتسليم **المخطط بالكامل** إلى `drizzle()` عند التهيئة.[^rqb] إليك مخطط بنمط المدونة — مستخدمون، منشورات، تعليقات، وجدول ربط `posts_to_tags` للوسوم:

```TypeScript
// schema.ts
import { relations from 'drizzle-orm';
import { pgTable, serial, text, integer, primaryKey from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  published: integer('published').notNull().default(0),
  authorId: integer('author_id').notNull().references(() => users.id),
});

export const comments = pgTable('comments', {
  id: serial('id').primaryKey(),
  body: text('body').notNull(),
  postId: integer('post_id').notNull().references(() => posts.id),
});

export const tags = pgTable('tags', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
});

export const postsToTags = pgTable('posts_to_tags', {
  postId: integer('post_id').notNull().references(() => posts.id),
  tagId: integer('tag_id').notNull().references(() => tags.id),
}, (t) => [primaryKey({ columns: [t.postId, t.tagId] })]);
```

الآن قم بالإعلان عن العلاقات. يحتاج جانب الـ `one` إلى `fields` و `references`؛ أما جانب الـ `many` فيكتفي بتسمية الجدول المستهدف:

```TypeScript
// schema.ts (continued)
export const usersRelations = relations(users, ({ many ) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one, many ) => ({
  author: one(users, { fields: [posts.authorId], references: [users.id] }),
  comments: many(comments),
  postsToTags: many(postsToTags),
}));

export const commentsRelations = relations(comments, ({ one ) => ({
  post: one(posts, { fields: [comments.postId], references: [posts.id] }),
}));

export const tagsRelations = relations(tags, ({ many ) => ({
  postsToTags: many(postsToTags),
}));

export const postsToTagsRelations = relations(postsToTags, ({ one ) => ({
  post: one(posts, { fields: [postsToTags.postId], references: [posts.id] }),
  tag: one(tags, { fields: [postsToTags.tagId], references: [tags.id] }),
}));
```

أخيرًا، قم بتهيئة قاعدة البيانات مع استيراد المخطط كمساحة اسم (namespace). يعتمد استيراد `drizzle` ووسائطه بالضبط على المحرك (driver) الذي تستخدمه، ولكن الهيكل هو نفسه — يجب عليك تمرير `schema`:[^connect]

```TypeScript
// db.ts
import { drizzle from 'drizzle-orm/node-postgres'; // or /pglite, /postgres-js, etc.
import * as schema from './schema';

export const db = drizzle(client, { schema );
```

## كيف يمكنني الاستعلام عن العلاقات المتداخلة في Drizzle ORM؟

للاستعلام عن العلاقات المتداخلة، استدعِ `db.query.<table>.findMany()` (أو `findFirst()`) ومرر كائن `with` يحدد كل علاقة تريد تضمينها. للتعمق أكثر، قم بتداخل `with` آخر بداخله. يعيد هذا المستخدمين، كل منهم مع منشوراته، وكل منشور مع تعليقاته:

```TypeScript
const usersWithPostsAndComments = await db.query.users.findMany({
  with: {
    posts: {
      with: {
        comments: true,
      },
    },
  },
});
// usersWithPostsAndComments[0].posts[0].comments -> Comment[]
```

يقوم Drizzle بإنشاء جملة SQL **واحدة** فقط لهذا الغرض (باستخدام lateral joins للاستعلامات الفرعية تحت الغطاء) ويعيد كائنات متداخلة ومحددة النوع بالكامل — دون الحاجة لتعيين الصفوف يدويًا.[^rqb] في التشغيل الذي تم التحقق منه، أعاد المنشور الأول للمستخدم الأول مصفوفة من تعليقين، ويستمر نوع النتيجة عبر كل مستويات التداخل.

`findFirst()` هو نفس الـ API مع تطبيق `LIMIT 1`، مما يعيد كائنًا واحدًا بدلاً من مصفوفة:

```TypeScript
const onePost = await db.query.posts.findFirst({
  with: { comments: true ,
});
```

## لماذا يكون db.query غير معرف في Drizzle ORM؟

يكون `db.query` غير معرف (`undefined`) (أو فارغًا) عندما لا تمرر مخططك (`schema`) — بما في ذلك صادرات `relations()` الخاصة به — إلى `drizzle()`. يتم بناء استعلامات العلاقات بالكامل من كائن المخطط هذا، لذا بدونه لا يوجد شيء لربطه بـ `db.query`. الحل هو سطر واحد:

```TypeScript
// ❌ db.query is empty — no schema
const db = drizzle(client);

// ✅ db.query.<table> now exists
import * as schema from './schema';
const db = drizzle(client, { schema );
```

تأكد من أن استيراد مساحة الاسم الخاص بك (`import * as schema`) يلتقط **كلاً من** الجداول وإعلانات `relations()` من نفس الملف (أو الملفات). إذا قمت بتقسيم المخطط عبر ملفات متعددة، فقم بنشرها جميعًا: `drizzle(client, { schema: { ...schema1, ...schema2 } })`.[^rqb]

## كيف يمكنني اختيار أعمدة معينة فقط؟

استخدم خيار `columns` لتضمين أو استبعاد الحقول، في الجدول الأصلي وفي أي علاقة متداخلة. يقوم Drizzle بدفع هذا إلى SQL، لذا لا يتم نقل الأعمدة غير المختارة أبدًا:[^rqb]

```TypeScript
const posts = await db.query.posts.findMany({
  columns: { id: true, title: true ,
  with: {
    author: { columns: { name: true ,
  },
});
// each row: { id, title, author: { name } }
```

عندما تخلط بين مفاتيح `true` و `false`، يتم تجاهل مدخلات `false` — فإدراج أي عمود بقيمة `true` يعني استبعاد كل شيء آخر تلقائيًا. لإسقاط حقل واحد فقط بدلاً من ذلك، استخدم `false` فقط: `columns: { content: false }`.

## كيف يمكنني تصفية وترتيب وتقييد الصفوف التابعة؟

يمكنك تطبيق `where` و `orderBy` و `limit` و `offset` على الاستعلام عالي المستوى، و `where` و `orderBy` و `limit` على العلاقات المتداخلة. يجلب هذا مستخدمًا واحدًا، ثم منشوراته **المنشورة** فقط، الأحدث أولاً، بحد أقصى واحد:

```TypeScript
import { eq, desc from 'drizzle-orm';

const user = await db.query.users.findFirst({
  where: eq(users.id, 1),
  with: {
    posts: {
      where: eq(posts.published, 1),
      orderBy: [desc(posts.id)],
      limit: 1,
    },
  },
});
```

هناك قيدان يستحقان المعرفة في v1. أولاً، **`offset` يعمل فقط على الاستعلام عالي المستوى**، وليس على العلاقات المتداخلة.[^rqb] ثانيًا — وهذا ما يربك الناس — فإن `where` المتداخل يصفي الصفوف **التابعة** التي تعود، وليس الصفوف **الأصلية**. لا توجد طريقة في v1 لقول "أعد فقط المستخدمين الذين لديهم منشور يطابق X" من خلال الـ API الخاص بالعلاقات؛ تصفية الصفوف عالية المستوى بواسطة عمود جدول مرتبط هي ميزة في v2.[^v1v2] إذا كنت بحاجة إلى ذلك اليوم في الإصدار المستقر، فانتقل إلى `select()` مع ربط صريح، أو قم بالتصفية في استعلام فرعي.

تفصيل واحد مطمئن: عندما لا يتطابق شيء، يعيد Drizzle **مصفوفة فارغة**، وليس `null`. الأصل الذي ليس له تابع يعود مع `children: []`، والاستعلام الذي لا يحتوي على صفوف مطابقة يعيد `[]`. تم تأكيد كلا الأمرين في التشغيل الذي تم التحقق منه.

## كيف يمكنني تشغيل استعلام "متعدد إلى متعدد" في Drizzle ORM؟

في الإصدار v1 لا يوجد مساعد مخصص لـ "متعدد إلى متعدد"، لذا تقوم بالاستعلام **من خلال** جدول الربط: قم بتضمين علاقة الربط، وامسح أعمدتها الخاصة باستخدام `columns: {}`، وقم بتداخل علاقة الجانب البعيد بداخلها. يعيد هذا منشورًا بمسار مسطح تقريبًا إلى وسومه:

```TypeScript
const post = await db.query.posts.findFirst({
  where: eq(posts.id, 1),
  with: {
    postsToTags: {
      columns: {}, // omit junction columns from the result
      with: {
        tag: true,
      },
    },
  },
});

// tag names:
const tagNames = post?.postsToTags.map((pt) => pt.tag.name); // ['orm', 'sql']
```

يحافظ `columns: {}` على صفوف الربط خارج حمولة البيانات الخاصة بك مع السماح لك بالوصول إلى `tag`. أعاد التشغيل الذي تم التحقق منه `['orm', 'sql']` لمنشور مرتبط بوسمين. (يستبدل Relational Queries v2 هذا النمط بالكامل بإعلان `through` واحد — انظر أدناه.)[^v1v2]

## الاستعلامات العلائقية مقابل select + join: أيهما يجب أن أستخدم؟

استخدم **الاستعلامات العلائقية** (`db.query`) عندما تريد كائنات متداخلة مشكلة حسب نطاق عملك — مستخدم مع منشورات مع تعليقات — وأنت تقوم بقراءة البيانات. استخدم **منشئ الاستعلامات الشبيه بـ SQL** (`db.select()` مع `leftJoin`) عندما تحتاج إلى صفوف مسطحة، أو تجميعات (`count`، `sum`)، أو عمليات المجموعات، أو تصفية/ترتيب العناصر الأب بواسطة عمود منضم لا يمكن لـ API العلائقي في الإصدار v1 التعبير عنه. كلاهما يتشاركان نفس المخطط (schema) ويمكنهما العمل جنباً إلى جنب؛ الاستعلامات العلائقية هي طبقة اختيارية فوق المنشئ الأساسي، وليست بديلاً له.[^rqb] لمزيد من أشكال الوصول المعقدة للبيانات، يوضح دليلنا حول [المعاملات الذرية مع Drizzle ORM و pg-boss](https://nerdleveltech.com/posts/drizzle-orm-pg-boss-atomic-transactions-tutorial) المنشئ الأساسي في سير عمل كثيف الكتابة.

## ما الفرق بين استعلامات Drizzle العلائقية v1 و v2؟

الاستعلامات العلائقية **v2** هي إعادة تصميم يتم شحنها في `drizzle-orm@beta` (الإصدار v1.0.0-beta.1 وما فوق)؛ أما خط الإصدار المستقر `0.45.x` الذي تقوم بتثبيته افتراضياً فهو **v1**.[^v1v2] التغييرات الرئيسية هي:

- **مكان واحد للعلاقات.** يستبدل v2 استدعاءات `relations()` لكل جدول باستدعاء واحد لـ `defineRelations(schema, (r) => ({ ... }))`، وتقوم بتمرير `{ relations }` إلى `drizzle()` بدلاً من `{ schema }`.
- **إعادة تسمية المفاتيح.** `fields` ← `from`، و `references` ← `to` (كل منهما يقبل عموداً واحداً أو مصفوفة)، و `relationName` ← `alias`.
- **تصفية العناصر الأب بواسطة العلاقات.** يضيف v2 نمط الكائنات لـ `where`/`orderBy` ويسمح لك بتصفية صفوف المستوى الأعلى بواسطة أعمدة جدول مرتبط — وهو بالضبط الشيء الذي لا يستطيع v1 فعله.
- **علاقة "متعدد إلى متعدد" أصلية.** مساعد `through` يزيل تكرار الكود الخاص بجدول الربط (junction-table) تماماً.
- **تبديل الوصول.** هذه هي المفاجأة الكبرى: في v2 يتم **إعادة تعيين** `db.query` النظيف إلى الصيغة الجديدة، وينتقل API القديم الخاص بـ v1 إلى `db._query` (مع استيراد `relations` من `drizzle-orm/_relations`). لهذا السبب تظهر الوثائق الحية `db._query` — تلك الصفحات توثق الإصدار v1.0 المرشح للإصدار. في الإصدار المستقر `0.45.x`، لا تزال الاستعلامات العلائقية v1 هي `db.query`.[^v1v2]

إذا كنت تستخدم الإصدار المستقر اليوم، فاستخدم `db.query` كما هو موضح في هذا الدليل. عندما تقوم بالترقية إلى v1.0، يمكنك الهجرة استعلاماً تلو الآخر: يستمر الكود القديم في العمل عبر `db._query` بينما يستخدم الكود الجديد `db.query` المعاد تصميمه.

## الأسئلة الشائعة

**كيف يمكنني الاستعلام عن العلاقات المتداخلة في Drizzle ORM؟** قم باستدعاء `db.query.<table>.findMany({ with: { relationName: true } })`، وقم بتداخل `with` أخرى بالداخل للتعمق أكثر. يعيد Drizzle مجموعة نتائج واحدة متداخلة ومحددة النوع من جملة SQL واحدة.[^rqb]

**لماذا `db.query` غير معرف (undefined)؟** لم تقم بتمرير `schema` إلى `drizzle()`. يتم بناء الاستعلامات العلائقية من كائن المخطط، لذا استخدم `drizzle(client, { schema })` مع استيراد مساحة الاسم (namespace import) الذي يتضمن تصديرات `relations()` الخاصة بك.

**هل يمكنني تصفية صفوف الأب بواسطة عمود الابن في v1؟** لا. في v1، يقوم `where` المتداخل بتصفية صفوف الأبناء المضمنة فقط، وليس الآباء. تصفية الآباء بواسطة جدول مرتبط هي ميزة في الاستعلامات العلائقية v2؛ في الإصدار المستقر، استخدم `db.select()` مع join بدلاً من ذلك.[^v1v2]

**هل تعيد العلاقة المفقودة null أم مصفوفة فارغة؟** مصفوفة فارغة. الأب الذي ليس له أبناء يعيد `children: []`، والاستعلام الذي لا يحتوي على تطابقات يعيد `[]` — تم التحقق من ذلك في `drizzle-orm@0.45.2`.

**كيف أقوم بعمل استعلام "متعدد إلى متعدد" في v1؟** استعلم من خلال جدول الربط: قم بتضمين علاقة الربط مع `columns: {}` وقم بتداخل العلاقة في الطرف البعيد (مثل `tag: true`) بداخلها.[^v1v2]

**هل `db._query` هو نفسه `db.query`؟** فقط في v2. في الإصدار المستقر `0.45.x`، يكون `db._query` هو `undefined` و `db.query` هو API العلائقي. في v1.0+ يتبادلان: `db._query` يشغل صيغة v1 القديمة و `db.query` يشغل v2.[^v1v2]

**هل يمكنني استخدام `limit` و `offset` في العلاقات المتداخلة؟** يعمل `limit` و `orderBy` في العلاقات المتداخلة في v1؛ أما `offset` فيعمل فقط في استعلام المستوى الأعلى.[^rqb] يضيف v2 خاصية `offset` المتداخلة.[^v1v2]

## الخلاصة

تمنحك الاستعلامات العلائقية في Drizzle ORM قراءات متداخلة ومحددة النوع باستخدام `db.query` وعامل التشغيل `with`، مع الحفاظ على توفر منشئ الاستعلامات الشبيه بـ SQL لعمليات الربط والتجميع. في الإصدار المستقر `0.45.x`، قم بتعريف `relations()`، ومرر `schema` الكامل إلى `drizzle()`، وتذكر أن v1 يصفي الأبناء ولكن ليس الآباء. عندما تنتقل إلى v1.0، انتبه لتبديل `db.query` ← `db._query`.

الخطوات التالية: ادمج هذه القراءات مع [الترقيم المعتمد على المؤشر (cursor pagination) في Postgres و Node.js](https://nerdleveltech.com/posts/cursor-pagination-postgres-nodejs-tutorial) لمجموعات النتائج الكبيرة، وأضف عمليات إعادة محاولة آمنة لكتاباتك باستخدام [مفاتيح عدم التكرار (idempotency keys) في Node.js و Postgres](https://nerdleveltech.com/posts/idempotency-keys-nodejs-postgres-API-tutorial).

[^rqb]: Drizzle ORM — وثائق الاستعلام (الاستعلامات العلائقية). <https://orm.drizzle.team/docs/rqb>

[^versions]: drizzle-orm `0.45.2` (npm الأحدث، يونيو 2026)؛ تم التحقق منه مقابل Postgres عبر `@electric-sql/pglite` `0.5.3` وفحص الأنواع باستخدام `tsc --noEmit` (strict، nodenext). <https://www.npmjs.com/package/drizzle-orm>

[^connect]: Drizzle ORM — نظرة عامة على اتصال قاعدة البيانات (مسارات استيراد `drizzle()` الخاصة بالمحرك). <https://orm.drizzle.team/docs/connect-overview>

[^v1v2]: Drizzle ORM — دليل الهجرة للاستعلامات العلاقاتية من الإصدار الأول إلى الثاني (ينطبق على `drizzle-orm@beta`، v1.0.0-beta.1+). <https://orm.drizzle.team/docs/relations-v1-v2>
