firebase-tools icon indicating copy to clipboard operation
firebase-tools copied to clipboard

[firestore-emulator: datastore-mode] Unexpected response from query filter on nested property in array

Open boris-hocde opened this issue 1 year ago • 1 comments

[REQUIRED] Environment info

gcloud: v493.0.0

Platform: Ubuntu

[REQUIRED] Test case

package.json

{
  "version": "1.0.0",
  "description": "",
  "main": "lib/index.js",
  "scripts": {
    "build": "tsc",
    "start": "DATASTORE_EMULATOR_HOST=127.0.0.1:8080 node lib/index.js",
    "start:emulator:old": "gcloud --project=dummy-project-id beta emulators datastore start --use-firestore-in-datastore-mode --host-port=127.0.0.1:8080",
    "start:emulator:new": "gcloud emulators firestore start --database-mode=datastore-mode --host-port=127.0.0.1:8080"
  },
  "devDependencies": {
    "@google-cloud/datastore": "^9.1.0",
    "typescript": "^5.4.3"
  }
}

src/index.ts

import {and, Datastore, PropertyFilter} from "@google-cloud/datastore";

const datastore = new Datastore({
  projectId: "dummy-project-id",
});

const KIND_USER = 'User'

type User = {
  email: string
  fields : {
    key: string,
    value: string,
  }[]
}

export async function getUsersByName(name: string) {
  const query = datastore.createQuery(KIND_USER).filter(and([
    new PropertyFilter('fields.key','=','name'),
    new PropertyFilter('fields.value','=',name),
  ]));
  const [entities] = await datastore.runQuery(query);
  return entities
}

export async function addUser(data: User) {
  const key = datastore.key(KIND_USER);
  const entity = {
    key: key,
    data,
  };
  await datastore.save(entity);
}

async function main() {
  await addUser({email: "[email protected]", fields: [{key: "name", value: "Awesome Name"}]})

  const users = await getUsersByName("Awesome Name");
  console.log(users);
}

main();

tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "lib"
  },
  "include": ["src"],
  "exclude": []
}

[REQUIRED] Steps to reproduce

Datastore Emulator (Old)

npm install
npm run start:emulator:old
$ npm run build && npm run start

> build
> tsc

> start
> DATASTORE_EMULATOR_HOST=127.0.0.1:8080 node lib/index.js

[
  {
    fields: [ [Object] ],
    email: '[email protected]',
    [Symbol(KEY)]: Key {
      namespace: undefined,
      id: '1',
      kind: 'User',
      path: [Getter]
    }
  },
]

Firestore Emulator (New, Preview)

npm install
npm run start:emulator:new
$ npm run build && npm run start

> build
> tsc

> start
> DATASTORE_EMULATOR_HOST=127.0.0.1:8080 node lib/index.js

[]

[REQUIRED] Expected behavior

[
  {
    fields: [ [Object] ],
    email: '[email protected]',
    [Symbol(KEY)]: Key {
      namespace: undefined,
      id: '1',
      kind: 'User',
      path: [Getter]
    }
  },
]

[REQUIRED] Actual behavior

The result filtered by fields.name and fields.value should include the user, but not when using the new emulator.

[]

This issue slightly differs from https://github.com/firebase/firebase-tools/issues/6999, as the fields property is an array here.

boris-hocde avatar Sep 18 '24 16:09 boris-hocde

Hey @boris-hocde, thanks for creating a detailed report! I'm able to reproduce the issue using the code snippets and steps provided. I’ll mark this as reproducible and raise this to our engineering team.

aalej avatar Sep 20 '24 12:09 aalej