MongoDB и Mongoose — создание пользовательских, автоматически индексируемых вторичных идентификаторов?

#node.js #mongodb #express #mongoose #indexing

#node.js #mongodb #экспресс #mongoose #индексирование

Вопрос:

Я инициализирую свою базу данных обычным способом:

 mongoose.connect(`mongodb://uname:pword@127.0.0.1:port/dbname?authSource=admin`, {useNewUrlParser: true, autoIndex: false});
  

И у меня есть схема, что-то вроде:

 var materialSchema = new Schema({
    bookID: {type: String, required: true},
    active: Boolean,
    name: {type: String, required: true},
    stockLength: {type: Number, required: true}
});

module.exports = mongoose.model('material', materialSchema);
  

Когда я создаю новый материал и добавляю его в базу данных, ему автоматически присваивается обычный _id — это поведение, которое я хочу поддерживать. Но я бы также хотел, чтобы bookID был уникальным индексом с автоматическим увеличением. Это для физического хранилища на полке, а не для запросов или чего-то подобного.

Я бы хотел, чтобы bookID увеличивался следующим образом:

 A-001
A-002
A-003
...
A-098
A-099
A-100
B-001
...
B-100
...
Z-001
...
Z-100
  

В случае, если приведенный выше шаблон неясен, шаблон начинается с A-001 и в конечном итоге заканчивается на Z-100 . Каждая буква начинается с 001 через 100 , прежде чем перейти к следующей букве. Каждая новая запись коллекции — это просто следующий идентификатор в шаблоне. Маловероятно, что конец когда-либо будет достигнут, но мы перейдем этот мост, когда доберемся туда.

Я когда-либо использовал только значение по умолчанию _id для индексации и не могу понять, как создать этот шаблон.

Спасибо за любую информацию!

Правка # 1

Лучшее решение, которое я придумал на данный момент, — это создать отдельный .txt файл со всеми идентификаторами, перечисленными по порядку. По мере создания каждого нового объекта перемещайте (… shift) следующий идентификатор с верхней части файла. Это также может иметь дополнительное преимущество в виде простого добавления дополнительных идентификаторов позже. Вероятно, я воспользуюсь таким подходом, но меня все еще интересует решение mongoose, запрошенное выше.

Правка # 2

Итак, я думаю, что решение, которое я собираюсь использовать, немного отличается. В основном, findOne отсортированы по bookID убыванию. Затем используйте возвращенное значение для установки следующего.

 Material.findOne()
    .sort({bookID : -1})
    .exec((err, mat) => {
        if(err) {
            // Send error
        }else if(!mat) {
            // First bookID
        }else {
            // Indexes exist...
            let nextId = getNextID(mat.bookID);

            // ...
        }
    });
  

Все еще легко изменить getNextID() , чтобы добавить новые / отличающиеся идентификаторы в будущем (если / когда будет достигнут «Z100»)

Еще раз спасибо!

Ответ №1:

Хорошо, итак, чтобы немного расширить правку № 2, я предложил следующее решение.

В файл модели (схемы) мы добавляем промежуточное программное обеспечение pre() schema , которое выполняется при .save() вызове перед сохранением:

 // An arrow function will not work on this guy, if you want to use the "this" keyword
materialSchema.pre('save', function(next) {
    this.model('material').findOne()  // Don't forget the .model(...) bit!
        .sort({bookID : -1})  // All I need is the highest (i.e. most recent) bookID
        .select('bookID')  // Ditto above (not really necessary)
        .exec((err, result) => {
            if(err) {
                return next(err);  // Oopsies, an error!
            }else if(!result) {
                this.bookID = 'A-001';  // The case when collection is empty
            }else {
                this.bookID = getNextID(result.bookID);  // Otherwise, increment ID
            }

            next();  // Don't forget this sucker! This is how you save
        });
});
  

И это примерно все! Это не встроенное решение непосредственно от Mongoose, но оно отлично работает.

Просто для полноты, getNextID функция выглядит как:

 function getNextID(curID) {
    let letter = curID.split('-')[0];
    let number = parseInt(curID.split('-')[1]);

    if(number >= 100) {  // Increase the letter and reset the number
        letter = String.fromCharCode(letter.charCodeAt(0)   1)
        number = '001';
    }else { // Only increase the number
        number = (''   (number   1)).padStart(3, '0'); // Makes sure the numbers are always 3 digits long
    }

    return `${letter}-${number}`;
}
  

На данный момент это будет просто замечательно. Пока мы не доберемся до Z100 . Но я перейду этот мост, если / когда это произойдет. Ничего особенного вообще.

И вам не нужно делать ничего особенного, чтобы использовать это. Просто сохраните новый документ как обычный, и он автоматически запустится:

 new Material({
    // New material properties
}).save((err, mat) => {
    // Handle errors and returns ...
});