Elasticsearch Node.js client: delete a nested element from a object - node.js

I have a object like bellow
"_source": {
"name": "capsicum",
"id": "60f759b934e43100195c6142",
"userId": "60f7209ecdb2c0001935fa8f",
"version": 1,
"tags": [
{
"name": "fruit",
"id": "60f75990be3b530019062790",
"group": "location"
},
{
"name": "green",
"id": "60f75990be3b530019062766",
"group": "food"
},
{
"name": "red",
"id": "60f75990be3b530019062722",
"group": "food"
},
{
"name": "vegetable",
"id": "60f75990be3b53001906272d",
"group": "food"
}
]
}
how can I delete one element from tags, add an element in tags, in separate api calls.
I tried bellow code for deleting but not working
elasticClient.updateByQuery(
{
index: ElasticIndexs.Products,
body: {
script: {
source: `ctx._source.tags.removeIf(a -> a.id == params.id)`,
params: {
id: product.tag.id,
},
},
query: {
match: {
id: product.id,
},
},
},
},
function (error, response) {
console.log(response);
}
);

for adding:
await elasticWrapper.client.update({
index: ElasticIndexs.Products,
id: data.id,
body: {
script: {
inline: 'ctx._source.tags.add(params.tag)',
// lang: 'painless',
params: {
tag: data.tag,
},
},
},
});
for deleting:
await elasticWrapper.client.update({
index: ElasticIndexs.Products,
id: data.id,
body: {
script: {
inline: `for (int i = 0; i < ctx._source.tags.size();
i++){if(ctx._source.tags[i].id == params.id){ctx._source.tags.remove(i);}}`,
params: {
id: data.tag.id,
},
},
},
});

Related

Filter data using nodejs and elasticsearch

I'm currently facing an issue with my datatable implemented in ReactJS. I'm retrieving data from elasticsearch and populating the datatable with it. The data retrieval process works fine without the filter applied, however, when I apply filters to the data, the datatable remains empty, even though the data _source has matching records.
The structure of the parameters I am sending is as follows:
{
pageIndex: 1,
pageSize: 10,
sort: { order: '', key: '' },
query: '',
filterData: {
analysis: [ '0', '1', '2', '3' ],
threat_level_id: [ '1', '2', '3', '4' ],
}
}
EndPoint:
POST /api/v1/events/public/list
Controller:
exports.getPublicEvents = async (req, res) => {
try {
client.ping()
const { pageIndex, pageSize, sort, query, filterData } = req.body
let esQuery = {
index: 'ns_*',
body: {
query: {
bool: {
must: [
{
match_all: {},
},
],
filter: [],
},
},
from: (pageIndex - 1) * pageSize,
size: pageSize,
},
}
if (query) {
esQuery.body.query.bool.must = [
{
match: {
'Event.info': {
query: query,
fuzziness: 'AUTO',
},
},
},
]
}
if (filterData.analysis.length > 0) {
esQuery.body.query.bool.filter.push({
terms: {
'Event.analysis': filterData.analysis,
},
})
}
if (filterData.threat_level_id.length > 0) {
esQuery.body.query.bool.filter.push({
terms: {
'Event.threat_level_id': filterData.threat_level_id,
},
})
}
let esResponse = await client.search(esQuery)
let data = esResponse.hits.hits.map((hit) => hit._source)
let total = esResponse.hits.total.value
res.status(200).json({
status: 'success',
data: data,
total: total,
})
} catch (error) {
res.status(500).json({
error: 'Error connecting to Elasticsearch',
errorMessage: error.message,
})
}
}
The controller below is without filters and it works just fine.
exports.getPublicEvents = async (req, res) => {
try {
client.ping()
const { pageIndex, pageSize, sort, query } = req.body
let esQuery = {
index: 'ns_*',
body: {
query: {
match_all: {},
},
from: (pageIndex - 1) * pageSize,
size: pageSize,
},
}
if (query) {
esQuery.body.query = {
match: {
'Event.info': {
query: query,
fuzziness: 'AUTO',
},
},
}
}
let esResponse = await client.search(esQuery)
let data = esResponse.hits.hits.map((hit) => hit._source)
let total = esResponse.hits.total.value
res.status(200).json({
status: 'success',
data: data,
total: total,
})
} catch (error) {
res.status(500).json({
error: 'Error connecting to Elasticsearch',
errorMessage: error.message,
})
}
}
ElasticSearech version: 7.17.8
Result of: console.log(JSON.stringify(esQuery))
{
"index": "INDEX_NAME",
"body": {
"query": {
"bool": {
"must": [{ "match_all": {} }],
"filter": [
{ "terms": { "Event.analysis": ["0", "1", "2"] } },
{ "terms": { "Event.threat_level_id": ["1", "2", "3", "4"] } }
]
}
},
"from": 0,
"size": 10
}
}
Data in elascticsearch schema
{
"#version": "1",
"#timestamp": "2023-02-01T14:43:09.997Z",
"Event": {
"info": ".......................",
"description": ".......................",
"analysis": 0,
"threat_level_id": "4",
"created_at": 1516566351,
"uuid": "5a64f74f0e543738c12bc973322",
"updated_at": 1675262417
}
}
Index Mapping
{
"index_patterns": ["INDEX_NAME"],
"template": "TEMPLATE_NAME",
"settings": {
"number_of_replicas": 0,
"index.mapping.nested_objects.limit": 10000000
},
"mappings": {
"dynamic": false,
"properties": {
"#timestamp": {
"type": "date"
},
"Event": {
"type": "nested",
"properties": {
"date_occured": {
"type": "date"
},
"threat_level_id": {
"type": "integer"
},
"description": {
"type": "text"
},
"is_shared": {
"type": "boolean"
},
"analysis": {
"type": "integer"
},
"uuid": {
"type": "text"
},
"created_at": {
"type": "date"
},
"info": {
"type": "text"
},
"shared_with": {
"type": "nested",
"properties": {
"_id": {
"type": "text"
}
}
},
"updated_at": {
"type": "date"
},
"author": {
"type": "text"
},
"Attributes": {
"type": "nested",
"properties": {
"data": {
"type": "text"
},
"type": {
"type": "text"
},
"uuid": {
"type": "text"
},
"comment": {
"type": "text"
},
"category": {
"type": "text"
},
"value": {
"type": "text"
},
"timestamp": {
"type": "date"
}
}
},
"organisation": {
"type": "nested",
"properties": {
"name": {
"type": "text"
},
"uuid": {
"type": "text"
}
}
},
"Tags": {
"type": "nested",
"properties": {
"color": {
"type": "text"
},
"name": {
"type": "text"
}
}
},
"TLP": {
"type": "nested",
"properties": {
"color": {
"type": "text"
},
"name": {
"type": "text"
}
}
}
}
}
}
}
}
Event is a nested field, so you need to use nested queries, like this:
{
"index": "INDEX_NAME",
"body": {
"query": {
"bool": {
"must": [{ "match_all": {} }],
"filter": [
{
"nested": {
"path": "Event",
"query": {"terms": { "Event.analysis": ["0", "1", "2"] }}
}
},
{
"nested": {
"path": "Event",
"query": {"terms": { "Event.threat_level_id": ["1", "2", "3", "4"] }}
}
}
]
}
},
"from": 0,
"size": 10
}
}

I have a question about multiple choice grid in google forms api

![]:(https://i.stack.imgur.com/5IOEd.png)
![]:(https://i.stack.imgur.com/feAyS.png)
How do I write json code?
I need an example of writing batchUpdate json in google form api.
thank you.
********
const update = {
requests: [
{
createItem: {
item: {
title: "TEST ",
title: "TEST RADIO",
questionGroupItem: {
grid: {
columns: {
type: "RADIO",
options: [
{ value: "A"},
{ value: "B"},
{ value: "C"},
{ value: "D"},
]
}
}
}
},
location: { index: 1 },
},
}
]
};
const res = await forms.forms.batchUpdate({
formId: createResponse.data.formId,
requestBody: update,
});
********
When I saw your request body, I thought that there are 2 modification points.
There are 2 title properties like title: "TEST ", and title: "TEST RADIO",.
In your request body, it is required to include rowQuestion of questions.
When these points are reflected in your script, it becomes as follows.
Modified request body:
const update = {
"requests": [
{
"createItem": {
"item": {
"title": "TEST ",
"questionGroupItem": {
"grid": {
"columns": {
"type": "RADIO",
"options": [
{
"value": "A"
},
{
"value": "B"
},
{
"value": "C"
},
{
"value": "D"
}
]
}
},
"questions": [
{
"rowQuestion": {
"title": "smaple1"
}
},
{
"rowQuestion": {
"title": "sample2"
}
}
]
}
},
"location": {
"index": 1
}
}
}
]
};
In this sample modified request body, 4 columns and 2 rows are created.
References:
Method: forms.batchUpdate
CreateItemRequest

Filter nested result inside a nested object with elasticsearch

I'm trying to filter a nested object and sort by the result, however, I tried some things without success, I'll leave my initial attempt and it works partially, it just filters according to what I have in my search variable, but all the results come of this nested object as it is inside the 'root' which is another nested object
Elastic version: 7.13.0 with NodeJS
using #elastic/elasticsearch official package from npm
let params: RequestParams.Search = {
index: index,
body: {
size: 30,
query: {
bool: {
must: [
{
nested: {
path: "profile",
query: {
bool: {
must: [
{
match: {
"profile.id": profileId,
},
},
],
},
},
},
},
],
filter: [
{
nested: {
path: "profile.following",
ignore_unmapped: true,
query: {
query_string: {
fields: [
"profile.following.name",
"profile.following.username",
],
query: searchWord + "*",
},
},
},
},
],
},
},
},
};
I need it to be this specific 'profile.id' that is passed by parameter in the function, so the result is only 1 profile with N people that it follows
the document is mapped as follows, I left only the fields relevant to the question:
{
"mappings": {
"_doc": {
"properties": {
"id": {
"type": "integer"
},
"phone": {
"type": "text"
},
"profile": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"username": {
"type": "text"
},
"following": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"isAwaitingApproval": {
"type": "boolean"
},
"name": {
"type": "text"
},
"profilePicURL": {
"type": "text"
},
"username": {
"type": "text"
}
}
}
}
}
}
}
}
}
an example of a current result is:
with the following parameters (profileId:141, searchWord: "para" )
{
"res": [
{
"profilePicURL": "localimage",
"name": "donor donor",
"id": 140,
"username": "victorTesteElastic2",
"isAwaitingApproval": false
},
{
"profilePicURL": "localimage",
"name": "para ser seguido",
"id": 142,
"username": "victorprivate",
"isAwaitingApproval": true
}
]
}
the desired result is:
{
"res": [
{
"profilePicURL": "localimage",
"name": "para ser seguido",
"id": 142,
"username": "victorprivate",
"isAwaitingApproval": true
}
]
}
with some more research I got what I needed, I'll leave the answer here in case anyone needs it too
let params: RequestParams.Search = {
index: index,
body: {
size: 30,
query: {
bool: {
must: [
{
nested: {
path: "profile",
query: {
bool: {
must: [
{
match: {
"profile.id": profileId,
},
},
],
},
},
},
},
{
nested: {
path: "profile",
inner_hits: {
name: "profile",
},
query: {
nested: {
path: "profile.following",
inner_hits: {
name: "following",
},
ignore_unmapped: true,
query: {
query_string: {
fields: [
"profile.following.name",
"profile.following.username",
],
query: searchWord + "*",
},
},
},
},
},
},
],
},
},
},
};
I basically put in must what was in the filter, mapped the nested object from above, in this case the profile, and put the tag inner_hits for profile and inner_hits for followings, that's the only way it worked
the answer I need was returned here:
body.hits.hits[0].inner_hits.profile.hits.hits[0].inner_hits.following.hits.hits
below is an example of the answer:
{
"res": [
{
"_index": "donor",
"_type": "_doc",
"_id": "P3VWNnsB4coAEhD-F3fF",
"_nested": {
"field": "profile",
"offset": 0,
"_nested": {
"field": "following",
"offset": 0
}
},
"_score": 1,
"_source": {
"profilePicURL": "localimage",
"name": "donor donor",
"id": 140,
"username": "victorTesteElastic2",
"isAwaitingApproval": false
}
},
{
"_index": "donor",
"_type": "_doc",
"_id": "P3VWNnsB4coAEhD-F3fF",
"_nested": {
"field": "profile",
"offset": 0,
"_nested": {
"field": "following",
"offset": 1
}
},
"_score": 1,
"_source": {
"profilePicURL": "localimage",
"name": "para ser seguido",
"id": 142,
"username": "victorprivate",
"isAwaitingApproval": true
}
}
]
}
the filtered data I really need that have been matched in must is in this array, where I need to iterate and look at _source which is the data that is indexed

Update many sub of sub-document array using $set / $push

Following is the Sample Workspace document.I want to update box positions when we drag and drop at front end.
{
"_id": ObjectId("5eaa9b7c87e99ef2430a320b"),
"logo": {
"url": ".../../../assets/logo/dsdsds.png",
"name": "testUpload"
},
"name": "My World",
"sections": [{
"box": [{
"_id": ObjectId("5da87b33502d6c634b3aa7ce"),
"name": "Meran To",
"position": 0
},
{
"_id": ObjectId("5da87b33502d6c7d873aa7d0"),
"name": "Documentation",
"position": 2
},
{
"_id": ObjectId("5da87b33502d6cdbb93aa7cf"),
"name": "File Manager Upload File Drive",
"position": 1
},
{
"_id": ObjectId("5da87b33502d6c276a3aa7cd"),
"name": "File Manager Upload File Drive",
"position": 1
}
],
"id": 1,
"title": "Simplicity",
"description": "Follow your barriers"
},
{
"box": [],
"id": 2,
"title": "xfxdfxcx 34",
"description": "sdsdsd sfsfsd ewrewrewre"
}
]
}
I send the updated positions from front-end to back-end via API, in an array as shown below.
[
{
"id": "5da87b33502d6c634b3aa7ce",
"position": 0
}, {
"id": "5da87b33502d6c7d873aa7d0",
"position": 1
}, {
"id": "5da87b33502d6cdbb93aa7cf",
"position": 2
}, {
"id": "5da87b33502d6c276a3aa7cd",
"position": 3
}]
I am currently updating DB using the below code
for (const el of req.body) {
await this.model.updateOne({
_id: req.params.workspaceId,
sections: {
$elemMatch: {
id: req.params.sectionId
}
},
'sections.box': {
$elemMatch: {
_id: el.id
}
},
}, {
$set: {
'sections.$[outer].box.$[inner].position': el.position
}
}, {
arrayFilters: [{
'outer.id': req.params.sectionId
}, {
'inner._id': el.id
}],
upsert: false,
});
}
But this is not the best method, it hits DB multiple times.
so I need to optimize this code with mongoose query itself.
May be using $set / $push.I don't know any exact methods.
So basically we need to remove the external for loop and make it work with mongoose itself.This is my requirement.
Thanks in advance for all the support.
There are 2 methods for doing it.
bulkWrite
const bulkOps = [];
req.body.forEach((el) => {
const upsertDoc = {
updateOne: {
filter: {
_id: req.params.workspaceId,
sections: {
$elemMatch: {
id: req.params.sectionId
}
},
'sections.box': {
$elemMatch: {
_id: el.id
}
},
},
update: {
$set: {
'sections.$[outer].box.$[inner].position': el.position
}
},
arrayFilters: [{
'outer.id': req.params.sectionId
}, {
'inner._id': el.id
}],
upsert: false,
}
};
bulkOps.push(upsertDoc);
});
const result = await this.model.collection.bulkWrite(bulkOps);
bulkUpdate
const bulkUpdate = this.model.collection.initializeUnorderedBulkOp();
req.body.forEach((el) => {
bulkUpdate.find({
_id: req.params.workspaceId,
sections: { $elemMatch: { id: req.params.sectionId } },
'sections.box': { $elemMatch: { _id: el.id } },
}).arrayFilters([{ 'outer.id': req.params.sectionId }, { 'inner._id': el.id }])
.updateOne({ $set: { 'sections.$[outer].box.$[inner].position': el.position }
});
});
await bulkUpdate.execute();

GraphQL Query - cannot see nested elements in JSON respone

if anyone can hazard a guess or where to look it would be greatly appreciated.
I can get nested data when I run using graphgl API, however, from my node program it only shows top-level items - does not display the nested elements for the customer and lineitem object.
I am using Koa middle where, with promise response:
router.get('/orders/', async (ctx) => {
const auth = prepareAuth(ctx);
await getOrders(auth).then(response => ctx.body = response.data.data.orders);
console.log(ctx.body.edges)
However from the console it has (customer null and 'object':
[
{
node: {
createdAt: '2020-02-24T12:53:20Z',
customer: null,
name: '#1001',
lineItems: [Object]
}
},
{
node: {
createdAt: '2020-02-24T12:53:50Z',
customer: null,
name: '#1002',
lineItems: [Object]
}
},
{
node: {
createdAt: '2020-03-10T21:11:04Z',
customer: null,
name: '#1003',
lineItems: [Object]
}
}
]
when i use the GraphQL API directly the query works fine and I get full response:
{
"data": {
"orders": {
"edges": [
{
"node": {
"createdAt": "2020-02-24T12:53:20Z",
"customer": {
"displayName": "franko girl"
},
"name": "#1001",
"lineItems": {
"edges": [
{
"node": {
"name": "dance mat red",
"quantity": 4
}
}
]
}
}
},
{
"node": {
"createdAt": "2020-02-24T12:53:50Z",
"customer": {
"displayName": "franko man"
},
"name": "#1002",
"lineItems": {
"edges": [
{
"node": {
"name": "dance mat black",
"quantity": 2
}
}
]
}
}
},
{
"node": {
"createdAt": "2020-03-10T21:11:04Z",
"customer": {
"displayName": "franko man"
},
"name": "#1003",
"lineItems": {
"edges": [
{
"node": {
"name": "dance mat black",
"quantity": 1
}
},
{
"node": {
"name": "dance mat red",
"quantity": 1
}
}
]
}
}
}
]
}
},
Okay, so finally figured this out, for anyone else who stumbles accross this problem, you need to the convert the json object to a string using built in javascript function: JSON.stringify()
from W3schools.com
var obj = { name: "John", age: 30, city: "New York" };
var myJSON = JSON.stringify(obj);

Resources