Mongoose Chỉ mục duy nhất không hoạt động!


95

Tôi đang cố gắng để MongoDB phát hiện một giá trị trùng lặp dựa trên chỉ mục của nó. Tôi nghĩ rằng điều này có thể xảy ra trong MongoDB, nhưng thông qua trình bao bọc Mongoose, mọi thứ dường như bị hỏng. Vì vậy, đối với một cái gì đó như thế này:

User = new Schema ({
  email: {type: String, index: {unique: true, dropDups: true}}
})

Tôi có thể lưu 2 người dùng với cùng một email. Em yêu.

Vấn đề tương tự đã được thể hiện ở đây: https://github.com/LearnBoost/mongoose/issues/56 , nhưng chủ đề đó đã cũ và không dẫn đến đâu cả.

Hiện tại, tôi đang thực hiện một cuộc gọi thủ công tới db để tìm người dùng. Cuộc gọi đó không đắt vì "email" được lập chỉ mục. Nhưng vẫn sẽ tốt nếu để nó được xử lý nguyên bản.

Có ai có giải pháp cho điều này không?


1
Tin xấu, nó vẫn là vấn đề với mongod v2.4.3, Mongoose v3.6.20
damphat

Unique dường như hoạt động trên một trong các máy chủ của tôi, nhưng không thực thi các khối duy nhất sử dụng chính xác mã nút / mongoose trên một máy chủ khác. Máy chủ hoạt động bình thường chạy mongod đơn 3.4.10, máy không chạy - chạy bản sao được đặt với mongod 3.2.17. Trên cả hai máy chủ, tôi đang tạo một bộ sưu tập từ đầu, vì vậy các lỗi hiện có không phải là vấn đề. Tôi đã thử hầu hết các giải pháp trên trang này và giải pháp hoạt động là mongoose-unique-validator từ @Isaac Pak.
Maksym

Câu trả lời:


95

Giáo sư! Bạn chỉ cần khởi động lại mongo.


4
Tôi đang gặp sự cố tương tự, nhưng tôi không thể tìm ra cách khởi động lại mongo trên OSX. Khi tôi giết quá trình, nó sẽ tự động xuất hiện trở lại và chỉ mục duy nhất vẫn không hoạt động ... bất kỳ ý tưởng nào?
ragulka

7
ý bạn là gì "rất tiếc bạn chỉ cần khởi động lại mongo" ?! bạn nói rằng không thể thêm chỉ mục mà không đưa cơ sở dữ liệu xuống?
andrewrk

7
Điều này không đúng. Bạn không cần phải khởi động lại mongo để thêm chỉ mục.
JohnnyHK

1
vì điều độc đáo .. TÔI ĐÃ khởi động lại mongo .. và nó đã hoạt động .. Cảm ơn!
Muhammad Ahsan Ayaz

2
Tôi đã thử khởi động lại. Cũng đang lập lại chỉ mục. Đã phải bỏ cơ sở dữ liệu để giải quyết điều này. Đoán vấn đề là các bản sao đã tồn tại trước khi thêm chỉ mục vì vậy trong một db sản xuất, tôi sẽ cần loại bỏ các bản sao và sau đó khởi động lại?
isimmons

40

Giáo sư! Bạn chỉ cần khởi động lại mongo.

Và lập chỉ mục lại quá, với:

mongo <db-name>
> db.<collection-name>.reIndex()

Trong quá trình thử nghiệm, vì tôi không có dữ liệu quan trọng, bạn cũng có thể làm:

mongo <db-name>
> db.dropDatabase()

16
db.dropDatabase () xóa sạch cơ sở dữ liệu của bạn!
Isaac Pak

28

Tôi gặp phải vấn đề tương tự: Tôi đã thêm ràng buộc duy nhất cho emailtrường vào trường của chúng tôi UserSchemasau khi đã thêm người dùng vào db và vẫn có thể lưu người dùng bằng email dupe. Tôi đã giải quyết vấn đề này bằng cách làm như sau:

1) Xóa tất cả tài liệu khỏi bộ sưu tập người dùng.

2) Từ trình bao mongo, thực hiện lệnh: db.users.createIndex({email: 1}, {unique: true})

Về bước 1, hãy lưu ý rằng từ tài liệu của Mongo:

MongoDB không thể tạo một chỉ mục duy nhất trên (các) trường chỉ mục được chỉ định nếu bộ sưu tập đã chứa dữ liệu vi phạm giới hạn duy nhất cho chỉ mục.

https://docs.mongodb.com/manual/core/index-unique/


11

Hành vi này cũng xảy ra nếu bạn để lại một số bản sao trong Mongo. Mongoose sẽ cố gắng tạo chúng trong Mongo khi ứng dụng của bạn khởi động.

Để tránh điều này, bạn có thể xử lý lỗi này theo cách sau:

yourModel.on('index', function(err) {
  if (err?) {
    console.error err
  }
);

10

Được rồi, tôi đã có thể giải quyết vấn đề này từ mongoshell bằng cách thêm chỉ mục vào trường và đặt thuộc tính duy nhất:

db.<collectionName>.ensureIndex({fieldName: 1}, {unique: true});

Shell sẽ phản hồi theo cách này:

{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

Bây giờ để kiểm tra nhanh từ mongoshell:

var doc = {fieldName: 'abc'};
db.<collectionName>.insert(doc)

Nên cung cấp: WriteResult ({"nInserted": 1})

Nhưng khi lặp lại một lần nữa:

db.<collectionName>.insert(doc)

Sẽ cho:

WriteResult({
    "nInserted" : 0,
    "writeError" : {
        "code" : 11000,
        "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: fuelConsumption.users.$email_1  dup key: { : \"martyna@martycud.com\" }"
    }
})

10

Tôi đã làm một cái gì đó như thế này:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FooSchema = new Schema({
   name: { type: String, required: true, index: true, unique: true }
});

const Foo = mongoose.model('Foo', FooSchema);

Foo.createIndexes();

module.exports = Foo

Tôi đã thêm Foo.createIndexes()dòng bc Tôi nhận được cảnh báo không dùng nữa sau khi mã đang được chạy:

(node:21553) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.

Tôi không chắc liệu Foo.createIndexes()có phải là không đồng bộ hay không nhưng AFAIK mọi thứ dường như đang hoạt động tốt


Câu trả lời duy nhất cho câu hỏi giải quyết vấn đề hơn là xoay quanh nó.
Saksham Gupta

Câu trả lời này đã giúp tôi. Tất cả những gì tôi còn thiếu là index: truechỉ mục duy nhất của tôi được tạo.
elcid


5

Mongoose hơi lỏng lẻo khi thực thi các chỉ số duy nhất từ ​​cấp ứng dụng; do đó, bạn nên thực thi các chỉ mục duy nhất của mình từ chính cơ sở dữ liệu bằng cli mongo hoặc nói rõ ràng với mongoose rằng bạn nghiêm túc về uniquechỉ mục bằng cách viết dòng mã sau ngay sau UserSchema của bạn:

UserSchema.index({ username: 1, email: 1 }, { unique: true});

Điều này sẽ thực thi chỉ mục duy nhất trên cả hai usernameemailcác trường trong UserSchema của bạn. Chúc mừng.


5
Đến bữa tiệc hơi muộn, nhưng điều tốt là một ORM "hơi lỏng lẻo khi thực thi các chỉ số duy nhất"
Imre_G

Có ích lợi gì khi coi thường những bình luận không giúp ích được gì. Giải pháp này là rắn và đã sẵn sàng sản xuất.
Isaac Pak

4

Mongoose sẽ không thể thêm một chỉ mục duy nhất khi:

  1. Bộ sưu tập đã có một chỉ mục cùng tên
  2. Bộ sưu tập đã chứa các tài liệu có bản sao của trường được lập chỉ mục

Trong trường hợp đầu tiên, liệt kê các chỉ mục với db.collection.getIndexes()và bỏ chỉ mục cũ với db.collection.dropIndex("index_name"). Khi bạn khởi động lại ứng dụng Mongoose, nó sẽ thêm chỉ mục mới một cách chính xác.

Trong trường hợp thứ hai, bạn cần xóa các bản sao trước khi khởi động lại ứng dụng Mongoose.


3

mongoose-unique-validator

Cách sử dụng plugin này:

1) npm install - lưu mongoose-unique-validator

2) trong lược đồ của bạn, hãy làm theo hướng dẫn sau:

// declare this at the top
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');

// exampleSchema = mongoose.Schema({}) etc...

exampleSchema.plugin(uniqueValidator);

// module.exports = mongoose.model(...) etc....

3) phương pháp mongoose

Khi sử dụng các phương thức như vậy, findOneAndUpdatebạn sẽ cần chuyển đối tượng cấu hình này:

{ runValidators: true, context: 'query' }

ie. User.findOneAndUpdate(
      { email: 'old-email@example.com' },
      { email: 'new-email@example.com' },
      { runValidators: true, context: 'query' },
      function(err) {
        // ...
    }

4) các tùy chọn bổ sung

  1. trường hợp không nhạy cảm

    sử dụng tùy chọn uniqueCaseInsensitive trong giản đồ của bạn

    ie. email: { type: String, index: true, unique: true, required: true, uniqueCaseInsensitive: true }

  2. thông báo lỗi tùy chỉnh

    ie. exampleSchema.plugin(uniqueValidator, { message: 'Error, expected {PATH} to be unique.' });

Giờ đây, bạn có thể thêm / xóa thuộc tính duy nhất vào các lược đồ của mình mà không cần lo lắng về việc khởi động lại mongo, bỏ cơ sở dữ liệu hoặc tạo chỉ mục.

Lưu ý (từ tài liệu):

Vì chúng tôi dựa vào các hoạt động không đồng bộ để xác minh xem một tài liệu có tồn tại trong cơ sở dữ liệu hay không, nên hai truy vấn có thể thực thi cùng một lúc, cả hai đều lấy lại 0 và sau đó cả hai đều chèn vào MongoDB.

Ngoài việc tự động khóa bộ sưu tập hoặc buộc một kết nối duy nhất, không có giải pháp thực sự nào.

Đối với hầu hết người dùng của chúng tôi, đây không phải là vấn đề, nhưng là một trường hợp nguy hiểm cần lưu ý.


2

Câu trả lời mới nhất: không cần khởi động lại mongodb, nếu colleciton đã có các chỉ mục cùng tên, mongoose sẽ không tạo lại các chỉ mục của bạn, vì vậy, trước tiên hãy thả các chỉ mục hiện có của colleciton và bây giờ, khi bạn chạy mongoose, nó sẽ tạo chỉ mục mới , quá trình trên đã giải quyết vấn đề của tôi.


1

Bạn cũng có thể giải quyết vấn đề này bằng cách giảm chỉ mục;

giả sử bạn muốn xóa chỉ mục duy nhất khỏi bộ sưu tập usersvà trường username, hãy nhập thế này:

db.users.dropIndex('username_1');


1

Nếu bảng / bộ sưu tập trống, thì hãy tạo chỉ mục duy nhất cho trường:

db.<collection_name>.createIndex({'field':1}, {unique: true})

Nếu bảng / bộ sưu tập không trống, sau đó bỏ bộ sưu tập và tạo chỉ mục:

db.<collection_name>.drop()
db.<collection_name>.createIndex({'field':1}, {unique: true})

Bây giờ khởi động lại mongoDB.


1

kiểm tra xem autoIndex trong lược đồ có đúng không, nó có thể đặt sai (mặc định đúng) khi bạn sử dụng tùy chọn mongoose.connect


1

Tôi đã đối mặt với vấn đề tương tự trong một thời gian và đã tìm kiếm rất nhiều và giải pháp cho tôi là createIndexes()chức năng.

Tôi ước điều đó có ích.

vì vậy mã sẽ như vậy.

User = new Schema ({
   email: {type: String, unique: true}
});
User.createIndexes();

0

Nếu bạn đang sử dụng tùy chọn autoIndex: falsetrong phương thức kết nối như sau:

mongoose.connect(CONNECTION_STRING, { autoIndex: false });

Hãy thử loại bỏ nó. Nếu điều đó không hiệu quả, hãy thử khởi động lại mongodb như nhiều đề xuất trong chủ đề này.


0

Bạn có thể xác định lược đồ của mình là

User = new Schema ({
  email: {
      type: String,
      unique: true
  }
})

Nhưng có thể điều này sẽ không hoạt động khi đã có tài liệu và sau đó, bạn đã thay đổi lược đồ của Người dùng. Bạn có thể tạo chỉ mục trên email cho bộ sưu tập Người dùng này nếu bạn không muốn bỏ bộ sưu tập. Sử dụng lệnh này, bạn có thể tạo chỉ mục trên email.

db.User.createIndex({email:1},{unique: true})

Hoặc bạn có thể chỉ cần bỏ bộ sưu tập và thêm lại người dùng.
Đối với Bỏ bộ sưu tập, bạn có thể nhập vào:

db.User.drop()

0

Khi tôi gặp sự cố này, tôi đã thử thả cơ sở dữ liệu xuống, khởi động lại máy chủ (gật đầu) nhiều lần và không có thủ thuật nào không hoạt động. Tôi đã tìm thấy công việc sau đây, thông qua Robo 3T:

  1. Trong Robo 3T, nhấp đúp vào Cơ sở dữ liệu để mở các bộ sưu tập
  2. Mở bộ sưu tập để hiển thị Bộ sưu tập được đề cập. Đảm bảo bộ sưu tập của bạn trống, để bắt đầu
  3. Bấm chuột phải vào Thư mục Chỉ mục. Theo mặc định, bạn sẽ thấy _id_là mặc định. Bây giờ, chọn Thêm chỉ mục
  4. Chọn một cái tên, emailchẳng hạn như trường e-mail
  5. Cung cấp Khóa dưới dạng JSON. Ví dụ

    {
       "email": 1
    }
    
  6. Nhấp vào hộp kiểm Duy nhất

  7. Tiết kiệm

Điều này sẽ đảm bảo không có email trùng lặp nào được lưu trong MongoDB.


ý nghĩa của 1 ở đây trong đối tượng {"email": 1} là gì?
siluveru kiran kumar

0

Đảm bảo rằng bộ sưu tập của bạn không có phần thừa của trường mà bạn vừa đặt chỉ mục duy nhất vào.

Sau đó, chỉ cần khởi động lại ứng dụng của bạn (mongoose). Nó chỉ âm thầm thêm chỉ mục không thành công.


0

Xóa tất cả tài liệu khỏi bộ sưu tập:

db.users.remove({})

Và một khởi động lại, như những người khác đã đề cập, đã làm việc cho tôi


0

Nếu MongoDB của bạn đang hoạt động như một dịch vụ (cách dễ dàng để tìm thấy điều này là nếu bạn không cần kết nối với cơ sở dữ liệu mà không khởi động tệp mongod.exe qua thiết bị đầu cuối), thì sau khi thực hiện các thay đổi, bạn có thể cần khởi động lại dịch vụ và / hoặc thả đầy đủ cơ sở dữ liệu của bạn.

Nó khá lạ vì đối với một số người dùng chỉ cần thả một bộ sưu tập duy nhất đã hoạt động. Một số người trong số họ chỉ cần bỏ cơ sở dữ liệu. Nhưng những điều đó không hiệu quả với tôi. Tôi đã bỏ cơ sở dữ liệu, sau đó khởi động lại dịch vụ Máy chủ MongoDB.

Để khởi động lại dịch vụ tìm kiếm Dịch vụ trên thanh tìm kiếm của Windows, sau đó tìm dịch vụ MongoDB, nhấp đúp để mở rồi dừng và khởi động lại dịch vụ.

Nếu các phương pháp khác không hiệu quả với bạn, tôi tin rằng cách này sẽ làm được việc.


0

Trong trường hợp của tôi, mongoose đã lỗi thời. tôi đã kiểm tra nó bằng cách chạy npm đã lỗi thời trên CMD. và cập nhật 'mongoose'.

Vui lòng cho biết nếu điều đó cũng hiệu quả với bạn.


Bạn đang sử dụng phiên bản nào?
Baboo_

0

Khi bạn kết nối cơ sở dữ liệu của mình với ứng dụng, hãy thêm tùy chọn này: "audoIndex: true", ví dụ: trong mã của tôi, tôi đã làm điều này:

const options = {
// your options go here
...
// this code is the solution
audoIndex: true
}
mongoose.connect(DB_URI, options);

Tôi cũng bỏ bộ sưu tập mà tôi có vấn đề và tạo lại nó để đảm bảo rằng nó sẽ hoạt động. Tôi đã tìm thấy giải pháp này tại: https://dev.to/emmysteven/solved-mongoose-unique-index-not-working-45d5 Tôi cũng đã thử các giải pháp như "khởi động lại MongoDB" nhưng không hiệu quả với tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.