Nedb
Cơ Sở Dữ Liệu JavaScript
⚠️ ⚠️ ⚠️ CẢNH BÁO: Thư viện này không còn được duy trì nữa và có thể có lỗi và vấn đề về bảo mật. Hãy thoải mái fork nhưng không có pull request hoặc cảnh báo bảo mật sẽ được trả lời.
Cơ sở dữ liệu nhúng hoặc trong bộ nhớ cho Node.js, nw.js, Electron và trình duyệt, 100% JavaScript, không phụ thuộc vào mã nhị phân. API là một phần của MongoDB và nó rất nhanh chóng.
LƯU Ý QUAN TRỌNG: Xin đừng gửi vấn đề về câu hỏi liên quan đến mã của bạn. Chỉ các lỗi thực tế hoặc yêu cầu tính năng mới sẽ được trả lời, tất cả những vấn đề khác sẽ bị đóng mà không có bình luận. Hãy tuân theo hướng dẫn báo cáo lỗi và kiểm tra change log trước khi gửi một lỗi đã được sửa lại 🙂
Cài Đặt, Kiểm Tra
Tên mô-đun trên npm và bower là nedb
.
npm install nedb --save # Put latest version in your package.json
npm test # You'll need the dev dependencies to launch tests
bower install nedb # For the browser versions, which will be in browser-version/out
API
Đó là một phần của API của MongoDB (các hoạt động được sử dụng nhiều nhất).
- Tạo/tải một cơ sở dữ liệu
- Sự bền vững
- Chèn tài liệu
- Tìm tài liệu
- Truy vấn Cơ Bản
- Toán Tử ($lt, $lte, $gt, $gte, $in, $nin, $ne, $exists, $regex)
- Trường Mảng
- Toán tử logic $or, $and, $not, $where
- Sắp xếp và phân trang
- Chiếu
- Đếm tài liệu
- Cập nhật tài liệu
- Xóa tài liệu
- Tạo chỉ mục
- Phiên bản trình duyệt
Tạo/tải một cơ sở dữ liệu
Bạn có thể sử dụng NeDB như một cơ sở dữ liệu chỉ tồn tại trong bộ nhớ hoặc cơ sở dữ liệu tồn tại. Một cơ sở dữ liệu là tương đương của một bộ sưu tập MongoDB. Constructor được sử dụng như sau new Datastore(options)
trong đó options
là một đối tượng với các trường sau đây:
filename
(tùy chọn): đường dẫn đến tệp nơi dữ liệu được lưu trữ. Nếu để trống, cơ sở dữ liệu sẽ tự động được xem xét chỉ tồn tại trong bộ nhớ. Không được kết thúc bằng~
được sử dụng trong các tệp tạm thời NeDB sử dụng để thực hiện ghi an toàn tránh sự cố.inMemoryOnly
(tùy chọn, mặc định làfalse
): như tên gọi của nó.timestampData
(tùy chọn, mặc định làfalse
): ghi dấu thời gian cho việc chèn và cập nhật cuối cùng của tất cả tài liệu, với các trườngcreatedAt
vàupdatedAt
. Giá trị do người dùng chỉ định sẽ ghi đè lên việc tạo tự động, thường hữu ích cho việc kiểm tra.autoload
(tùy chọn, mặc định làfalse
): nếu được sử dụng, cơ sở dữ liệu sẽ tự động được nạp từ tệp dữ liệu khi tạo (bạn không cần gọiloadDatabase
). Bất kỳ lệnh nào được gửi trước khi quá trình nạp hoàn tất đều được lưu vào bộ đệm và sẽ được thực hiện khi nạp hoàn tất.onload
(tùy chọn): nếu bạn sử dụng tính năng tự động nạp, đây là trình xử lý được gọi sau khiloadDatabase
. Nó nhận một đối sốerror
. Nếu bạn sử dụng tính năng tự động nạp mà không chỉ định trình xử lý này, và một lỗi xảy ra trong quá trình nạp, sẽ có một lỗi được ném.afterSerialization
(tùy chọn): hook bạn có thể sử dụng để biến đổi dữ liệu sau khi nó được chuyển thành dạng được lưu và trước khi nó được ghi vào đĩa. Có thể sử dụng ví dụ để mã hóa dữ liệu trước khi ghi vào cơ sở dữ liệu trên đĩa. Hàm này nhận một chuỗi như tham số (một dòng trong tệp dữ liệu NeDB) và xuất chuỗi đã biến đổi, điều này tuyệt đối không được chứa ký tự**\n**
(hoặc dữ liệu sẽ bị mất).beforeDeserialization
(tùy chọn): nghịch đảo củaafterSerialization
. Đảm bảo bao gồm cả hai và không chỉ một trong chúng, nếu không bạn có nguy cơ mất dữ liệu. Vì cùng một lý do, hãy đảm bảo cả hai hàm đều là nghịch đảo của nhau. Có một số cơ chế an toàn để ngăn mất dữ liệu nếu bạn sử dụng các kết nối nối dữ liệu một cách sai lầm: NeDB kiểm tra rằng không bao giờ có một cái được khai báo mà không có cái kia và kiểm tra rằng chúng nghịch đảo của nhau bằng cách kiểm tra trên các chuỗi ngẫu nhiên có độ dài khác nhau. Ngoài ra, nếu phát hiện quá nhiều dữ liệu bị hỏng, NeDB sẽ từ chối khởi động vì có thể có nghĩa là bạn không sử dụng kết nối nối dữ liệu phù hợp với kết nối nối dữ liệu đã sử dụng trước đó (xem bên dưới).corruptAlertThreshold
(tùy chọn): từ 0 đến 1, mặc định là 10%. NeDB sẽ từ chối khởi động nếu nhiều hơn tỷ lệ này của tệp dữ liệu bị hỏng. 0 có nghĩa là bạn không dung thứ chất lượng bị hỏng nào, 1 có nghĩa là bạn không quan tâm.compareStrings
(tùy chọn): hàm compareStrings(a, b) so sánh các chuỗi a và b và trả về -1, 0 hoặc 1. Nếu được chỉ định, nó sẽ ghi đè lên việc so sánh chuỗi mặc định, không phải là lựa chọn tốt cho các ký tự không phải là tiếng Anh, đặc biệt là chữ có dấu. Thường thì việc sử dụnglocalCompare
trong native sẽ là lựa chọn đúng đắn hầu hết các trường hợp.nodeWebkitAppName
(tùy chọn, ĐÃ LỖI THỜI ): nếu bạn đang sử dụng NeDB trong ứng dụng Node Webkit, hãy chỉ định tên của nó (giống như bạn sử dụng trongpackage.json
) trong trường này vàfilename
sẽ được tính từ thư mục mà Node Webkit sử dụng để lưu trữ các dữ liệu khác của ứng dụng (bộ nhớ cục bộ vv.). Nó hoạt động trên Linux, OS X và Windows. Bây giờ bạn có thể sử dụngrequire('nw.gui').App.dataPath
trong Node Webkit để lấy đường dẫn đến thư mục dữ liệu cho ứng dụng của bạn, bạn không nên sử dụng tùy chọn này nữa và nó sẽ bị loại bỏ.
Nếu bạn sử dụng một cơ sở dữ liệu có tính liên tục mà không có tùy chọn autoload
, bạn cần gọi loadDatabase
bằng cách thủ công. Hàm này lấy dữ liệu từ tệp dữ liệu và chuẩn bị cơ sở dữ liệu. Đừng quên điều này! Nếu bạn sử dụng cơ sở dữ liệu có tính liên tục, không có lệnh nào (chèn, tìm kiếm, cập nhật, xóa) sẽ được thực hiện trước khi gọi loadDatabase
, vì vậy hãy đảm bảo tự gọi nó hoặc sử dụng tùy chọn autoload
.
Ngoài ra, nếu loadDatabase
thất bại, tất cả các lệnh đã đăng ký vào bộ thực thi sau đó sẽ không được thực hiện. Chúng sẽ được đăng ký và thực hiện, theo trình tự, chỉ sau khi loadDatabase
thành công.
// Type 1: In-memory only datastore (no need to load the database)
var Datastore = require('nedb')
, db = new Datastore();
// Type 2: Persistent datastore with manual loading
var Datastore = require('nedb')
, db = new Datastore({ filename: 'path/to/datafile' });
db.loadDatabase(function (err) { // Callback is optional
// Now commands will be executed
});
// Type 3: Persistent datastore with automatic loading
var Datastore = require('nedb')
, db = new Datastore({ filename: 'path/to/datafile', autoload: true });
// You can issue commands right away
// Type 4: Persistent datastore for a Node Webkit app called 'nwtest'
// For example on Linux, the datafile will be ~/.config/nwtest/nedb-data/something.db
var Datastore = require('nedb')
, path = require('path')
, db = new Datastore({ filename: path.join(require('nw.gui').App.dataPath, 'something.db') });
// Of course you can create multiple datastores if you need several
// collections. In this case it's usually a good idea to use autoload for all collections.
db = {};
db.users = new Datastore('path/to/users.db');
db.robots = new Datastore('path/to/robots.db');
// You need to load each database (here we do it asynchronously)
db.users.loadDatabase();
db.robots.loadDatabase();
Bền vững
Dưới nền, tính bền vững của NeDB sử dụng định dạng chỉ ghi thêm, có nghĩa là tất cả các cập nhật và xóa thực tế là kết quả của việc thêm dòng vào cuối tệp dữ liệu, vì lý do hiệu suất. Cơ sở dữ liệu sẽ tự động được nén lại (tức là đưa lại định dạng một dòng cho mỗi tài liệu) mỗi khi bạn tải cơ sở dữ liệu trong ứng dụng của bạn.
Bạn có thể gọi thủ công hàm nén với yourDatabase.persistence.compactDatafile
không cần đối số. Nó xếp hàng một quá trình nén tệp dữ liệu trong bộ thực thi, được thực hiện tuần tự sau tất cả các hoạt động đang chờ. Datastore sẽ phát ra sự kiện compaction.done
sau khi quá trình nén hoàn thành.
Bạn cũng có thể đặt nén tự động ở các khoảng thời gian đều đặn với yourDatabase.persistence.setAutocompactionInterval(interval)
, interval
tính bằng mili giây (tối thiểu 5 giây được bắt buộc), và dừng nén tự động với yourDatabase.persistence.stopAutocompaction()
.
Hãy nhớ rằng quá trình nén mất một chút thời gian (không nhiều: 130ms cho 50k bản ghi trên máy phát triển điển hình) và không có hoạt động khác có thể xảy ra khi nó diễn ra, nên hầu hết các dự án thực tế không cần sử dụng nó.
Nén cũng sẽ ngay lập tức loại bỏ bất kỳ tài liệu nào dữ liệu của chúng đã bị hỏng, giả sử tỷ lệ tổng của tất cả tài liệu bị hỏng trong cơ sở dữ liệu đó vẫn còn thấp hơn giá trị tùy chọn corruptAlertThreshold
.
Tính bền vững hoạt động tương tự như các cơ sở dữ liệu lớn: quá trình nén buộc hệ điều hành phải ghi dữ liệu xuống đĩa cứng, trong khi các thao tác thêm vào tệp dữ liệu thì không (hệ điều hành chịu trách nhiệm ghi dữ liệu). Điều này đảm bảo rằng một sự cố máy chủ không thể gây mất dữ liệu hoàn toàn, đồng thời bảo toàn hiệu suất. Tệ nhất có thể xảy ra là một sự cố xảy ra giữa hai lần đồng bộ hóa, gây mất dữ liệu giữa hai lần đồng bộ hóa đó. Thông thường, đồng bộ hóa xảy ra cách nhau 30 giây, vì vậy tối đa là 30 giây dữ liệu. This post by Antirez on Redis persistence giải thích điều này chi tiết hơn, NeDB gần giống với tính bền vững Redis AOF với tùy chọn appendfsync
được đặt thành no
.
Chèn tài liệu
Các loại dữ liệu nguyên thủy bao gồm String
, Number
, Boolean
, Date
và null
. Bạn cũng có thể sử dụng mảng và tài liệu con (đối tượng). Nếu một trường là undefined
, nó sẽ không được lưu trữ (điều này khác với MongoDB, nơi undefined
được chuyển đổi thành null
, điều này tôi thấy khá khó hiểu).
Nếu tài liệu không chứa trường _id
, NeDB sẽ tự động tạo một _id
cho bạn (là một chuỗi chữ số và chữ cái có 16 ký tự). _id
của một tài liệu, sau khi được đặt, không thể thay đổi.
Tên trường không thể bắt đầu bằng ‘$’ hoặc chứa dấu ‘.’.
var doc = { hello: 'world'
, n: 5
, today: new Date()
, nedbIsAwesome: true
, notthere: null
, notToBeSaved: undefined // Will not be saved
, fruits: [ 'apple', 'orange', 'pear' ]
, infos: { name: 'nedb' }
};
db.insert(doc, function (err, newDoc) { // Callback is optional
// newDoc is the newly inserted document, including its _id
// newDoc has no key called notToBeSaved since its value was undefined
});
Bạn cũng có thể chèn nhiều tài liệu một lúc bằng mảng. Thao tác này là nguyên tử, có nghĩa là nếu một lần chèn thất bại do vi phạm ràng buộc duy nhất, tất cả các thay đổi sẽ bị quay trở lại.
db.insert([{ a: 5 }, { a: 42 }], function (err, newDocs) {
// Two documents were inserted in the database
// newDocs is an array with these documents, augmented with their _id
});
// If there is a unique constraint on field 'a', this will fail
db.insert([{ a: 5 }, { a: 42 }, { a: 5 }], function (err) {
// err is a 'uniqueViolated' error
// The database was not modified
});
Tìm tài liệu
Sử dụng find
để tìm kiếm nhiều tài liệu phù hợp với truy vấn của bạn, hoặc findOne
để tìm kiếm một tài liệu cụ thể. Bạn có thể lựa chọn tài liệu dựa trên sự bằng nhau của trường hoặc sử dụng các toán tử so sánh ($lt
, $lte
, $gt
, $gte
, $in
, $nin
, $ne
). Bạn cũng có thể sử dụng các toán tử logic $or
, $and
, $not
và $where
. Xem bên dưới để biết cú pháp.
Bạn có thể sử dụng biểu thức chính quy theo hai cách: trong truy vấn cơ bản thay vì một chuỗi, hoặc với toán tử $regex
.
Bạn có thể sắp xếp và phân trang kết quả bằng cách sử dụng API con trỏ (xem bên dưới).
Bạn có thể sử dụng các chiếu theo tiêu chuẩn để hạn chế các trường xuất hiện trong kết quả (xem bên dưới).
Truy vấn cơ bản
Truy vấn cơ bản có nghĩa là bạn đang tìm kiếm các tài liệu có trường khớp với những trường bạn chỉ định. Bạn có thể sử dụng biểu thức chính quy để khớp chuỗi. Bạn có thể sử dụng chú thích dấu chấm để điều hướng trong các tài liệu lồng nhau, mảng, mảng của các tài liệu con và để khớp một phần tử cụ thể của một mảng.
// Let's say our datastore contains the following collection
// { _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false, satellites: ['Phobos', 'Deimos'] }
// { _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true, humans: { genders: 2, eyes: true } }
// { _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false }
// { _id: 'id4', planet: 'Omicron Persei 8', system: 'futurama', inhabited: true, humans: { genders: 7 } }
// { _id: 'id5', completeData: { planets: [ { name: 'Earth', number: 3 }, { name: 'Mars', number: 2 }, { name: 'Pluton', number: 9 } ] } }
// Finding all planets in the solar system
db.find({ system: 'solar' }, function (err, docs) {
// docs is an array containing documents Mars, Earth, Jupiter
// If no document is found, docs is equal to []
});
// Finding all planets whose name contain the substring 'ar' using a regular expression
db.find({ planet: /ar/ }, function (err, docs) {
// docs contains Mars and Earth
});
// Finding all inhabited planets in the solar system
db.find({ system: 'solar', inhabited: true }, function (err, docs) {
// docs is an array containing document Earth only
});
// Use the dot-notation to match fields in subdocuments
db.find({ "humans.genders": 2 }, function (err, docs) {
// docs contains Earth
});
// Use the dot-notation to navigate arrays of subdocuments
db.find({ "completeData.planets.name": "Mars" }, function (err, docs) {
// docs contains document 5
});
db.find({ "completeData.planets.name": "Jupiter" }, function (err, docs) {
// docs is empty
});
db.find({ "completeData.planets.0.name": "Earth" }, function (err, docs) {
// docs contains document 5
// If we had tested against "Mars" docs would be empty because we are matching against a specific array element
});
// You can also deep-compare objects. Don't confuse this with dot-notation!
db.find({ humans: { genders: 2 } }, function (err, docs) {
// docs is empty, because { genders: 2 } is not equal to { genders: 2, eyes: true }
});
// Find all documents in the collection
db.find({}, function (err, docs) {
});
// The same rules apply when you want to only find one document
db.findOne({ _id: 'id1' }, function (err, doc) {
// doc is the document Mars
// If no document is found, doc is null
});
Toán tử ($lt, $lte, $gt, $gte, $in, $nin, $ne, $exists, $regex)
Cú pháp là { trường: { $op: giá trị } }
trong đó $op
là bất kỳ toán tử so sánh nào:
$lt
,$lte
: nhỏ hơn, nhỏ hơn hoặc bằng$gt
,$gte
: lớn hơn, lớn hơn hoặc bằng$in
: thành viên của.giá trị
phải là một mảng giá trị$ne
,$nin
: không bằng, không phải là thành viên của$exists
: kiểm tra xem tài liệu có thuộc tínhtrường
không.giá trị
nên là true hoặc false-
$regex
: kiểm tra xem một chuỗi có khớp với biểu thức chính quy hay không. Khác với MongoDB, việc sử dụng$options
với$regex
không được hỗ trợ, vì nó không mang lại sức mạnh hơn các cờ biểu thức chính quy. Truy vấn cơ bản dễ đọc hơn, vì vậy chỉ sử dụng toán tử$regex
khi bạn cần sử dụng một toán tử khác với nó (xem ví dụ bên dưới)// $lt, $lte, $gt and $gte work on numbers and strings
db.find({ “humans.genders”: { $gt: 5 } }, function (err, docs) {
// docs contains Omicron Persei 8, whose humans have more than 5 genders (7).
});// When used with strings, lexicographical order is used
db.find({ planet: { $gt: ‘Mercury’ }}, function (err, docs) {
// docs contains Omicron Persei 8
})// Using $in. $nin is used in the same way
db.find({ planet: { $in: [‘Earth’, ‘Jupiter’] }}, function (err, docs) {
// docs contains Earth and Jupiter
});// Using $exists
db.find({ satellites: { $exists: true } }, function (err, docs) {
// docs contains only Mars
});// Using $regex with another operator
db.find({ planet: { $regex: /ar/, $nin: [‘Jupiter’, ‘Earth’] } }, function (err, docs) {
// docs only contains Mars because Earth was excluded from the match by $nin
});
Trường mảng
Khi một trường trong tài liệu là một mảng, NeDB trước tiên thử xem giá trị truy vấn có phải là một mảng để thực hiện sự khớp chính xác, sau đó xem xét xem có một hàm so sánh cụ thể cho mảng (hiện tại chỉ có $size
và $elemMatch
) đang được sử dụng. Nếu không, truy vấn được xem xét như một truy vấn trên mọi phần tử và có một sự khớp nếu ít nhất một phần tử khớp.
$size
: khớp theo kích thước của mảng-
$elemMatch
: khớp nếu ít nhất một phần tử của mảng khớp hoàn toàn với truy vấn// Exact match
db.find({ satellites: [‘Phobos’, ‘Deimos’] }, function (err, docs) {
// docs contains Mars
})
db.find({ satellites: [‘Deimos’, ‘Phobos’] }, function (err, docs) {
// docs is empty
})// Using an array-specific comparison function
// $elemMatch operator will provide match for a document, if an element from the array field satisfies all the conditions specified with the$elemMatch
operator
db.find({ completeData: { planets: { $elemMatch: { name: ‘Earth’, number: 3 } } } }, function (err, docs) {
// docs contains documents with id 5 (completeData)
});db.find({ completeData: { planets: { $elemMatch: { name: ‘Earth’, number: 5 } } } }, function (err, docs) {
// docs is empty
});// You can use inside #elemMatch query any known document query operator
db.find({ completeData: { planets: { $elemMatch: { name: ‘Earth’, number: { $gt: 2 } } } } }, function (err, docs) {
// docs contains documents with id 5 (completeData)
});// Note: you can’t use nested comparison functions, e.g. { $size: { $lt: 5 } } will throw an error
db.find({ satellites: { $size: 2 } }, function (err, docs) {
// docs contains Mars
});db.find({ satellites: { $size: 1 } }, function (err, docs) {
// docs is empty
});// If a document’s field is an array, matching it means matching any element of the array
db.find({ satellites: ‘Phobos’ }, function (err, docs) {
// docs contains Mars. Result would have been the same if query had been { satellites: ‘Deimos’ }
});// This also works for queries that use comparison operators
db.find({ satellites: { $lt: ‘Amos’ } }, function (err, docs) {
// docs is empty since Phobos and Deimos are after Amos in lexicographical order
});// This also works with the $in and $nin operator
db.find({ satellites: { $in: [‘Moon’, ‘Deimos’] } }, function (err, docs) {
// docs contains Mars (the Earth document is not complete!)
});
Các toán tử logic $or, $and, $not, $where
Bạn có thể kết hợp các truy vấn bằng cách sử dụng các toán tử logic:
- Đối với
$or
và$and
, cú pháp là{ $op: [truy vấn1, truy vấn2, ...] }
. - Đối với
$not
, cú pháp là{ $not: truy vấn }
-
Đối với
$where
, cú pháp là{ $where: function () { /* đối tượng là "this", trả về một giá trị boolean */ } }
db.find({ $or: [{ planet: ‘Earth’ }, { planet: ‘Mars’ }] }, function (err, docs) {
// docs contains Earth and Mars
});db.find({ $not: { planet: ‘Earth’ } }, function (err, docs) {
// docs contains Mars, Jupiter, Omicron Persei 8
});db.find({ $where: function () { return Object.keys(this) > 6; } }, function (err, docs) {
// docs with more than 6 properties
});// You can mix normal queries, comparison queries and logical operators
db.find({ $or: [{ planet: ‘Earth’ }, { planet: ‘Mars’ }], inhabited: true }, function (err, docs) {
// docs contains Earth
});
Sắp xếp và phân trang
Nếu bạn không chỉ định một hàm gọi lại cho find
, findOne
hoặc count
, một đối tượng Cursor
được trả về. Bạn có thể sửa đổi con trỏ bằng cách sử dụng sort
, skip
và limit
sau đó thực thi nó bằng exec(callback)
.
// Let's say the database contains these 4 documents
// doc1 = { _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false, satellites: ['Phobos', 'Deimos'] }
// doc2 = { _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true, humans: { genders: 2, eyes: true } }
// doc3 = { _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false }
// doc4 = { _id: 'id4', planet: 'Omicron Persei 8', system: 'futurama', inhabited: true, humans: { genders: 7 } }
// No query used means all results are returned (before the Cursor modifiers)
db.find({}).sort({ planet: 1 }).skip(1).limit(2).exec(function (err, docs) {
// docs is [doc3, doc1]
});
// You can sort in reverse order like this
db.find({ system: 'solar' }).sort({ planet: -1 }).exec(function (err, docs) {
// docs is [doc1, doc3, doc2]
});
// You can sort on one field, then another, and so on like this:
db.find({}).sort({ firstField: 1, secondField: -1 }) ... // You understand how this works!
Chiếu dự án
Bạn có thể cung cấp cho find
và findOne
một đối số thứ hai tùy chọn, projections
. Cú pháp tương tự như MongoDB: { a: 1, b: 1 }
để chỉ trả lại các trường a
và b
, { a: 0, b: 0 }
để bỏ qua hai trường này. Bạn không thể sử dụng cả hai chế độ cùng một lúc, ngoại trừ _id
mặc định luôn được trả về và bạn có thể chọn bỏ qua. Bạn có thể chiếu dự án trên các tài liệu lồng nhau.
// Same database as above
// Keeping only the given fields
db.find({ planet: 'Mars' }, { planet: 1, system: 1 }, function (err, docs) {
// docs is [{ planet: 'Mars', system: 'solar', _id: 'id1' }]
});
// Keeping only the given fields but removing _id
db.find({ planet: 'Mars' }, { planet: 1, system: 1, _id: 0 }, function (err, docs) {
// docs is [{ planet: 'Mars', system: 'solar' }]
});
// Omitting only the given fields and removing _id
db.find({ planet: 'Mars' }, { planet: 0, system: 0, _id: 0 }, function (err, docs) {
// docs is [{ inhabited: false, satellites: ['Phobos', 'Deimos'] }]
});
// Failure: using both modes at the same time
db.find({ planet: 'Mars' }, { planet: 0, system: 1 }, function (err, docs) {
// err is the error message, docs is undefined
});
// You can also use it in a Cursor way but this syntax is not compatible with MongoDB
db.find({ planet: 'Mars' }).projection({ planet: 1, system: 1 }).exec(function (err, docs) {
// docs is [{ planet: 'Mars', system: 'solar', _id: 'id1' }]
});
// Project on a nested document
db.findOne({ planet: 'Earth' }).projection({ planet: 1, 'humans.genders': 1 }).exec(function (err, doc) {
// doc is { planet: 'Earth', _id: 'id2', humans: { genders: 2 } }
});
Đếm tài liệu
Bạn có thể sử dụng count
để đếm tài liệu. Nó có cú pháp giống như find
. Ví dụ:
// Count all planets in the solar system
db.count({ system: 'solar' }, function (err, count) {
// count equals to 3
});
// Count all documents in the datastore
db.count({}, function (err, count) {
// count equals to 4
});
Cập nhật tài liệu
db.update(query, update, options, callback)
sẽ cập nhật tất cả các tài liệu khớp với query
theo quy tắc update
:
query
là loại truy vấn tìm kiếm giống như bạn sử dụng vớifind
vàfindOne
update
chỉ định cách tài liệu sẽ được sửa đổi. Đó có thể là một tài liệu mới hoặc một tập hợp các bộ biến đổi (bạn không thể sử dụng cả hai cùng nhau, điều này không hợp lý!)- Một tài liệu mới sẽ thay thế các tài liệu khớp
- Các bộ biến đổi tạo ra các trường cần thiết để sửa đổi nếu chúng không tồn tại, và bạn có thể áp dụng chúng cho các tài liệu con. Các bộ biến đổi trường có sẵn bao gồm
$set
để thay đổi giá trị của một trường,$unset
để xóa một trường,$inc
để tăng giá trị của một trường và$min
/$max
để thay đổi giá trị của trường, chỉ khi giá trị được cung cấp ít hơn/lớn hơn giá trị hiện tại. Để làm việc trên các mảng, bạn có$push
,$pop
,$addToSet
,$pull
, và đặc biệt là$each
và$slice
. Xem ví dụ dưới đây về cú pháp. options
là một đối tượng với hai tham số có thểmulti
(mặc định làfalse
) cho phép sửa đổi nhiều tài liệu nếu được đặt thành trueupsert
(mặc định làfalse
) nếu bạn muốn chèn một tài liệu mới tương ứng với quy tắcupdate
nếuquery
của bạn không khớp với bất kỳ thứ gì. Nếuupdate
của bạn là một đối tượng đơn giản không có bộ biến đổi, đó là tài liệu được chèn. Trong trường hợp khác,query
sẽ bị loại bỏ từ tất cả các toán tử theo cách đệ quy, vàupdate
sẽ được áp dụng vào nó.returnUpdatedDocs
(mặc định làfalse
, không tương thích với MongoDB) nếu được đặt thành true và cập nhật không phải là upsert, sẽ trả về mảng các tài liệu khớp với truy vấn tìm kiếm và đã được cập nhật. Tài liệu đã được cập nhật sẽ được trả về ngay cả khi cập nhật thực sự không làm thay đổi chúng.callback
(tùy chọn) chữ ký:(err, numAffected, affectedDocuments, upsert)
. Cảnh báo: API đã thay đổi giữa phiên bản 1.7.4 và 1.8. Vui lòng tham khảo change log để xem sự thay đổi.- Đối với upsert,
affectedDocuments
chứa tài liệu được chèn và cờupsert
được đặt thànhtrue
. - Đối với cập nhật tiêu chuẩn với cờ
returnUpdatedDocs
được đặt thànhfalse
,affectedDocuments
không được đặt. - Đối với cập nhật tiêu chuẩn với cờ
returnUpdatedDocs
được đặt thànhtrue
vàmulti
thànhfalse
,affectedDocuments
là tài liệu đã được cập nhật. - Đối với cập nhật tiêu chuẩn với cờ
returnUpdatedDocs
được đặt thànhtrue
vàmulti
thànhtrue
,affectedDocuments
là mảng các tài liệu đã được cập nhật.
Lưu ý: bạn không thể thay đổi _id của một tài liệu.
// Let's use the same example collection as in the "finding document" part
// { _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false }
// { _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true }
// { _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false }
// { _id: 'id4', planet: 'Omicron Persia 8', system: 'futurama', inhabited: true }
// Replace a document by another
db.update({ planet: 'Jupiter' }, { planet: 'Pluton'}, {}, function (err, numReplaced) {
// numReplaced = 1
// The doc #3 has been replaced by { _id: 'id3', planet: 'Pluton' }
// Note that the _id is kept unchanged, and the document has been replaced
// (the 'system' and inhabited fields are not here anymore)
});
// Set an existing field's value
db.update({ system: 'solar' }, { $set: { system: 'solar system' } }, { multi: true }, function (err, numReplaced) {
// numReplaced = 3
// Field 'system' on Mars, Earth, Jupiter now has value 'solar system'
});
// Setting the value of a non-existing field in a subdocument by using the dot-notation
db.update({ planet: 'Mars' }, { $set: { "data.satellites": 2, "data.red": true } }, {}, function () {
// Mars document now is { _id: 'id1', system: 'solar', inhabited: false
// , data: { satellites: 2, red: true }
// }
// Not that to set fields in subdocuments, you HAVE to use dot-notation
// Using object-notation will just replace the top-level field
db.update({ planet: 'Mars' }, { $set: { data: { satellites: 3 } } }, {}, function () {
// Mars document now is { _id: 'id1', system: 'solar', inhabited: false
// , data: { satellites: 3 }
// }
// You lost the "data.red" field which is probably not the intended behavior
});
});
// Deleting a field
db.update({ planet: 'Mars' }, { $unset: { planet: true } }, {}, function () {
// Now the document for Mars doesn't contain the planet field
// You can unset nested fields with the dot notation of course
});
// Upserting a document
db.update({ planet: 'Pluton' }, { planet: 'Pluton', inhabited: false }, { upsert: true }, function (err, numReplaced, upsert) {
// numReplaced = 1, upsert = { _id: 'id5', planet: 'Pluton', inhabited: false }
// A new document { _id: 'id5', planet: 'Pluton', inhabited: false } has been added to the collection
});
// If you upsert with a modifier, the upserted doc is the query modified by the modifier
// This is simpler than it sounds :)
db.update({ planet: 'Pluton' }, { $inc: { distance: 38 } }, { upsert: true }, function () {
// A new document { _id: 'id5', planet: 'Pluton', distance: 38 } has been added to the collection
});
// If we insert a new document { _id: 'id6', fruits: ['apple', 'orange', 'pear'] } in the collection,
// let's see how we can modify the array field atomically
// $push inserts new elements at the end of the array
db.update({ _id: 'id6' }, { $push: { fruits: 'banana' } }, {}, function () {
// Now the fruits array is ['apple', 'orange', 'pear', 'banana']
});
// $pop removes an element from the end (if used with 1) or the front (if used with -1) of the array
db.update({ _id: 'id6' }, { $pop: { fruits: 1 } }, {}, function () {
// Now the fruits array is ['apple', 'orange']
// With { $pop: { fruits: -1 } }, it would have been ['orange', 'pear']
});
// $addToSet adds an element to an array only if it isn't already in it
// Equality is deep-checked (i.e. $addToSet will not insert an object in an array already containing the same object)
// Note that it doesn't check whether the array contained duplicates before or not
db.update({ _id: 'id6' }, { $addToSet: { fruits: 'apple' } }, {}, function () {
// The fruits array didn't change
// If we had used a fruit not in the array, e.g. 'banana', it would have been added to the array
});
// $pull removes all values matching a value or even any NeDB query from the array
db.update({ _id: 'id6' }, { $pull: { fruits: 'apple' } }, {}, function () {
// Now the fruits array is ['orange', 'pear']
});
db.update({ _id: 'id6' }, { $pull: { fruits: $in: ['apple', 'pear'] } }, {}, function () {
// Now the fruits array is ['orange']
});
// $each can be used to $push or $addToSet multiple values at once
// This example works the same way with $addToSet
db.update({ _id: 'id6' }, { $push: { fruits: { $each: ['banana', 'orange'] } } }, {}, function () {
// Now the fruits array is ['apple', 'orange', 'pear', 'banana', 'orange']
});
// $slice can be used in cunjunction with $push and $each to limit the size of the resulting array.
// A value of 0 will update the array to an empty array. A positive value n will keep only the n first elements
// A negative value -n will keep only the last n elements.
// If $slice is specified but not $each, $each is set to []
db.update({ _id: 'id6' }, { $push: { fruits: { $each: ['banana'], $slice: 2 } } }, {}, function () {
// Now the fruits array is ['apple', 'orange']
});
// $min/$max to update only if provided value is less/greater than current value
// Let's say the database contains this document
// doc = { _id: 'id', name: 'Name', value: 5 }
db.update({ _id: 'id1' }, { $min: { value: 2 } }, {}, function () {
// The document will be updated to { _id: 'id', name: 'Name', value: 2 }
});
db.update({ _id: 'id1' }, { $min: { value: 8 } }, {}, function () {
// The document will not be modified
});
Xóa tài liệu
db.remove(query, options, callback)
sẽ xóa tất cả các tài liệu khớp với query
theo options
query
giống như các truy vấn được sử dụng để tìm kiếm và cập nhậtoptions
chỉ có một tùy chọn hiện tại:multi
cho phép xóa nhiều tài liệu nếu được đặt thành true. Mặc định là false-
callback
là tùy chọn, chữ ký: err, numRemoved// Let’s use the same example collection as in the “finding document” part
// { _id: ‘id1’, planet: ‘Mars’, system: ‘solar’, inhabited: false }
// { _id: ‘id2’, planet: ‘Earth’, system: ‘solar’, inhabited: true }
// { _id: ‘id3’, planet: ‘Jupiter’, system: ‘solar’, inhabited: false }
// { _id: ‘id4’, planet: ‘Omicron Persia 8’, system: ‘futurama’, inhabited: true }// Remove one document from the collection
// options set to {} since the default for multi is false
db.remove({ _id: ‘id2’ }, {}, function (err, numRemoved) {
// numRemoved = 1
});// Remove multiple documents
db.remove({ system: ‘solar’ }, { multi: true }, function (err, numRemoved) {
// numRemoved = 3
// All planets from the solar system were removed
});// Removing all documents with the ‘match-all’ query
db.remove({}, { multi: true }, function (err, numRemoved) {
});
Tạo chỉ mục
NeDB hỗ trợ việc tạo chỉ mục. Nó cung cấp một tăng tốc rất đáng kể và có thể được sử dụng để bắt buộc ràng buộc duy nhất trên một trường. Bạn có thể tạo chỉ mục cho bất kỳ trường nào, bao gồm cả các trường trong tài liệu lồng nhau bằng cách sử dụng ký hiệu chấm. Hiện tại, chỉ mục chỉ được sử dụng để tăng tốc các truy vấn cơ bản và các truy vấn sử dụng $in
, $lt
, $lte
, $gt
và $gte
. Các giá trị đã được tạo chỉ mục không thể là kiểu mảng của đối tượng.
Để tạo chỉ mục, hãy sử dụng datastore.ensureIndex(options, cb)
, trong đó callback là tùy chọn và sẽ nhận được một lỗi nếu có (thường là vi phạm ràng buộc duy nhất). ensureIndex
có thể được gọi bất kỳ lúc nào bạn muốn, ngay cả sau khi đã có một số dữ liệu được chèn, tuy nhiên, tốt nhất là gọi nó khi khởi động ứng dụng. Các tùy chọn bao gồm:
- fieldName (bắt buộc): tên của trường cần tạo chỉ mục. Sử dụng ký hiệu chấm để tạo chỉ mục cho một trường trong một tài liệu lồng nhau.
- unique (tùy chọn, mặc định là
false
): bắt buộc tính duy nhất cho trường. Lưu ý rằng một chỉ mục duy nhất sẽ gây lỗi nếu bạn cố gắng tạo chỉ mục cho hai tài liệu mà trường này không được định nghĩa. - sparse (tùy chọn, mặc định là
false
): không tạo chỉ mục cho các tài liệu mà trường này không được định nghĩa. Sử dụng tùy chọn này cùng với “unique” nếu bạn muốn chấp nhận nhiều tài liệu mà trường này không được định nghĩa. - expireAfterSeconds (số giây, tùy chọn): nếu được đặt, chỉ mục được tạo là chỉ mục TTL (thời gian sống), sẽ tự động xóa các tài liệu khi ngày hệ thống trở nên lớn hơn ngày trên trường được tạo chỉ mục cộng với
expireAfterSeconds
. Các tài liệu mà trường đã được tạo chỉ mục không được chỉ định hoặc không phải là một đối tượngDate
sẽ bị bỏ qua.
Lưu ý: _id
được tạo chỉ mục tự động với ràng buộc duy nhất, không cần gọi ensureIndex
cho nó.
Bạn có thể xóa một chỉ mục đã được tạo trước đó bằng datastore.removeIndex(fieldName, cb)
.
Nếu cơ sở dữ liệu của bạn là bền vững, các chỉ mục bạn đã tạo sẽ được lưu trữ trong tệp dữ liệu, khi bạn tải cơ sở dữ liệu lần thứ hai, chúng sẽ tự động được tạo cho bạn. Không cần phải loại bỏ bất kỳ ensureIndex
nào, nếu nó được gọi trên một cơ sở dữ liệu đã có chỉ mục, không có gì xảy ra.
db.ensureIndex({ fieldName: 'somefield' }, function (err) {
// If there was an error, err is not null
});
// Using a unique constraint with the index
db.ensureIndex({ fieldName: 'somefield', unique: true }, function (err) {
});
// Using a sparse unique index
db.ensureIndex({ fieldName: 'somefield', unique: true, sparse: true }, function (err) {
});
// Format of the error message when the unique constraint is not met
db.insert({ somefield: 'nedb' }, function (err) {
// err is null
db.insert({ somefield: 'nedb' }, function (err) {
// err is { errorType: 'uniqueViolated'
// , key: 'name'
// , message: 'Unique constraint violated for key name' }
});
});
// Remove index on field somefield
db.removeIndex('somefield', function (err) {
});
// Example of using expireAfterSeconds to remove documents 1 hour
// after their creation (db's timestampData option is true here)
db.ensureIndex({ fieldName: 'createdAt', expireAfterSeconds: 3600 }, function (err) {
});
// You can also use the option to set an expiration date like so
db.ensureIndex({ fieldName: 'expirationDate', expireAfterSeconds: 0 }, function (err) {
// Now all documents will expire when system time reaches the date in their
// expirationDate field
});
Lưu ý: Hàm ensureIndex
tạo chỉ mục một cách đồng bộ, vì vậy tốt nhất là sử dụng nó khi khởi động ứng dụng. Nó khá nhanh nên không làm tăng thời gian khởi động nhiều (35 ms cho một bộ sưu tập chứa 10,000 tài liệu).
Phiên bản trình duyệt
Phiên bản trình duyệt và phiên bản đã được tối ưu hóa của nó nằm trong thư mục browser-version/out
. Bạn chỉ cần yêu cầu nedb.js
hoặc nedb.min.js
trong tệp HTML của bạn và đối tượng toàn cục Nedb
có thể được sử dụng ngay lập tức, với cùng API như phiên bản máy chủ:
<script src="nedb.min.js"></script>
<script>
var db = new Nedb(); // Create an in-memory only datastore
db.insert({ planet: 'Earth' }, function (err) {
db.find({}, function (err, docs) {
// docs contains the two planets Earth and Mars
});
});
</script>
Nếu bạn chỉ định một filename
, cơ sở dữ liệu sẽ là bền vững và tự động chọn phương thức lưu trữ tốt nhất có sẵn (IndexedDB, WebSQL hoặc localStorage) tùy thuộc vào trình duyệt. Trong hầu hết các trường hợp, điều đó có nghĩa là có thể lưu trữ nhiều dữ liệu, thường là hàng trăm MB. CẢNH BÁO: hệ thống lưu trữ đã thay đổi giữa phiên bản 1.3 và 1.4 và KHÔNG tương thích ngược! Ứng dụng của bạn cần phải đồng bộ lại phía máy khách khi bạn nâng cấp NeDB.
NeDB tương thích với tất cả các trình duyệt chính: Chrome, Safari, Firefox, IE9+. Các bài kiểm tra nằm trong thư mục browser-version/test
(các tệp index.html
và testPersistence.html
).
Nếu bạn sao chép và sửa đổi nedb, bạn có thể xây dựng phiên bản trình duyệt từ mã nguồn, tập lệnh xây dựng là browser-version/build.js
.
Hiệu suất
Tốc độ
NeDB không được thiết kế để thay thế các cơ sở dữ liệu quy mô lớn như MongoDB, và vì vậy không được thiết kế cho tốc độ. Tuy nhiên, cũng cần nói rằng nó vẫn khá nhanh trên các bộ dữ liệu dự kiến, đặc biệt là nếu bạn sử dụng chỉ mục. Trên một máy phát triển thông thường, không quá nhanh, cho một bộ sưu tập chứa 10,000 tài liệu, với chỉ mục:
- Chèn: 10,680 ops/s
- Tìm kiếm: 43,290 ops/s
- Cập nhật: 8,000 ops/s
- Xóa: 11,750 ops/s
Bạn có thể chạy các kiểm tra hiệu suất đơn giản này bằng cách thực thi các tập lệnh trong thư mục benchmarks
. Chạy chúng với cờ --help
để xem cách chúng hoạt động.
Dấu vết bộ nhớ
Một bản sao của toàn bộ cơ sở dữ liệu được giữ trong bộ nhớ. Điều này không nhiều trên loại dữ liệu dự kiến (20MB cho 10,000 tài liệu 2KB).
Sử dụng trong các dịch vụ khác
- connect-nedb-session là một cửa hàng phiên cho Connect và Express, được hỗ trợ bởi nedb
- Nếu bạn chủ yếu sử dụng NeDB cho mục đích đăng nhập và không muốn dấu vết bộ nhớ của ứng dụng của bạn tăng lên quá lớn, bạn có thể sử dụng NeDB Logger để chèn tài liệu vào một cơ sở dữ liệu có thể đọc được bởi NeDB
- Nếu bạn đã phát triển qua NeDB, việc chuyển sang MongoDB sẽ không quá khó khăn vì chúng có cùng API. Sử dụng this utility để chuyển dữ liệu từ cơ sở dữ liệu NeDB sang một bộ sưu tập MongoDB
- Một ODM cho NeDB: Camo
Yêu cầu kéo (Pull requests)
Quan trọng: Tôi xem xét rằng NeDB đã hoàn thành tính năng, tức là nó thực hiện mọi thứ mà tôi nghĩ nó nên thực hiện và không có gì thêm. Như một quy tắc chung, tôi sẽ không chấp nhận yêu cầu kéo nữa, ngoại trừ sửa lỗi (tất nhiên) hoặc nếu tôi thuyết phục được rằng tôi đã bỏ sót một trường hợp sử dụng mạnh. Hãy đảm bảo mở một vấn đề trước khi tiêu thời gian vào bất kỳ yêu cầu kéo nào.
Nếu bạn gửi một yêu cầu kéo (pull request), cảm ơn bạn! Tuy nhiên, có một số quy tắc cần tuân theo để làm cho nó dễ quản lý:
- Yêu cầu kéo nên là nguyên tử, tức là chỉ chứa một tính năng duy nhất. Nếu nó chứa nhiều hơn, vui lòng gửi nhiều yêu cầu kéo riêng biệt. Việc xem xét các yêu cầu kéo lớn, có hơn 1000 dòng mã (loc+), rất khó khăn.
- Tương tự, nếu đối với một tính năng duy nhất, yêu cầu kéo trở nên quá lớn (hơn 200 dòng mã, không bao gồm các bài kiểm tra), vui lòng liên hệ trước.
- Xin vui lòng tuân thủ phong cách mã hóa hiện tại. Điều quan trọng là mã nguồn sử dụng một phong cách thống nhất để dễ đọc.
- Đừng bao gồm các cải tiến về mặt kiểu thức (“housekeeping”). Nếu bạn nghĩ rằng một phần cần nhiều cải tiến về mặt kiểu thức, hãy sử dụng một yêu cầu kéo riêng biệt để không làm bẩn mã nguồn.
- Đừng quên viết các bài kiểm tra cho tính năng mới của bạn. Cũng đừng quên chạy toàn bộ bộ kiểm tra trước khi gửi để đảm bảo bạn không gây ra sự suy giảm.
- Đừng xây dựng phiên bản trình duyệt trong nhánh của bạn, tôi sẽ lo sau khi mã nguồn được hợp nhất.
- Cập nhật tài liệu theo cách thích hợp.
- Cuối cùng nhưng không kém: hãy nhớ tới tư duy của NeDB! Mục tiêu không phải là thay thế cho MongoDB, mà là có một cơ sở dữ liệu JS thuần túy, dễ sử dụng, đa nền tảng, nhanh chóng và đủ biểu đạt cho các dự án mục tiêu (ứng dụng nhỏ và độc lập trên máy chủ/máy tính để bàn/trình duyệt/điện thoại di động). Đôi khi, việc hướng đến tính đơn giản có thể tốt hơn là hướng đến tính hoàn thiện API liên quan đến MongoDB.
Hướng dẫn báo cáo lỗi (Bug reporting guidelines)
Nếu bạn báo cáo một lỗi, cảm ơn bạn! Tuy nhiên, để quá trình này có thể quản lý được, vui lòng tuân thủ nghiêm ngặt các hướng dẫn sau đây. Tôi sẽ không thể xử lý các báo cáo lỗi không tuân thủ:
- Báo cáo lỗi của bạn nên là một gist tự chứa đầy đủ với một tệp package.json cho bất kỳ phụ thuộc nào bạn cần. Tôi cần chạy qua một bước đơn giản
git clone gist; npm install; node bugreport.js
, không có gì khác. - Nó nên sử dụng các phát biểu (assertions) để thể hiện hành vi dự kiến so với hành vi thực tế và phải chống lại hiện tượng “hysteresis”. Thực tế khá đơn giản, xem ví dụ này: https://gist.github.com/louischatriot/220cf6bd29c7de06a486
- Đơn giản hóa càng nhiều càng tốt. Loại bỏ tất cả mã cụ thể cho ứng dụng của bạn. Hầu hết thời gian, bạn sẽ thấy rằng không có lỗi mà chỉ là một lỗi trong mã của bạn 🙂
- Tối đa 50 dòng. Nếu bạn cần nhiều hơn, hãy đọc điểm trên và điều chỉnh lại báo cáo lỗi của bạn. Nếu bạn thực sự tin rằng bạn cần nhiều hơn, vui lòng giải thích cụ thể trong vấn đề.
- Mã nên là Javascript, không phải Coffeescript.
Bitcoins
Bạn không có thời gian? Bạn có thể ủng hộ NeDB bằng cách gửi bitcoins đến địa chỉ này: 1dDZLnWpBbodPiN8sizzYrgaz5iahFyb1
Chi tiết Tải xuống:
Tác giả: louischatriot
Mã nguồn: https://github.com/louischatriot/nedb
Giấy phép: MIT license