How to perform match query in Elasticsearch in Node.js? - javascript

I wrote an Elaticsearch query for match category and name in my records.
My sample record:
{category: "disease", name: "Heart Diseases", dui: "D006331", url_name: "Heart Disease"}
I need to match my parameter with 'name' and category should be 'disease' and 'hospital' always and wrote it like this:
match: {
name: {
query: searchString,
fuzziness: 2,
operator: "or"
},
category: ['hospital', 'disease']
}
Updated code:
client.search({
index: 'xpertdox',
type: 'disease',
size: 20,
body: {
query: {
bool: {
must: {
match: {
name: {
query: req.param('disease'),
//fuzziness:"AUTO",
fuzziness: 2,
operator: "or"
}
}
},
should : [
{ term : { category: disease} }
],
minimum_should_match: 2
}
}
But this is not working. I don't know where the mistake is.

To match all categories is one doc use query below
{
"query": {
"bool": {
"must": {
"match": {
"name": {
"query": searchString,
"fuzziness": 2,
"operator": "or"
}
}
},
"should": [
{ "term": { "category": "hospital" } },
{ "term": { "category": "disease" } }
],
"minimum_should_match": 2
}
}
}
minimum_should_match should be equal to the number of optional clauses (the clauses in "should" array).
UPDATE
To match one category from the list of categories, use filter and terms query
{
"query": {
"bool": {
"must": {
"match": {
"name": {
"query": searchString,
"fuzziness": 2,
"operator": "or"
}
}
},
"filter": {
"terms": { "category": [ "hospital", "disease"] }
}
}
}
}

Related

How to access an objects value when its key will be dynamically obtained while sending it as a mongodb query?

I have an object as follows.
cityConfiguration -
{
_id: 62e135519567726de42421c2,
configType: 'cityConfig'
'16': {
cityName: 'Delhi',
metro: true,
tier1: true
},
'17': {
cityName: 'Indore',
metro: false,
tier1: false
}
}
I have another table called BusDetails with fields like - [cityId, revenue, tax...]. Example document -
{
"busNumber": "KA25D5057",
"cityId": "17",
"revenue": 46594924,
"totalCollection": 3456342
"tax": "2906",
"passengerCount": 40
......
}
I want to do an aggregation ($project) on the BusDetails table such that I take the cityId from that BusDetails document and if that cityId is a metro city (according to the cityConfig object) then I will return 0 or 1. I have designed the below code but its not working. How to fix it.
return await BsDetails.aggregate([
{
$match: { busNumber: 'KA25D5057' }
},
{
$project: {
totalCollection: 1,
passengerCount: 1,
.
.
.
metro: {"$cond": [ cityPassConfig["$cityId"].metro == true, 1, 0]},
}
}
]);
So for ex, in the above code, the cityId is 17 which is a non-metro city. So the "metro" field should store 0. Here I am facing problem in
cityPassConfig["$cityId"].metro
this particular statement.
Using objects with dynamic field name is generally considered as anti-pattern and should be avoided since it introduces unnecessary complexity into queries. Nevertheless, you can use $objectToArray to achieve what you want.
db.BusDetails.aggregate([
{
$match: {
busNumber: "KA25D5057"
}
},
{
"$addFields": {
// put your object here
"cityConfiguration": {
_id: ObjectId("62e135519567726de42421c2"),
configType: "cityConfig",
"16": {
cityName: "Delhi",
metro: true,
tier1: true
},
"17": {
cityName: "Indore",
metro: false,
tier1: false
}
}
}
},
{
"$addFields": {
// convert into array of k-v tuples
"cityConfiguration": {
"$objectToArray": "$cityConfiguration"
}
}
},
{
"$addFields": {
"cityConfiguration": {
// iterate through the array and get the matched cityConfiguration entry
"$reduce": {
"input": "$cityConfiguration",
"initialValue": null,
"in": {
"$cond": {
"if": {
$eq: [
"$$this.k",
"$cityId"
]
},
"then": "$$this.v",
"else": "$$value"
}
}
}
}
}
},
{
$project: {
totalCollection: 1,
passengerCount: 1,
metro: {
"$cond": {
"if": "$cityConfiguration.metro",
"then": 1,
"else": 0
}
}
}
}
])
Here is the Mongo playground for your reference.

Filter nested of nested in Mongoose Populate

I have an object that looks like this: (that is the output of Mongoose query)
let systems = [
{
"maxUserLevel": 1,
"subsystems": [
{
"sections": [],
"name": "apple"
},
{
"sections": [
{
"name": "banana"
}
],
"name": "sun",
},
{
"sections": [],
"name": "orange"
}
],
"systemID": "12345"
},
{
"maxUserLevel": 3,
"subsystems": [
{
"sections": [],
"name": "blue"
},
{
"sections": [
{
"name": "pink"
}
],
"name": "red",
},
],
"systemID": "15654"
}];
The Mongoose query:
this.model.System.find({username: user.username}, {
_id: 0,
allowedOrganizations: 0,
name: 0,
updatedAt: 0,
createdAt: 0,
versionKey: 0
})
.populate(
{
path: "subsystems",
populate: {
path: "sections",
select: "name -_id",
match: {
allowedUsers: user.id
}
},
select: "name metadata -_id",
}
)
.exec((error, systems) => {
return res.status(200).json({
data: systems,
success: true
});
});
I'm looking for a way to removes the subsystems that do not have sections.
After hours of searching I think there's no way to filter populate based on nested populate, so I tried with some ways like this:
if (systems.subsystems.length > 0) {
let test = [];
systems.subsystems.forEach((value, index) => {
if (value.sections.length !== 0) {
test[index] = value;
}
if (systems.subsystems.length === index + 1) {
return test;
}
})
}
But I'm not sure if this is the correct way.
You can use an aggregate query with $filter like this:
db.collection.aggregate([
{
"$project": {
"_id": 1,
"maxUserLevel": 1,
"subsystems": {
"$filter": {
"input": "$subsystems",
"as": "s",
"cond": {
"$ne": [
"$$s.sections",
[]
]
}
}
}
}
}
])
Example here
Also your query should contains a $match stage (like your find stage) and $lookup.
I'm not sure this is the best way, but it solved my problem:
const _ = require('lodash');
systems.forEach((value, index) => {
systems[index].subsystems = _.filter(value.subsystems,
item => !item.sections.length == 0
);
if (systems.length === index + 1) {
return systems;
}
});
It removes all subsystems that do not have sections.

Add alias to the collection targeted in lookup

I am a beginner in MongoDB. I will try my best to explain this as easy as possible, so I will take an internet example of collection, instead of the more complicated schema I am working with!
I have two collections as follows:
Users:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE"
}
]
Posts:
[
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8"
}
]
I want to join these two collections using $lookup. So I use the following aggregation on the users collection:
{
'$addFields': {
'userStrId': {
'$toString': '$_id'
}
}
}, {
'$lookup': {
'from': 'posts',
'localField': 'userStrId',
'foreignField': 'user_id',
'as': 'user_posts'
}
},
I used $addFields to add the _id field of the user as a string field, so I can use it in $lookup,
The following result is generated:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE",
"user_posts": [
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8"
}
]
}
]
The question that I have right now is, how can I add a field to each of the documents in the user_posts such that I get the following result:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE",
"user_posts": [
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8",
"post_id":"610bce417b0c4008346547bc"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8",
"post_id": "610bce417b0c4008346547be"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8",
"post_id":"610bce417b0c4008346547bd"
}
]
}
]
post_id added to each of the documents, and its value equal to the _id of that document converted to string.
You can add a stage at the end of your pipeline stages,
$map to iterate loop of user_posts
$mergeObjects to merge current object of user_posts and new fields user_id and post_id
{
$addFields: {
user_posts: {
$map: {
input: "$user_posts",
in: {
$mergeObjects: [
"$$this",
{
user_id: "$userStrId",
post_id: { $toString: "$$this._id" }
}
]
}
}
}
}
}
Playground

Mongo - Aggregation of sum of values based on name

I'm attempting to perform an aggregate function to calculate the sum of the cost and margin values based on the name value. So if multiple results have the name "Fake Provider' I want the sum of the cost results and the sum of the margin results.
Query statement:
Pharmacy.aggregate([
{
$match: {
$and: [{ 'prescription.status': 'Ready for Pickup' }]
}
},
{
$project: {
'insurance.primary.name': 1,
'prescription.financial.cost': 1,
'prescription.financial.margin': 1
}
}
])
Results are similar to:
[
{
"_id": "5cab98cd293bd54e94c40461",
"insurance": {
"primary": {
"name": "Fake Provider 1"
}
},
"prescription": [
{
"financial": {
"cost": "2.89",
"margin": "5.60"
}
},
{
"financial": {
"cost": "0.88",
"margin": "1.24"
}
}
]
},
{
"_id": "5cab98d0293bd54e94c40470",
"insurance": {
"primary": {
"name": "Fake Provider 1"
}
},
"prescription": [
{
"financial": {
"cost": "3.22",
"margin": "9.94"
}
},
{
"financial": {
"cost": "2.57",
"margin": "9.29"
}
},
{
"financial": {
"cost": "2.03",
"margin": "10.17"
}
}
]
}
]
I have attempted to create a group statement without any luck. Also, the cost and margin values are currently stored as strings.
$group: {
_id: '$insurance.primary.name',
Financial: {
$push: {
id: '$insurance.primary.name',
name: '$insurance.primary.name',
cost: '$prescription.financial.cost',
margin: '$prescription.financial.margin'
}
}
}
I would like to get results similar to:
[
{
"primaryInsurance": "Fake Provider 1",
"totalFinancialCost": "11.59",
"totalFinancialMargin": "36.24"
},
{
"primaryInsurance": "Fake Provider 2",
"totalFinancialCost": "12.82",
"totalFinancialMargin": "22.16"
}
]
I think I have a solution that returns the results using a find and projection then using javascript to map thru the results and perform the addition. However, I would prefer to do this at the Database level.
You must first unwind the 'prescription' field then perform a group. Try this pipeline:
let pipeline = [
{
$unwind: {
path: '$prescription'
}
},
{
$group: {
_id: '$insurance.primary.name',
totalFinancialCost: {
$sum: { $convert: { input: '$prescription.financial.cost', to: "decimal" } }
},
totalFinancialMargin: {
$sum: { $convert: { input: '$prescription.financial.margin', to: "decimal" } }
}
}
}]
Notice how the values are converted to decimal in order to perform the sum.

Meteor AutoForm adding users to select

I know I am close on this, but just cant seem to sort out what I am missing.
I am trying to add all users as options to a select in an autoform quickform. I was able to get it working with another collection, but when I use the same code for the users the value shows up as the _id but I cannot get the label to return.
Here is the structure of my users:
{
"_id": "s3EYXXK5N8NExHrke",
"emails": [{
"address": "admin#gmail.com",
"verified": false
}],
"profile": {
"firstName": "Joe",
"lastName": "Smuck",
"licenseNumber": "1234567",
"accountType": "administrator"
},
"roles": [
"administrator"
],
"createdAt": "2016-12-02T21:51:11.844Z",
"services": {
"password": {
"bcrypt": "$2a$10$NheMU2x/8RvcMxNHeWxbQOpHlWAQmopvk3KrMG9oo5ruTir2ARf8W"
},
"resume": {
"loginTokens": [{
"when": "2016-12-02T21:51:11.948Z",
"hashedToken": "8PktpX6kqK6yM+LMrqRaoqXCbwYG6gdO7MH9V/Th/dI="
}, {
"when": "2016-12-03T03:01:06.600Z",
"hashedToken": "ihn93xaN6rE8fvwBHZ3p8H6z0T7o7WChQoqD4dlkSpw="
}, {
"when": "2016-12-05T14:37:41.147Z",
"hashedToken": "7QE7HxcmDrZPFI3Omn5c1o73pMa3XzOBj3RbquCmo6U="
}]
}
}
}
I am trying to print the firstName to the select. Here is the schema I have now:
inspector: {
type: String,
label: "Inspector",
autoform: {
firstOption: 'Choose an Inspector',
options: function() {
return Meteor.users.find({}, {
sort: {
profile: 1,
firstName: 1
}
}).map(function(c) {
return {
label: c.firstName,
value: c._id
};
});
}
}
},
I would appreciate any help someone can offer!
To tie this off it should have been:
inspector: {
type: String,
label: "Inspector",
autoform: {
firstOption: 'Choose an Inspector',
options: function() {
return Meteor.users.find({}, {
sort: {
profile: 1,
firstName: 1
}
}).map(function(c) {
return {
label: c.profile.firstName,
value: c._id
};
});
}
}
},

Categories

Resources