Mongodb Liqbase script findOneAndUpdate for json type values - mongodb

I have code that uses existing collection that I want to update name field value which is present in taskMap key how i run with run liqubase changeset for that .
sample code :
"changes": [
{
"collectionName": "bulk_import_job_spec"
},
{
"findOneAndUpdate": {
{"_id": 101},
[{"$set": {"task_map": {"import": {"_id": 1, "name": "Category Association"}}}}]
},
{
"returnNewDocument": true
}
]

your questions is quite vague. Please provide some more details so that we can understand your issue.

Related

Mongo pull multiple elements inside an array of object

I'm trying to pull one or multiple objects from an array and I noticed something odd.
Let's take the following document.
{
"_id" : UUID("f7e80c8e-6b4a-4741-95a3-2567cccf9e5f"),
"createdAt" : ISODate("2021-07-19T17:07:28.499Z"),
"description" : null,
"externalLinks" : [
{
"id" : "ZV8xMjM0NQ==",
"type" : "event"
},
{
"id" : "cF8xMjM0NQ==",
"type" : "planning"
}
],
"updatedAt" : ISODate("2021-07-19T17:07:28.499Z")
}
I wrote a basic query to pull one element of externalLinks which looks like
db.getCollection('Collection').update(
{
_id: {
$in: [UUID("f7e80c8e-6b4a-4741-95a3-2567cccf9e5f")]
}
}, {
$pull: {
externalLinks: {
"type": "planning",
"id": "cF8xMjM0NQ=="
}
}
})
And it's working fine. But it's getting trickier when I want to pull multiple element from the externalLinks. And I'm using the operator $in for that.
And the strange behaviour is here :
db.getCollection('Collection').update(
{
_id: {
$in: [UUID("f7e80c8e-6b4a-4741-95a3-2567cccf9e5f")]
}
}, {
$pull: {
externalLinks: {
$in: [{
"type": "planning",
"id": "cF8xMjM0NQ=="
}]
}
}
})
And this query doesn't work. The solution is to switch both field from externalLinks
and do something like :
$in: [{
"id": "cF8xMjM0NQ==",
"type": "planning"
}]
I tried multiple things like : $elemMatch, $positioning but it should be possible to pull multiple externalLinks.
I also tried the $and operator without success.
I could easily iterate over the externalLinks to update but it'd be too easy.
And it's tickling my brain to choose that solution.
Any help would be appreciate, thank you !
Document fields have order, and MongoDB compares documents based on the order of the fields see here, so what field you put first matters.
After MongoDB 4.2 we can also do pipeline updates, that can be sometimes bigger, but they are much more powerful, and feels more like programming.
(less declarative and pattern matching)
This doesn't mean that you need pipeline update in your case but check this way also.
Query
pipeline update
filter and keep members that the condition doesn't exist
Test code here
db.collection.update(
{_id: {$in: ["f7e80c8e-6b4a-4741-95a3-2567cccf9e5f"]}},
[{"$set":
{"externalLinks":
{"$filter":
{"input": "$externalLinks",
"cond":
{"$not":
[{"$and":
[{"$eq": ["$$this.id", "ZV8xMjM0NQ=="]},
{"$eq": ["$$this.type", "event"]}]}]}}}}}])

mongodb check regex on fields from one collection to all fields in other collection

After digging google and SO for a week I've ended up asking the question here. Suppose there are two collections,
UsersCollection:
[
{...
name:"James"
userregex: "a|regex|str|here"
},
{...
name:"James"
userregex: "another|regex|string|there"
},
...
]
PostCollection:
[
{...
title:"a string here ..."
},
{...
title: "another string here ..."
},
...
]
I need to get all users whose userregex will match any post.title(Need user_id, post_id groups or something similar).
What I've tried so far:
1. Get all users in collection, run regex on all products, works but too dirty! it'll have to execute a query for each user
2. Same as above, but using a foreach in Mongo query, it's the same as above but only Database layer instead of application layer
I searched alot for available methods such as aggregations, upwind etc with no luck.
So is it possible to do this in Mongo? Should i change my database type? if yes what type would be good? performance is my first priority. Thanks
It is not possible to reference the regex field stored in the document in the regex operator inside match expression.
So it can't be done in mongo side with current structure.
$lookup works well with equality condition. So one alternative ( similar to what Nic suggested ) would be update your post collection to include an extra field called keywords ( array of keyword values it can be searched on ) for each title.
db.users.aggregate([
{$lookup: {
from: "posts",
localField: "userregex",
foreignField: "keywords",
as: "posts"
}
}
])
The above query will do something like this (works from 3.4).
keywords: { $in: [ userregex.elem1, userregex.elem2, ... ] }.
From the docs
If the field holds an array, then the $in operator selects the
documents whose field holds an array that contains at least one
element that matches a value in the specified array (e.g. ,
, etc.)
It looks like earlier versions ( tested on 3.2 ) will only match if array have same order, values and length of arrays is same.
Sample Input:
Users
db.users.insertMany([
{
"name": "James",
"userregex": [
"another",
"here"
]
},
{
"name": "John",
"userregex": [
"another",
"string"
]
}
])
Posts
db.posts.insertMany([
{
"title": "a string here",
"keyword": [
"here"
]
},
{
"title": "another string here",
"keywords": [
"another",
"here"
]
},
{
"title": "one string here",
"keywords": [
"string"
]
}
])
Sample Output:
[
{
"name": "James",
"userregex": [
"another",
"here"
],
"posts": [
{
"title": "another string here",
"keywords": [
"another",
"here"
]
},
{
"title": "a string here",
"keywords": [
"here"
]
}
]
},
{
"name": "John",
"userregex": [
"another",
"string"
],
"posts": [
{
"title": "another string here",
"keywords": [
"another",
"here"
]
},
{
"title": "one string here",
"keywords": [
"string"
]
}
]
}
]
MongoDB is good for your use case but you need to use a approach different from current one. Since you are only concerned about any title matching any post, you can store the last results of such a match. Below is a example code
db.users.find({last_post_id: {$exists: 0}}).forEach(
function(row) {
var regex = new RegExp(row['userregex']);
var found = db.post_collection.findOne({title: regex});
if (found) {
post_id = found["post_id"];
db.users.updateOne({
user_id: row["user_id"]
}, {
$set :{ last_post_id: post_id}
});
}
}
)
What it does is that only filters users which don't have last_post_id set, searches post records for that and sets the last_post_id if a record is found. So after running this, you can return the results like
db.users.find({last_post_id: {$exists: 1}}, {user_id:1, last_post_id:1, _id:0})
The only thing you need to be concerned about is a edit/delete to an existing post. So after every edit/delete, you should just run below, so that all matches for that post id are run again.
post_id_changed = 1
db.users.updateMany({last_post_id: post_id_changed}, {$unset: {last_post_id: 1}})
This will make sure that next time you run the update these users are processed again. The approach does have one drawback that for every user without a matching title, the query for such users would run again and again. Though you can workaround that by using some timestamps or post count check
Also you should make to sure to put index on post_collection.title
I was thinking that if you pre-tokenized your post titles like this:
{
"_id": ...
"title": "Another string there",
"keywords": [
"another",
"string",
"there"
]
}
but unfortunately $lookup requires that foreignField is a single element, so my idea of something like this will not work :( But maybe it will give you another idea?
db.Post.aggregate([
{$lookup: {
from: "Users",
localField: "keywords",
foreignField: "keywords",
as: "users"
}
},
]))

Cannot add _id field to mongo subdocument in Mlab

In my Mlab mongo 3.2 database I have a collection that looks like this:
{
"_id": {
"$oid": "5752d....87985"
},
"name": "...etation",
"description": null,
"user_id": ".....",
"questions": [
{
"prompt": "The conclusions drawn seemed clear to most researchers, however, others were unconvinced, arguing that everything is open to ____________.",
"answer": "interpretation",
"created_at": "2014-11-09T14:59:38.154",
"updated_at": "2014-11-09T14:59:38.154",
"filled_answer": null
},
{
"id": 922,
"prompt": "His existential quest for Truth is in fact the key to his understanding and ____________ of the Bhagavad-Gītā.",
"answer": "interpretation",
"created_at": "2014-10-03T08:07:40.295",
"updated_at": "2014-10-03T08:07:40.295",
"filled_answer": null
},
}
There are two problems with the questions subdocument that I am struggling with:
Sometimes but not always there is a legacy "id" field that I want to $unset but my query is not working.
I want to add an _id ObjectID field where they do not already exist. Currently some have them and some don't.
I have tried a number of queries but none seem to work. For example:
db.droplets.updateMany({"questions.$._id": { $exists: false }},{ $set: {"questions.$._id": new ObjectId()}},{"multi": true, "upsert": true})
Mongo tells me "The positional operator did not find the match needed from the query"
Update
I have successfully found a way to delete all the questions using the following script:
db.droplets4.find().forEach(function (doc) {
doc.questions.forEach(function (question) {
if (question.id) {
delete question.id
}
});
db.droplets.save(doc);
});
But the same strategy is not working for adding Object IDs. This code does not work:
db.droplets4.find().forEach(function (doc) {
doc.questions.forEach(function (question) {
if (!question._id) { question._id = new ObjectId() }
});
db.droplets.save(doc);
});
This should work fine for you
db.droplets4.updateMany( {
"questions._id" : null
},{ $set: {"questions.$._id": new ObjectId()}},{"multi": true, "upsert": true})

MongoDB updating wrong subdocument in an array

my gamefamilies collection looks like this
{
"_id": ObjectId('54cc3ee7894ae60c1c9d6c74'),
"game_ref_id": "REF123",
..
"yearwise_details": [
{
"year": 1,
...
"other_details": [
{
"type": "cash",
"openingstock": 988
..
},
{
"type": "FLU",
"openingstock": 555
..
},
..other items
]
},
{
"year": 2,
...
"other_details": [
{
"type": "cash",
"openingstock": 3000,
....
},
...
{
"type": "ghee",
"openingstock": 3000,
...
},
..
]
}
]
}
My update query
db.gamefamilies.update({"game_ref_id": "REF123", "teamname": "manisha","yearwise_details.year": 2, "yearwise_details.other_details.type": "ghee"}, {"$set": {"yearwise_details.0.other_details.$.openingstock": 555} });
Document is getting picked up correctly. I expect to update year 2's item type="ghee" but instead year 1's 2nd item (type FLU) gets updated. What am I doing wrong ?
Any help would be greatly appreciated.
regards
Manisha
Unfortunately, there is not yet support for nested $ positional operator updates.
So you can hardcode the update with
db.gamefamilies.update({"game_ref_id": "REF123",
"teamname": "manisha",
"yearwise_details.year": 2,
"yearwise_details.other_details.type": "ghee"},
{"$set":
{"yearwise_details.1.other_details.$.openingstock": 555}});
But notice that the yearwise_details.1.other_details is hardcoding that you want the second value of the array (it is 0-indexed, so the 1 is referencing the second element). I am assuming you found the command you have in your question because it worked for the first element of the array. But it will only ever work on the first element and the command above will only ever work on the second element.

Updating embedded document property in Mongodb

I have a document that looks like this:
{
"_id": 3,
"Slug": "slug",
"Title": "title",
"Authors": [
{
"Slug": "slug",
"Name": "name"
}
]
}
I want to update all Authors.Name based on Authors.Slug.
I tried this but it didn't work:
.update({"Authors.Slug":"slug"}, {$set: {"Authors.Name":"zzz"}});
What am I doing wrong here?
.update(Authors:{$elemMatch:{Slug:"slug"}}, {$set: {'Authors.$.Name':"zzz"}});
You can use update with array filters:
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#positional-update-arrayfilters
Probably something like this:
yourcollection.update(
{},
{
"$set": {
"Authors.$[element].Name": "zzz"
}
},
{
"multi": true,
"arrayFilters": [
{ "element.Slug": "slug" }
]
}
)
Ps.: it will not work in Robo3T as explained here: Mongodb 3.6.0-rc3 array filters not working?
However, you can try on a mongo shell with version >= 3.6.
yes, Rock's solution is working, P.S Notes is really helpful when trying Robo31..
If we want to update all
db.collection_name.update({}, {$set: {"Authors.$[].Name": "zzz"}})
If we want to update with matching object in an array
db.collection_name.update({}, {$set: {"Authors.$[i].Name": "zzz"}}, {arrayFilters: [{"i.Slug": "slug"}]})
Ref: https://jira.mongodb.org/browse/SERVER-1243