Uploaded image for project: 'Geode'
  1. Geode
  2. GEODE-9174

The result of a gfsh query containing a UUID may not be displayed properly

    XMLWordPrintableJSON

Details

    Description

      For example, if the key is a UUID, then a query like this won't show the results even though there is one:

      gfsh>query --query="select key from /data.entries where value.id = '55e907b6-a1fe-42ea-90a2-6a5698e9b27c'"
      Result : true
      Limit  : 100
      Rows   : 1
      

      But a query like this will:

      gfsh>query --query="select key,value from /data.entries where value.id = '55e907b6-a1fe-42ea-90a2-6a5698e9b27c'"
      Result : true
      Limit  : 100
      Rows   : 1
      
                       key                   | value
      -------------------------------------- | ---------------------------------------------------------------------------------------
      "55e907b6-a1fe-42ea-90a2-6a5698e9b27c" | {"id":"55e907b6-a1fe-42ea-90a2-6a5698e9b27c","cusip":"AAPL","shares":22,"price":352.32}
      

      Thats because of the way DataCommandResult.resolveObjectToColumns works.

      private void resolveObjectToColumns(Map<String, String> columnData, Object value) {
        if (value instanceof PdxInstance) {
          resolvePdxToColumns(columnData, (PdxInstance) value);
        } else if (value instanceof Struct) {
          resolveStructToColumns(columnData, (StructImpl) value);
        } else {
          ObjectMapper mapper = new ObjectMapper();
          JsonNode node = mapper.valueToTree(value);
          node.fieldNames().forEachRemaining(field -> {
            ...
            columnData.put(field, mapper.writeValueAsString(node.get(field)));
          });
        }
      }
      

      The value in the first query is a UUID so the last else clause is invoked. In this case, a JsonNode is used to determine the columns. ObjectMapper.valueToTree converts a UUID to a TextNode. TextNodes have no fieldNames, and JsonNode.fieldNames returns an EmptyIterator by default:

      public Iterator<String> fieldNames() {
        return ClassUtil.emptyIterator();
      }
      

      So, resolveObjectToColumns doesn't fill in columnData, which causes the DataCommandResult.buildTable in the locator to not add any rows to the table.

      The value in the second query is a Struct so the second else clause is invoked. The resolveStructToColumns method does:

      private void resolveStructToColumns(Map<String, String> columnData, StructImpl struct) {
        for (String field : struct.getFieldNames()) {
          columnData.put(field, valueToJson(struct.get(field)));
        }
      }
      

      I'm not sure if there is a way to make ObjectMapper.valueToTree handle UUIDs differently, but they can easily be special-cased like PdxInstances and Structs:

      } else if (value instanceof UUID) {
        columnData.put("uuid", valueToJson(value));
      

      I'm not sure if this is the best solution, but it works. With this clause added, the query does:

      gfsh>query --query="select key from /data.entries where value.id = '55e907b6-a1fe-42ea-90a2-6a5698e9b27c'"
      Result : true
      Limit  : 100
      Rows   : 1
      
      uuid
      --------------------------------------
      "55e907b6-a1fe-42ea-90a2-6a5698e9b27c"
      

      Attachments

        Issue Links

          Activity

            People

              mkevo Mario Kevo
              boglesby Barrett Oglesby
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: