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)
}
Related
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')]"
]
}
}
}
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} }}})
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.
Given the following data structure:
{
"comments" : {
"-JcBbk64Gpm1SKoFHv8b" : {
"content" : "blah",
"createdAt" : 1417550954985,
"link" : "http%3A%2F%2Flocalhost%3A3000%2F",
"recommendedCount" : 0,
"replies" : {
"-JcBbk8gF_nQ_vjwag61" : true
},
"replyCount" : 1
},
"-JcBbk8gF_nQ_vjwag61" : {
"content" : "blah blah",
"createdAt" : 1417550955151,
"link" : "http%3A%2F%2Flocalhost%3A3000%2F",
"recommendedCount" : 0,
"replyCount" : 1,
"replyToComment" : "-JcBbk64Gpm1SKoFHv8b"
}
},
"links" : {
"http%3A%2F%2Flocalhost%3A3000%2F" : {
"author" : 5,
"commentCount" : 2,
"comments" : {
"-JcBbk64Gpm1SKoFHv8b" : true,
"-JcBbk8gF_nQ_vjwag61" : true
},
"createdAt" : 1417550954931,
"ratingCount" : 2,
"recommendedCount" : 32,
"score" : 91,
"title" : "A Christian vs. an Atheist: Round 2",
"url" : "http://localhost:3000/"
}
}
}
I would like to retrieve all the comments for a given link and sort them in reverse chronological order. I've gotten this far, but I can't figure out how to do the reverse sort because I've already used orderByChild to narrow down the results by link:
ref.orderByChild('link').equalTo(currentLink).on('value', function (snap) {
console.log(snapshot.val());
});
If I call orderByChild() a second time like this:
ref.orderByChild('link').equalTo(currentLink).orderByChild('createdAt').on('value', function (snap) {
console.log(snapshot.val());
});
it fails with this error message:
Uncaught Error: Query.orderByChild: You can't combine multiple orderBy calls.
I'm stumped. Any suggestions?
I encountered the same issues on my firebase project and I solved that like this.
Please take a look.
var formRef = firebase.child('links').child(currentLink);
formRef.orderByChild('createdAt').on('value', function (snap) {
console.log(snapshot.val());
});
It will works.
Best Regards.
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} ... }