12

I'm working on a site built in KeystoneJS that allows users to post words and get suggested synonyms from other users. Words are submitted as part of a phrase or sentence, like "The cat was [perilously] close to knocking over the glass."

My Sentence model looks like this:

Sentence.add({
    sentence: { type: Types.Text, required: true, initial: "New Sentence", index: true },
    word: { type: Types.Relationship, ref: 'Word', required: true, index: true, unique: true, initial: true },
    submitter: { type: Types.Relationship, ref: 'User', required: true, index: true, unique: true, initial: true },
    source: { type: Types.Text },
    createdAt: { type: Date, default: Date.now }
});

And I tried to make the Word model unique according to Mongoose docs:

var Word = new keystone.List('Word', { 
    map: { name: 'word' },
    _id: { from: 'word', path: 'word', unique: true, fixed: false}
});

Word.add({
    word: { type: Types.Text, required: true, initial: "New word", index: true }
});

But if I test it by submitting two sentences with the same word, it just makes a second instance of that word with the _id [word]-1, [word]-2, etc.

I need to be able to query all sentences that use a particular word, so I really need one item per word. But for the life of me, I can't figure out how to make a field unique.

It's possible my problem is with when I add a new Word from the route responsible for accepting AJAX requests:

var newWord = new Word.model({
    word: req.body.word // read from the input box on the home page
});

newWord.save(function(err) {
    if (err) {
        console.error(err);
    }
});

But I thought .save would just update an existing unique field?

Community
  • 1
  • 1
Chris Wilson
  • 6,599
  • 8
  • 35
  • 71

2 Answers2

3

you need to change the add method of your model like this:

Word.add({
    word: { type: Types.Text, required: true, initial: "New word", index: true, unique: true }
});

i tried it and worked for me, notice i added unique:true

i created a keystone project using yeoman's keystone generator, and created a model like yours (you can find it in model/Test.js), then in the admin page when i try to add the same word twice i get:

Error saving changes to Word 569b98f27b5786db1c367b7a:
{ [MongoError: E11000 duplicate key error collection: test_unique_field.words index: word_1 dup key: { : "test" }]
  name: 'MongoError',
  code: 11000,
  err: 'E11000 duplicate key error collection: test_unique_field.words index: word_1 dup key: { : "test" }' }

this is the link to the repo if you want to play with it: keystonejs_test_unique

davide bubz
  • 1,321
  • 13
  • 31
  • I'm still getting multiple entries for the same word when I look at the Mongo database directly. If I add the same word twice, the `_id` becomes "[word]-1", even with `{ unique: true }` in the model. I'm also seeing this error though: `{ [CastError: Cast to ObjectId failed for value "{ word: 'sleek' }" at path "word"]` – Chris Wilson Jan 16 '16 at 15:22
  • Actually, the `Cast to ObjectId` is unrelated, but even if I use the default ObjectId for _id, setting `unique: true` still makes a new entry – Chris Wilson Jan 16 '16 at 19:00
3

I was only able to enforce the uniqueness by using the mongoose-unique-validator module like so:

var keystone = require('keystone');
var Types = keystone.Field.Types;
var uniqueValidator = require('mongoose-unique-validator');

var Word = new keystone.List('Word', { 
    map: { name: 'word' }
});

Word.add({
    word: { type: Types.Text, index: true, unique: true }
});

Word.schema.plugin(uniqueValidator);

Word.defaultColumns = 'word';

Word.register();
dege
  • 2,824
  • 2
  • 25
  • 33
Chris Wilson
  • 6,599
  • 8
  • 35
  • 71