I`am filtering a search query by clicking at a checkbox. I have different check boxes. All uses the same function to add and remove my query build.
I am storing the values in a array and format them for my query build.
I'am using Elasticsearch for the query and angular7.
onChangeLocations(value: string, checked: boolean) {
if (checked) {
this.selectedLocations.push(value);
this.selectedTopics.push({
'match_phrase': { '134_facet_locations': value },
});
this.selectedTopics.push({
'match_phrase': { '135_facet_locations': value },
});
this.selectedTopics.push({
'match_phrase': { '136_facet_locations': value },
});
} else {
this.selectedTopics = this.selectedTopics.filter((topic: any) => {
return topic.match_phrase['134_facet_locations'] !== value;
});
this.selectedTopics = this.selectedTopics.filter((topic: any) => {
return topic.match_phrase['135_facet_locations'] !== value;
});
this.selectedTopics = this.selectedTopics.filter((topic: any) => {
return topic.match_phrase['136_facet_locations'] !== value;
});
this.selectedLocations = this.selectedLocations.filter((facet: any) => {
return facet !== value;
});
}
}
this is my Output that works for OR querys.
[
{
"bool": {
"should": [
{
"match_phrase": {
"134_facet_locations": "Deutschland"
}
},
{
"match_phrase": {
"135_facet_locations": "Deutschland"
}
},
{
"match_phrase": {
"136_facet_locations": "Deutschland"
}
},
{
"match_phrase": {
"134_facet_locations": "Basel"
}
},
{
"match_phrase": {
"135_facet_locations": "Basel"
}
},
{
"match_phrase": {
"136_facet_locations": "Basel"
}
}
]
}
}
]
I Need an Output for and querys that must look like this:
{
"should": [
{
"bool": {
"should": [
{
"match_phrase": {
"134_facet_locations": "Basel"
}
},
{
"match_phrase": {
"135_facet_locations": "Basel"
}
},
{
"match_phrase": {
"136_facet_locations": "Basel"
}
}
]
}
},
{
"bool": {
"should": [
{
"match_phrase": {
"134_facet_locations": "Deutschland"
}
},
{
"match_phrase": {
"135_facet_locations": "Deutschland"
}
},
{
"match_phrase": {
"136_facet_locations": "Deutschland"
}
}
]
}
}
]
}
I know how to add them once into an array. But i dont know how to build this complex json query in a good way.
This is my suggestions for the answer. I must check if value changed. if it changed add topics to new "bool should" array. Something like this.
Related
I have this document :
{"_id":"1", "elem":"ok",
"arrayOfObjects":[
{"type":"k","fieldx":"lol"},
{"type":"SndObject","fieldy":"foo"},
{"type":"Object1","fieldx":"bob"}
]
}
what is the aggregation to have this output:
{"_id":"1", "elem":"ok",
"Object1":[
{"type":"Object1","fieldx":"lol"},
{"type":"Object1","fieldx":"bob"}
],
"SndObject":[{"type":"SndObject","fieldy":"foo"}]
}
I found a way out, but it need me to know all the type i have:
{
"$addFields" : {
"Object1" : {
"$filter": {
"input": "$arrayOfObjects",
"as": "types",
"cond": {
"$and": [{ "$eq": [ "$$types.type", "Object1" ] }]
}
}
}
}
}
It would be best if i can loop over my arrayOfObjects and get the same result without pre knowledge of the type.
Might be there would be more easy option than this,
$unwind deconstruct arrayOfObjects array
$group by _id, type and elem, construct array of arrayOfObjects
$arrayToObject convert k and v from array to object
$group by _id and merge objects in root
db.collection.aggregate([
{ $unwind: "$arrayOfObjects" },
{
$group: {
_id: {
type: "$arrayOfObjects.type",
_id: "$_id"
},
elem: { $first: "$elem" },
arrayOfObjects: { $push: "$arrayOfObjects" }
}
},
{
$group: {
_id: "$_id._id",
elem: { $first: "$elem" },
arrayOfObjects: {
$mergeObjects: {
$arrayToObject: [[
{
k: "$_id.type",
v: "$arrayOfObjects"
}
]]
}
}
}
}
])
Playground
From this 'houses' collection
{
_id: "0",
rooms: [
{
roomName: "living-room"
chairs: "6"
},
{
roomName: "kitchen"
chairs: "0"
}
]
}
I need to find the house with _id = 0, and select only
the 'chairs' from the "living-room" so that the result looks like this:
{
chairs: 6
}
I think of something that looks like this:
House.findOne({_id: '0'}).select('rooms.chairs') // but only from {roomName: "living-room"}
How do I complete the query?
How about this one:
db.houses.aggregate([
{ $match: { _id: "0" } },
{
$project: {
chairs: {
$filter: {
input: "$rooms",
cond: { $eq: ["$$this.roomName", "living-room"] }
}
}
}
},
{
$replaceRoot: {
newRoot: {
chairs: { $arrayElemAt: ["$chairs.chairs", 0] }
}
}
},
])
db.house.findOne({"rooms.chairs":"6"},{"rooms.$":1})
I want to set the depth of JSON parsing in Express middleware express.json().
For example, if I would set the option to parse the depth=1, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: "[object Object]" }
-- or --
When I set depth=2, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: { '$ne': 'user#example.com' } }
And so on,
In this case, there will be no issue of default depth, as the developer will be aware of how many nesting they will allow while development.
PS: It will prevent the application from being vulnerable to NoSQL Injection.
Just write you own middleware:
const get_depth = (obj) => {
let depth = 0
for(const key in obj) {
if( obj[key] instanceof Object ) {
depth = Math.max(get_depth(obj[key]), depth)
}
}
return depth+1
}
const depth_limit = 2
const limit_depth = function(req, res, next) {
if( get_depth(req.body) > depth_limit ) throw new Error("Possible NoSQL Injection")
next()
}
app.use(limit_depth)
Or, if you prefer "[object Object]":
let limit_depth = (obj, current_depth, limit) => {
for(const key in obj) {
if( obj[key] instanceof Object ) {
if( current_depth+1 === limit ) {
obj[key] = "[object Object]" // or something similar
}
else limit_depth(obj[key], current_depth+1, limit)
}
}
}
app.use(function(req, res, next) { limit_depth(req.body, 0, depth_limit); next() })
I write down the query, Maximum 6-8 depth goes. when use lookup inside the lookup.
const [result] = await Collection.aggregate([
{ $match:statusObj },
{
$project:{
_id:1,
name:1
}
},
{
$lookup:{
from:"articles",
let: { "cat_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$category_id", "$$cat_id"] },
{ $eq: ["$isDeleted", false] },
{ $eq: ["$type", type] }
]
}
}
},
{
$lookup:{
from:"view_articles",
let: { "article_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$article_id", "$$article_id"] },
{ $eq: ["$isDeleted", false] }
]
}
}
}
],
as:"viewCount"
}
},
{
$addFields:{
noOfViewCount : { $size:"$viewCount"}
}
} ],
as:"articleCategoryData"
}
},
{
$addFields: {
postCount: {$size:"$articleCategoryData" },
tempsArray: { $map:
{
input: "$articleCategoryData",
as: "tempData",
in: { $add: "$$tempData.noOfViewCount" }
}
},
},
},
{
$addFields: {
viewCount:{ $sum:"$tempsArray" }
},
},
{
$project:{
_id: 1,
name: 1,
postCount: 1,
viewCount: 1
}
},
{
$facet: {
count: [
{
$count: "total"
}
],
result: [{ $match: {} }, { $skip: skipRecord }, { $limit: limit }]
}
}
]);
you can set depth to 10. If you feel JSON is coming wrong then increase it :)
In case anyone who doesn't want to change the value of req.body, can use this function from here
function serializer(payload: any, cdepth: number, options: Options): void {
const main: any = {}
const maxDepth = typeof options.maxNestingLevel == 'number' ? (options.maxNestingLevel == 0 ? 1 : options.maxNestingLevel) : 1
for (const key in payload) {
// check for object
if (payload[key] instanceof Object) {
// check if depth is limited, replace if needed
if (cdepth === maxDepth) {
main[key] = options.replaceWith
} else {
// serialize the nested
main[key] = serializer(payload[key], cdepth + 1, options)
}
} else {
// add to main object if not to be checked
main[key] = payload[key]
}
}
return main
}
I am having json template for elasticsearch query:
var getTemplate = function (agg, filter_term) {
var template = {
"size": 0,
track_total_hits: true,
query: {
bool: {
must: [
{
"query_string": {
"query": filter_term
}
},
aggs: {
"nested" : { "value_count" : { "field" : agg } },
agg: {
terms: {
field: agg,
size: 10,
order: {
"_count": "desc"
}
}
}
}
};
return template;
Sometimes, I want to skip the block with query_string so I want to pass in in the function call. So instead of this:
const callTerminated = agg_filter.getTemplate( 'bbbbb', 'aaa');
Do that:
const callTerminated = agg_filter.getTemplate( 'bbbbb', {"query_string": {"query": aaa }});
Or:
const callTerminated = agg_filter.getTemplate( 'bbbbb', "");
But how to change the template query? It wants comma after variable but when I skip query_string, I don't need comma.
New json template:
var getTemplate = function (agg, filter_term) {
var template = {
"size": 0,
track_total_hits: true,
query: {
bool: {
must: [
filter_term //here it wants comma
aggs: {
"nested" : { "value_count" : { "field" : agg } },
agg: {
terms: {
field: agg,
size: 10,
order: {
"_count": "desc"
}
}
}
}
};
return template;
So how to do it?
I am using mongoDb, restheart and also new in these technologies. I want to add one more field in aggregation. So my exist aggregation working fine. I am sharing aggregation
Aggregation:
aggrs: [{
type: "pipeline",
uri: "by_time",
stages: [{
_$match: {
bus::destination: {
_$in: {
_$var: "stands"
}
},
bus::eta: {
_$gte: {
_$var: "dateFrom"
},
_$lte: {
_$var: "dateTo"
}
}
}
},
]
}]
Json Data:
{
_id: "1938378_32323",
bus: {
valid: true,
eta: {
$date: 1500661200000
},
busDate: {
$date: 1500595200000
},
etd: {
$date: 1500661200000
},
origin: "kalu",
destination: "cell",
},
apose: false
}
Above aggregation working fine, But I want to add "apose" in aggregation. So I have implemented this code for aggregation.
stages: [{
_$match: {
bus::destination: {
_$in: {
_$var: "stands"
}
},
bus::eta: {
_$gte: {
_$var: "dateFrom"
},
_$lte: {
_$var: "dateTo"
}
},
"apose": {
"_$eq": {
"_$var": "opposit"
}
}
}
},
]
But When I hit this aggregation, I got only
{"_embedded":[],"_size":0,"_total_pages":0,"_returned":0}
My hitting url is
http:...abc.../_aggrs/by_time?avars={"stands":["KALU","MAL","SALU"],"dateFrom":{"$date":"2007-12-21T01:30:00Z"},"dateTo":{"$date":"2010-02-15T09:30:00Z"},"opposit":"false"}
your answer is valuable for me. Thanks