I am trying to build in one aggregation query a match text search followed by another match query to avoid sending multiple queries to the database, but I am not receiving any result.
Here is what my query looks like:
query = await Product.aggregate([
{ $match: { $text: { $search: userQuery } } },
{ $addFields: { score: { $meta: "textScore" } } },
{
$facet: {
Forfait: [
{
$match: { Forfait: "midnight" }
}
]
}
}
]).exec();
Here is what I am trying to accomplish:
1. Perform a text search on user input on the collection
2. and also perform a match on the collection on the forfait field
The query returns nothing, it seems that it is performing a sort of logical AND query and short-circuits but I don't know how I can perform the two queries in one aggregation.
I have tried following a similar issue here MongoDB Facet Error pipeline requires text score metadata, but there is no text score available which reflects my query.
Any directions or help will be greatly appreciated.
Below is what the document model looks just a simple doc
{
id: String
title: String
description: String
forfait: String
}
Related
I'm working on building a database from a set of CSV files and am running into issues pushing elements to a deeply nested array. I've seen examples for 2D arrays and the use of positional operators but I can't quite figure out how to use them for my situation.
What I am trying to do read the CSV file which has columns for the answer_id and the url string for the photo. I want to push the url string to the photos array for the corresponding answer_id. When I try to use the code below, I get a long error message which starts with:
MongoBulkWriteError: Cannot create field 'answers' in element
{
results: [
{
question_id: "1",
_id: ObjectId('6332e0b015c1d1f4eccebf4e'),
answers: [
{
answer_id: "5",
//....
}
],
}
]
}
I formatted the above message to make things easier to read. It may be worth noting the first row of my CSV file has '5' in the answer_id column which makes me think things are failing at the first try to update my document.
Here is an example of my code:
const ExampleModel = new Schema({
example_id: String,
results: [
{
question_id: String,
answers: [
{
answer_id: String,
photos: [
{ url: String },
]
}
]
}
]
});
// Example Operation
// row.answer_id comes from CSV file
updateOne: {
'filter': {
'results.answers.answer_id': row.answer_id,
},
'update': {
'$push': {
'results.answers.$.photos' : { 'url': 'test'}
}
}
}
I guess my question is can I update an array this deeply nested using Mongoose?
I have the problem that I can do the full text search with one word smoothly, but how can I search for several parameters or rather enter them?
my function:
export const searching = (req: Request, res: Response) => {
Company.find({ $or: [{ $text: { $search: req.query.search as string } }] }).exec((err, docs) => {
console.log(req.query.search?.toString()!)
if (docs) {
res.status(200).json(docs)
} else {
console.log(err)
}
})
}
I want to combine two parameters with AND and thus search for the data containing exactly these two parameters and nothing else.
How can I implement this?
You can use the aggregate function to do this.
Company.aggregate([
{
$match : {$fieldName : 1st phrase here}
},
{
$match : {$fieldName : 2nd phrase here}
}
])
Code explanation :
In the first stage, mongodb will find all documents with the first phrase that you have entered. This is basically what your current query does. Now, the output of this stage will be the input of next stage where we match the 2nd phrase.
Hence, we are filtering the documents in a step-wise process. First filter the documents having the 1st phrase then find out the documents having the 2nd phrase from the previous result.
I have a mongo query that projects what I ned accordingly.
When I lookup to another collection (table), I want to ensure all the previous projected results do not match anything in the new collection I am looking into.
The projected results are as follows:
[
{ _id: 5d2fe82c4bd12894218deeb2, result: '5d3f6e86f8e09c10c4258c64' },
{ _id: 5d2fe90c4bd12894218deeb3, result: '5d3f6e9ff8e09c10c4258c68' }
]
After the projected result, bellow is the lookup query:
{$lookup:{
from:'Room',
let:{result:'$result'},
pipeline:[{$match:{$expr:{$and:[ {$ne:['$$result','$host_id']}, {$ne:['$host_id',myId]} ]}}}],
as:'publicHostings'
}},
{$unwind:'$publicHostings'}
The lookup query generates the following:
[
{
_id: 5d2fe90c4bd12894218deeb3,
result: '5d3f6e9ff8e09c10c4258c68',
publicHostings: {
_id: 5e03daaaf2496202ae4eb7c1,
host_id: '5d3f6e86f8e09c10c4258c64',
host_name: 'Z',
}
}
]
One can see that in one of the projected result that is generated early, matches the host_id. So it should not generate the final result.
**Actually it would generate one more document but I have a check ({$ne:['$host_id',myId]})
I want to try $elemMatch but I do not know how to do it with pipeline.
As usual, thank you for the help.
I hit an API which follows 50 members' data in a game once a day, and use mongoose to convert the JSON into individual documents in a collection. Between days there is data which is consistent, for example each member's tag (an id for the member in game), but there is data which is different (different scores etc.). Each document has a createdAt property.
I would like to find the most recent document for each member, and thus have an array with each member's tag.
I an currently using the following query to find all documents where tags match, however they are returning all documents, not just one. How do I sort/limit the documents to the most recent one, whilst keep it as one query (or is there a more "mongodb way")?
memberTags = [1,2,3,4,5];
ClanMember.find({
'tag': {
$in: memberTags
}
}).lean().exec(function(err, members) {
res.json(members);
});
Thanks
You can query via the aggregation framework. Your query would involve a pipeline that has stages that process the input documents to give you the desired result. In your case, the pipeline would have a $match phase which acts as a query for the initial filter. $match uses standard MongoDB queries thus you can still query using $in.
The next step would be to sort those filtered documents by the createdAt field. This is done using the $sort operator.
The preceding pipeline stage involves aggregating the ordered documents to return the top document for each group. The $group operator together with the $first accumulator are the operators which make this possible.
Putting this altogether you can run the following aggregate operation to get your desired result:
memberTags = [1,2,3,4,5];
ClanMember.aggregate([
{ "$match": { "tag": { "$in": memberTags } } },
{ "$sort": { "tag": 1, "createdAt: -1 " } },
{
"$group": {
"_id": "$tag",
"createdAt": { "$first": "$createdAt" } /*,
include other necessary fields as appropriate
using the $first operator e.g.
"otherField1": { "$first": "$otherField1" },
"otherField2": { "$first": "$otherField2" },
...
*/
}
}
]).exec(function(err, members) {
res.json(members);
});
Or tweak your current query using find() so that you can sort on two fields, i.e. the tag (ascending) and createdAt (descending) attributes. You can then select the top 5 documents using limit, something like the following:
memberTags = [1,2,3,4,5];
ClanMember.find(
{ 'tag': { $in: memberTags } }, // query
{}, // projection
{ // options
sort: { 'createdAt': -1, 'tag': 1 },
limit: memberTags.length,
skip: 0
}
).lean().exec(function(err, members) {
res.json(members);
});
or
memberTags = [1,2,3,4,5];
ClanMember.find({
'tag': {
$in: memberTags
}
}).sort('-createdAt tag')
.limit(memberTags.length)
.lean()
.exec(function(err, members) {
res.json(members);
});
Ok, so, first, let's use findOne() so you get only one document out of the request
Then to sort by the newest document, you can use .sort({elementYouWantToSort: -1}) (-1 meaning you want to sort from newest to oldest, and 1 from the oldest to the newest)
I would recommend to use this function on the _id, which already includes creation date of the document
Which gives us the following request :
ClanMember.findOne({
'tag': {
$in: memberTags
}
}).sort({_id: -1}).lean().exec(function(err, members) {
res.json(members);
});
I have this Mongoose Query:
return Domain.find({domain:req.params.domain})
.where('date').equals(date)
.exec(function (err, domain) {
if (!err) {
if (!isEmpty(domain[0].visitors)) {
domain[0]['visitors'] = domain[0].visitors.slice(0,99);
}
}
I want to slice directly in the database and not after retrieving object. Mongoose cannot do this or it is not documented or I did not find documentation.
So I fall back to node.js native MongoDB Driver by using the collection keyword.
My code looks now as follow and fails:
return Domain.collection.find(
{ "domain":req.params.domain },
{ "date":date },
{ "visitors": { $slice:100 } },
function(err,domain){
if (!err) {
res.status(200).send({
domain:domain
});
}
Full code: https://gist.github.com/nottinhill/b3837d4c913b9e5dd879
I tried in MongoDB console to construct a query that will work, but cannot get this simple query to work. MongoDB documentation does not show how to query with two conditions. I want to:
Pseudo-Code of what I want:
find
giveBack wholeDomainObject
where domain == domain
where date == date
also slice visitorsArray
Projection is a single object definition. Also you "query" for things rather than ask for matches in projections other than specific fields matching criteria. $slice is a special case that does not exclude other fields in the projection by default:
Domain.collection.find(
{ "domain":req.params.domain, "date": date },
{ "visitors": { "$slice":100 } },
function(err,domain){
// process results here
}
);
Probably also to note that the $slice here ( just like JavaScript ) is a defined "number of entries" and not a n-1 reference as in an array index.