Query in command must target single shard key - mongodb

I have an array of document ids using which I wish to delete the documents with the given id. The document id is also the shard key of the document. So I provided the following query for model.deleteMany(query)
query:
{ doc_id: { '$in': [ 'docid1', 'docid2' ] } }
I still get the error Query in command must target single shard key.
Is it possible to overcome this without looping through the array and deleting the docs one by one?

By matching any document in the collection with the _id field, you can make a query or delete command:
db.collection.deleteMany({ _id: { $exists: true }})
During the specification of the schema model in the code, a Shard Key (Partition Key) must be specified. We may execute operations such as save, update, and delete once it is provided
const mySchema = new Schema({
requestId: { type: String, required: true },
data: String,
documents: [{ docId: String, name: String, attachedBy: String }],
updatedBy: {
type: {
name: { type: String, required: true },
email: { type: String, required: true },
}, required: true
},
createdDate: { type: Date, required: true },
updatedDate: { type: Date },
}, { shardKey: { requestId: 1 } }
);

Related

Optimizing query that makes 3 API calls to the MongoDB server

const user_schema = mongoose.Schema(
{
user_name: {
type: String,
required: true,
},
},
{
collection: `user`,
timestamps: true,
}
);
const test_schema = mongoose.Schema(
{
test_name: {
type: String,
required: true,
},
},
{
collection: `test`,
timestamps: true,
}
);
const score_schema = mongoose.Schema(
{
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: true,
},
test_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "test",
required: true,
},
test_score: {
type: Number,
required: true,
},
},
{
collection: `score`,
timestamps: true,
}
);
query:
Given an array of user_id and an array of test_id, query the score model to find out the test scores.
To get the array of user_id, a set of conditions is given and the user model must be queried to find the set of users matching the conditions.
To get the array of test_id, a set of conditions is given and the test model must be queried to find the set of tests matching the conditions.
What needs to be done:
Make one query request to the MongoDB server to get the array of user_id.
Make a separate query request to the MongoDB server to get the array of test_id.
Make another query request to the MongoDB server to get the test scores:
db.getCollection("score").aggregate([
{$match: {$and: {user_id: {$in: array_of_user_id}, {test_id: {$in: array_of_test_id}}}}}
])
Is this the most optimal way to get the test scores? Is it possible to make just one request to the MongoDB server?

How to define a unique field in mongoose schema?

My Schema is like below:
const schema = new mongoose.Schema({
a: {
b: { type: String, unique: true },
c: { type: String }
},
aa: {
bb: [{
cc: { type: String, unique: true },
dd: { type: String }
}]
}
})
now I want to 'b' and 'cc' fields be unique.
how can i do this?
I added this code at the end of the top code, but the schema allows duplicate values.
schema.index({'a.b':1}, {unique:true})
schema.index({'aa.bb.cc':1, {unique:true})
Do you have any idea to solve this problem?
try adding dropDups as shown below.
const schema = new mongoose.Schema({
a: {
b: { type: String, unique: true, dropDups: true },
c: { type: String }
},
aa: {
bb: [{
cc: { type: String, unique: true, dropDups: true },
dd: { type: String }
}]
}
})
if it doesn't work try: -
const schema = new mongoose.Schema({
a: {
b: { type: String, index: { unique: true, dropDups: true } },
c: { type: String }
},
aa: {
bb: [{
cc: { type: String, index: { unique: true, dropDups: true } },
dd: { type: String }
}]
}
})
The answer here depends on exactly what you mean by "I want to 'b' and 'cc' fields be unique."
Mongoose implements the unique constraint by creating a MongoDB index on that field with the unique:true option.
MongoDB enforces the unique option by not allowing the same value to be stored twice in the index.
When a document is written to MongoDB, it extracts the field values from the document that are required by the index, deduplicates the list, and then stores the values in the index with a pointer to the document.
This means that only 1 document may contain a specific value, but that document may contain that value many times.
For example:
If there is an index on {i:1}, these sample documents would have the following entries in the index:
Document
Index entries
{i:1}
1=>
{i:[2,3,4]
2=>
3=>
4=>
{i:[5,6,5,6,5]}
5=>
6=>
{i:[2,6]}
2=>
6=>
If the index were unique, and the documents were inserted in exactly that order, the first 3 documents would be perfectly acceptible, and the last would result in a duplicate key error.

GraphQL Mutation Updating Users Followers with Mongoose/MongodDB - $set is empty error

I have this mutation set up:
followUser: {
type: UserType,
args: {
_id: { type: GraphQLString },
firebaseUid: { type: GraphQLString },
following: { type: new GraphQLList(GraphQLString)},
},
resolve(parentValue, { firebaseUid, _id, following}) {
const update = {
$set: { "following": [firebaseUid] },
$push: { "following": { firebaseUid } }
}
return UserSchema.findOneAndUpdate(
{ _id },
update,
{new: true, upsert: true}
)
}
},
I'm trying to add new followers into my graphql user's collection. My user model:
const UserSchema = new Schema(
{
firebaseUid: String,
following: [{ type: Schema.Types.ObjectId, ref: 'User' }],
followers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
},
{ timestamps: true }
);
module.exports = mongoose.model("User", UserSchema);
So at first, the user doesn't have any followers, so it won't have that field yet. When user adds someone to their friends list, thats when the field will appear in mongodb. Right now I'm getting this error:
"message": "'$set' is empty. You must specify a field like so: {$set: {<field>: ...}}",
I'm not sure if I'm doing the $set correctly.
The UserType
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
_id: { type: GraphQLString },
firebaseUid: { type: GraphQLString },
following: { type: new GraphQLList(GraphQLString) },
followers: { type: new GraphQLList(GraphQLString) },
...
})
});
edit:
current mongodb data collection:
_id: ObjectId("5e5c24111c9d4400006d0001")
name: "Mr. Smith"
username: "mrsmith"
after running the update
_id: ObjectId("5e5c24111c9d4400006d0001")
name: "Mr. Smith"
username: "mrsmith"
following: ["fdsaduybfeaf323dfa"] // <-- this gets added
Currently mongooses validator is rejecting the update. To fix this you need the following:
You only need to $push since it will automatically create an array if the property does not exist
You should remove the extra { } around the firebaseUid in the $push because otherwise the following array will contain objects with a firebaseUid property instead of directly containing the Uid (or would if the schema validator allowed it)
Mongo ObjectIds can only be converted from strings when they are 12-byte hexadecimal, and firebaseUid is not, so the schema should be typed to String instead of ObjectId as the validator will reject the field for update otherwise.

How to make sure there is no duplicate data on model's keys?

This is my user model:
const UserSchema = new mongoose.Schema(
{
email: { type: String, required: true, unique: true },
watched: [{ type: String}],
watchLater: [{ type: String}],
},
{ timestamps: true },
)
there is watched and watchLater array which contains strings. When I add a string to watched I want to remove or make sure there is no same string on watchLater and vice versa. What's the best approach for this? Do I have to query both keys separately, compare, and write back to the database or there is some other way?
You can put the criteria in query part
db.collection.update(
{ watched: { $ne: string } watchedLater: { $ne:string }},
{ $push: { watched: string }}
)

Populate query with match returns null

I have three schemas, that need them to be separated and I can't use subdocuments. The important one is this
export var TestSchema = new mongoose.Schema({
hash: { type: String, index: { unique: true }, default: common.randomHash },
date: { type: Date, default: Date.now },
result: { type: Object },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
data: { type: Object },
finished: Date,
lang: { type: String, default: 'pt' },
benchmark: { type: String, required: true },
order: { type: mongoose.Schema.Types.ObjectId, ref: 'Transaction' },
/* TODO: remove */
name: { type: String }
});
I have a query that does the populate (it's actually a pagination helper, but I'm cutting to the chase):
TestModel.find({hide: {$ne: true}, user: id}).populate({
path: 'user',
match: {$or: [
{email: new RegExp(search, i)},
{name: new RegExp(search, i)},
{empresa: new RegExp(search, i)},
]}
}).exec().then(/*...*/)
when populate.match doesn't find anything, it sets the user to null. I tried setting the find({'user':{$ne: null}}) but it ignores it. (I guess the populate happen after the find call, maybe that's the reason).
Is there any way I can filter it in the database layer instead having to rely on iterating of the results, check for null then filter out?
This answer on GitHub clarifies that it is not possible with populate, due to how MongoDB works. However, you should be able to do it with $lookup.