Array included in array search with elasticsearch - arrays

I have users indexed with categories as follows
{
id: 1
name: John
categories: [
{
id: 1
name: Category 1
},
{
id: 2
name: Category 2
}
]
},
{
id: 2
name: Mark
categories: [
{
id: 1
name: Category 1
}
]
}
And I'm trying to get all the documents with Category 1 or Category 2 with
{
filter:
{
bool: {
must: [
{
terms: {user.categories.id: [1, 2]}
}
]
}
}
}
But It only returns the first document that has the two categories, what I am doing wrong?
As I understood, terms search that one of the values is contained in the field, so for user 1
user.categories.id: [1, 2]
user 2
user.categories.id: [1]
Categoy id 1 is contained in both documents

The best way to handle this is probably with a nested filter. You'll have to specify the "nested" type in your mapping, though.
I can set up an index like this:
PUT /test_index
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"doc": {
"properties": {
"categories": {
"type": "nested",
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "string"
}
}
},
"id": {
"type": "long"
},
"name": {
"type": "string"
}
}
}
}
}
then add some docs:
PUT /test_index/doc/1
{
"id": 1,
"name": "John",
"categories": [
{ "id": 1, "name": "Category 1" },
{ "id": 2, "name": "Category 2" }
]
}
PUT /test_index/doc/2
{
"id": 2,
"name": "Mark",
"categories": [
{ "id": 1, "name": "Category 1" }
]
}
PUT /test_index/doc/3
{
"id": 3,
"name": "Bill",
"categories": [
{ "id": 3, "name": "Category 3" },
{ "id": 4, "name": "Category 4" }
]
}
Now I can use a nested terms filter like this:
POST /test_index/doc/_search
{
"query": {
"constant_score": {
"filter": {
"nested": {
"path": "categories",
"filter": {
"terms": {
"categories.id": [1, 2]
}
}
}
},
"boost": 1.2
}
}
}
...
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "test_index",
"_type": "doc",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"name": "John",
"categories": [
{
"id": 1,
"name": "Category 1"
},
{
"id": 2,
"name": "Category 2"
}
]
}
},
{
"_index": "test_index",
"_type": "doc",
"_id": "2",
"_score": 1,
"_source": {
"id": 2,
"name": "Mark",
"categories": [
{
"id": 1,
"name": "Category 1"
}
]
}
}
]
}
}
Here is the code I used:
http://sense.qbox.io/gist/668aefe910643b52a3a10d40aca67104491668fc

Related

Cypress intercept nested array and object and asserts it value

I am trying to assert value's in a requestbody which I have intercepted with cypress.
The values I need to assert are "name": "NewName", and "title": "STUB1-Draft" you can see in the requestbody example that I have attached.
My testscript in Cypress:
it.only('Check the requestbody', function () {
cy.intercept('PUT', '**/api/assessmenttest/**', req => {
req.reply({ statusCode: 200 });
}).as('NewSectionAndItem');
cy.wait('#NewSectionAndItem')
.its('request.body.test')
.its('testParts')
.its('testSections')
.its('name')
.should('include', 'NewName');
//cy.wait('#NewSectionAndItem').its('request.body.testParts').expect(arr_obj[1].name).to.equal('NewName')
The request body look like the following:
{
"structureStatistics": {
"testPartCount": 1,
"testSectionCount": 6,
"itemCount": 23
},
"name": "BIMMA",
"title": "OTAP",
"correctionInstructionsUrl": "C:Stub/FakePath/For/Cypress",
"correctionInstructionAppendices": [],
"testParts": [
{
"testSections": [
{
"order": 1,
"name": "Tekst 1 Looking for the one? ",
"itemReferences": [
{
"itemId": "55eb5a28-24d8-4705-b465-8e1454f73ac8",
"weight": 11,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "55eb5a28-24d8-4705-b465-8e1454f73ac8",
"title": "H-E-T1-1"
}
}
],
"id": "5c3eef2d-1094-4b9e-84c1-f184956f87fa"
},
{
"id": "ffaebc93-0bf6-4f75-944a-f61345a7be90",
"name": "NewName",
"itemReferences": [
{
"itemId": "58a29037-c92c-48f6-a7c3-a2f94e288992",
"weight": 0,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "58a29037-c92c-48f6-a7c3-a2f94e288992",
"title": "STUB1-Draft",
"state": "Draft"
}
}
]
},
{
"order": 2,
"name": "Tekst 2 The fruit Iron Ox bears",
"itemReferences": [
{
"itemId": "abfc0811-26c7-4d9d-b3cc-0c920e5af259",
"weight": 2,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "abfc0811-26c7-4d9d-b3cc-0c920e5af259",
"title": "H-E-T2-2"
}
},
{
"itemId": "3cfda5e0-0d64-44ef-8a4d-21f37484c024",
"weight": 12,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "3cfda5e0-0d64-44ef-8a4d-21f37484c024",
"title": "H-E-T2-3"
}
},
{
"itemId": "19ba8a53-9755-4beb-8f69-edd107b80230",
"weight": 1,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "19ba8a53-9755-4beb-8f69-edd107b80230",
"title": "H-E-T2-4"
}
},
{
"itemId": "3f5b7b81-df1f-4f01-8165-cb2226d9044d",
"weight": 1,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "3f5b7b81-df1f-4f01-8165-cb2226d9044d",
"title": "H-E-T2-5"
}
}
],
"id": "00f7455e-6d7d-4311-80cd-eff45c83ef2c"
},
{
"order": 3,
"name": "Tekst 3 How to live like a tramp",
"itemReferences": [
{
"itemId": "7e2d568c-4cde-4500-9c6b-c09f246155e4",
"weight": 1,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "7e2d568c-4cde-4500-9c6b-c09f246155e4",
"title": "H-E-T3-6"
}
},
{
"itemId": "87a5bf1c-451a-40b8-802a-53ee842cafcd",
"weight": 1,
"neutralisationType": "NoNeutralisation",
"itemSummary": {
"id": "87a5bf1c-451a-40b8-802a-53ee842cafcd",
"title": "H-E-T3-7"
}
}
],
"id": "390ecc2e-6715-4898-aaea-158e790525a2"
}
],
"navigationMode": "Linear",
"submissionMode": "Individual",
"id": "a546a67c-ac39-4e81-bf03-beb482c920a0"
}
],
"metadataToBePublished": [
"be63002c-dcf8-449f-a0ae-6ba50d4e2712",
"4d70239e-7a6e-47c3-b157-462d6c8c5edc"
],
"created": "2022-07-08T09:00:00+00:00",
"modified": "2022-09-21T23:55:58.6532451+02:00",
"createdBy": {
"id": "a45ea6db-bf04-427d-9354-7081b7592a3d",
"fullName": "Manual Construction"
},
"lastModifiedBy": {
"id": "129a584c-a677-4d9f-b289-019d1815064f",
"fullName": "OZKAN"
},
"id": "300eea01-ee10-4bd9-9356-8aaa933e949c"
}
I could not figure out how I can assert nested arrays and value's, without using deep.equal for the complete request. Thank you indeed!
It basically just looks like a couple of things
an extra test property in the test that's not there in the request
testParts and testSections are arrays (square bracket instead of curly bracket), so you need an index for them
Generally I think this would work
cy.wait('#NewSectionAndItem')
.its('request.body')
.its('testParts.0')
.its('testSections.1')
.its('name')
.should('include', 'NewName');
The problem is identifying the correct array indexes. testParts has only one element, so 0 is the only index option there.
To find the testSection index, create a dummy .json file in VS Code (or other editor) paste in the json from above and use the collapse toggles on the left to easily see which index contains the value you seek.
You can use a cy-spok plugin to make a spok assertion to easily check a nested property.
Example use
const spok = require('cy-spok')
// later in your test
cy.wait('#NewSectionAndItem')
.its('request.body')
.should(spok({
test: {
testParts: {
testSections: {
name: 'NewName',
title: 'STUB1-Draft'
}
}
}
});

calculate the sum from sub array of objects for individual object in mongoose

How to calculate the sum of confident_score for every individual vendor?
Data stored in the DB:
[
{
"_id": "61cab38891152daf9387c0c7",
"name": "dummy",
"company_email": "abc#mailinator.com",
"brief_msg": "Cillum sed est prae",
"similar_case_ids": [],
"answer_id": [
"61cab38891152daf9387c0c9"
],
"pros_cons": [
{
"vendor_name": "xyzlab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor F",
"confident_score": 80,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf0f"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor FFF",
"confident_score": 40,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf10"
}
]
},
{
"vendor_name": "abclab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor B",
"confident_score": 50,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf16"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor BB",
"confident_score": 60,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf17"
}
]
}
]
the query for getting the matching id and grouping objects according to the vendor_name:
aggregate([
{
$match: { _id: id }
},
{
$unwind: {
path: '$pros_cons'
}
},
{
$group: {
_id: '$pros_cons'
}
},
])
};
After query I'm getting this:
[
{
"_id": {
"vendor_name": "abclab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor B",
"confident_score": 50,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf16"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor BB",
"confident_score": 60,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf17"
}
],
}
},
{
"_id": {
"vendor_name": "xyzlab",
"score": [
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor F",
"confident_score": 80,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf0f"
},
{
"question_id": "61c5b47198b2c5bbf9f6471c",
"title": "Vendor FFF",
"confident_score": 40,
"text": "text1",
"_id": "61cac505caeeeb3cec78bf10"
}
],
}
}
]
Need to calculate sum for (vendor_name:abclab)TOTAL=110 and for (vendor_name:xyzlab)TOTAL=120 INDIVIDUALLY
required output:
[
{
"vendor_name": "abclab",
"totalScore": 110,
"count" : 2
},
{
"vendor_name": "xyzlab",
"totalScore": 120,
"count" : 2
}
]
$match - Filter documents by id.
$unwind - Deconstruct pros_cons array to multiple documents.
$project - Decorate output documents. With $reduce, to create totalScore field by summing confident_score from each element in pros_cons.score array.
db.collection.aggregate([
{
$match: {
_id: "61cab38891152daf9387c0c7"
}
},
{
$unwind: {
path: "$pros_cons"
}
},
{
$project: {
_id: 0,
vendor_name: "$pros_cons.vendor_name",
totalScore: {
$reduce: {
input: "$pros_cons.score",
initialValue: 0,
in: {
$sum: [
"$$value",
"$$this.confident_score"
]
}
}
}
}
}
])
Sample Demo on Mongo Playground

Array : Replace part of array object with other array object

I have an array :
{
"count": 8,
"id": "accountId",
"name": " Account Id",
"values": [
{
"count": 2,
"id": "1234456789000",
"name": "1234456789000"
},
{
"count": 1,
"id": "135792468045",
"name": "135792468045"
},
{
"count": 1,
"id": "309983110022",
"name": "309983110022"
},
{
"count": 2,
"id": "432112426686",
"name": "432112426686"
},
{
"count": 1,
"id": "6ee26149-a156-4665-bd26-a6e46b49a70f",
"name": "6ee26149-a156-4665-bd26-a6e46b49a70f"
},
{
"count": 1,
"id": "927b48ce-efe4-4c20-98f0-ec6c54f59b45",
"name": "927b48ce-efe4-4c20-98f0-ec6c54f59b45"
}
]
}
And I have a 2nd array :
[
{
"count": 2,
"id": "432112426686",
"name": "432112426686"
},
{
"count": 2,
"id": "1234456789000",
"name": "1234456789000"
}
]
The second array, I have filtered based on some requirement.
I want to replace the values from 1st array with second array.
Using typescript code.
Please help here.
If I understand question, you just need iterate by values array and find same id from second array.
function findAndSwitch(object1, array1): void {
object1.values = object1.values.map(el => {
const objToSwitch = array1.find(eleArr => eleArr.id === el.id);
if (objToSwitch) {
return el = objToSwitch;
} else {
return el;
}
});
}
https://stackblitz.com/edit/typescript-dvty4p
Let's say your variable names are obj1 and arr2, respectively. First transform arr2 into an object obj2:
let obj2 = arr2.reduce((acc,cur) => ({...acc,[cur.id]:cur}),{});
Which now looks like this:
{
'432112426686': {
count: 20,
id: '432112426686',
name: '432112426686'
},
'1234456789000': {
count: 20,
id: '1234456789000',
name: '1234456789000'
}
}
Then transform obj1 based on obj2 as follows:
obj1.values = obj1.values.map(value => obj2[value.id] || value);
Please note that since the data in arr2 is identical to the corresponding data in obj1, I have altered the values of count property in arr2 in order to demonstrate that obj1 will be updated accordingly.
DEMO ONE
let obj1 = {
"count": 8,
"id": "accountId",
"name": " Account Id",
"values": [
{
"count": 2,
"id": "1234456789000",
"name": "1234456789000"
},
{
"count": 1,
"id": "135792468045",
"name": "135792468045"
},
{
"count": 1,
"id": "309983110022",
"name": "309983110022"
},
{
"count": 2,
"id": "432112426686",
"name": "432112426686"
},
{
"count": 1,
"id": "6ee26149-a156-4665-bd26-a6e46b49a70f",
"name": "6ee26149-a156-4665-bd26-a6e46b49a70f"
},
{
"count": 1,
"id": "927b48ce-efe4-4c20-98f0-ec6c54f59b45",
"name": "927b48ce-efe4-4c20-98f0-ec6c54f59b45"
}
]
};
let arr2 = [
{
"count": 20,
"id": "432112426686",
"name": "432112426686"
},
{
"count": 20,
"id": "1234456789000",
"name": "1234456789000"
}
];
let obj2 = arr2.reduce((acc,cur) => ({...acc,[cur.id]:cur}),{});
obj1.values = obj1.values.map(value => obj2[value.id] || value);
console.log( obj1 );
--
However, if all you want is to replace all the obj1.values with arr2, all you need is:
obj1.values = [...arr2];
And this will give rise to:
{
count: 8,
id: 'accountId',
name: ' Account Id',
values: [{
count: 20,
id: '432112426686',
name: '432112426686'
},
{
count: 20,
id: '1234456789000',
name: '1234456789000'
}
]
}
DEMO TWO
let obj1 = {
"count": 8,
"id": "accountId",
"name": " Account Id",
"values": [
{
"count": 2,
"id": "1234456789000",
"name": "1234456789000"
},
{
"count": 1,
"id": "135792468045",
"name": "135792468045"
},
{
"count": 1,
"id": "309983110022",
"name": "309983110022"
},
{
"count": 2,
"id": "432112426686",
"name": "432112426686"
},
{
"count": 1,
"id": "6ee26149-a156-4665-bd26-a6e46b49a70f",
"name": "6ee26149-a156-4665-bd26-a6e46b49a70f"
},
{
"count": 1,
"id": "927b48ce-efe4-4c20-98f0-ec6c54f59b45",
"name": "927b48ce-efe4-4c20-98f0-ec6c54f59b45"
}
]
};
let arr2 = [
{
"count": 20,
"id": "432112426686",
"name": "432112426686"
},
{
"count": 20,
"id": "1234456789000",
"name": "1234456789000"
}
];
obj1.values = arr2;
console.log( obj1 );

How to replace a value of an array in MongoDB aggregation?

I'm trying to replace the value only in the aggregation result, not in the stored document, thats why I use an aggregation and not a update.
Suppose these are the documents stored in the collection:
{ "_id": 1, "first_name": "foo1", items: [
{ "item": "gloves", "body": "hands" },
{ "item": "gloves", "body": "hands" },
{ "item": "shirts" , "body": "torso" }
]
},
{ "_id": 2, "first_name": "foo2", items: [
{ "item": "skirts", "where": "legs" },
{ "item": "jeans", "body": "legs" },
{ "item": "socks" , "body": "feet" },
{ "item": "gloves" , "body": "hands" }
]
},
{ "_id": 3, "first_name": "foo3", items: [
{ "item": "jacket", "where": "torso" },
{ "item": "socks", "body": "feet" },
{ "item": "shirts" , "body": "torso" }
]
}
The aggregation must replace "gloves" by "sandals", and it correspondent attribute "body" from "hands" to "feet".
The expected result is:
{ "_id": 1, "first_name": "foo1", items: [
{ "item": "sandals", "body": "feet" },
{ "item": "sandals", "body": "feet" },
{ "item": "shirts" , "body": "torso" }
]
},
{ "_id": 2, "first_name": "foo2", items: [
{ "item": "skirts", "where": "legs" },
{ "item": "jeans", "body": "legs" },
{ "item": "socks" , "body": "feet" },
{ "item": "sandals" , "body": "feet" }
]
},
{ "_id": 3, "first_name": "foo3", items: [
{ "item": "jacket", "where": "torso" },
{ "item": "socks", "body": "feet" },
{ "item": "shirts" , "body": "torso" }
]
}
So far, I've tried using "$unwind" and $project with "$switch", among other approaches, but none of them work.

How to sort words by their relevance to a keyword?

I'm currently working on a search using elasticsearch. We have a very large amount of users.
Here is the elasticsearch mapping:
PUT /example_index/_mapping/users
{
"properties": {
"user_autocomplete": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
},
"completion": {
"type": "text",
"analyzer": "user_autocomplete_analyzer",
"search_analyzer": "standard"
}
}
},
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
}
}
}
Here is the search query.
For example, I get 3 records
GET example_index/users/_search
{
"from": 0,
"size": 3,
"query": {
"query_string": {
"query": "*ro*",
"fields": [
"firstName",
"lastName"
]
}
},
"aggs": {
"user_suggestions": {
"terms": {
"size": 3,
"field": "user_autocomplete.raw"
}
}
}
}
Here is the output of elasticsearch
{
"took": 53,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 13,
"max_score": 1,
"hits": [
{
"_index": "example_index",
"_type": "users",
"_id": "08",
"_score": 1,
"_source": {
"firstName": "Eero",
"lastName": "Saarinen",
"user_autocomplete": "Eero Saarinen"
}
},
{
"_index": "example_index",
"_type": "users",
"_id": "16",
"_score": 1,
"_source": {
"firstName": "Aaron",
"lastName": "Judge",
"user_autocomplete": "Aaron Judge"
}
},
{
"_index": "example_index",
"_type": "users",
"_id": "20",
"_score": 1,
"_source": {
"firstName": "Robert",
"lastName": "Langdon",
"user_autocomplete": "Robert Langdon"
}
}
]
},
"aggregations": {
"user_suggestions": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 10,
"buckets": [
{
"key": "Eero Saarinen",
"doc_count": 1
},
{
"key": "Aaron Judge",
"doc_count": 1
},
{
"key": "Robert Langdon",
"doc_count": 1
}
]
}
}
}
I need result like in the following order:
Robert Langdon
Aaron Judge
Eero Saarinen
I have tried order method. It won't work. Is there a way to do this?

Resources