Getting Error: Lambda expects an array with 3 elements. Array contains 1

I am getting an error which says Lambda function got one element in args while I’m adding three. :thinking:

This is unexpected check my code:

Filter(
  Map(
    Paginate(
      Intersection(
        Match(Index("by_room_type"), "single"),
        Match(Index("by_room_size"), "big")
      )
    ),
    Lambda("ref", Get(q.Var("ref")))
  ),
  Lambda(
    ["ref", "ts", "data"],
    Equals(Select("Price", Var("data")), "£700")
  )
)

See the screenshot
image

I think the Filter lambda is expecting a Lambda with a single parameter, and not 3. It seems like the error message is written from the point of view of the Filter’s Lambda and not the Filter function, which is why it is confusing.

It’s not the definition that contains 1 element, it is what is returned from the Map function. Since you are using Map to get the Documents, it is those Documents that will be passed to the Filter.

You should be able to refactor to pass just the Document to the Filter, and use Let to take the values that you need.

Filter(
  Map(
    Paginate(
      Intersection(
        Match(Index("by_room_type"), "single"),
        Match(Index("by_room_size"), "big")
      )
    ),
    Lambda("ref", Get(q.Var("ref")))
  ),
  Lambda(
    ["doc"],
    Let(
      {
        ref: Select("ref", Var("doc")),
        ts: Select("ts", Var("doc")),
        data: Select("data", Var("doc"))
      },
      Equals(Select("Price", Var("data")), "£700")
    )
  )
)

alternatively

You could return an array of what you want from the Map

Filter(
  Map(
    Paginate(
      Intersection(
        Match(Index("by_room_type"), "single"),
        Match(Index("by_room_size"), "big")
      )
    ),
    Lambda(
      "ref", 
      Let(
        {
          doc: Get(Var("ref")),
        },
        [
          Select("ref", Var("doc")),
          Select("ts", Var("doc")),
          Select("data", Var("doc"))
        ]
      )
    )
  ),
  Lambda(
    ["ref", "ts", "data"],
    Equals(Select("Price", Var("data")), "£700")
  )
)

I’d do the filter before the paginate, so you don’t end up with variable-sized pages:

Map(
  Paginate(
    Filter(
      Intersection(
        Match(Index("by_room_type"), "single"),
        Match(Index("by_room_size"), "big")
      )
      Lambda(
        ["doc"],
        Let(
          {
            ref: Select("ref", Var("doc")),
            ts: Select("ts", Var("doc")),
            data: Select("data", Var("doc"))
          },
          Equals(Select("Price", Var("data")), "£700")
        )
      )
    )
  ),
  Lambda("ref", Get(q.Var("ref")))
)
2 Likes

Filter is indeed permitted to work directly on a Set (inside of Paginate), but please note that means it might read a lot more Documents than what is actually returned. That may be what you desire but beware of performance issues.

Also important is that you cannot Get Documents within a Filter-on-a-Set, though can filter on the Index values. So, I don’t think that Wallside’s snippet would work in this case.

Generally speaking, if you find yourself calling Filter on a Set, then you should consider if it is more appropriate to add a new Index and use Intersection. If there is more complex logic required, then you can create a binding on the Index and use that as the term.

With an additional Index, the subject query could become the following:

Map(
  Paginate(
    Intersection(
      Match(Index("by_room_type"), "single"),
      Match(Index("by_room_size"), "big"),
      Match(Index("by_room_price"), "£700")
    )
  ),
  Lambda("ref", Get(q.Var("ref")))
)

EDIT:

You can use Get inside of a Filter. However, I would recommend avoiding this, as it will cost you a Read Op for every document in the Set checked until you satisfy the size of the page requested. For example, your page size is 64, but it takes reading 1000 documents to find 64 documents that pass the filter, then you paid 1000 TROs for a page of 64 Documents.

It can also play havoc on your ABAC rules. If you expect a filter to result in Documents that a caller can read, then you may still get an unauthorized error since the filter actually reads every single document.

1 Like

I appreciate @ptpaterson and @wallslide for such nice explanation.

Actually problem is that I don’t need exact match in price. My need is to get price in a range like between 500-1000 which mean filter out rooms/flats of price less than 1000 and higher than 500. So index with exact match term will not work :neutral_face:

You should still be able to replace the Filter function with Indexes.

Searching for inequalities (>, <=, etc.) requires using Index values and the Range function.

Check out this on Stack Overflow and this other forums topic for more discussion on searching and combining searches.

If you have any more questions on using Range to finish your query, I encourage you to start a new topic.

Cheers!

@ptpaterson Can you tell if this is efficient?

 q.Filter(
     Map(
       Paginate(
         Intersection(
           Match(Index("by_room_type"), "single"),
           Match(Index("by_room_size"), "big")
         )
       ),
        q.Lambda("ref", q.Get(q.Var("ref")))
      ),
      q.Lambda(
        ["doc"],
        q.Let(
          {
            ref: q.Select("ref", q.Var("doc")),
            ts: q.Select("ts", q.Var("doc")),
            price: q.ToInteger(
              q.ReplaceStrRegex( // to remove currency sign
                q.Select(["data", "Price"], q.Var("doc")),
                "[^\\d.]",
                ""
              )
            ),
          },
          q.And(q.GT(q.Var("price"), minValue), q.LTE(q.Var("price"), maxValue))
        )
      )
    )

I tried with Range function a lot ago but intersection expects all entries of same type like it can’t be like this below:

Intersection(
   Match(Index("by_room_type"), "single"),
   Match(Index("by_room_size"), "big"),
   Range(Match(Index("by_room_price"), [0], [300])
)

That all depends on how selective the filter function is. If The Intersection returns 100 Documents, but the filter only passes 2 with the correct price, then no, it will not be efficient, and you would probably be better off using Indexes. If, on the other hand, your filter returns about the same number of Documents as the Intersection, then most of your filtering compute could be wasteful, and you might be better off sending the whole result to the client and filtering there. In the latter case, it might not be worth all of the extra Compute Operations, but then again, maybe you really need to restrict the client from observing the filtered documents, so filtering in the client is not an option.

It’s hard to say for certain what is the most optimized solution without testing on your actual data, and also knowing what your priorities are, e.g. performance vs. cost.

Generally speaking, however, the way to minimize read cost and query time is using Indexes, and then using as few Indexes as possible. Note that the more Indexes you have on a source Collection the more storage you require and the more expensive that writes get.

Map(
  Paginate(
    Intersection(
      Match(Index("by_room_type_and_size"), ["single", "big"]),
      Join(
        Range(Match(Index("by_room__price_asc")), [0], [300]),
        Lambda(["price", "ref"], Singleton(Var("ref")))
      ) 
    )
  ),
  Lambda("ref", Get(q.Var("ref")))
)

Please see the previously linked posts for how to combine Indexes with different shapes. If you have follow-up questions about that, then I suggest another Forums topic.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.