예제 #1
0
  /**
   * Add a single, possibly compound index for the given field names and ensure all indexing
   * constraints are met.
   *
   * <p>This function generates a name for the new index.
   *
   * @param index The object that defines an index. Includes field list, name, type and options.
   * @return name of created index
   */
  @SuppressWarnings("unchecked")
  private String ensureIndexed(final Index index) {
    if (index == null) {
      return null;
    }

    if (index.indexType.equalsIgnoreCase("text")) {
      if (!IndexManager.ftsAvailable(queue, database)) {
        logger.log(
            Level.SEVERE,
            "Text search not supported.  To add support for text "
                + "search, enable FTS compile options in SQLite.");
        return null;
      }
    }

    final List<String> fieldNamesList = removeDirectionsFromFields(index.fieldNames);

    for (String fieldName : fieldNamesList) {
      if (!validFieldName(fieldName)) {
        // Logging handled in validFieldName
        return null;
      }
    }

    // Check there are no duplicate field names in the array
    Set<String> uniqueNames = new HashSet<String>(fieldNamesList);
    if (uniqueNames.size() != fieldNamesList.size()) {
      String msg =
          String.format("Cannot create index with duplicated field names %s", index.fieldNames);
      logger.log(Level.SEVERE, msg);
    }

    // Prepend _id and _rev if it's not in the array
    if (!fieldNamesList.contains("_rev")) {
      fieldNamesList.add(0, "_rev");
    }

    if (!fieldNamesList.contains("_id")) {
      fieldNamesList.add(0, "_id");
    }

    // Check the index limit.  Limit is 1 for "text" indexes and unlimited for "json" indexes.
    // Then check whether the index already exists; return success if it does and is same,
    // else fail.
    try {
      Map<String, Object> existingIndexes = listIndexesInDatabaseQueue();
      if (indexLimitReached(index, existingIndexes)) {
        String msg =
            String.format("Index limit reached.  Cannot create index %s.", index.indexName);
        logger.log(Level.SEVERE, msg);
        return null;
      }
      if (existingIndexes != null && existingIndexes.get(index.indexName) != null) {
        Map<String, Object> existingIndex =
            (Map<String, Object>) existingIndexes.get(index.indexName);
        String existingType = (String) existingIndex.get("type");
        String existingSettings = (String) existingIndex.get("settings");
        List<String> existingFieldsList = (List<String>) existingIndex.get("fields");
        Set<String> existingFields = new HashSet<String>(existingFieldsList);
        Set<String> newFields = new HashSet<String>(fieldNamesList);
        if (existingFields.equals(newFields)
            && index.compareIndexTypeTo(existingType, existingSettings)) {
          boolean success =
              IndexUpdater.updateIndex(index.indexName, fieldNamesList, database, datastore, queue);
          return success ? index.indexName : null;
        }
      }
    } catch (ExecutionException e) {
      logger.log(Level.SEVERE, "Execution error encountered:", e);
      return null;
    } catch (InterruptedException e) {
      logger.log(Level.SEVERE, "Execution interrupted error encountered:", e);
      return null;
    }

    Future<Boolean> result =
        queue.submit(
            new Callable<Boolean>() {
              @Override
              public Boolean call() {
                Boolean transactionSuccess = true;
                database.beginTransaction();

                // Insert metadata table entries
                for (String fieldName : fieldNamesList) {
                  ContentValues parameters = new ContentValues();
                  parameters.put("index_name", index.indexName);
                  parameters.put("index_type", index.indexType);
                  parameters.put("index_settings", index.settingsAsJSON());
                  parameters.put("field_name", fieldName);
                  parameters.put("last_sequence", 0);
                  long rowId = database.insert(IndexManager.INDEX_METADATA_TABLE_NAME, parameters);
                  if (rowId < 0) {
                    transactionSuccess = false;
                    break;
                  }
                }

                // Create SQLite data structures to support the index
                // For JSON index type create a SQLite table and a SQLite index
                // For TEXT index type create a SQLite virtual table
                List<String> columnList = new ArrayList<String>();
                for (String field : fieldNamesList) {
                  columnList.add("\"" + field + "\"");
                }

                List<String> statements = new ArrayList<String>();
                if (index.indexType.equalsIgnoreCase(Index.TEXT_TYPE)) {
                  List<String> settingsList = new ArrayList<String>();
                  // Add text settings
                  for (String key : index.indexSettings.keySet()) {
                    settingsList.add(String.format("%s=%s", key, index.indexSettings.get(key)));
                  }
                  statements.add(
                      createVirtualTableStatementForIndex(
                          index.indexName, columnList, settingsList));
                } else {
                  statements.add(createIndexTableStatementForIndex(index.indexName, columnList));
                  statements.add(createIndexIndexStatementForIndex(index.indexName, columnList));
                }
                for (String statement : statements) {
                  try {
                    database.execSQL(statement);
                  } catch (SQLException e) {
                    String msg = String.format("Index creation error occurred (%s):", statement);
                    logger.log(Level.SEVERE, msg, e);
                    transactionSuccess = false;
                    break;
                  }
                }

                if (transactionSuccess) {
                  database.setTransactionSuccessful();
                }
                database.endTransaction();

                return transactionSuccess;
              }
            });

    // Update the new index if it's been created
    boolean success;
    try {
      success = result.get();
    } catch (ExecutionException e) {
      logger.log(Level.SEVERE, "Execution error encountered:", e);
      return null;
    } catch (InterruptedException e) {
      logger.log(Level.SEVERE, "Execution interrupted error encountered:", e);
      return null;
    }

    if (success) {
      success =
          IndexUpdater.updateIndex(index.indexName, fieldNamesList, database, datastore, queue);
    }

    return success ? index.indexName : null;
  }