set datetime inside of runCommand in mongoDB - mongodb

I have a problem with date and datetime inside runCommand of MongoDB. It has been saving my date in null. I could not use ISODate() because it give a error. The variable $date hasn't show me a error but it storged a null value.
This is the mongoDB's query string that I'm executing from Java application:
{
q: {
'person_id': '1003', 'authoriz.auth_id': { $ne: '1025' } },
u: {
$addToSet: {
'authoriz': {
'decision': "TRUE",
'start_date': { $date: "2018-02-09 00:00:00.0" },
'decision_id': 125,
'decision_dsc': "PERMITED",
'block_id': "1025"
}
},
$currentDate: {
'last_modified': true
}
},
multi: false,
upsert: false
}
In Java:
DBObject dbObject = (BasicDBObject) JSON.parse(query);
CommandResult result =anyDB.command(dbObject);
Instead of save '2018-02-09 00:00:00.0' value in field start_date it has been storaging a null value.
Could you help me how I set up my query string to save the date?
Are there any others option How I use date with runCommand?

You don`t have the ISO complaint string so the parser returns null.
Use ISO string and it will be fine. More here
Something like
"'start_date': { $date: \"2018-02-09T00:00:00.000Z\" }"
Btw command is low level interface and you should be using methods like update, findAndModify provided in DBCollection.

Related

MongoDB Shell: Is it possible do an update query using a function?

I have a collection with data like this:
[{
_id: 1,
address: '1/23 Fake Street'
},
{
_id: 2,
address: '5/20 Whatever Lane'
},
{
_id: 3,
address: '10 Foo Avenue'
}]
I'd like to perform a Mongo bulk update query, which does the following:
Transforms the address field to lowercase
Creates a new field, 'buildingAddress', which splits an address at the slash (if present, as with the first two items) and uses the text after it to populate the new field
In Node, I'd do something like this:
const cursor = db.items.find({});
for await (const item of cursor) {
try {
await pageMapper(item);
} catch (e) {
console.error(e);
}
}
async function pageMapper(item){
const newAddress = item.address.toLowerCase()
const buildingAddress = newAddress.split('/ ')[1];
return db.items.updateOne(item._id, {
$set: {
address: newAddress,
buildingAddress
}
})
}
I'm wondering if there's a way to do this in the MongoDB shell itself, passing in a function to db.collection.update? Or should I stick to the node driver for doing more complex update operations?
If you are using MongoDB 4.2+, you can use aggregation or the pipeline form of update to accomplish that.
$toLower converts a string to lower case
$split to split the field
$slice or $arrayElemAt to pick the element(s) to keep
One possible way to do that with update:
db.items.updateMany({},[
{$addFields:{
address:{$toLower:"$address"}
}},
{$addFields:{
buildingAddress:{
$arrayElemAt:[
{$split:["$address","/"]},
-1
]
}
}}
])

TypeORM returns empty array when querying MongoDB for date

I have the following query that is supposed to get the objects that have a future hospitalizedUntil date from my MongoDB:
return await this.hospitalRepository.find({
where: {
hospitalizedUntil: MoreThanDate(new Date(), EDateType.Datetime)
}
});
Where MoreThanDate:
const MoreThanDate = (date: Date, type: EDateType) => MoreThan(format(date, type));
However, this query doesn't return any objects, even though there is an object in the Mongo where hospitalizedUntil is in the future:
(Current datetime: 2020-08-28T17:11:09.888Z)
[
Hospital {
_id: 5f492efd50e81f63dc60c5d3,
discordId: 'xxxxxxxxxxxxxxxxx',
hospitalizedUntil: 2020-08-28T18:51:17.627Z,
reason: 'Was robbed',
hospitalizedBy: 'xxxxxxxxxxxxxxxxx'
}
]
I've also tried to use the Between operator:
where: {
hospitalizedUntil: Between(new Date(), new Date('9999-12-31'))
}
But this also returned an empty array.
Had the same issue, and solved it by using the native mongoDB query.
hospitalizedUntil: { $gte: format(new Date(), EDateType.Datetime) }
I think those functions just don't work on mongoDB (although I've not found anything related to that issue)

What is wrong with this mongo $or query

This query works perfectly
{
$or:[{author:this.userId} , {somethingelse:true} ]
}
But when I try:
{
$or:[{author:this.userId} , {sharedwith[this.userId]:true} ]
}
I receive the message
Errors prevented startup:
While processing files with ecmascript (for target os.linux.x86_64): server/main.js:113:43: Unexpected token, expected
, (113:43)
=> Your application has errors. Waiting for file change.
And thats where the comma , in the $or statement is
Help
I guess that you are trying to retrieve all documents for which the current user is the author, or which have been shared with him/her? And therefore that you have structured your documents with a sharedWith field which is a hash map of userId as keys and boolean as value?
Document structure:
{
author: string,
sharedWith: {
<userId1>: boolean
// <userId2>…
}
}
In that case, your MongoDB query should use the dot notation to specify the value of a nested field within sharedWith field:
{
$or: [{
author: string
}, {
"sharedWith.<userId>": boolean
}]
}
To easily create the query with the interpolated value of <userId>, you can use a computed key in your object (ES6 syntax):
{
$or:[{
author: this.userId
} , {
// The query computed key must be in square brackets.
// Specify the field child key using the dot notation within your query.
["sharedwith." + this.userId]: true
}]
}
Or with good old ES5 syntax, similarly to #MichelFloyd's answer:
var query = {
$or: [{
author: this.userId
}]
};
var newCondition = {};
newCondition["sharedWith." + this.userId] = true;
query.$or.push(newCondition);
Note: the above described document structure could conveniently replace the sharedWith hash map by an array (since having a false value for the boolean could simply be replaced by removing the corresponding userId from the array):
{
author: string,
sharedWith: [
<userId1>
// <userId2>…
]
}
In which case the query would simply become:
{
$or:[{
author: this.userId
} , {
// In MongoDB query, the below selector matches either:
// - documents where `sharedWith` value is a string equal to
// the value of `this.userId`, or
// - documents where `sharedWith` value is an array which contains
// an element with the same value as `this.userId`.
sharedwith: this.userId
}]
}
Try building the query as a variable before running it.
let query = { $or: [{ author: this.userId }]};
const sw = sharedwith[this.userId];
query.$or.push({sw: true});
MyCollection.find(query);

Trouble updating a Simple Schema sub document

I'm trying to update a sub document on an existing collection. I'm getting a MongoDB error message.
"MongoError: The positional operator did not find the match needed from the query. Unexpanded update: articleWords.$ [409]"
From my Articles Simple Schema
"articleWords.$": {
type: Object
},
"articleWords.$.wordId": {
type: String,
label: 'Word ID'
},
"articleWords.$.word": {
type: String,
label: 'Word'
},
Update Function
function updateArticle(_id,wordArr) {
_.each(wordArr,function(elem) {
var ret = Articles.update(
{'_id': _id},
{ $set: { 'articleWords.$': { 'wordId': elem.wordId, 'word': elem.word } }
});
});
return true;
}
As you can see I am passing an array of objects. Is there a better way to do this than _.each ?
CLARIFICATION
Thank you to #corvid for the answer. I think I didn't make my question clear enough. There does exist an article record, but there is no data added to the articleWords attribute. Essentially we are updating a record but insert into the articleWords array.
A second attempt, is also not working
_.each(wordArr,function(elem) {
var ret = Articles.update(
{'_id': _id},
{ $set: { 'articleWords.$.wordId': elem.wordId, 'articleWords.$.word': elem.word } }
);
});
Yes, you need your selector to match something within the subdocument. For example,
Articles.update({
'_id': <someid>,
'words.wordId': <somewordid>
}, {
$set: {
'words.$.word': elem.word,
'words.$.wordId': elem.wordId
}
});
If the array doesn't exist yet then you're going about this in the hardest way possible. You can just set the entire array at one go:
var ret = Articles.update(
{'_id': _id},
{ $set: { articleWords: wordArr }}
);
I can see that wordArr already has the id and string. This will work as long as it doesn't have more content. If it does then you can just make a second version with the parts you want to keep.

Invoke db.eval in FindAndModify using MongoDB C# Client

I have the following Document:
{
"_id": 100,
"Version": 1,
"Data": "Hello"
}
I have a function which return a number from a sequence:
function getNextSequence(name) {
var ret = db.Counter.findAndModify(
{
query: { _id: name },
update: { $inc: { value: 1 } },
new: true,
upsert: true
}
);
return ret.value;
}
I can use this for optimistic concurrency by performing the following Mongo command:
db.CollectionName.findAndModify({
query: { "_id" : NumberLong(100), "Version" : 1 },
update: { "$set" : {
"Data": "Here is new data!",
"Version" : db.eval('getNextSequence("CollectionName")') }
},
new: true
}
);
This will update the document (as the _id and Version) match, with the new Data field, and also the new number out of the eval call.
It also returns a modified document, from which I can retrieve the new Version value if I want to make another update later (in the same 'session').
My problem is:
You cannot create an Update document using the MongoDB C# client that will serialize to this command.
I used:
var update = Update.Combine(
new UpdateDocument("$set", doc),
Update.Set(versionMap.ElementName, new BsonJavaScript("db.eval('getNextSequence(\"Version:CollectionName\")')")))
);
If you use what I first expected to perform this task, BsonJavascript, you get the following document, which incorrectly sets Version to a string of javascript.
update: { "$set" : {
"Data": "Here is new data!",
"Version" : { "$code" : "db.eval('getNextSequence(\"Version:CollectionName\")')" }
}
}
How can I get MongoDB C# client to serialize an Update document with my db.eval function call in it?
I have tried to add a new BsonValue type in my assembly which I would serialize down to db.eval(''); However there is a BsonType enum which I cannot modify, without making a mod to MongoDB which I would not like to do incase of any issues with the change, compatibility etc.
I have also tried simply creating the Update document myself as a BsonDocument, however FindAndModify will only accept an IMongoUpdate interface which a simply a marker that at present I find superfluous.
I have just tried to construct the command manually by creating a BsonDocument myself to set the Value: db.eval, however I get the following exception:
A String value cannot be written to the root level of a BSON document.
I see no other way now than drop down to the Mongo stream level to accomplish this.
So I gave up with trying to get Mongo C# Client to do what I needed and instead wrote the following MongoDB function to do this for me:
db.system.js.save(
{
_id : "optimisticFindAndModify" ,
value : function optimisticFindAndModify(collectionName, operationArgs) {
var collection = db.getCollection(collectionName);
var ret = collection.findAndModify(operationArgs);
return ret;
}
}
);
This will get the collection to operate over, and execute the passed operationArgs in a FindAndModify operation.
Because I could not get the shell to set a literal value (ie, not a "quoted string") on a javascript object, I had to to this in my C# code:
var counterName = "Version:" + CollectionName;
var sequenceJs = string.Format("getNextSequence(\"{0}\")", counterName);
var doc = entity.ToBsonDocument();
doc.Remove("_id");
doc.Remove(versionMap.ElementName);
doc.Add(versionMap.ElementName, "SEQUENCEJS");
var findAndModifyDocument = new BsonDocument
{
{"query", query.ToBsonDocument()},
{"update", doc},
{"new", true},
{"fields", Fields.Include(versionMap.ElementName).ToBsonDocument() }
};
// We have to strip the quotes from getNextSequence.
var findAndModifyArgs = findAndModifyDocument.ToString();
findAndModifyArgs = findAndModifyArgs.Replace("\"SEQUENCEJS\"", sequenceJs);
var evalCommand = string.Format("db.eval('optimisticFindAndModify(\"{0}\", {1})');", CollectionName, findAndModifyArgs);
var modifiedDocument = Database.Eval(new EvalArgs
{
Code = new BsonJavaScript(evalCommand)
});
The result of this is that I can now call my Sequence Javascript, the getNextSequence function, inside the optimisticFindAndModify function.
Unforunately I had to use a string replace in C# as again there is no way of setting a BsonDocument to use the literal type db.eval necessary, although Mongo Shell likes it just fine.
All is now working.
EDIT:
Although, if you really want to push boundaries, and are actually awake, you will realize this same action can be accomplished by performing an $inc on the Version field.... and none of this is necessary....
However: If you want to follow along to the MongoDB tutorial on how they to say to implement concurrency, or you just want to use a function in a FindAndModify, this will help you. I know I'll probably refer back to it a few times in this project!