Finding on any key in ElasticSearch - search

We have an index of Users with Cars e.g.
{
"name": "Eli",
"cars": {
"Honda": { "color": "Red", "Price": 120 },
"Toyota":{ "color": "Blue", "Price": 110 }
"Mazda": { "color": "White", "Price": 100 }
}
}
{
"name": "Eli",
"cars": {
"Honda": { "color": "Blue", "Price": 110 },
"BMW": { "color": "Red", "Price": 200 }
}
}
We are trying to retrieve all the users with a Red Car.
cars.*.color = "Red"
But we couldn't find a way to do it in ElasticSearch.

You can do it with a query_string query like this:
POST index/_search
{
"query": {
"query_string": {
"query": "cars.\\*.color:Red"
}
}
}

Related

How to get multiply of 2 collections mongodb? (CROSS JOIN??)

I have 2 collections in mongodb
Clothes and Colors
Clothes:
[
{ "name": "t-shirt" },
{ "name": "pants" }
]
Colors:
[
{ "color": "yellow" },
{ "color": "red" }
]
I want to have multiply of 2 collections in result
[
{ "name": "t-shirt", "color": "red" },
{ "name": "t-shirt", "color": "yellow" },
{ "name": "pants", "color": "red" },
{ "name": "pants", "color": "yellow" }
]
How can I do this? Thx!
Query
lookup without any criteria (all with all)
unwind
project to fix the structure to get the expected output
Playmongo
clothes.aggregate(
[{"$lookup": {"from": "Colors", "pipeline": [], "as": "results"}},
{"$unwind": "$results"},
{"$project": {"_id": 0, "name": 1, "color": "$results.color"}}])

MongoDB dynamically conditional filtering

I am trying to create a form builder and generator and from generated forms, I will collect answers. After collecting answers I want to filter them according to some filter parameters and get average results for each category.
Questions have categories(attributeGroupOne&Two) bind to them.
I have the following structure for my answers.
{
"form": [{
"_id": {
"$oid": "62295d028b6b895048b7da96"
},
"questionType": "lickert5",
"label": "deneme sorusu 1",
"attributeGroupTwo": "deneme kategorisi alt 2",
"attributeGroupOne": "deneme kategorisi",
"name": "Question_1",
"weight": 4,
"type": "radio",
"options": {
"1": "Çok Az",
"2": "Az",
"3": "Bazen",
"4": "Genellikle",
"5": "Her Zaman"
}
}, {
"_id": {
"$oid": "62295d028b6b895048b7da95"
},
"questionType": "lickert10",
"label": "deneme sorusu 2",
"attributeGroupTwo": "deneme alt 3",
"attributeGroupOne": "deneme kategorisi",
"name": "Question_2",
"weight": 4,
"type": "radio",
"options": {
"1": "1",
"2": "2",
"3": "3",
"4": "4",
"5": "5",
"6": "6",
"7": "7",
"8": "8",
"9": "9",
"10": "10"
}
}, {
"_id": {
"$oid": "62295d028b6b895048b7da94"
},
"questionType": "trueFalse",
"label": "deneme sorusu 3",
"attributeGroupTwo": "deneme alt 5",
"attributeGroupOne": "deneme kategorisi",
"name": "Question_3",
"weight": 4,
"type": "radio",
"options": {
"0": "Yanlış",
"10": "Doğru"
}
}, {
"_id": {
"$oid": "62295d028b6b895048b7da93"
},
"questionType": "lickert5",
"label": "deneme sorusu 4",
"attributeGroupTwo": "deneme main sub",
"attributeGroupOne": "deneme main 2",
"name": "Question_4",
"weight": 4,
"type": "radio",
"options": {
"1": "Çok Az",
"2": "Az",
"3": "Bazen",
"4": "Genellikle",
"5": "Her Zaman"
}
}],
"surveyName": "deneme formu",
"createdBy": {
"$oid": "61becc2c230fc12274683b6a"
},
"createdAt": {
"$date": "2022-03-10T02:05:54.039Z"
},
"updatedAt": {
"$date": "2022-03-10T02:05:54.039Z"
},
"__v": 0
}
https://i.stack.imgur.com/xT2Cq.png
Without additional filter just by filtering category with following query I am getting reports that I wanted.
.aggregate([
{ $unwind: '$answers' },
{
$group: {
_id: '$answers.SubCategory',
CalculatedWeight: { $avg: '$answers.CalculatedWeight' },
formId: { $first: formId }
}
},
{ $project: { _id: 1, CalculatedWeight: 1, formId: 1 } },
{ $out: 'results' }
])
results:
{
"CalculatedWeight": 19.35483870967742,
"formId": {
"$oid": "62295d028b6b895048b7da92"
}
}
https://i.stack.imgur.com/HxqBe.png
I successfully added age filtering with following block
const docs = await model
.aggregate([
{
$match: {
$and: [
{ age: { $gt: minAge, $lt: maxAge } },
{ formId: { $eq: formId } }
]
}
},
{ $unwind: '$answers' },
{
$group: {
_id: '$answers.SubCategory',
CalculatedWeight: { $avg: '$answers.CalculatedWeight' },
formId: { $first: formId }
}
},
{ $project: { _id: 1, CalculatedWeight: 1, formId: 1 } },
{ $out: 'results' }
])
But I have 5 filters that are, gender, department, age, working years, and education level. If possible I want to be able to combine them with dynamic querying but I don't want to write 120 if cases for combining them. I can't think a way to programmatically filter and calculate according to filters.
Thank you in advance. The question itself might be unclear, sorry about it. I can elaborate if you point out where it is lacking.
Edit:
Filter types can be seen below. Age and working years will be a period with min and max values. If possible I want to be able to combine all filter types but it will probably statistically unreasonable due to the small pool of entries ~200. Most common combinations for filters are: Gender-Department-Age, Working Years- Department, Education - Gender.
Filters:
gender: String - enum
department: String - enum
age: Number - Min and Max
working years: Number - Min and Max
education: String - enum
For anyone else struggling with the same thing, I figured it out this way.
In my situation since only the match query is subject to change I constructed match queries according to query parameters I am getting with following code.
var array = Object.entries(req.query)
array.forEach(x => {
var z = { [x[0]]: { $eq: x[1] } }
matchTry.$and.push(z)
})
output:
{
"$and": [
{
"department": {
"$eq": "IT"
}
},
{
"gender": {
"$eq": "male"
}
}
]
}
Then use mongoose with it
const docs = await model
.aggregate()
.match(match)
.exec()
res.status(200).json({ data: docs })

How to delete a property from each object in an array of objects in all documents in a collection?

Desired Behavior:
Delete a property from each object in an array of objects in all documents in a collection.
What I've Tried:
The docs show $unset is used for deleting object properties:
db.products.update(
{ sku: "unknown" },
{ $unset: { quantity: "", instock: "" } }
)
Another post gives an example of how to delete a nested property in multiple documents:
db.collectionName.update(
{ },
{ "$unset": { "values.727920": "" } },
{ "multi":true }
)
Source: https://stackoverflow.com/a/31384375
I had a look at:
https://docs.mongodb.com/manual/reference/operator/update/positional-all/
https://docs.mongodb.com/manual/reference/operator/update/positional-all/#positional-update-all
which gave the example:
db.students.update(
{ },
{ $inc: { "grades.$[]": 10 } },
{ multi: true }
)
So I tried the following and it seems to work:
db.my_collection.update(
{ },
{ "$unset" : { "array_of_objects.$[].weight": "" } },
{ "multi" : true }
)
Question:
Is this the correct way to:
delete each weight property in each object in array_of_objects in
all documents
Schema:
{
"_id": ObjectId("5d1d85aa00341124bc90d158"),
"title": "hello 01",
"array_of_objects": [
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
},
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
},
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
}]
},
{
"_id": ObjectId("5d1d85aa11341124bc90d158"),
"title": "hello 02",
"array_of_objects": [
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
},
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
},
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
}]
},
{
"_id": ObjectId("5d1d85aa22341124bc90d158"),
"title": "hello 03",
"array_of_objects": [
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
},
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
},
{
"color": "blue",
"weight": "100",
"date": "2019-07-04T11:12:59.356Z"
}]
}

Finding a multi value element in ElasticSearch Array

We have an index of Users e.g.
{
"name": "Eli",
"cars": [
{ "model": "Honda", "color": "Red" },
{ "model": "Honda", "color": "Blue" },
{ "model": "Toyota", "color": "Red" }
]
}
{
"name": "Don",
"cars": [
{ "model": "Honda", "color": "Blue" },
{ "model": "Honda", "color": "Black" },
{ "model": "Toyota", "color": "Red" }
]
}
We are trying to retrieve all the users with a Red Honda, but we couldn't find a way to do it in ElasticSearch
Since i dont know which elasticsearch version you are using, i'm referencing to the current.
What you are looking at is the following:
https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-objects.html
and
https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-mapping.html
and
https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-query.html
with the nested mapping you can create a query like the following:
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "cars",
"query": {
"bool": {
"must": [
{
"term": {
"cars.model": "honda"
}
},
{
"term": {
"cars.color": "red"
}
}
]
}
}
}
}
]
}
}
}
link to example: https://www.found.no/play/gist/91c5a6c8c9fe81928b1cc497f8740a3f
(click run)
Be aware, this is only working when you are working with nested objects! The mapping must know this.

Elasticsearch correct aggregations for faceted search

I want to offer a faceted search for the clothing products on some platform. As I already have Elasticsearch-based search functionality (simple query, only the name of the product), it would be nice to also implement the faceted search with ES.
This should be done with aggregations as facets are deprecated and one can also have nested aggregations.
However I cannot wrap my head around the millions of aggregations and which ones are the right for me - there is terms, filter, filters, nested, children and so on. And all of them seem suitable.
What I want to achieve may sound pretty basic: I have different facets (brand, condition, color) each having different values. For some facets (brand) the user can select only one value. For others (color) the user is allowed to select up to 3 (as some clothes have more than one color).
I started with multi-field terms facet. Now the next natural step would be to convert this to a terms aggregation (reasons above) but multi-fields are not supported in terms aggregations.
{
"query" : {
"match_all" : { }
},
"facets" : {
"groupByBrandAndCondition" : {
"terms" : {
"fields" : ["brand", "condition"],
"size" : 10
}
}
}
}
I am somehow missing some easy but crucial point here on how to proceed with having parallel multi level bucketing. Speaking in UI terms the user should be able to select something like:
Brands (10)
A (7)
B (3) [X]
Colors (5)
Blue (3) [X]
Red (2) [X]
Read: select A (7), Blue (3) and Red (2)
I created basic mapping like this
POST your_index/your_type/_mapping
{
"your_type": {
"properties": {
"product": {
"type": "string"
},
"brand": {
"type": "string"
},
"color": {
"type": "string"
}
}
}
}
I inserted some documents like this
PUT your_index/your_type/111
{
"product" : "jeans" ,"brand" : "lee", "color" : "blue"
}
PUT your_index/your_type/1111
{
"product" : "shoes" ,"brand" : "levi", "color" : "black"
}
And so on
Simple aggregation query like this
GET your_index/_search
{
"size": 0,
"aggs": {
"prod_agg": {
"terms": {
"field": "product"
},
"aggs": {
"brand_agg": {
"terms": {
"field": "brand"
},
"aggs": {
"color_agg": {
"terms": {
"field": "color"
}
}
}
}
}
}
}
}
will retrun
"aggregations": {
"prod_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "shoes",
"doc_count": 4,
"brand_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "nike",
"doc_count": 3,
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "blue",
"doc_count": 2
},
{
"key": "black",
"doc_count": 1
}
]
}
},
{
"key": "levi",
"doc_count": 1,
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "black",
"doc_count": 1
}
]
}
}
]
}
},
{
"key": "jeans",
"doc_count": 3,
"brand_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "lee",
"doc_count": 2,
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "black",
"doc_count": 1
},
{
"key": "blue",
"doc_count": 1
}
]
}
},
{
"key": "levi",
"doc_count": 1,
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "black",
"doc_count": 1
}
]
}
}
]
}
}
]
}
}
This could be used to populate UI search criteria.
Then If user wants to search for shoes, you could query
GET your_index/_search
{
"size": 0,
"query": {
"match": {
"product": "shoes"
}
},
"aggs": {
"brand_agg": {
"terms": {
"field": "brand"
},
"aggs": {
"color_agg": {
"terms": {
"field": "color"
}
}
}
}
}
}
which will give you
"aggregations": {
"brand_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "nike",
"doc_count": 3,
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "blue",
"doc_count": 2
},
{
"key": "black",
"doc_count": 1
}
]
}
},
{
"key": "levi",
"doc_count": 1,
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "black",
"doc_count": 1
}
]
}
}
]
}
}
or you could have them as separate buckets with query like
GET your_index/_search
{
"size": 0,
"query": {
"match": {
"product": "shoes"
}
},
"aggs": {
"brand_agg": {
"terms": {
"field": "brand"
}
},
"color_agg" : {
"terms": {
"field": "color"
}
}
}
}
which will give you
"aggregations": {
"color_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "black",
"doc_count": 2
},
{
"key": "blue",
"doc_count": 2
}
]
},
"brand_agg": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "nike",
"doc_count": 3
},
{
"key": "levi",
"doc_count": 1
}
]
}
}
Use doc_count value to tell users how many options they have.
Does this satisfy your requirements?

Resources