MongoDB nested array query how to - mysql

I am trying to query a document in my MongoDB
Document:
{
_id: '111',
subEntities: [
{
subId: '999',
dateOfStart: '2098-01-01',
dateOfTermination: '2099-12-31'
},
{
subId: '998',
dateOfStart: '2088-01-01',
dateOfTermination: '2089-12-31'
}
]
}
My Query:
{"$and": [
{"subEntities.dateOfStart": {"$lte": "2098-01-02"}},
{"subEntities.dateOfTermination": {"$gte": "2099-12-30"}},
{"subEntities.subId": {"$in": ["998"]}}
]}
As you can see, I am trying to apply a date value and an ID to the subentities.
The date value should be between dateOfStart and dateOfTermination.
The query returns a match, although the date value only matches the first subentity and the ID query matches the second subquery.
How can I make it so that there is only one match when both queries match the same subentity?
Can I aggregate the subentities?
Thanks a lot!

When you query arrays Mongo by default "flattens" them, which means each condition of the query get's executed independently.
You want to be using $elemMatch, this allows you to query full objects from within an array, like so:
db.collection.find({
subEntities: {
$elemMatch: {
dateOfStart: {
"$lte": "2098-01-02"
},
dateOfTermination: {
"$gte": "2099-12-30"
},
subId: {
"$in": [
"998"
]
}
}
}
})
Mongo Playground

If you want to filter dates between dateOfStart and dateOfTermination you should invert the $gte and $lte conditions:
{
"$and": [
{ "subEntities.dateOfStart": { "$gte": "2098-01-02" } },
{ "subEntities.dateOfTermination": { "$lte": "2099-12-30" } },
{ "subEntities.subId": { "$in": ["998"] } }
]
}

Related

mongosh: How to create ascending date object for every record in a collection?

I have a small collection with records of the format:
db.presentations =
[
{
"_id": "1",
"student": "A",
"presentationDate": {
"$date": "2023-01-17T00:00:00Z"
}
},
{
"_id": "2",
"student": "B",
"presentationDate": {
"$date": "2023-01-17T00:00:00Z"
}
},
...
,
{
"_id": "26",
"student": "Z",
"presentationDate": {
"$date": "2023-01-17T00:00:00Z"
}
},
]
Instead of all the presentationDates being the same, I want to set them to an ascending order. So, student A's presentationDate is 2023-01-17, student B's is 2023-01-18, student C's is 2023-01-19, and so on.
I've been exploring some functions that could do this, but none really seem to fit what I'm trying to do, eg:
$dateAdd: allows specification of the unit and amount (eg, day, 3) by which to increase a date object, but it must be used as part of an aggregation pipeline. I don't see how to increment by variable amount for each document.
forEach() / map(): allows flexibility in function applied to each record, but again, I don't see how to increment by variable (uniformly increasing) amount for each document. I'm also not sure it's possible to edit documents within a forEach?
Put another way, I'm basically trying to iterate through my cursor/collection and update each document, incrementing a global variable on each itereation.
I'm new to mongosh, so any ideas, feedback are appreciated!
Of course you could select the data, iterate over all documents, change the value and save back. You can also do it with an aggregation pipeline like this:
db.collection.aggregate([
{
$setWindowFields: {
sortBy: { student: 1 },
output: {
pos: { $documentNumber: {} }
}
}
},
{
$set: {
presentationDate: {
$dateAdd: {
startDate: "$presentationDate",
unit: "day",
amount: "$pos"
}
}
}
}
])
If you like to modify the data, then use
db.collection.updateMany({}, [
{
$setWindowFields: {
sortBy: { student: 1 },
output: {
pos: { $documentNumber: {} }
}
}
},
{
$set: {
presentationDate: {
$dateAdd: {
startDate: "$presentationDate",
unit: "day",
amount: "$pos"
}
}
}
}
])

MariaDB-JSON-document- Not able to select only specific elements from array without knowing his index after applying exact where clause

I have JSON document. structure/sample data is like -
{
"id":"201",
"portfolio":[
{
"portfolio_id":"PORTFOLIO_001",
"portfolio_name":"AAA",
"product":[
{
"product_id":"PORTFOLIO_001_PRODUCT_001",
"product_trigram":"PORTFOLIO_001_PRODUCT_001_1",
"product_name":"PORTFOLIO_001_PRODUCT_001_NAME"
},
{
"product_id":"PORTFOLIO_001_PRODUCT_002",
"product_trigram":"PORTFOLIO_001_PRODUCT_002_1",
"product_name":"PORTFOLIO_001_PRODUCT_002_NAME"
}
]
},
{
"portfolio_id":"PORTFOLIO_002",
"portfolio_name":"BBB",
"product":[
{
"product_id":"PORTFOLIO_002_PRODUCT_001",
"product_trigram":"PORTFOLIO_002_PRODUCT_001_1",
"product_name":"PORTFOLIO_002_PRODUCT_001_NAME"
}
]
}
]
}
I have written select SQL as below. I want to fetch products of that specific portfolio(PORTFOLIO_001) and id. I am getting all products of all portfolios instead of products of specific portfolio(PORTFOLIO_001)
SELECT json_extract(j, '$.portfolio[*].product') FROM t WHERE json_contains(json_extract(j, '$.portfolio[*].portfolio_id'), '"PORTFOLIO_001"') AND JSON_CONTAINS(j, '"201"', '$.id')
Actual output -
[
[
{
"product_id":"PORTFOLIO_001_PRODUCT_001",
"product_trigram":"PORTFOLIO_001_PRODUCT_001_1",
"product_name":"PORTFOLIO_001_PRODUCT_001_NAME"
},
{
"product_id":"PORTFOLIO_001_PRODUCT_002",
"product_trigram":"PORTFOLIO_001_PRODUCT_002_1",
"product_name":"PORTFOLIO_001_PRODUCT_002_NAME"
}
],
[
{
"product_id":"PORTFOLIO_002_PRODUCT_001",
"product_trigram":"PORTFOLIO_002_PRODUCT_001_1",
"product_name":"PORTFOLIO_002_PRODUCT_001_NAME"
}
]
]
Expected output -
[
{
"product_id":"PORTFOLIO_001_PRODUCT_001",
"product_trigram":"PORTFOLIO_001_PRODUCT_001_1",
"product_name":"PORTFOLIO_001_PRODUCT_001_NAME"
},
{
"product_id":"PORTFOLIO_001_PRODUCT_002",
"product_trigram":"PORTFOLIO_001_PRODUCT_002_1",
"product_name":"PORTFOLIO_001_PRODUCT_002_NAME"
}
]
Can someone please help me to resolve this issue ?
Thanks in advance for your action :-)
Regards,
Prashant

MongoDB Split document field into two fields

I have a MongoDB document with over 2.8m documents of common passwords (hashed in SHA1) and their popularity.
Currently I've imported the documents with the following schema
{"_id":"5ded1a559015155eb8295f48","password":"20EABE5D64B0E216796E834F52D61FD0B70332FC:2512537"}
Although I'd like to split this so I can have the popularity value and it would look something like this
{"_id":"5ded1a559015155eb8295f48","password":"20EABE5D64B0E216796E834F52D61FD0B70332FC","popularity":2512537}
Question is im unsure how I can split the password into two password, popularity using : to split the string
You can use Aggregation Framework to split current password into two fields. You need to start with $indexOfBytes to get the position of : and then you need $substr to create new fields based on evaluated position.
db.collection.aggregate([
{
$addFields: {
colonPos: { $indexOfBytes: ["$password",":"] }
}
},
{
$addFields: {
password: { $substr: [ "$password", 0, "$colonPos" ] },
popularity: { $substr: [ "$password", "$colonPos", { $strLenBytes: "$password" } ] }
}
},
{
$project: {
colonPos: 0
}
}
])
Mongo Playground
As a last step you can use $out which takes all your aggregation results and writes them into new or existing collection.
EDIT: Alternative approach using $split (thank to #matthPen):
db.collection.aggregate([
{
$addFields: {
password: { $arrayElemAt: [ { "$split": [ "$password", ":"] }, 0 ] },
popularity: { $arrayElemAt: [ { "$split": [ "$password", ":"] }, 1 ] }
}
}
])
Mongo Playground

Return selected JSON object from mongo find method

Here is the sample JSON
Sample JSON:
[
{
"_id": "123456789",
"YEAR": "2019",
"VERSION": "2019.Version",
"QUESTION_GROUPS": [
{
"QUESTIONS": [
{
"QUESTION_NAME": "STATE_CODE",
"QUESTION_VALUE": "MH"
},
{
"QUESTION_NAME": "COUNTY_NAME",
"QUESTION_VALUE": "IN"
}
]
},
{
"QUESTIONS": [
{
"QUESTION_NAME": "STATE_CODE",
"QUESTION_VALUE": "UP"
},
{
"QUESTION_NAME": "COUNTY_NAME",
"QUESTION_VALUE": "IN"
}
]
}
]
}
]
Query that am using :
db.collection.find({},
{
"QUESTION_GROUPS.QUESTIONS.QUESTION_NAME": "STATE_CODE"
})
My requirement is retrive all QUESTION_VALUE whose QUESTION_NAME is equals to STATE_CODE.
Thanks in Advance.
If I get you well, What you are trying to do is something like:
db.collection.find(
{
"QUESTION_GROUPS.QUESTIONS.QUESTION_NAME": "STATE_CODE"
},
{
"QUESTION_GROUPS.QUESTIONS.QUESTION_VALUE": 1
})
Attention: you will get ALL the "QUESTION_VALUE" for ANY document which has a QUESTION_GROUPS.QUESTIONS.QUESTION_NAME with that value.
Attention 2: You will get also the _Id. It is by default.
In case you would like to skip those issues, you may need to use Aggregations, and unwind the "QUESTION_GROUPS"-> "QUESTIONS". This way you can skip both the irrelevant results, and the _id field.
It sounds like you want to unwind the arrays and grab only the question values back
Try this
db.collection.aggregate([
{
$unwind: "$QUESTION_GROUPS"
},
{
$unwind: "$QUESTION_GROUPS.QUESTIONS"
},
{
$match: {
"QUESTION_GROUPS.QUESTIONS.QUESTION_NAME": "STATE_CODE"
}
},
{
$project: {
"QUESTION_GROUPS.QUESTIONS.QUESTION_VALUE": 1
}
}
])

Is there a way to see if a query matches any element in an array in Elasticsearch?

I have this query (e.g. 'hello') and this id (e.g. '12345') and I want to search for something that matches both the query in a 'text' field and the id in a 'thread' field. But the given ids are in an array, so the logic is something like:
function runThisQuery(query, ids) {
client.search({
index: '_all',
type: 'text',
body: {
query: {
bool: {
must: {
match: { text: query }
},
should: [
{ match: { thread: { query: ids[0], operator: 'AND'} } },
{ match: { thread: { query: ids[1], operator: 'AND'} } }
],
minimum_should_match: 1
}
}
}
})
}
Is there like an $in operator (like in MongoDB) that matches the thread if it's in the 'ids' array? Thanks!
You can use an ids query like this
{
"filter": {
"ids": {
"type": "my_type",
"values": [
"12345","67891","12346"
]
}
}
}