Mongoose Compound Index Unique + Sparse - node.js

I want to create an index which ensures, that I don't have a duplicate serialNr within the combination of a manufacturer + art. But some items don't have a serialNr. These I don't want to check/index.
Code
mySchema.index({ serialNr: 1, art: 1 , manufacturer: 1, deleted: 1}, { unique: true, sparse: true)
I tried it also with adding a partial Filter:
partialFilterExpression: { serialNr: {$ne:null} } to the index options.
Question
How can I index it that inserting:
[{art: 'a', manufacturer:'a', deleted:null}, {art: 'a', manufacturer:'a', deleted:null}] passes
and
[{serialNr: '123', art: 'a', manufacturer:'a', deleted:null}, {serialNr: '123', art: 'a', manufacturer:'a', deleted:null}]fails
I'm not trying to save:
[{serialNr: null, art: 'a', manufacturer:'a', deleted:null}, {serialNr: null, art: 'a', manufacturer:'a', deleted:null}]
The mongo docs state, that it should not be indexed if a field is missing in a compound index:
Sparse indexes selects documents to index solely based on the
existence of the indexed field, or for compound indexes, the existence
of the indexed fields.
https://docs.mongodb.org/manual/core/index-partial/
EDIT:
New Index
implantSchema.index({ serialNr: 1, art: 1 , manufacturer: 1, deleted: 1}, { unique: true, partialFilterExpression: { serialNr: {$exists:true} }})
Before I make changes on the index I drop my collection in my dev-database, to ensure that there is no old index existing. This is the new index which is returned on db.items.getIndexes()
/* 0 */
{
"0": {
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "LOC_db.items"
},
"1": {
"v" : 1,
"unique" : true,
"key" : {
"serialNr" : 1,
"art" : 1,
"manufacturer" : 1,
"deleted" : 1
},
"name" : "serialNr_1_art_1_manufacturer_1_deleted_1",
"ns" : "LOC_db.items",
"partialFilterExpression" : {
"serialNr" : {
"$exists" : true
}
},
"background" : true
}
}
Data
I have a pre save hook where I check the value of serialNr, which does not exist before, the dup error is returned.
When querying the database there is no doc existing which has null, "" as value for the serialNr.
The query db.items.find({ serialNr: {$exists:true} }) does not match any items.
Error
insertDocument :: caused by :: 11000 E11000 duplicate key error index: LOC_db.items.$serialNr_1_art_1_manufacturer_1_deleted_1 dup key: { : null, : "Robo", : "Olymp", : null }
Additional Information
Maybe the error is related to:
https://github.com/Automattic/mongoose/issues/2457

You were actually quite close to the solution, all you need to use this when creating index :
partialFilterExpression: { serialNr: {$exists:true} }
Be sure to have at least 3.2 version of mongo
Example :
db.col.createIndex({a:1, b:1},{unique:true, partialFilterExpression: { a: {$exists:true} })
db.col.save({a:50, b:27}) //pass
db.col.save({a:50, b:27}) //fails
db.col.save({b:27}) //pass
db.col.save({b:27}) //pass
db.col.save({b:27}) //pass
db.col.save({a:50, b:27}) //fails
db.col.save({a:50}) //pass
db.col.save({a:50}) //fails
PS : Maybe you can do little "hack" and use this : partialFilterExpression: { serialNr: {$exists:true}, serialNr: {$gte:0} }
I tried using $ne, but it was throwing me an error.

Related

mongodb case insensitive index for 3.4 version

I have created a collection say users, example documents like given below
[{
"name" : "John",
"code" : "B7"
},
{
"name" : "Sara",
"code" : "F7"
}]
I have created one index on field "name"
db.users.createIndex(
{ name: 1 },
{
collation: {locale: "en", strength: 1},
unique: true
}
)
I want to prevent duplicate case insensitive data for name field for example it will not allow entry for name "jOhn" or "jOHn" like that.
It's working but when I'm inserting data in mongodb is giving error messages
Inserted data given below
db.users.insert([{ "name" : "JoHn", "code" : "B9" }])
Error message given below
{
"message" : "WriteError({'code':11000,'index':0,'errmsg':'E11000 duplicate key error collection: digital_data_delivery.users index: name_1 dup key: { : \\';E7C\\' }','op':{'name':'JoHn','code':'B9','_id':'5b4836a458abe34b442a9811'}})",
"stack" : "script:1:10"
}
Some junk characters are coming here "E7C" I want to know what is this

Mongodb error for duplicate indexes

I am new to MongoDB/Mongoose and am in the process of dumping my localdb in order to create a DB for heroku. When I run mongorestore I get the following error(s):
2016-08-29T22:05:00.411-0500 building a list of collections to restore from /Users/micahsherman/tmp/mongodump/Loc8r dir
2016-08-29T22:05:00.518-0500 reading metadata for heroku_n1kxxxxxx.locations from /Users/micahsherman/tmp/mongodump/Loc8r/locations.metadata.json
2016-08-29T22:05:00.518-0500 reading metadata for heroku_n1kxxxxxx.test from /Users/micahsherman/tmp/mongodump/Loc8r/test.metadata.json
2016-08-29T22:05:00.519-0500 restoring heroku_n1kxxxxxx.test from /Users/micahsherman/tmp/mongodump/Loc8r/test.bson
2016-08-29T22:05:00.519-0500 restoring heroku_n1kxxxxxx.locations from /Users/micahsherman/tmp/mongodump/Loc8r/locations.bson
2016-08-29T22:05:00.520-0500 restoring indexes for collection heroku_n1kxxxxxx.test from metadata
2016-08-29T22:05:00.574-0500 finished restoring heroku_n1kxxxxxx.test (0 documents)
2016-08-29T22:05:00.799-0500 error: multiple errors in bulk operation:
- E11000 duplicate key error index: heroku_n1kxxxxxx.locations.$_id_ dup key: { : ObjectId('57c334af05803d85c7b9e780') }
- E11000 duplicate key error index: heroku_n1kxxxxxx.locations.$_id_ dup key: { : ObjectId('57c3819605803d85c7b9e783') }
2016-08-29T22:05:00.799-0500 restoring indexes for collection heroku_n1kxxxxxx.locations from metadata
2016-08-29T22:05:00.852-0500 finished restoring heroku_n1kxxxxxx.locations (2 documents)
2016-08-29T22:05:00.852-0500 done
These prevent a connection from Heroku.
I have gone into the console and printed the indexes, here are the results:
rs-ds017886:PRIMARY> db.system.indexes.find();
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "heroku_n1kxxxxxx.test" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "heroku_n1kxxxxxx.locations" }
{ "v" : 1, "key" : { "coords" : "2dsphere" }, "name" : "coords_2dsphere", "ns" : "heroku_n1kxxxxxx.locations", "background" : true, "2dsphereIndexVersion" : 2 }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "heroku_n1kxxxxxx.objectlabs-system" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "heroku_n1kxxxxxx.objectlabs-system.admin.collections" }
rs-ds017886:PRIMARY>
I'm not quite sure how to go about debugging this. Thoughts??
Double check your locations.bson file to ensure you don't have duplicated documents ('57c334af05803d85c7b9e780' & '57c3819605803d85c7b9e783'). Looks like you're adding data before you create your indexes.
Here's an example of what I believe to be happening (hard to tell without seeing the data)
> db.people.insert({name: "Matt"})
WriteResult({ "nInserted" : 1 })
> db.people.insert({name: "Matt"})
WriteResult({ "nInserted" : 1 })
> db.people.createIndex({name: 1}, {unique: true})
{
"ok" : 0,
"errmsg" : "E11000 duplicate key error index: test.people.$name_1 dup key: { : \"Matt\" }",
"code" : 11000
}
the _id field already contains the unique constraint

MongoDB-Query Optimization

I have a collection with a sub-document consisting of more than 40K records.
My aggregate query takes about 300 secs. I have tried optimizing the same using compound as well as multi-key indexing, which completes in 180 secs.
I still require a reduced query time execution.
here is my collection:
{
"_id" : ObjectId("545b32cc7e9b99112e7ddd97"),
"grp_id" : 654,
"user_id" : 2,
"mod_on" : ISODate("2014-11-06T08:35:40.857Z"),
"crtd_on" : ISODate("2014-11-06T08:35:24.791Z"),
"uploadTp" : 0,
"tp" : 1,
"status" : 3,
"id_url" : [
{"mid":"xyz12793"},
{"mid":"xyz12794"},
{"mid":"xyz12795"},
{"mid":"xyz12796"}
],
"incl" : 1,
"total_cnt" : 25,
"succ_cnt" : 25,
"fail_cnt" : 0
}
and following is my query
db.member_id_transactions.aggregate([ { '$match':
{ id_url: { '$elemMatch': { mid: 'xyz12794' } } } },
{ '$unwind': '$id_url' },
{ '$match': { grp_id: 654, 'id_url.mid': 'xyz12794' } } ])
has anyone faced the same issue?
here's the o/p for aggregate query with explain option
{
"result" : [
{
"_id" : ObjectId("546342467e6d1f4951b56285"),
"grp_id" : 685,
"user_id" : 2,
"mod_on" : ISODate("2014-11-12T11:24:01.336Z"),
"crtd_on" : ISODate("2014-11-12T11:19:34.682Z"),
"uploadTp" : 1,
"tp" : 1,
"status" : 3,
"id_url" : [
{"mid":"xyz12793"},
{"mid":"xyz12794"},
{"mid":"xyz12795"},
{"mid":"xyz12796"}
],
"incl" : 1,
"__v" : 0,
"total_cnt" : 21406,
"succ_cnt" : 21402,
"fail_cnt" : 4
}
],
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(0, 0),
"electionId" : ObjectId("545c8d37ab9cc679383a1b1b")
}
}
One way to reduce the number of records being filtered further is to include the field grp_id, in the first $match operator.
db.member_id_transactions.aggregate([
{$match:{ "id_url.mid": 'xyz12794',"grp_id": 654 } },
{$unwind: "$id_url" },
{$match: { "id_url.mid": "xyz12794" } }
])
See how the performance is now. Add grp_id to the index to get better response time.
The above aggregation query though it works, is unnecessary. since you are not altering the structure of the document, and you expect only one element in the array to match the filter condition, you could just use a simple find and project.
db.member_id_transactions.find(
{ "id_url.mid": "xyz12794","grp_id": 654 },
{"_id":0,"grp_id":1,"id_url":{$elemMatch:{"mid":"xyz12794"}},
"user_id":1,"mod_on":1,"crtd_on":1,"uploadTp":1,
"tp":1,"status":1,"incl":1,"total_cnt":1,
"succ_cnt":1,"fail_cnt":1
}
)

MongoDB: Point not in interval when using $near operator with $maxDistance

When I try to find all members within 50km of Salt Lake City, Utah from the Mongo shell I get the error:
error: {
"$err" : "point not in interval of [ -180, 180 ] :: caused by :: { 0: 0.0, 1: 50000.0 }",
"code" : 16433
}
Here is the query I am running:
db.members.find(
{ 'geo.point' :
{ $near :
{
$geometry : {
type : "Point" ,
coordinates : [ 111.000 , 40.000 ]
},
$maxDistance : 50000
}
}
}
)
Member schema is like this:
var memberSchema = mongoose.Schema({
name: {
first: {type:String, default:''},
last: {type:String, default:''},
},
geo: {
latitude: {type:String, default:''},
longitude: {type:String, default:''},
country: {type:String, default:''},
state: {type:String, default:''},
place: {type:String, default:''},
zip: {type:String, default:''},
point: {type: [Number], index: '2d'}
}
});
Member object in DB looks like this:
{
"_id" : ObjectId("xxxxxxxxxxxxxxxxxxx"),
"name": {
"first": "Thom",
"last": "Allen"
},
"geo" : {
"point" : [ -111.8833, 40.7500 ],
"zip" : "84115",
"state" : "UT",
"country" : "US",
"longitude" : "-111.8833",
"latitude" : "40.7500"
}
}
Is it possible that my fields are not stored in the correct format? If I change 50000 to anything below 180 it will work, but that is not how it should function according to the docs here:
http://docs.mongodb.org/manual/reference/operator/query/near/
** Just a heads up, the proper mongo location array IS in fact [longitude, latitude].
A few things. First, I think your query is off - you are querying for coordinates : [ 111.000 , 40.000 ] and it should be coordinates : [ -111.000 , 40.000 ]
Second, the example data point your provide [ -111.8833, 40.7500 ] is more than 50 km from your corrected query point, it's actually about 122 km (test it here: http://andrew.hedges.name/experiments/haversine/ )
So, correcting for those two issues if I store the data in mongodb as you have stored it I can do the following:
1) create the correct index:
db.members.ensureIndex({ "geo.point": "2dsphere" })
2) run this query:
db.members.find({ 'geo.point':
{$geoWithin:
{$centerSphere: [[ -111.000 , 40.000 ], 113/6371]}
}
} )
Note that I've divided 113 km/ 6371 which gives you radians which is what is required for this specific query.
Try it yourself. In general you will be better off if you can store things in the future using GeoJSON but with your existing schema and the above index and query I'm able to get the correct results.
What you have in your data is the format for legacy co-ordinate pairs but you are trying to query using the GeoJSON syntax.
The only valid index form for legacy co-ordinate pairs is a "2d" index, so if you have created a "2d sphere" index that will not work. So you need to remove any "2d sphere" index and create a "2d" index as follows:
db.members.ensureIndex({ "geo.point": "2d" })
If you actually intend to use the GeoJSON form and "2dsphere" index type, then you need the data to support it, for example:
{
"loc" : {
"type" : "Point",
"coordinates" : [ 3, 6 ]
}
}
So it needs that underlying structure of "type" and "coordinates" in order to use this index type and query form.

MongoDB geospatial index, how to use it with array elements?

I would like to get Kevin pub spots near a given position. Here is the userSpots collection :
{ user:'Kevin',
spots:[
{ name:'a',
type:'pub',
location:[x,y]
},
{ name:'b',
type:'gym',
location:[v,w]
}
]
},
{ user:'Layla',
spots:[
...
]
}
Here is what I tried :
db.userSpots.findOne(
{ user: 'Kevin',
spots: {
$elemMatch: {
location:{ $nearSphere: [lng,lat], $maxDistance: d},
type: 'pub'
}
}
},
},
function(err){...}
)
I get a strange error. Mongo tells me there is no index :2d in the location field. But when I check with db.userSpots.getIndexes(), the 2d index is there. Why doesn't mongodb see the index ? Is there something I am doing wrong ?
MongoError: can't find special index: 2d for : { spots: { $elemMatch: { type:'pub',location:{ $nearSphere: [lng,lat], $maxDistance: d}}}, user:'Kevin'}
db.userSpots.getIndexes() output :
{
"0" : {
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "mydb.userSpots",
"name" : "_id_"
},
"1" : {
"v" : 1,
"key" : {
"spots.location" : "2d"
},
"ns" : "mydb.usersBoxes",
"name" : "spots.location_2d",
"background" : true,
"safe" : null
}
}
For a similar geospatial app, I transformed the location into GeoJSON:
{
"_id" : ObjectId("5252cbdd9520b8b18ee4b1c3"),
"name" : "Seattle - Downtown",
"location" : {
"type" : "Point",
"coordinates" : [
-122.33145,
47.60789
]
}
}
(the coordinates are in longitude / latitude format. Mongo's use of GeoJSON is described here.).
The index is created using:
db.userSpots.ensureIndex({"location": "2dsphere"})
In my aggregation pipeline, I find matches using:
{"$match":{"location":{"$geoWithin": {"$centerSphere":[[location.coordinates[0], location.coordinates[1]], radius/3959]}}}}
(where radius is measured in miles - the magic number is used to convert to radians).
To index documents containing array of geo data MongoDB uses multi-key index. Multi-key index unwinds document to some documents with single value instead of array before indexing. So the index consider that key field as single value field not array.
Try query it without $elemMatch operator.

Resources