aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.43k stars 2.13k forks source link

Filters Doesn't Pass When There's Negative Numbers #13364

Closed fimbres closed 6 months ago

fimbres commented 6 months ago

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

GraphQL API

Amplify Version

v6

Amplify Categories

api

Backend

Amplify CLI

Environment information

``` System: OS: macOS 14.4.1 CPU: (10) arm64 Apple M1 Max Memory: 1.60 GB / 32.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 18.17.1 - ~/.nvm/versions/node/v18.17.1/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 9.6.7 - ~/.nvm/versions/node/v18.17.1/bin/npm pnpm: 8.7.0 - /opt/homebrew/bin/pnpm bun: 1.0.23 - ~/.bun/bin/bun Browsers: Chrome: 124.0.6367.201 Safari: 17.4.1 npmPackages: @aws-amplify/react-native: ^1.0.28 => 1.0.28 @babel/core: ^7.20.0 => 7.24.4 @expo/vector-icons: ^14.0.0 => 14.0.0 @hookform/resolvers: ^3.3.4 => 3.3.4 @hookform/resolvers/ajv: 1.0.0 @hookform/resolvers/arktype: 1.0.0 @hookform/resolvers/class-validator: 1.0.0 @hookform/resolvers/computed-types: 1.0.0 @hookform/resolvers/io-ts: 1.0.0 @hookform/resolvers/joi: 1.0.0 @hookform/resolvers/nope: 1.0.0 @hookform/resolvers/superstruct: 1.0.0 @hookform/resolvers/typanion: 1.0.0 @hookform/resolvers/typebox: 1.0.0 @hookform/resolvers/valibot: 1.0.0 @hookform/resolvers/vest: 1.0.0 @hookform/resolvers/yup: 1.0.0 @hookform/resolvers/zod: 1.0.0 @react-native-async-storage/async-storage: 1.21.0 => 1.21.0 @react-native-community/datetimepicker: 7.6.1 => 7.6.1 @react-native-community/netinfo: 11.1.0 => 11.1.0 @react-native-picker/picker: 2.6.1 => 2.6.1 @react-navigation/native: ^6.0.2 => 6.1.17 @types/numeral: ^2.0.5 => 2.0.5 @types/react: ~18.2.45 => 18.2.79 HelloWorld: 0.0.1 aws-amplify: ^6.0.30 => 6.0.30 aws-amplify/adapter-core: undefined () aws-amplify/analytics: undefined () aws-amplify/analytics/kinesis: undefined () aws-amplify/analytics/kinesis-firehose: undefined () aws-amplify/analytics/personalize: undefined () aws-amplify/analytics/pinpoint: undefined () aws-amplify/api: undefined () aws-amplify/api/server: undefined () aws-amplify/auth: undefined () aws-amplify/auth/cognito: undefined () aws-amplify/auth/cognito/server: undefined () aws-amplify/auth/enable-oauth-listener: undefined () aws-amplify/auth/server: undefined () aws-amplify/data: undefined () aws-amplify/data/server: undefined () aws-amplify/datastore: undefined () aws-amplify/in-app-messaging: undefined () aws-amplify/in-app-messaging/pinpoint: undefined () aws-amplify/push-notifications: undefined () aws-amplify/push-notifications/pinpoint: undefined () aws-amplify/storage: undefined () aws-amplify/storage/s3: undefined () aws-amplify/storage/s3/server: undefined () aws-amplify/storage/server: undefined () aws-amplify/utils: undefined () expo: ~50.0.14 => 50.0.17 expo-dev-client: ~3.3.11 => 3.3.11 expo-font: ~11.10.3 => 11.10.3 expo-image: ~1.10.6 => 1.10.6 expo-image-picker: ~14.7.1 => 14.7.1 expo-linear-gradient: ~12.7.2 => 12.7.2 expo-linking: ~6.2.2 => 6.2.2 expo-location: ~16.5.5 => 16.5.5 expo-router: ~3.4.8 => 3.4.8 expo-splash-screen: ~0.26.4 => 0.26.4 expo-status-bar: ~1.11.1 => 1.11.1 expo-system-ui: ~2.9.3 => 2.9.4 expo-web-browser: ~12.8.2 => 12.8.2 lucide-react-native: ^0.372.0 => 0.372.0 moment: ^2.30.1 => 2.30.1 numeral: ^2.0.6 => 2.0.6 react: 18.2.0 => 18.2.0 react-dom: 18.2.0 => 18.2.0 react-hook-form: ^7.51.3 => 7.51.3 react-native: 0.73.6 => 0.73.6 react-native-gesture-handler: ~2.14.1 => 2.14.1 react-native-get-random-values: ~1.8.0 => 1.8.0 react-native-google-places-autocomplete: ^2.5.6 => 2.5.6 react-native-mask-input: ^1.2.3 => 1.2.3 react-native-otp-entry: ^1.6.1 => 1.6.1 react-native-reanimated: ~3.6.2 => 3.6.3 react-native-reanimated-carousel: ^3.5.1 => 3.5.1 react-native-safe-area-context: 4.8.2 => 4.8.2 react-native-screens: ~3.29.0 => 3.29.0 react-native-svg: 14.1.0 => 14.1.0 react-native-svg-transformer: ^1.3.0 => 1.3.0 react-native-web: ~0.19.6 => 0.19.10 react-native-woodpicker: ^0.3.17 => 0.3.17 typescript: ^5.1.3 => 5.4.5 zod: ^3.23.4 => 3.23.4 zustand: ^4.5.2 => 4.5.2 npmGlobalPackages: corepack: 0.18.0 npm: 9.6.7 ```

Describe the bug

I'm trying to get all the records in my db filtering by lat and lng, I have a range for each field (lat, lng)

for lat it's all good because I don't have negative numbers. but for lng it's not passing.

In the following example you can see this is correct: -172.9272442976304 <= -116.604305 && -116.604305 <= -60.28136570236959. But for the amplify filters it's not correct.

This is my query statement:

const { data } = await client.graphql({
        query: listAffiliates,
        variables: {
          filter: {
            and: [
              {
                lat: {
                  between: [
                    minLat.toString(),
                    maxLat.toString(),
                  ]
                }
              },
              {
                lng: {
                  between: [
                    minLng.toString(),
                    maxLng.toString(),
                  ]
                }
              },
            ]
          }
        }
      });

it does not return any data when it should. Also if the minLng is bigger than the second one I get this error:

"data": {"listAffiliates": null}, "errors": [{"data": null, "errorInfo": null, "errorType": "DynamoDB:DynamoDbException", "locations": [Array], "message": "Invalid FilterExpression: The BETWEEN operator requires upper bound to be greater than or equal to lower bound; lower bound operand: AttributeValue: {S:-99}, upper bound operand: AttributeValue: {S:-172.9272442976304} (Service: DynamoDb, Status Code: 400, Request ID: GM23LH500RQJS300U71GAKBUNFVV4KQNSO5AEMVJF66Q9ASUAAJG)", "path": [Array]}]}

for example: -90 and -120 triggers the error, but -120, and -90 doesn't. It's kinda like if the values are parsed to positive to make the comparison or something like that. Because -90 is bigger than -120.

So I'm just wondering if there's any solution for this, in order to filter using negative values as lat, lng.

Expected behavior

Filter the records in my db in order to get the nearest to the user.

Reproduction steps

  1. initialize a new amplify project.
  2. create a model with a Int field.
  3. create a record for this model and enter a negative number in this numeric field
  4. try to filter this record by its numeric field using a couple negative values.
  5. face the error.

Code Snippet

// Put your code below this line.
const { data } = await client.graphql({
        query: listAffiliates,
        variables: {
          filter: {
            and: [
              {
                lat: {
                  between: [
                    minLat.toString(),
                    maxLat.toString(),
                  ]
                }
              },
              {
                lng: {
                  between: [
                    minLng.toString(),
                    maxLng.toString(),
                  ]
                }
              },
            ]
          }
        }
      });
GraphQL model
type Affiliate @model @searchable {
  id: ID!
  name: String!
  lat: String!
  lng: String!
}

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

Iphone 15 pro Simulator

Mobile Operating System

IOS 17.4

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

ChristopherGabba commented 6 months ago

@fimbres

I think it's because you defined Lat and Long as a String in your schema.

You need to use Float or Int for the filter to be able to work.


GraphQL model
type Affiliate @model @searchable {
  id: ID!
  name: String!
  lat: Float!
  lng: Float!
}
fimbres commented 6 months ago

Hey @ChristopherGabba lol you were right. I thought it was already a Float. But now after migrating it seems that any Float comparison is not returning data at all. I did the migration and in my db all Affiliate's records have lat & lng of type number. But even if I do something like this:

lat: {
    eq: 31.8754079,
},

it's not returning any data. using eq with the name field (String) works tho. any idea on this one?

ChristopherGabba commented 6 months ago

Disclaimer, I'm some random struggling with DynamoDB as well.

Without seeing your data, and knowing what's in there, the filter line just filters the first set of results after they have been fetched. I've struggled a lot here, but I think you may need to set up secondaryIndex queries for your data with sortKeys, instead of using the filter expression.

For example, lets say I just use a basic list query like you have used to find all people with the first name John.

If I have 500 users registered, and I set my query limit to 100, it's going to read through the first 100 people in the database and completely ignore the next 400 unless I provide a nextToken. That's called a scan query in dynamoDB vs. a query operation, and its basically worthless for most applications. I honestly don't know why they even structure that option. Instead you assign the firstName as a secondaryIndex and it actually queries all your data for people with a first name as John.

That being said, using your filter expressions like you did above is going to get the first say 100 affiliates in your database and then filter them by your data, because you are using the scan method. Also using the eq flag is only going to get fields with that exact number. As an experiment to test your query, make the limit on your query above to like 1000000000 and see if you get results, but don't use this in production because it will be expensive. You need to set up secondaryIndexes.

chrisbonifacio commented 6 months ago

I'm going to close this issue as the original issue has been solved thanks to @ChristopherGabba, but I would recommend opening a new one regarding the issue of no data being returned to track it separately as a potential bug and with details for the team to reproduce