Related
I have a document contain title with "Hard work & Success". I need to do a search for this document. And if I typed "Hardwork" (without spacing) it didn't returning any value. but if I typed "hard work" then it is returning the document.
this is the query I have used :
const search = qObject.search;
const payload = {
from: skip,
size: limit,
_source: [
"id",
"title",
"thumbnailUrl",
"youtubeUrl",
"speaker",
"standards",
"topics",
"schoolDetails",
"uploadTime",
"schoolName",
"description",
"studentDetails",
"studentId"
],
query: {
bool: {
must: {
multi_match: {
fields: [
"title^2",
"standards.standard^2",
"speaker^2",
"schoolDetails.schoolName^2",
"hashtags^2",
"topics.topic^2",
"studentDetails.studentName^2",
],
query: search,
fuzziness: "AUTO",
},
},
},
},
};
if I searched for title "hard work" (included space)
then it returns data like this:
"searchResults": [
{
"_id": "92",
"_score": 19.04531,
"_source": {
"standards": {
"standard": "3",
"categoryType": "STANDARD",
"categoryId": "S3"
},
"schoolDetails": {
"categoryType": "SCHOOL",
"schoolId": "TPS123",
"schoolType": "PUBLIC",
"logo": "91748922mn8bo9krcx71.png",
"schoolName": "Carmel CMI Public School"
},
"studentDetails": {
"studentId": 270,
"studentDp": "164646972124244.jpg",
"studentName": "Nelvin",
"about": "good student"
},
"topics": {
"categoryType": "TOPIC",
"topic": "Motivation",
"categoryId": "MY"
},
"youtubeUrl": "https://www.youtube.com/watch?v=wermQ",
"speaker": "Anna Maria Siby",
"description": "How hardwork leads to success - motivational talk by Anna",
"id": 92,
"uploadTime": "2022-03-17T10:59:59.400Z",
"title": "Hard work & Success",
}
},
]
And if i search for the Keyword "Hardwork" (without spacing) it won't detecting this data. I need to make a space in it or I need to match related datas with the searching keyword. Is there any solution for this can you please help me out of this.
I made an example using a shingle analyzer.
Mapping:
{
"settings": {
"analysis": {
"filter": {
"shingle_filter": {
"type": "shingle",
"max_shingle_size": 4,
"min_shingle_size": 2,
"output_unigrams": "true",
"token_separator": ""
}
},
"analyzer": {
"shingle_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"shingle_filter"
]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "shingle_analyzer"
}
}
}
}
Now I tested it with your term. Note that the token "hardwork" was generated but the others were also generated which may be a problem for you.
GET idx-separator-words/_analyze
{
"analyzer": "shingle_analyzer",
"text": ["Hard work & Success"]
}
Results:
{
"tokens" : [
{
"token" : "hard",
"start_offset" : 0,
"end_offset" : 4,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "hardwork",
"start_offset" : 0,
"end_offset" : 9,
"type" : "shingle",
"position" : 0,
"positionLength" : 2
},
{
"token" : "hardworksuccess",
"start_offset" : 0,
"end_offset" : 19,
"type" : "shingle",
"position" : 0,
"positionLength" : 3
},
{
"token" : "work",
"start_offset" : 5,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "worksuccess",
"start_offset" : 5,
"end_offset" : 19,
"type" : "shingle",
"position" : 1,
"positionLength" : 2
},
{
"token" : "success",
"start_offset" : 12,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 2
}
]
}
I´m trying to make a query to my bbdd in order to get some info that involve two collections.
First, I have one collecction, called Collectables that is a collection that store all available items that One user can get using an App.
For example, this collection can have... 100 items. This is the maximum number of items.
This is one document of this collection (called collectables)
{
"_id" : ObjectId("5d387ecfbb676b173aa57fe3"),
"img" : "some url",
"name" : "La 5",
"__v" : 0,
"amount" : 17,
"available" : 16,
"collec" : ObjectId("5d36c0c34c86991db93bd7c8"),
"gen" : 3,
"metadata" : {},
"position" : 1
}
Then, I have another collection called AppUsers. In this collection I store all info related to the user. Each user of the App has his own record here. The point is that besides meta info of the user such alias, avatar, age... I have one field called collectables. Is an array. Here I store what collectable have each user.
For example, if one user have 10 collectables (from the other collection) I have 10 entries in this array with that info. Is possible that one user "win" the same item (collectable) twice or more.. so in this collection I store a count field with the total. For example, a user win the collectable with Id 1 the first time, so I add the entry in the array with count 1. If then the user win the same collectable, the count is 2... and so on.
This is an example of one user... with 3 collectables, but several number of each item.
{
"_id" : ObjectId("5d36dc9445526a215c4eff52"),
"twitter" : "1",
"alias" : "ViktorCrowley",
"__v" : 25,
"collectables" : [
{
"count" : 12,
"collectable" : ObjectId("5d36c1ba4c86991db93bd7e7")
},
{
"count" : 25,
"collectable" : ObjectId("5d36c13d4c86991db93bd7c9")
},
{
"count" : 8,
"collectable" : ObjectId("5d381e122f25221126a98f9c")
}
]
}
So, in this case, this user, for example have 3 differents items (collectables). But imagine that the total of collectables from the first collection is 100.
Now... what I´m looking for. I need a query that give me (paginated) the items from the first collection (collectables) and in the case that the user already have one of this items, marked with the total count. I mean, I want all the items from the first collection, with a new field, called count. If the user doens´t have any entry in his array, count will be 0, and if the user for that item, has for example 4 collectables, the count will be 4.
Some thing like this:
[
{
"_id" : ObjectId("5d387ecfbb676b173aa57fe3"),
"img" : "some url",
"name" : "THe one",
"__v" : 0,
"amount" : 17,
"available" : 16,
"collec" : ObjectId("5d36c0c34c86991db93bd7c8"),
"gen" : 3,
"metadata" : {},
"position" : 1,
"count" : 0
},
{
"_id" : ObjectId("5d387ecfbb676b173aa57fe3"),
"img" : "some url",
"name" : "The two",
"__v" : 0,
"amount" : 17,
"available" : 16,
"collec" : ObjectId("5d36c0c34c86991db93bd7c8"),
"gen" : 3,
"metadata" : {},
"position" : 2,
"count" : 1
},
{
"_id" : ObjectId("5d387ecfbb676b173aa57fe4"),
"img" : "some url",
"name" : "The Three",
"__v" : 0,
"amount" : 17,
"available" : 16,
"collec" : ObjectId("5d36c0c34c86991db93bd7c8"),
"gen" : 3,
"metadata" : {},
"position" : 3,
"count" : 0
},
{
"_id" : ObjectId("5d387ecfbb676b173aa57fe4"),
"img" : "Some url",
"name" : "La 5",
"__v" : 0,
"amount" : 17,
"available" : 16,
"collec" : ObjectId("5d36c0c34c86991db93bd7c8"),
"gen" : 3,
"metadata" : {},
"position" : 4,
"count" : 12
}
I tried several things using aggregate and lookup but I can´t get make it work.
The only I could get, was retrieve the info from AppUser and the total count..
Something like this (with mongoose):
AppUser.aggregate([
{ $match: matchQuery },
{$unwind: "$collectables"},
{
$lookup:
{
from: "collectables",
localField: "collectables.collectable",
foreignField: "_id",
as: "result"
}
},
{ $sort: { "result.position": 1 } },
{$unwind: "$result"},
{ $addFields : { "result.count" : "$collectables.count" }
},
{ $replaceRoot: { newRoot: "$result" } },
{ $skip: size * (page - 1) },
{ $limit: size }
]).exec((err, result) =>
{
if (err)
{
console.log(err);
return res.status(401).send({ success: false });
}
else {
return res.status(200).send({ success: true, result });
}
});
So I need help because i have three days with this and I can´t get something nice...
Thanks in advance...
Below query will bring us the expected output
db.appuser.aggregate([
{ $unwind: "$collectables" },
{
$lookup: {
from: "collectables",
localField: "collectables.collectable",
foreignField: "_id",
as: "result"
}
},
{
$project: {
result: 1,
flag: { "$gt": [ {"$size": "$result"}, 0 ] },
"collectables.count": 1,
alias: 1,
_id: 0
}
},
{
$match: { flag: true }
}
]);
Unwind the appuser.collectables array, then do a lookup between the collections collectables and appuser, then use $project and $match to get the desired result.
In the $project stage added a flag to filter out the desired documents, by checking the size of the result array of the $lookup output.
Sample Data used
db.collectables.find()
{
"_id": "5d387ecfbb676b173aa57fe3",
"img": "some url",
"name": "La 5",
"__v": 0,
"amount": 17,
"available": 16,
"collec": "5d36c0c34c86991db93bd7c8",
"gen": 3,
"metadata": {
},
"position": 1
}
db.appuser.find()
[{
"_id": "5d36dc9445526a215c4eff52",
"twitter": "1",
"alias": "ViktorCrowley",
"__v": 25,
"collectables": [
{
"count": 12,
"collectable": "5d36c1ba4c86991db93bd7e7"
},
{
"count": 25,
"collectable": "5d36c13d4c86991db93bd7c9"
},
{
"count": 8,
"collectable": "5d381e122f25221126a98f9c"
}
]
},
{
"_id": "5d36dc9445526a215c4efga1",
"twitter": "1",
"alias": "John",
"__v": 11,
"collectables": [
{
"count": 10,
"collectable": "5d387ecfbb676b173aa57fe3"
}
]
},
{
"_id": "6dc2dc9445526a215c4efga1",
"twitter": "1",
"alias": "Alice",
"__v": 11,
"collectables": [
{
"count": 25,
"collectable": "5d387ecfbb676b173aa57fe3"
},
{
"count": 3,
"collectable": "5d36c13d4c86991db9312349"
},
{
"count": 5,
"collectable": "5d381e122f25221126a9711c"
}
]
}]
Final Output
{
"alias": "John",
"collectables": {
"count": 10
},
"result": [
{
"_id": "5d387ecfbb676b173aa57fe3",
"img": "some url",
"name": "La 5",
"__v": 0,
"amount": 17,
"available": 16,
"collec": "5d36c0c34c86991db93bd7c8",
"gen": 3,
"metadata": {
},
"position": 1
}
],
"flag": true
}
{
"alias": "Alice",
"collectables": {
"count": 25
},
"result": [
{
"_id": "5d387ecfbb676b173aa57fe3",
"img": "some url",
"name": "La 5",
"__v": 0,
"amount": 17,
"available": 16,
"collec": "5d36c0c34c86991db93bd7c8",
"gen": 3,
"metadata": {
},
"position": 1
}
],
"flag": true
}
Hope it helps!
This question already has an answer here:
Finding top N entries from the Array
(1 answer)
Closed 3 years ago.
I have an array of objects in the following collection having levels i want
only the top 3 levels from an array in descending order of levels
{
"_id" : ObjectId("5ce10ffb0a9531d98ad11819"),
"_course" : ObjectId("593107b273790c30c4e08b02"),
"users" : [
{
"level" : 17,
"_user" : ObjectId("5c08be10d1a0c91e4c739629")
},
{
"level" : 16,
"_user" : ObjectId("593107b273790c30c4e08b07")
},
{
"level" : 5,
"_user" : ObjectId("593107b273790c30c4e08b08")
},
{
"level" : 17,
"_user" : ObjectId("593107b273790c30c4e08b05")
}
],
"acr" : "MH"
}
/* 2 */
{
"_id" : ObjectId("5ce10ffb0a9531d98ad1181a"),
"_course" : ObjectId("593107b273790c30c4e08b02"),
"users" : [
{
"level" : 9,
"_user" : ObjectId("5c08be10d1a0c91e4c739629")
},
{
"level" : 6,
"_user" : ObjectId("593107b273790c30c4e08b02")
},
{
"level" : 5,
"_user" : ObjectId("593107b273790c30c4e08b07")
},
{
"level" : 4,
"_user" : ObjectId("593107b273790c30c4e08b07")
}
],
"acr" : "MA"
}
Expected output
{
"_id": "5ce10ffb0a9531d98ad11819",
"users": [
{
"level": 17,
"_user": "593107b273790c30c4e08b09"
},
{
"level": 17,
"_user": "593107b273790c30c4e08b05"
},
{
"level": 16,
"_user": "593107b273790c30c4e08b07"
}
]
},
{
"_id": "5ce10ffb0a9531d98ad11819",
"users": [
{
"level": 9,
"_user": "593107b273790c30c4e08b09"
},
{
"level": 6,
"_user": "593107b273790c30c4e08b02"
},
{
"level": 5,
"_user": "593107b273790c30c4e08b07"
}
]
}
I want the expected output like above i am using aggregation but i cant find a way. I used other way also but still i am not getting the proper output something is missing that i cant find
You can use below aggregation
db.collection.aggregate([
{ "$unwind": "$users" },
{ "$sort": { "users.level": -1 }},
{ "$group": {
"_id": "$_id",
"users": { "$push": "$users" }
}},
{ "$addFields": { "users": { "$slice": ["$users", 3] }}}
])
MongoPlayground
I have in mongodb differents records. I write down a little example:
{_id:"sad547er4w2v5x85b8", name:"Jhon", jobTime:600, floor:2, dept:5, age:25},
{_id:"xcz547wer4xcvcx1g2", name:"Alex", jobTime:841, floor:4, dept:1, age:55},
{_id:"xcnwep2321954ldfsl", name:"Alice", jobTime:100, floor:3, dept:3, age:55},
{_id:"23s3ih94h548jhfk2u", name:"Anne", jobTime:280, floor:2, dept:8, age:22},
{_id:"03dfsk9342hjwq1503", name:"Alexa", jobTime:355, floor:2, dept:6, age:25}
I tried to obtain this output, but I don't know how to group by twice to get that structure.
{[
{age:22, floors:[{floor:2,persons:[{name:"Anne",jobTime:280,dept:8}]}]},
{age:25, floors:[{floor:2,persons:[{name:"Jhon",jobTime:600,dept:5},{name:"Alexa",jobTime:355,dept:6}]}]},
{age:55, floors:[{floor:3,persons:[{name:"Alex",jobTime:841,dept:1}]},{floor:4,persons:[{name:"Alice",jobTime:100,dept:3}]}]}
]}
Exactly. Use "two" $group stages
collection.aggregate([
{ "$group": {
"_id": {
"age": "$age",
"floor": "$floor",
},
"persons": { "$push": {
"name": "$name",
"jobTime": "$jobTime",
"dept": "$dept"
}}
}},
{ "$group": {
"_id": "$_id.age",
"floors": { "$push": {
"floor": "$_id.floor",
"persons": "$persons"
}}
}}
],function(err,results) {
// deal with results here
})
Which produces:
{
"_id" : 25,
"floors" : [
{ "floor" : 2,
"persons" : [
{ "name" : "Jhon", "jobTime" : 600, "dept" : 5 },
{ "name" : "Alexa", "jobTime" : 355, "dept" : 6 }
]
}
]
},
{
"_id" : 55,
"floors" : [
{ "floor" : 3,
"persons" : [
{ "name" : "Alice", "jobTime" : 100, "dept" : 3 }
]
},
{ "floor" : 4,
"persons" : [
{ "name" : "Alex", "jobTime" : 841, "dept" : 1 }
]
}
]
},
{
"_id" : 22,
"floors" : [
{ "floor" : 2,
"persons" : [
{ "name" : "Anne", "jobTime" : 280, "dept" : 8 }
]
}
]
}
So the initial $group is on a compound key including the detail down to the items you want to add to the initial "array", for "persons". Then the second $group takes only part of the initial _id for it's key and again "pushes" the content into a new array.
I have a collection with documents like below.I want to get the all distinct value of name of attributes sub-document with their distinct value and count in collection.
Example :
var records = [
{
"attributes": [
{
"name": "color",
"value": "black",
"_id": "5441103a0348ebc91ee75b33"
}
],
"name": "ddd"
},
{
"attributes": [
{
"name": "color",
"value": "red",
"_id": "5441091393450f1619be99af"
},
{
"name": "size",
"value": "L",
"_id": "5441091393450f1619be99b0"
}
],
"name": "one"
},
{
"attributes": [
{
"name": "color",
"value": "black",
"_id": "5441092593450f1619be99b1"
},
{
"name": "size",
"value": "L",
"_id": "5441092593450f1619be99b2"
}
],
"name": "sdfsda"
},
{
"attributes": [
{
"name": "color",
"value": "green",
"_id": "5441093d93450f1619be99b3"
},
{
"name": "size",
"value": "S",
"_id": "5441093d93450f1619be99b4"
}
],
"name": "threee"
},
{
"attributes": [
{
"name": "color",
"value": "green",
"_id": "5441095793450f1619be99b5"
},
{
"name": "size",
"value": "M",
"_id": "5441095793450f1619be99b6"
}
],
"name": "one"
}
]
I want to get output like :
var output =
{
"color" : [
{value : 'red', count : 1}
{value : 'black', count : 2}
{value : 'green', count : 2}
],
"size" : [
{value : 'S', count : 2}
{value : 'L', count : 1}
{value : 'M', count : 1}
]
}
how can i get this output in mongodb?
Can i get this output by aggregate framework of mongodb, if yes, then how? -- high priority
Yes, aggregate can make it.
var output = {};
db.c.aggregate([{
$unwind : "$attributes"
}, {
$group : {
_id : {
name : "$name",
value : "$value"
},
count : {
$sum : 1
}
} // the output after this stage such as
// {_id:{name:"color", value:"green"}, count:2}
// {_id:{name:"size", value:"S"}, count:2}
}, {
$group : {
_id : "$_id.name",
contents : {
$push : {
value : "$_id.value",
count : "$count"
}
}
} // the output after this stage such as
// {_id:"color", contents:[{value:"green", count:2}]}
// {_id:"size", contents:[{value : 'S', count : 2}]}
}]).forEach(function(doc) {
output[doc._id] = doc.contens; // just convert to the format as expected
});