MongoDB - unique string field constraint - mongodb

I have been searching for some hours how to make the ISBN field unique and haven't found a solution. Creating an index gives me the following error. I still didn't understand how these indexes work - I am in the process of learning. If you come with a quick solution let me know!
Attempt to create an index
db.books.createIndex( {ISBN : 1} , {unqiue : true} )
Error
/* 1 */
{
"ok" : 0.0,
"errmsg" : "Error in specification { key: { ISBN: 1.0 }, name: \"ISBN_1\", unqiue: true } :: caused by :: The field 'unqiue' is not valid for an index specification. Specification: { key: { ISBN: 1.0 }, name: \"ISBN_1\", unqiue: true }",
"code" : 197,
"codeName" : "InvalidIndexSpecificationOption"
}
Schema
db.createCollection("books", {
validator: {
$jsonSchema: {
bsonType: "object",
required: [
"ISBN",
"title"
],
properties: {
_id: {},
ISBN: {
bsonType: "string",
// make this unique
},
title: {
bsonType: "string",
},
},
},
},
});

Related

Duplicate error even though collection is empty

I try to insert multiple documents into my MongoDB collection, but whatever I do, I get a duplicate error. I made sure that there should be no duplicates possible by dropping the whole collection.
I tried it with .insertMany(), .save(), .create() - none of them do work. Though the docs get inserted, I still get the duplicate error 11000.
My function to insert the docs:
Words.prototype.addManyGeneralWordsEN = async function(words) {
await generalWordEN.create(words).then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err.code)
})
}
// add words to database
await this.addManyGeneralWordsEN(wordsToAdd)
My schema:
const generalWordENSchema = new mongoose.Schema(
{
german: {
type: String,
required: true
},
english: {
type: String,
required: true
},
partOfSpeech: {
required: true,
type: String
},
example: {
default: null,
type: String,
},
defintion: {
default: null,
type: String,
},
image: {
default: null,
type: String,
},
audio: {
default: null,
type: String,
},
level: {
default: null,
type: Number,
},
}
)
generalWordENSchema.index({ german: 1, english: 1, partOfSpeech: 1}, { unique: true })
module.exports = generalWordENSchema
My sample data:
[
{
"english": "clothes",
"german": "Kleidung",
"partOfSpeech": "noun",
"example": "My wife's wardrobe is filled with beautiful clothes.",
"definition": "",
"image": "",
"audio": "",
"level": ""
},
{
"english": "men's clothing",
"german": "Herrenbekleidung",
"partOfSpeech": "noun",
"example": "Men's clothing is on the second floor.",
"definition": "",
"image": "",
"audio": "",
"level": ""
}
]
The problem is probably on this line
generalWordENSchema.index({ german: 1, english: 1, partOfSpeech: 1}, { unique: true })
You created an index for the collection and used partOfSpeech as unique, but you have two documents with the same value noun.
It should work if you change it to:
generalWordENSchema.index({ german: 1, english: 1 }, { unique: true });
You also have a typo on the Schema declaration that might cause you different issues. You typed defintion instead of definition.

MongoDB aggregation to check if an element is contained in an array

I am new to MongoDB, my native language is Spanish. So I'm not sure how to find what I need.
I will try to make myself understandable.
my schema:
let noticiaSchema = new Schema({
titulo: {
type: String,
required: [true, 'El titulo de la noticia es requerido']
},
cuerpo_noticia: {
type: String,
required: [true, 'El cuerpo de la noticia es necesario']
},
publicacion_noticia: {
type: Date,
default: new Date()
},
actualizacion_noticia: {
type: Date,
default: new Date()
},
likes: {
type: Array,
default: []
},
foto: {
type: String
},
dislikes: {
type: Array,
default: []
}
})
Sample Doc :
{
"_id" : ObjectId("5e81868faf9d6e084cc60cc8"),
"titulo" : "fdsfsfs",
"cuerpo_noticia" : "fdsfsfs",
"actualizacion_noticia" : ISODate("2020-03-30T05:41:35.144Z"),
"foto" : "14335143.png",
"publicacion_noticia" : ISODate("2020-03-30T05:41:12.997Z"),
"likes" : [
"5e7ffb641650a326dcc1e1c7"
],
"dislikes" : [],
"__v" : 0
}
I'm basically trying to query for an array of elements called likes, if an element is contained in this array I would want to return true / false on a new field.
Below is what I've tried, but it only returns the documents where the element exists in likes.
//5e7ffb641650a326dcc1e1c7 element to search
Noticia.aggregate([
{
$match: {
"likes": { "$in": ["5e7ffb641650a326dcc1e1c7"] },
//likes is the array field with elements to search
}
},
{
$project: {
titulo: "$titulo"
}
}
], (err, trans) => {
})
I want it to return all my docs but with a field that tells me if this element is contained or not.
Finally, I want to know if there is a way to return the result by creation date, that is .sort {_id: -1}
If you wanted to return all docs, you should not be using $match, try below aggregation :
db.collection.aggregate([
/** Add a field across all docs by checking on specific condition */
{
$addFields: {
elementExists: {
$cond: [{ $in: ["5e7ffb641650a326dcc1e1c7", "$likes"] }, true, false]
}
}
},
{
$sort: {
_id: -1 // Sort by '_id' field in descending order
}
}
]);
Test : mongoDB-Playground

How to update with mongoose

I have this record
{
"_id" : ObjectId("5dfdff479ad032cbbc673507"),
"selection" : [
{
"highlights" : "test",
"comment" : "CHANGE THIS",
"el" : "body:nth-child(2)>div:nth-child(2)#root>div.App>p:nth-child(1)"
},
{
"highlights" : "Barrett’s lyrical prose opens with a clever and tender solution",
"comment" : "",
"el" : "body:nth-child(2)>div:nth-child(2)#root>div.App>p:nth-child(2)"
}
],
"category" : [],
"status" : "",
"url" : "http://localhost:3000/theone",
"title" : "React App test",
"__v" : 4
}
And I want to update the comment. I have tried to use update and findOneAndUpdate and nothing is working. Here is my attempt
WebHighlight.findOneAndUpdate(
{
_id: req.params.highlight,
"selection.highlights": "test"
},
{ "selection.$.comment": "yourValue" }
);
That req.params.highlight is the id (I even hardcoded it)
I also tried this
WebHighlight.findById(req.params.highlight, (err, book) => {
var test = [...book.selection];
test[0].comment = "somethibf"
book.save();
res.json(book);
});
And nothing is working.
This is the model
const webhighlightsModel = new Schema({
selection: { type: Array, default: "" },
category: { type: Array, default: [] },
title: { type: String },
url: { type: String },
status: { type: String, default: "" }
});
Actually your code seems to work, but findOneAndUpdate returns the old document if you don't give {new: true} option.
I think for this reason, you think the update wasn't successfull, but if you check your collection, you will see the update.
WebHighlight.findOneAndUpdate(
{
_id: req.params.highlight,
"selection.highlights": "test"
},
{ "selection.$.comment": "yourValue" },
{ new: true }
)
.then(doc => res.send(doc))
.catch(err => res.status(500).send(err));
Also I think it would be better if selection had a sub schema like this:
const mongoose = require("mongoose");
const schema = new mongoose.Schema({
selection: [
new mongoose.Schema({
highlights: String,
comment: String,
el: String
})
],
category: { type: Array, default: [] },
title: { type: String },
url: { type: String },
status: { type: String, default: "" }
});
module.exports = mongoose.model("WebHighlight", schema);
So with this every selection would an _id field, and it would be better to update with this _id.
You should use the $set operator to update existing values:
WebHighlight.findOneAndUpdate(
{
_id: req.params.highlight,
"selection.highlights": "test"
},
{ '$set': { "selection.$.comment": "yourValue" } }
);

MongoDB Linking a $jsonSchema to another $jsonSchema

I have two schemas with 1 to N relation. one is book and the other author.
I have crated three files names: book.js, genre.js and author.js below. As you see, I have referenced in the book the genre and author from other files.
author: {
$ref: "./models/author.js"
},
and
"genre" : {
$ref: "./models/genre.js",
description: "must be a string and is required"
}
However, When I issue that in mongo> i get the following:
{
"ok" : 0,
"errmsg" : "$jsonSchema keyword '$ref' is not currently supported",
"code" : 9,
"codeName" : "FailedToParse"
}
I was wondering How I could do that please?
// book.js
var book = {
validator: {
$jsonSchema: {
bsonType: "object",
required: [ "title", "author", "summary", "isbn", "genre" ],
properties: {
title: {
bsonType: "string",
description: "must be a string and is required"
},
author: {
$ref: "./models/author.js"
},
isbn: {
bsonType: "int",
minimum: 2017,
maximum: 3017,
exclusiveMaximum: false,
description: "must be an integer in [ 2017, 3017 ] and is required"
},
"summary" : {
bsonType: "string",
description: "must be a string and is required"
},
"genre" : {
$ref: "./models/genre.js"
}
}
}
}
};
module.exports = book;
//genre.js
var genre = {
validator: {
$jsonSchema: {
bsonType: "object",
required: [ "name"],
properties: {
first_name: {
bsonType: "string",
size: 100,
description: "must be a string and is required"
},
url: {
bsonType: "string",
minLength:3,
maxLength:20,
description: "must be a string and size between [3, 100] is not required"
}
}
}
}
};
module.exports = genre;
//author.js
var Author = {
validator: {
$jsonSchema: {
bsonType: "object",
required: [ "first_name", "family_name" ],
properties: {
first_name: {
bsonType: "string",
maxLength:100,
description: "must be a string and is required"
},
family_name: {
bsonType: "string",
maxLength:100,
description: "must be a string and is not required"
},
date_of_birth: {
bsonType: "int",
minimum: 0,
maximum: 2018,
exclusiveMaximum: false,
description: "must be an integer in [ 0, 3017 ] and is required"
},
date_of_death: {
bsonType: "int",
minimum: 0,
maximum: 2018,
exclusiveMaximum: false,
description: "must be an integer in [ 0, 2018 ] and is required"
}
}
}
}
};
module.exports = Author;
It seems the Manual References does not work:
$jsonSchema: {
bsonType: "object",
required: [ "title", "author_id", "summary", "isbn", "genre" ],
properties: {
title: {
bsonType: "string",
description: "must be a string and is required"
},
author_id: {
bsonType: ObjectId(),
description: "must be a string and is required"
},
isbn: {
as
{
"ok" : 0,
"errmsg" : "$jsonSchema keyword 'bsonType' must be either a string or an array of strings",
"code" : 14,
"codeName" : "TypeMismatch"
}
"$jsonSchema keyword '$ref' is not currently supported",
As per the error message you encountered, the implementation of JSON Schema does not (as at MongoDB 4.0) support references ($ref). Since $jsonSchema is being validated on the database server a relative file path isn't appropriate; you should instead inline the required schema validation rules.
If you want more flexibility you could look for a validation library you can use in your application code. There are several JSON Schema packages on NPM as well as alternative approaches such as Object-Document Mappers (for example, Mongoose).
It seems the Manual References does not work
bsonType: ObjectId(),
The ObjectId() usage here isn't valid JSON. You need to specify a string value with the BSON type: bsonType: "objectId".
For more information see $jsonSchema Extensions and Omissions in the MongoDB documentation for your server version.
Thank you very much. bsonType: "objectId". To be honest with you, I don't like using Mongoose. Using manual referencing:
original_author_id = ObjectId()
db.author.insert({
"_id": original_author_id,
first_name: "ghadamali",
family_name: "sarami",
date_of_birth: NumberInt(1944)
});
original_genre_id = ObjectId()
db.genre.insert({
"_id": original_genre_id,
name: "action",
url: "www.action.com"
});
db.book.insert({
title:"az range gol",
author_id: original_author_id,
summary: "shekle senasi shahname",
isbn: NumberInt(12312),
genre:original_genre_id
});

Mongo 3.6 debug validation using jsonSchema

I'm following a course from mongodb university to learn new features in the release 3.6, and I'm unable to resolve why my validator is invalid.
This is how I created the collection:
db.getSiblingDB("TSA").createCollection("claims", {
validator: {
$jsonSchema: {
bsonType: "object",
properties: {
_id: { },
airportCode: { type: "string", minLength: 3 },
airportName: { type: "string" },
airlineName: { type: "string", minLength: 5 },
claims: {
bsonType: "object",
properties: {
itemCategory: { bsonType: "array", maxItems: 3 },
amount: { type: "string", pattern: "^\$.*" }
}
}
},
required: ["airportCode", "airlineName", "claims"],
additionalProperties: false
}
}
})
Then, I try to insert this object:
db.getSiblingDB("TSA").claims.insertOne({
"airportCode": "ABE",
"airportName": "Lehigh Valley International Airport, Allentown",
"airlineName": "MongoAir",
"claims": {
"claimType": "Property Damage",
"claimSite": "Checked Baggage",
"itemCategory": [ "Sporting Equipment & Supplies" ],
"amount": "$180.00"
}
})
Getting the following error:
WriteError({
"index" : 0,
"code" : 121,
"errmsg" : "Document failed validation",
"op" : {
"_id" : ObjectId("5a705318d3d6c18337f07282"),
"airportCode" : "ABE",
"airportName" : "Lehigh Valley International Airport, Allentown",
"airlineName" : "MongoAir",
"claims" : {
"claimType" : "Property Damage",
"claimSite" : "Checked Baggage",
"itemCategory" : [
"Sporting Equipment & Supplies"
],
"amount" : "$180.00"
}
}
})
My question is, is there some way to debug the validator like "property X must be Y type" instead of getting a generic "Document failed validation"?
As of MongoDB 3.6, there is no feedback mechanism that would inform what part of a document failed validation during a server-side check. A corresponding feature request is open: SERVER-20547: Expose the reason an operation fails document validation. For now, it is left to the application code to perform its own validation if detailed feedback is required.
If you use regex pattern string, backslash itself must be escaped too:
db.getSiblingDB("TSA").createCollection("claims", {
validator: {
$jsonSchema: {
bsonType: "object",
properties: {
_id: { },
airportCode: { type: "string", minLength: 3 },
airportName: { type: "string" },
airlineName: { type: "string", minLength: 5 },
claims: {
bsonType: "object",
properties: {
itemCategory: { bsonType: "array", maxItems: 3 },
amount: { type: "string", pattern: "^\\$.*" }
}
}
},
required: ["airportCode", "airlineName", "claims"],
additionalProperties: false
}
}
})