How to project all nested documents from an array of objects? - regex

I have a document like this:
{
"_id": "5ffc130e9fb31b26162e0bad",
"results": [
{
"customer": {
"display_name": "Manno Dispensary - first",
"ext_acct_id": "267"
}
},
{
"customer": {
"display_name": "Manno Dispensary - second",
"ext_acct_id": "262"
}
},
{
"customer": {
"display_name": "Kako Dispensary - first",
"ext_acct_id": "261"
}
},
{
"customer": {
"display_name": "Kako Dispensary - second",
"ext_acct_id": "263"
}
}
]
}
I want to write a MongoDB query which does a regex search on "customer.display_name" and returns all those documents in results that satisfies this criteria.
I have written this query till now, and it returns me the desired output, but the problem is, it is only retuning one document inside results, Am I missing anything in this?
my desired output:
{
"_id": "5ffc130e9fb31b26162e0bad",
"results": [
{
"customer": {
"display_name": "Manno Dispensary - first",
"ext_acct_id": "267"
}
},
{
"customer": {
"display_name": "Manno Dispensary - second",
"ext_acct_id": "262"
}
}
]
}
What I am actually getting:
{
"_id": "5ffc130e9fb31b26162e0bad",
"results": [
{
"customer": {
"display_name": "Manno Dispensary - first",
"ext_acct_id": "267"
}
}
]
}
this is the query which I have written to fetch all customers which contain "Manno" in their customer name.
My collection name is Order(mongoose), search="Manno"
Order.find({
results: {
$elemMatch : {
"customer.display_name": {$regex: search}
}
}
},{
results: {
$elemMatch : {
"customer.display_name": {$regex: search}
}
}
});

The $elemMatch and $ will return only single matching document/object form array, try $filter and $regexMatch,
$filter to iterate loop of results array
$regexMatch to check regular expression condition, it will return true or false
Order.find({
results: {
$elemMatch: {
"customer.display_name": { $regex: "Manno" }
}
}
},
{
results: {
$filter: {
input: "$results",
cond: {
$regexMatch: {
input: "$$this.customer.display_name",
regex: "Manno"
}
}
}
}
})
Playground

Related

How to apply custom score to a search filed in Elastic Search

I am making a search query in Elastic Search and I want to treat the fields the same when they match. For example if I search for field field1 and it matches, then the _score is increase by 10(for example), same for the field2.
I was tried function_score but it's not working. It throws an error.
"caused_by": {
"type": "class_cast_exception",
"reason": "class
org.elasticsearch.index.fielddata.plain.SortedSetDVOrdinalsIndexFieldData
cannot be cast to class
org.elasticsearch.index.fielddata.IndexNumericFieldData
(org.elasticsearch.index.fielddata.plain.SortedSetDVOrdinalsIndexFieldData
and org.elasticsearch.index.fielddata.IndexNumericFieldData are in unnamed
module of loader 'app')"
}
The query:
{
"track_total_hits": true,
"size": 50,
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"term": {
"field1": {
"value": "Value 1"
}
}
},
{
"term": {
"field2": {
"value": "value 2"
}
}
}
]
}
},
"functions": [
{
"field_value_factor": {
"field": "field1",
"factor": 10,
"missing": 0
}
},
{
"field_value_factor": {
"field": "field2",
"factor": 10,
"missing": 0
}
}
],
"boost_mode": "multiply"
}
}
}
You can use function score with filter function to boost.
assuming that your mapping looks like the one below
{
"mappings": {
"properties": {
"field_1": {
"type": "keyword"
},
"field_2": {
"type": "keyword"
}
}
}
}
with documents
{"index":{}}
{"field_1": "foo", "field_2": "bar"}
{"index":{}}
{"field_1": "foo", "field_2": "foo"}
{"index":{}}
{"field_1": "bar", "field_2": "bar"}
you can use weight parameter to boost the documents matched for each query.
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{
"filter": {
"term": {
"field_1": "foo"
}
},
"weight": 10
},
{
"filter": {
"term": {
"field_2": "foo"
}
},
"weight": 20
}
],
"score_mode": "multiply"
}
}
}
You can refer below solution if you want to provide manual weight for different field in query. This will always replace highest weight field on top of your query response -
Elasticsearch query different fields with different weight

Use $regex inside $expr in mongodb aggregation

My doc looks as follows
doc = {
name: 'abc',
age:20
}
and my query looks like
{ $expr: {$and:[{ $gt:[ "$age", 10 ] },
{ $regex:["$name",'ab']}
]
}
} }
But it's not working and I get an error
Unrecognized expression '$regex'
How can I make it work?
My original query looks like this
db.orders.aggregate([{
$match: {}},
{$lookup: {
from: "orders",
let: {
"customer_details": "$customerDetails"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{ $or: [
{
$eq: ["$customerDetails.parentMobile","$$customer_details.parentMobile"]
},
{$eq: ["$customerDetails.studentMobile","$$customer_details.parentMobile"]
},
{$eq: ["$customerDetails.studentMobile","$$customer_details.parentMobile"]
},
{$eq: ["$customerDetails.studentMobile","$$customer_details.studentMobile"]
}
]
},
{$eq: ["$customerDetails.zipCode","$$customer_details.zipCode"]},
{$eq: ["$customerDetails.address","$$customer_details.address"]}
]
}
}
}],
as: "oldOrder"
}
}])
I want to use regex for matching address.
Any help will be greatly appreciated. Thanks in advance.
If your mongoDB version is 4.2, then you can use $regexMatch
try this
db.collection.find({
$expr: {
$and: [
{
$gt: [
"$age",
10
]
},
{
$regexMatch: {
input: "$name",
regex: "ab"
}
}
]
}
})
check this Mongo Playground
$regex is a query operator you cannot use inside $expr because it only supports aggregation pipeline operators.
{
"$expr": { "$gt": ["$age", 10] } ,
"name": { "$regex": "ab" }
}
If you have mongodb 4.2, you can use $regexMatch
{ "$expr": {
"$and": [
{ "$gt": ["$age", 10] },
{
"$regexMatch": {
"input": "$name",
"regex": "ab", //Your text search here
"options": "i",
}
}
]
}}

Mongo query to find not null string inside list of objects

I want to query an array of objects with a particular key should have text in it.
this is the query I have tried
to find disclaimer.text exists and not empty. But It always prints 0
db.slideshows.count({"config.slides": { $elemMatch: {disclaimer: {"text" : {"$exists" : true, "$ne" : ""} }} } })
This is my data
{
"id": 1002,
"config": {
"firstSlide": "vehicle",
"slides": [
{
"slideKey": "sk1",
"disclaimer": {
"text": ""
}
},
{
"slideKey": "sk2"
}
]
}
}
{
"id": 1003,
"config": {
"firstSlide": "book",
"slides": [
{
"slideKey": "sk3",
"disclaimer": {
"text": "Hello"
}
},
{
"slideKey": "sk4"
}
]
}
}
{
"id": 1004,
"config": {
"firstSlide": "book",
"slides": [
{
"slideKey": "sk3",
"disclaimer": {
"text": "nope"
}
},
{
"slideKey": "sk4",
"disclaimer": {
"text": ""
}
}
]
}
}
I want all the rows which have not empty disclaimer.text. ex. in the above set I need to get id 1004 and 1003 as a result.
Try
db.slideshows.count({
"config.slides": {
$elemMatch: {
"disclaimer.text": {
"$exists": true,
"$ne": ""
}
}
}
})

MongoDB: Create an Exact Match field

I'm trying order some data, and want to have the exact match as first results and then the rest.
I tried with this $eq in $project, but it seems that something won't work.
Is it possible to do something similar?
db.getCollection('words').aggregate([
{ $match: { "kanji.text": /^彼/ } },
{ $project: {
"kanji": 1,
"kana": 1,
"sense":1,
"exact": {
$eq : [ "$kanji.0.text", "彼" ]
}
}
},
{ $sort: {"exact": 1} }
])
Document Structure:
EDIT: I have found a bad workaround
db.getCollection('words').aggregate([
{ $match: { "kanji.text": /^彼/ } },
{ $limit : 10 },
{ $addFields: {
exact: {
$filter: {
input: '$kanji.text',
cond: {
$eq: ['$$this', "彼"]
}
}
}
}},
{ $project: {
"kanji": 1,
"kana": 1,
"sense":1,
"exact": 1
}
},
{ $sort: {"exact.0": -1} }
])

How can I exclude results from elasticsearch based on the contents of a field?

I'm using elasticsearch on AWS to store logs from Cloudfront. I have created a simple query that will give me all entries from the past 24h, sorted from new to old:
{
"from": 0,
"size": 1000,
"query": {
"bool": {
"must": [
{ "match": { "site_name": "some-site" } }
],
"filter": [
{
"range": {
"timestamp": {
"lt": "now",
"gte": "now-1d"
}
}
}
]
}
},
"sort": [
{ "timestamp": { "order": "desc" } }
]
}
Now, there a are certain sources (based on the user agent) for which I would like to exclude results. So my question boils down to this:
How can I filter out entries from the results when a certain field contains a certain string? Or:
query.filter.where('cs_user_agent').does.not.contain('Some string')
(This is not real code, obviously.)
I have tried to make sense of the Elasticsearch documentation, but I couldn't find a good example of how to achieve this.
I hope this makes sense. Thanks in advance!
Okay, I figured it out. What I've done is use a Bool Query in combination with a wildcard:
{
"from": 0,
"size": 1000,
"query": {
"bool": {
"must": [
{ "match": { "site_name": "some-site" } }
],
"filter": [
{
"range": {
"timestamp": {
"lt": "now",
"gte": "now-1d"
}
}
}
],
"must_not": [
{ "wildcard": { "cs_user_agent": "some string*" } }
]
}
},
"sort": [
{ "timestamp": { "order": "desc" } }
]
}
This basically matches any user agent string containing "some string", and then filters it out (because of the "must_not").
I hope this helps others who run into this problem.
nod.js client version:
const { from, size, value, tagsIdExclude } = req.body;
const { body } = await elasticWrapper.client.search({
index: ElasticIndexs.Tags,
body: {
from: from,
size: size,
query: {
bool: {
must: {
wildcard: {
name: {
value: `*${value}*`,
boost: 1.0,
rewrite: 'constant_score',
},
},
},
filter: {
bool: {
must_not: [
{
terms: {
id: tagsIdExclude ? tagsIdExclude : [],
},
},
],
},
},
},
},
},
});