MongoDB
MongoDB هي قاعدة بيانات وثائقية، وحدتها الأساسية للبيانات هي الوثيقة، وتنسيق التخزين هو BSON (Binary JSON) وهو هيكل مشابه لـ JSON، حيث يتيح الهيكل المرن تخزين أنواع مختلفة من البيانات، مما يجعلها أكثر مرونة من قواعد البيانات العلائقية، كما تستخدم JavaScript كلغة برمجة نصية يمكن من خلالها إتمام العمليات المركبة. تقدم هذه المقالة كيفية استخدام مشغل MongoDB الرسمي في Go للتعامل مع قاعدة بيانات mongodb، وهي ليست درسًا تعليميًا عن mongodb، فإذا لم تكن لديك خلفية عن mongo، يرجى تعلمها بنفسك أولاً.
توثيق mongodb: Introduction to MongoDB — MongoDB Manual
المشغل
المكتبات الخاصة بـ mongodb في go قليلة نسبيًا، وفي وقت مبكر كانت هناك بعض المكتبات التي ي维护ها المجتمع، لكنها توقفت عن الصيانة لاحقًا، لكن مشغل mongo الرسمي كافٍ تمامًا للاستخدام.
المستودع مفتوح المصدر: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)
عنوان الوثائق: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)
التثبيت
لتحميل التبعية استخدم العنوان التالي.
$ go get go.mongodb.org/mongo-driver/mongoالاتصال
فيما يلي مثال بسيط لإنشاء اتصال بين عميل mongo والخادم.
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"log"
)
func main() {
ctx := context.Background()
// استخدام URI لإنشاء الاتصال
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://admin:123456@192.168.48.138:27017/"))
if err != nil {
log.Panicln(err)
}
// إغلاق الاتصال
defer client.Disconnect(ctx)
// اختبار ping للتحقق من صلاحية الاتصال
fmt.Println(client.Ping(ctx, readpref.Primary()))
}bson
يستخدم mongodb الأنواع التالية في go لتحويل وثائق قاعدة البيانات، الموجودة في bson/bson.go
// تمثيل مرتب لوثيقة BSON
type D = primitive.D
// زوج من المفتاح والقيمة، الوحدة الأساسية للتمثيل المرتب لوثيقة BSON
type E = primitive.E
// تمثيل غير مرتب لوثيقة BSON
type M = primitive.M
// تمثيل مرتب لمصفوفة BSON
type A = primitive.Aأنواعها الفعلية كالتالي
// تمثيل مرتب لوثيقة BSON
type D []E
// زوج من المفتاح والقيمة، الوحدة الأساسية للتمثيل المرتب لوثيقة BSON
type E struct {
Key string
Value interface{}
}
// تمثيل غير مرتب لوثيقة BSON
type M map[string]interface{}
// تمثيل مرتب لمصفوفة BSON
type A []interface{}من خلال الأنواع أعلاه، يمكن بناء استعلامات SQL وكذلك تحويل البيانات.
TIP
يحتوي دليل examples في المشغل على الكثير من أمثلة الاستخدام، حيث عرض المسؤول بشكل مفصل كيفية استخدام الأنواع الأربعة المذكورة أعلاه.
الاستعلام عن الوثائق
مثال الاستعلام الرسمي: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
أولاً أنشئ قاعدة بيانات user، وأدخل البيانات التالية في المجموعة users
> use user
> db.users.insertMany([
{
name: "mike",
age: 12,
},
{
name: "jenny",
age: 14,
},
{
name: "jack",
age: 18,
address: "usa"
}
])الاستعلام عن مستند واحد
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
Address string `bson:"address"`
}
var user User
result := client.Database("user"). // اختيار قاعدة البيانات
Collection("users"). // اختيار المجموعة
FindOne(ctx, bson.D{{"address", "usa"}}) // شرط التصفية
// إزالة التسلسل
if err := result.Decode(&user); err != nil {
log.Panicln(err)
}
fmt.Printf("%+v\n", user)كود الاستعلام أعلاه يكافئ
db.users.findOne({
address: "usa"
})نتيجة الإخراج
{Name:jack Age:18 Address:usa}الاستعلام عن عدة مستندات
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
Address string `bson:"address"`
}
var users []User
cursor, err := client.Database("user"). // اختيار قاعدة البيانات
Collection("users"). // اختيار المجموعة
Find(ctx, bson.D{}) // شرط التصفية
if err != nil {
log.Panicln(err)
}
if err := cursor.All(ctx, &users); err != nil {
log.Panicln(err)
}
fmt.Printf("%+v\n", users)يكافئ
db.users.find({})المخرجات
[{Name:jack Age:18 Address:usa} {Name:mike Age:12 Address:} {Name:jenny Age:14 Address:}]عند بناء شروط الاستعلام، يمكن أيضًا استخدام options
type User struct {
Name string `bson:"name"`
Age int `bson:"age"`
Address string `bson:"address"`
}
var users []User
find := options.Find()
find.SetSort(bson.M{"age": 1})
find.SetLimit(1)
cursor, err := client.Database("user"). // اختيار قاعدة البيانات
Collection("users"). // اختيار المجموعة
Find(ctx, bson.D{}, find) // شرط التصفية
if err != nil {
log.Panicln(err)
}
if err := cursor.All(ctx, &users); err != nil {
log.Panicln(err)
}
fmt.Printf("%+v\n", users)يكافئ
db.users.find({}).sort({age:1}).limit(1)المخرجات
[{Name:mike Age:12 Address:}]إنشاء المستندات
مثال الإنشاء الرسمي: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
فيما يلي مثال لإنشاء مستند واحد
one, err := client.Database("user").Collection("users").InsertOne(ctx, User{
Name: "lili",
Age: 20,
Address: "china",
})
if err != nil {
log.Panicln(err)
}
fmt.Println(one.InsertedID)بعد الإنشاء الناجح سيتم إرجاع ObjectID للمستند
ObjectID("64c60fa01e2548d9e4de6cf4")فيما يلي مثال لإنشاء عدة مستندات
users := []any{User{
Name: "john",
Age: 10,
Address: "usa",
}, User{
Name: "pop",
Age: 30,
Address: "uk",
}}
one, err := client.Database("user").Collection("users").InsertMany(ctx, users)
if err != nil {
log.Panicln(err)
}
fmt.Println(one.InsertedIDs)بعد الإنشاء الناجح يتم إرجاع مجموعة من ObjectID
[ObjectID("64c610d5aec2618d6ca0b515") ObjectID("64c610d5aec2618d6ca0b516")]الكودان أعلاه يكافئان db.users.insertOne و db.users.insertMany.
تحديث المستندات
مثال التحديث الرسمي: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
فيما يلي مثال لتحديث مستند واحد، تغيير اسم الشخص المسمى lili إلى mark
upres, err := client.Database("user").Collection("users").UpdateOne(ctx, bson.D{
{"name", "mark"},
},
bson.D{
{"$set", bson.D{
{"name", "lili"},
}},
})
if err != nil {
log.Panicln(err)
}
fmt.Printf("%+v", upres)يكافئ
db.users.updateOne({
name: "lili"
}, {
$set: {
name: "mark"
},
})المخرجات
&{MatchedCount:1 ModifiedCount:1 UpsertedCount:0 UpsertedID:<nil>}فيما يلي مثال لتحديث عدة مستندات، تحديث عنوان الأشخاص الذين أعمارهم 10 إلى cn
upres, err := client.Database("user").Collection("users").UpdateMany(ctx, bson.D{
{"age", 10},
},
bson.D{
{"$set", bson.D{
{"address", "cn"},
}},
})
if err != nil {
log.Panicln(err)
}
fmt.Printf("%+v", upres)بالإضافة إلى استخدام Update، يوفر mongo أيضًا Replace، والفرق بينهما أن الأول يقوم بتحديث حقول المستند، بينما الثاني يستبدل المستند بالكامل. مثلاً في الكود التالي، لا حاجة لاستخدام عوامل التشغيل.
upres, err := client.Database("user").Collection("users").ReplaceOne(ctx, bson.D{
{"age", 10},
},
bson.D{
{"address", "cn"},
})
if err != nil {
log.Panicln(err)
}
fmt.Printf("%+v", upres)كما يوفر mongo أيضًا FindOneAndUpdate و FindOneAndReplace للحصول على المستند وتحديثه. كالتالي
res := client.Database("user").Collection("users").FindOneAndReplace(ctx, bson.D{
{"address", "cn"},
},
bson.D{
{"address", "uk"},
})
if err := res.Err(); err != nil {
log.Panicln(err)
}
var user User
res.Decode(&user)
fmt.Printf("%+v", user)المخرجات
Name: Age:0 Address:cn}هذه العملية تقوم أولاً بالاستعلام عن المستند ثم تعديله.
حذف المستندات
مثال الحذف الرسمي: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
فيما يلي مثال لحذف مستند واحد
result, err := client.Database("user").Collection("users").DeleteOne(ctx, bson.D{
{"name", "jack"},
})
if err != nil {
log.Panicln(err)
}
fmt.Println(result.DeletedCount)فيما يلي مثال لحذف عدة مستندات
result, err := client.Database("user").Collection("users").DeleteMany(ctx, bson.D{
{"age", "10"},
})
if err != nil {
log.Panicln(err)
}
fmt.Println(result.DeletedCount)التجميع
مثال التجميع الرسمي: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)
تستخدم عملية التجميع نوع mongo.Pipeline، وهو في الأساس []bson.D
type Pipeline []bson.Dpipline := mongo.Pipeline{
{
{"$match", bson.D{
{"address", "uk"},
}},
},
{
{"$sort", bson.D{
{"age", 1},
}},
},
}
aggregate, err := client.Database("user").Collection("users").Aggregate(ctx, pipline)
if err != nil {
log.Panicln(err)
}
var users []User
if err := aggregate.All(ctx, &users); err != nil {
log.Panicln(err)
}
log.Println(users)المخرجات
[{lili 20 uk} {kak 30 uk}]هذه العملية التجميعية تقوم بمطابقة جميع المستخدمين الذين عنوانهم uk، ثم ترتيبهم حسب العمر.
