deitch/searchjs

Multiple conditions in nested objects in an array

risinek opened this issue · 3 comments

Lets assume the JSON object {cars: [{brand: 'porsche',hp:450},{brand: 'bmw',hp:250},{brand: 'lada',hp:10}]}
And now I want to check if there is a BMW with more then 300hp.
If I run the query {cars.brand: 'bmw', cars.hp: {gt: 300} I expect to get false as there is no BMW with over 300 hp, but I actually get true. I assume it's because it first matches the object {brand: 'bmw',hp:250} by the brand and then {brand: 'porsche',hp:450} by the condition on hp field -> hence both conditions are met -> true

Is it possible to search by multiple conditions for nested object in current implementation?

I think it is similar question to #54 and even maybe #3

deitch commented

It should work, I am surprised it does not. If you feel like stepping through it to figure out why and then propose a PR, that would be good.

So I checked the code and it confirms my assumption. Lets assume the input query and recapitulate the scenario

{cars.brand: 'bmw', cars.hp: 450}
  • Expected result: false
    • there is no object satisfying both conditions.
  • Actual result: true
    • {brand: 'porsche',hp:450} matches hp:450
    • {brand: 'bmw',hp:250} matches brand: 'bmw'

The reason for this "unexpected" behavior lies in the way the search terms are resolved. Every search term is resolved in isolation.

for(i in search) {

My proposal is to allow queries like this

{"cars": {brand: 'bmw', hp: 450}}

Then in the singleMatch() the returned object for cars property would be array of objects

[{brand: 'porsche',hp:450},{brand: 'bmw',hp:250},{brand: 'lada',hp:10}]

which is then recursively resolved in this block

searchjs/src/searchjs.js

Lines 78 to 85 in f612eca

} else if (field.length !== undefined) {
// array, so go through each
for (j=0;j<field.length;j++) {
oneMatch = singleMatch(field[j],s,text,word,regexp,start,end);
if (oneMatch) {
break;
}
}

which will resolve every object again by calling singleMatch() for each object.
Now the problem is, that {brand: 'porsche',hp:450} hence typeof object and hence satisfying this if block

searchjs/src/searchjs.js

Lines 86 to 88 in f612eca

} else if (t === "object") {
oneMatch = field[s] !== undefined;
}

Here it would be possible to call matchObject() on this object and let it resolve.

I'm writing you this as an headsup while coding the proposed solution. If you can see any obstacles, please let me know.

deitch commented

Makes sense. Given the doc, it should have done that already, I think.