MongoDB / Mongoose - Return only array items that matches $text - mongodb

I have the following object:
user: {
_id: "xxx",
name: "Lucas",
items: [
{ name: "shoes", description: "nice shoes" },
{ name: "pants", description: "old pants" },
],
places: [
{name: "my house", loc: { type: "Point", coordinates: [-27, -43] }}
]
}
I need to perform a text-search ($text) that returns only the items.
For example:
await User.find({ $text: { $search: "shoes" } });
This works! But it also returns pants, since it returns the user and not only the array item. And that is the problem, i need to paginate over the items in my database. So I need to return only the array items that matches the $text search. I know that if items was a collection itself it would work, but in my case I need those inside the user because I combine $text for items and $geoWithin for places.
So, how do I return the User keeping only his items that matched my $text search?

Did you try to use regex
To include a regular expression in a comma-separated list of query conditions for the field, use the $regex operator. For example:
{ name: { $regex: /acme.*corp/i, $nin: [ 'acmeblahcorp' ] } }
or smth like
db.users.find({"name": /^m/})
https://docs.mongodb.com/manual/reference/operator/query/regex/

Related

mongodb - how to insert a new key/value on each array's element if not present (with mongo query)

I would like to update each elements (object) in an array of a company.
Here my actual data :
{
_id: ObjectId("60d31024860ce0400b586111")
contracts:
[
{
name: 1.pdf
url: "https://someurl"
createdAt: 2021-06-23T10:42:44.594+00:00
}
{
name: 2.pdf
url: "https://someurl"
}
{
name: 3.pdf
url: "https://someurl"
}
]
}
I would like to add a defined date on each object (in contracts) that has no "updatedAt" key.
Here what I tried :
db.companies.update({ _id: ObjectId("60d31024860ce0400b586111"),"contracts.createdAt": { $exists: false } },{ $set: { "contracts.$.createdAt": "test" } })
but I got this error :
"The positional operator did not find the match needed from the query."
I have also tried this and it works, but I don't wanna query by file name. I just wanna add "createdAt" on each elements found that has no "createdAt"
db.companies.update({ "contracts.name": "2.pdf" },{ $set: { "contracts.$.createdAt": "atest" } })
I think you need to use the filtered position operator:
$ - updates the first matched array element
$[] - updates all the matched elements with a specific condition
The specific condition is mentioned in the arrayFilters key.
db.students.update(
{ },
{ $set: { "contracts.$[element].createdAt" : "atest"} },
{ multi: true,
arrayFilters: [ { "element.createdAt": { $exists: false } } ]
}
)
multi - true is to apply the operation on all the matching documents.
Also notice, how the first query parameter is empty, which means the query runs for all documents. I used it based on the second query you wrote but you can also add in an ObjectID query there.

Mongodb: Get last item from array in document

I have a collection in MongoDB with elements looking like this
{
userId: 'X',
access: [
{ deviceId: 'a', time: "A timestamp" },
{ deviceId: 'b', time: "Another timestamp" },
]
}
I want to match documents based on userId and then I want to get the last element in the access array. The value I am most interested in here for user with id "X" is "Another timestamp".
I do not want mongodb to return the entire document, just that last element and always the last one.
How can I write a query/aggregation that solves this?
Try using $slice:
db.collection.find({ userId: 'value' }, { access: { $slice: -1 } } )

MongoDB: Get documents depending on a search term for array field and string field

I need to perform a search on my mongoDB collection based on two fields: title, which is a string, and tags, which is an array.
Assume this is a sample data for the collection:
{
title: 'A tornado article',
tags: [
'nature',
'storm'
]
},
{
title: 'Just another article',
tags: [
'hurricane',
'type I'
]
},
{
title: 'Different article',
tags: [
'tornado',
'type II'
]
}
and my search string is const term = 'tornado type I', then I should get all documents as
the first one has tornado in the title
the second one has an tag type I
the third one has an tag tornado
I tried to start with this to perform the search:
Collection.find(
{
$or: [
{ 'meta.title': new RegExp(term, 'i') },
{ 'meta.tags': term }
]
}
)
So with this I would get the first document if the search term would be just tornado. With my sample term string I don't get any result.
If you wish to return an object that contains any of the tags you need to the the $in operator, it selects the documents where the value of a field equals any value in the specified array.
However to do so you need to pass it an array of your terms, to do so I've splitted your terms on
" " and passed that variable along to the $in like so
var term = 'tornado type I nature';
var splitted = term.split(' ');
Collection.find({
$or: [{
'title': new RegExp(term, 'i')
}, {
"tags": {
$in: splitted
}
}
]
})
Or if you also use to wish the regex to search through the tags you could join the array with | to build a regex statement after which you can pass it along like this.
var term = 'tornado type I nature';
var splitted = term.split(' ');
var splittedRegex = new RegExp(splitted.join('|'), 'i');
Colleciton.find({
$or: [{
'title': new RegExp(term, 'i')
}, {
"tags": {
$in: [splittedRegex]
}
}
]
})

MongoDB Regex $and $or Search Query

I am trying to construct a query that will accept multiple fields that can be searched over using regex for partial field matching that also has a hard constraint on other fields.
Example:
Collection: "Projects"
Required Information: { propertyId: "abc", clientId: "xyz" }
Fields to be Searched: name, serviceType.name, manager.name
Currently, I have a query like this, but if there are no results it returns all the results, which isn't helpful.
{
'$and': [
{ propertyId: '7sHGCHT4ns6z9j6BC' },
{ clientId: 'xyz' },
{ '$or':
[
{ name: /HVAC/gi },
{ 'serviceType.name': /HVAC/gi },
{ 'manager.name': /HVAC/gi }
]
}
]
}
If anyone has any insight into this it would be much appreciated.
Example Document:
{
_id: "abc",
propertyId: "7sHGCHT4ns6z9j6BC",
clientId: "xyz"
name: "16.000.001",
serviceType: {
_id: "asdf",
name: "HVAC"
},
manager: {
_id: "dfgh",
name: "Patrick Lewis",
}
}
The expected result is to only find documents where propertyId = 7sHGCHT4ns6z9j6BC AND one at least one of the following keys: name, serviceType.name, or manager.name match an inputted string, in this case, it's HVAC and if none of the regex fields match, then return nothing.
UPDATE
The issue was with MongoDB, after restarting it, everything worked.
Try following script:
db.collection.find({
$and:[
{propertyId:"7sHGCHT4ns6z9j6BC"},
{
$or:[
{name: /HVAC/i},
{"serviceType.name": /HVAC/i},
{"manager.name": /HVAC/i}
]
}]
})
Query above will return a document or documents if and only if propertyId matches and either of name, serviceType.name or manager.name matches desired regex.

Find matching partial string

I can't seem to find an answer on google, nor the mongoose website, so I am asking here. If it is possible, how can I search for a partially matching string in a document.
ex:
In a user collection:
{ name: "Louis", location: "Paris, France" },
{ name: "Bill", location: "Paris, Illinoid" },
{ name: "Stephen", location: "Toronto, Ontario" }
mongoose function:
searchForCity("Paris");
The result would be a list of documents from the User collection having "Paris" in the location String. ex:
[
{ name: "Louis", location: "Paris, France" },
{ name: "Homer", location: "Paris, Illinois" }
]
You could use a regex for that:
Query#regex, Query#$regex
Specifies the $regex operator.
query.regex('name.first', /^a/i)
So something like this:
User.where('location').$regex(/Paris/);
User.where('location').$regex(/paris/i); // Case insensitive version
Keep in mind that regex queries can be very expensive as MongoDB will have to run the regex against every single location. If you can anchor your regexes at the beginning:
User.where('location').$regex(/^Paris/);
then you can index the location and MongoDB will use that index.