Mongo Shell Find document with nested arrays [duplicate] - javascript

I have some data that looks like this:
[
{
"_id" : ObjectId("4e2f2af16f1e7e4c2000000a"),
"advertisers" : [
{
"created_at" : ISODate("2011-07-26T21:02:19Z"),
"category" : "Infinity Pro Spin Air Brush",
"updated_at" : ISODate("2011-07-26T21:02:19Z"),
"lowered_name" : "conair",
"twitter_name" : "",
"facebook_page_url" : "",
"website_url" : "",
"user_ids" : [ ],
"blog_url" : "",
},
and I was thinking that a query like this would give the id of the advertiser:
var start = new Date(2011, 1, 1);
> var end = new Date(2011, 12, 12);
> db.agencies.find( { "created_at" : {$gte : start , $lt : end} } , { _id : 1 , program_ids : 1 , advertisers { name : 1 } } ).limit(1).toArray();
But my query didn't work. Any idea how I can add the fields inside the nested elements to my list of fields I want to get?
Thanks!

Use dot notation (e.g. advertisers.name) to query and retrieve fields from nested objects:
db.agencies.find({
"advertisers.created_at": {
$gte: start,
$lt: end
}
},
{
_id: 1,
program_ids: 1,
"advertisers.name": 1
}
}).limit(1).toArray();
Reference: Retrieving a Subset of Fields
and Dot Notation

db.agencies.find(
{ "advertisers.created_at" : {$gte : start , $lt : end} } ,
{ program_ids : 1 , advertisers.name : 1 }
).limit(1).pretty();

There is one thing called dot notation that MongoDB provides that allows you to look inside arrays of elements. Using it is as simple as adding a dot for each array you want to enter.
In your case
"_id" : ObjectId("4e2f2af16f1e7e4c2000000a"),
"advertisers" : [
{
"created_at" : ISODate("2011-07-26T21:02:19Z"),
"category" : "Infinity Pro Spin Air Brush",
"updated_at" : ISODate("2011-07-26T21:02:19Z"),
"lowered_name" : "conair",
"twitter_name" : "",
"facebook_page_url" : "",
"website_url" : "",
"user_ids" : [ ],
"blog_url" : "",
},
{ ... }
If you want to go inside the array of advertisers to look for the property created_at inside each one of them, you can simply write the query with the property {'advertisers.created_at': query} like follows
db.agencies.find( { 'advertisers.created_at' : { {$gte : start , $lt : end} ... }

Related

Mongodb query combining $text search with $regex

My dilemma is that mongodb $text searches must match an exact word: e.g. if trying to match a post with 'testing123' a search for 'test' will not match, but using $regex will match. Now, I want to make use of indexes too, but I also want partial matches.
My thinking is that I could combine them with an $or operator, but it is not working. Is this possible? Each query alone inside the $or work, but when combining them I get no matches.
If this is not possible, I have found a pretty good solution, here , but I would like the combined $or to work if possible, but any other suggestions are welcome.
const posts = await Post.find({
name: { $regex: 'foo', $options: 'i' },
$or: [
{ $text: { $search: text, $caseSensitive: false } },
{ text: { $regex: text, $options: 'i' } },
],
});
One way of doing this is to downcase the text into another field, then use $regex search on that field.
You have text that you want to search for any substring case insensitively:
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.insert({foo:'hello world TESTING123'})
WriteResult({ "nInserted" : 1 })
Step 1: add another field which stores the text in lower case.
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.insert({foo:'hello world TESTING123',foo_lower:'hello world testing123'})
Step 2: add index.
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.createIndex({foo_lower:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"commitQuorum" : "votingMembers",
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1597711723, 7),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1597711723, 7)
}
Step 3: downcase the query to "testing123"
Step 4: use $regex.
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find({foo_lower:{$regex:'testing123'}})
{ "_id" : ObjectId("5f3b2498f885e53d90f30979"), "foo" : "hello world TESTING123", "foo_lower" : "hello world testing123" }
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.find({foo_lower:{$regex:'testing123'}}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.foo",
"indexFilterSet" : false,
"parsedQuery" : {
"foo_lower" : {
"$regex" : "testing123"
}
},
"queryHash" : "0D14CC56",
"planCacheKey" : "1974A2D4",
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"filter" : {
"foo_lower" : {
"$regex" : "testing123"
}
},
"keyPattern" : {
"foo_lower" : 1
},
"indexName" : "foo_lower_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"foo_lower" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"foo_lower" : [
"[\"\", {})",
"[/testing123/, /testing123/]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "serene",
"port" : 14420,
"version" : "4.4.0",
"gitVersion" : "563487e100c4215e2dce98d0af2a6a5a2d67c5cf"
},
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1597711761, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1597711761, 1)
}

MongoDb - Do i need an index on a second field when using deleteOne

I have a simple collection events
{
"_id" : ObjectId("5e2a9bb9dcb646448f9409b3"),
"year" : 2020,
"employee_id" : "5e1afe5ab7bad92b20365476",
"event" : ["Holidays"],
"total" : 21,
"used" : 1
}
and i don't want to be able to delete the documents that have used field grater than 0.
I use this
db.collection('events').deleteOne({_id: ObjectId("5e2a9bb9dcb646448f9409b3"), used: 0});
Do i need to set an index on used field if i already use _id?
Thanks
Not necessary. MongoDB already uses _id (unique index created by MongoDB) to delete documents.
db.collection('events').find({_id: ObjectId("5e2a9bb9dcb646448f9409b3"), used: 0}).explain();
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"used" : {
"$eq" : 0.0
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : []
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"[ObjectId('5e2a9bb9dcb646448f9409b3'), ObjectId('5e2a9bb9dcb646448f9409b3')]"
]
}
}
}

MongoDB - Query nested objects in nested array

I'm kindly requesting your help for this query that I need to do and I'm not very proficient yet in MongoDB. My data structure looks like this:
db.getCollection('EventDateValidation').find({}):
/* 1 */
{
"_id" : ObjectId("5b7b2e3ae5e2100007717d81"),
"_class" : "com.overwatch.common.model.EventDateValidation",
"caseNo" : "OW000002269122201810201135",
"loanNo" : "000002269122",
"eventType" : "BREACLETTR",
"validationStepData" : [
{
"startDate" : {
"isChecked" : "Y",
"comments" : "",
"auditedBy" : "Mahalakshmi M",
"auditedDate" : "2018-12-12"
}
},
{
"completedDate" : {
"isChecked" : "Y",
"comments" : "",
"auditedBy" : "Mahalakshmi M",
"auditedDate" : "2018-12-13"
}
},
{
"deadlineDate" : {
"isChecked" : "Y",
"comments" : "",
"auditedBy" : "Mahalakshmi M",
"auditedDate" : "2018-12-13"
}
}
]
}
/* 2 */
{
"_id" : ObjectId("5b7c11095c2b4d0007bc8c54"),
"_class" : "com.overwatch.common.model.EventDateValidation",
"caseNo" : "OW000000854076201808181158",
"loanNo" : "000000854076",
"eventType" : "FORSALAPPR",
"validationStepData" : [
{
"startDate" : {
"comments" : ""
}
},
{
"completedDate" : {
"comments" : "Received Date = 8/4/2017"
}
},
{
"deadlineDate" : {
"comments" : ""
}
}
]
}
/* 3 */
{
"_id" : ObjectId("5b7ad05d5c2b4d0007bc8631"),
"_class" : "com.overwatch.common.model.EventDateValidation",
"caseNo" : "OW000000873954201810201235",
"loanNo" : "000000873954",
"eventType" : "HUDNOTIFCA",
"validationStepData" : [
{
"startDate" : {
"isChecked" : "Y",
"comments" : "",
"auditedBy" : "Brett Scott",
"auditedDate" : "2018-09-25"
}
},
{
"completedDate" : {
"isChecked" : "Y",
"comments" : "",
"auditedBy" : "Brett Scott",
"auditedDate" : "2018-09-25"
}
},
{
"deadlineDate" : {
"isChecked" : "Y",
"comments" : "",
"auditedBy" : "Brett Scott",
"auditedDate" : "2018-09-25"
}
}
]
}
From this collection, I need to find the documents that have an "auditedDate" in the "deadlineDate". In this example, I would find the documents 1 and 3. Please help me as I'm stuck on this one.
I have tried
db.getCollection('EventDateValidation').find({"validationStepData.deadlineDate.auditedDate":{$exists:true}})
But doesn't seem to work. Help please!
Just for clearing things up: the query in the question works well. I chatted with #Gabriel, and the problem was that Robomongo added hidden non-printable unicode characters to the query.
All in all, for any interested nomads, here are few ways to query an array of objects:
1) Implicit $elemMatch / simple dot notation on an array:
db.getCollection('EventDateValidation').find({"validationStepData.deadlineDate.auditedDate": {$exists:true}})
2) Explicit $elemMatch (we can have multiple query criteria):
db.getCollection('EventDateValidation').find({"validationStepData": { $elemMatch: {"deadlineDate.auditedDate" : {$exists:true} }}})
3) Array dot notation with an index position (when we know the exact position of an element inside an array):
db.getCollection('EventDateValidation').find({"validationStepData.2.deadlineDate.auditedDate": {$exists:true}})
Dot notation wouldn't work since you have an array of objects within validationStepData. You could use $elemMatch to apply your query conditions to the array elements that match your expression.
db.getCollection('EventDateValidation').find({"validationStepData" : { $elemMatch: {"deadlineDate.auditedDate" : {$exists:true} }}})

Apply an expression to each array element in document

I've got a sample document that I'm trying to project within a MongoDB aggregate pipeline. I'm testing with a single document that looks roughly like this:
{
"_id" : "",
"title" : "Questions",
"sortIndex" : 0,
"topics" : [
{
"_id" : "",
"title" : "Creating a Question",
"sortIndex" : 1,
"thumbnail" : "CreatingAQuestion.jpg",
"seenBy" : [ "user101", "user202" ],
"pages" : [
{
"visual" : "SelectPlanets.gif",
"text" : "Some Markdown"
}
]
},
{
"_id" : "",
"title" : "Deleting a Question",
"sortIndex" : 0,
"thumbnail" : "DeletingAQuestion.jpg",
"seenBy" : [ "user101" ],
"pages" : [
{
"visual" : "SelectCard.gif",
"text" : "Some Markdown"
}
]
}
]
}
The output I'm trying to obtain is something along these lines:
{
"_id" : "",
"title" : "Questions",
"topics" : [
{
"title" : "Creating a Question",
"thumbnail" : "CreatingAQuestion.jpg",
"seen" : true
},
{
"title" : "Deleting a Question",
"thumbnail" : "DeletingAQuestion.jpg",
"seen" : false
}
]
}
Specifically the bit I'm struggling with is the seen flag.
I've read the docs which state:
When projecting or adding/resetting a field within an embedded document...
... Or you can nest the fields:
contact: { address: { country: <1 or 0 or expression> } }
I wish to use an expression and I took note of the following:
When nesting the fields, you cannot use dot notation inside the embedded document to specify the field, e.g. contact: { "address.country": <1 or 0 or expression> } is invalid.
So I'm trying to work out how to "reference" a subdocument field within an expression. That quote suggests I can't use dot notation but when I can't seem to get it working with nested notation either. Here's what I've got so far:
db
.getCollection('chapters')
.aggregate([
{
$project: {
title: 1,
topics: {
title: 1,
thumbnail: 1,
publishedAt: 1,
test: "$seenBy",
seen: { $in: ["user202", "$seenBy"] },
}
}
}
])
So I've hard coded user202 into my query for now, and expected to see true and false for the 2 documents. I've also put in a test field to map out the seenBy field from the sub-document. What this produces is:
{
"_id" : "",
"title" : "Questions",
"topics" : [
{
"title" : "Creating a Question",
"thumbnail" : "CreatingAQuestion.jpg",
"test" : [
"user101",
"user202"
],
"seen" : true
},
{
"title" : "Deleting a Question",
"thumbnail" : "DeletingAQuestion.jpg",
"test" : [
"user101",
"user202"
],
"seen" : true
}
]
}
So obviously my "$seenBy" isn't accessing the correct topic because the test field contains the data from the 1st document.
So ultimately my question is, how can I access the seenBy field within a subdocument, referring to the current subdocument so I can create an expression?
Note: I have got this working with multiple $project and an $unwind but wanted to try compress/clean it up a bit.
You really need to use $map here. Simply notating the array in projection ( which is a bit of a boon since MongoDB 3.2 ) does not really cut it when you need a localized value for the current element. That is what you need and it's what $map provides:
db.getCollection('chapters').aggregate([
{ $project: {
title: 1,
topics: {
$map: {
input: "$topics",
as: "t",
in: {
title: "$$t.title",
thumbnail: "$$t.thumbnail",
publishedAt: "$$t.publishedAt",
test: "$$t.seenBy",
seen: { $in: ["user202", "$$t.seenBy"] },
}
}
}}
])
So for each element the current value of "seenBy" as a property is being tested by the expression. Without the $map that is not possible, and you can only really notate the "whole" array. Which is really not what you want to test here.

How to delete deep array in mongodb?

I am trying to delete "virtualNumber" : "12345" in the following document:
{
"_id" : ObjectId("50a9db5bdc7a04df06000005"),
"billingInfo" : null,
"date" : "dsfdsfsdfsd",
"description" : "sdfsdff",
"pbx" : {
"_id" : ObjectId("50a9db5bdc7a04df06000006"),
"did" : {
"1234567890" : {
"inventoryId" : "509df7547e84b25e18000001",
"didcountry" : "india",
"didState" : "bangalore",
"routeType" : "CallForward",
"didNumber" : "1234567890",
"didVirtualNumbers" : [
{
"virtualNumber" : "12345"
},
{
"virtualNumber" : "56789"
}
],
"id" : ObjectId("50a9db9acdfb4f9217000002")
}
},
},
I am using node.js, so I constructed a query in JavaScript:
var query = {_id: ObjectId("50a9db5bdc7a04df06000005")};
var obj = {};
obj["pbx.did.1234567890.didVirtualNumbers.virtualNumber"]=12345;
//problem
collection.update(query,{$pull:obj});
You need to match the array element like:
{"$pull": {"pbx.did.7259591220.didVirtualNumbers": {"virtualNumber": "12345"}}}
So you should change your code to:
obj["pbx.did.7259591220.didVirtualNumbers"]={"virtualNumber": "12345"};
Please refer to http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull
It mentions the pull field should be an array.

Categories