How to Remove Specific Text / String Out of Array Object Values - node.js

I am using Node.js 16.17 and Express.
Forgive me if this is answered elsewhere, if a solution exists elsewhere, please point me in that direction.
On my server side, I have an array with objects with properties and their values. I want to be able to remove specific text/string from the property values.
What I Have
I currently have an array with objects (and sometimes arrays and object nested within):
DataArray =
[
{
page: {
results: [
{
id: '1234',
title: 'TextIWantA **(Text) I Dont Want**',
children: {
page: {
results: [
{
id: '5678',
title: 'ChildA TextIWant **(Text I) Dont Want**',
},
{
id: '9101',
title: 'ChildB TextIWant **(Text I) Dont Want**',
children: {
page: {
results: [
{
id: 'abcd',
title: 'GrandchildA TextIWant **(Text I (Dont) Want**',
}
]
}
}
},
],
},
},
},
{
id: '1121',
title: 'TextIWantB **(Text) I Dont Want**',
}
]
}
}
]
I am able to flatten this structure with this function:
function flatten(arr) {
const flattened = []
for (const { children, ...element } of arr) {
flattened.push(element)
if (children) {
flattened.push(...flatten(children.page.results))
}
}
return flattened;
}
const flat = [{ page: { results: flatten(DataArray[0].page.results) } }]
console.log(flat[0].page.results)
The returned data is:
[
{ id: '1234', 'page', title: 'TextIWantA **(Text) I Dont Want**' },
{ id: '5678', 'page', title: 'ChildA TextIWant **(Text I) Dont Want**' },
{ id: '9101', 'page', title: 'ChildB TextIWant **(Text I) Dont Want**' },
{ id: 'abcd', 'page', title: 'GrandchildA TextIWant **(Text I (Dont) Want**' },
{ id: '1121', 'page', title: 'TextIWantB **(Text) I Dont Want**' }
]
I am making an assumption that I have to change my text to a string in order to replace it then parse it again to turn back into an object. I'm happy to learn my assumption is true or incorrect, if incorrect, how to fix to be able to remove text.
So if I try to do a replace using the following, 1) it does not work and 2) it does not differentiate for the different text to remove (perhaps I just run multiple/different replaces/filters?):
const veryFlat = flat;
var veryFlatData = veryFlat.map(function(x){return x.toString().replace(/ **(Text) I Dont Want**/g, '');});
var removedTextData= JSON.parse(veryFlatData);
console.log(removedTextData);
Desired Result
I want to be able to remove all of the variances of Text I Dont Want, so the end result would look like (of now it will be flattened as seen above)
DataArray =
[
{
page: {
results: [
{
id: '1234',
title: 'TextIWantA',
children: {
page: {
results: [
{
id: '5678',
title: 'ChildA TextIWant',
},
{
id: '9101',
title: 'ChildB TextIWant',
children: {
page: {
results: [
{
id: 'abcd',
title: 'GrandchildA TextIWant',
}
]
}
}
},
],
},
},
},
{
id: '1121',
title: 'TextIWantB',
}
]
}
}
]
Each title is unique and I don't seem able to find anything to even say I've tried this or that.
I don't want to use .startswith or .length or .index and would prefer to avoid regex, and the example above using .replace doesn't seem to work.
How do I reach into these property values and rip out the text I don't want?
Thank you for any help you can provide.

Related

How to pass an optional argument in Mongoose/MongoDb

I have the following query:
Documents.find({
$and: [
{
user_id: {$nin:
myUserId
}
},
{ date: { $gte: dateMax, $lt: dateMin } },
{documentTags: {$all: tags}}
],
})
What I'm trying to do is make the documentTags portion of the query optional. I have tried building the query as follows:
let tags = " ";
if (req.body.tags) {
tags = {videoTags: {$all: req.body.tags}};
}
let query = {
$and: [
{
user_id: {$nin:
myUserId
}
},
{ date: { $gte: dateMax, $lt: dateMin } },
tags
],
}
and then Document.find(query). The problem is no matter how I modify tags (whether undefined, as whitespace, or otherwise) I get various errors like $or/$and/$nor entries need to be full objects and TypeError: Cannot read property 'hasOwnProperty' of undefined.
Is there a way to build an optional requirement into the query?
I tried the option below and the query is just returning everything that matches the other fields. For some reason it isn't filtering by tags. I did a console.log(queryArr) and console.log(query) get the following respectively:
[
{ user_id: { '$nin': [Array] } },
{
date: {
'$gte': 1985-01-01T00:00:00.000Z,
'$lt': 2020-01-01T00:00:00.000Z
}
},
push: { documentTags: { '$all': [Array] } }
]
console.log(query)
{
'$and': [
{ user_id: [Object] },
{ date: [Object] },
push: { documentTags: [Object] }
]
}
You are almost there. Instead you could construct the object outside the query and just put the constructed query in $and when done..
let queryArr = [
{
user_id: {$nin: myUserId}
},
{ date: { $gte: dateMax, $lt: dateMin } }
];
if (req.body.tags) {
queryArr.push({videoTags: {$all: req.body.tags}});
}
let query = {
$and: queryArr
}
Now you can control the query by just pushing object into the query Array itself.
I figured out why it wasn't working. Basically, when you do myVar.push it creates a key-value pair such as [1,2,3,push:value]. This would work if you needed to append a k-v pair in that format, but you'll have difficulty using it in a query like mine. The right way for me turned out to be to use concact which appends the array with just the value that you set, rather than a k-v pair.
if (req.body.tags){
queryArgs = queryArgs.concat({documentTags: {$all: tags}});
}
let query = {
$and: queryArgs
}

How to search part of a sentece in elasticsearch

I'm using Elasticsearch js to make a search engine, like so:
let jobs = await client.search({
index: 'index',
type: 'doc',
body: {
query: {
bool: {
must: [
{
match: {
title: 'test'
}
}
]
}
}
}
});
if the title has 'test' in it , it will show, but when it has something like 'hello this is/test' it wont show up, how do I fix it?
You can surround the string with *:
let jobs = await client.search({
index: 'index',
type: 'doc',
body: {
query: {
bool: {
must: [
{
match: {
title: '*test*'
}
}
]
}
}
}
});

searching with elasticsearch js with multiple fields

Hi guys I have this code :
let test = await client.search({
index: 'test',
type: 'doc',
body: {
query: {
match: {
title: 'something',
}
}
}
});
this code is searching by 1 query which is title: 'something' , but I want to change it to search with multiple keys, for example:
let test = await client.search({
index: 'test',
type: 'doc',
body: {
query: {
match: {
title: 'something',
desc: 'some Qualifications'
}
}
}
});
but this code doesn't work and I can't find anything that will work like that, can anyone help?
You need to combine all the match queries using a bool/must query, like this:
let test = await client.search({
index: 'test',
type: 'doc',
body: {
query: {
bool: {
must: [
{
match: {
title: 'something',
}
},
{
match: {
desc: 'some Qualifications',
}
}
]
}
}
}
});

Mongoose schema infinite nesting

I'm having some trouble creating a model that describes the object that I want to store in mongoDB.
This is the object:
simple: [
{
label: 'Satisfied customers',
children: [
{
label: 'Good food',
children: [
{ label: 'Quality ingredients' },
{ label: 'Good recipe' }
]
},
{
label: 'Good service (disabled node)',
children: [
{ label: 'Prompt attention' },
{ label: 'Professional waiter' }
]
},
{
label: 'Pleasant surroundings',
children: [
{ label: 'Happy atmosphere' },
{ label: 'Good table presentation' },
{ label: 'Pleasing decor' }
]
}
]
}
]
This is data that is input to a QTree: https://quasar.dev/vue-components/tree
I want to model this but this object can in theory expand indefinitely as each child can have children of it's own and so on. Is there a way to do this nicely in a Mongoose Schema? My search so far turned up empty.
Here is what I have now:
simple: [{
_id: false,
label: String,
children: [{
_id: false,
label: String,
}]
}]
I can ofcourse choose to limit the depth to a certain amount (say 5) and put that amount of nesting in my schema, but I'm trying to find a more elegant solution so that I dont have to do that.

How can I restructure this MongoDB result into the right format?

I'm querying mongo with something like this:
const loadedLists = await ListValues.find( {} )
I get results back that look more or less like this:
result = [ { listname: 'ListOne',
values: [ {
label: 'OptOne',
cost: '25'
},{
label: 'OptTwo',
cost: '22'
}]
}, { listname: 'ListTwo',
values: [...//and so on]
}]
I need to turn this into an object that looks like this:
const newData =
{ listOne: [ { label: 'OptOne', cost: '25' }, { label ...} ],
listTwo: [ { label: 'OptTwo', cost: '25' }, { label ...} ]}
With the goal of eventually doing Object.assign(res.locals, ...newData) so res.locals has a property of listOne and listTwo.
I've messed around with map like this:
const hh = {}
hh = result.map( async list => {
return { [list.listName]: list.values }
}
hh = await Promise.all(hh)
Which almost works, except hh looks like this:
[
{ listOne: [ ...//Properly has the values ] },
{ listTwo: [ ...//Same...]}
]
I just need to cleanly get those objects moved up a level. I've found NPM modules to do this, or loops to reprocess it, but I can't help but feel like there's a cleaner / tidier way to get it returned.
You need to use reduce
const loadedLists = await ListValues.find( {} )
const hh = loadedList.reduce(
(acc, {listname, values}) => (acc[listname] = values, acc)
{}
)
Also you don't need to scatter async keyword all over the place. :) It makes sense to use it only with async functions that use await keyword.
Example from your code.
// why do you even need async here? there is nothing async in creating an object
hh = result.map( async list => {
return { [list.listName]: list.values }
}
You can use Array.reduce() to accumulate the result.
'use strict';
let data = [{
listname: 'ListOne',
values: [{
label: 'OptOne',
cost: '25'
}, {
label: 'OptTwo',
cost: '22'
}]
}, {
listname: 'ListTwo',
values: [{
label: 'OptOne',
cost: '25'
}, {
label: 'OptTwo',
cost: '22'
}]
}];
let result = data.reduce(function (acc, {listname, values}) {
if (acc[listname]) {
acc[listname] = acc[listname].concat(values);
} else {
acc[listname] = values;
}
return acc;
}, {});
console.log('result', result);
This logs
result { ListOne:
[ { label: 'OptOne', cost: '25' },
{ label: 'OptTwo', cost: '22' } ],
ListTwo:
[ { label: 'OptOne', cost: '25' },
{ label: 'OptTwo', cost: '22' } ] }

Resources